Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Netduino Iambic Morse Code Keyer


  • Please log in to reply
3 replies to this topic

#1 quadral

quadral

    New Member

  • Members
  • Pip
  • 1 posts
  • LocationGermany

Posted 13 December 2010 - 09:44 PM

This is an iambic Morse code keyer. A dual lever paddle, a piezo buzzer and a potentiometer are connected to the Netduino. For details see attached file. The dual lever paddle consists of two separately actuated switches, one for the dits and one for the dahs. The speed of the keyer (5..50 wpm) is adjusted by the potentiometer. The Morse characters are decoded and printed on the debug console. The keyer supports iambic mode A and B. You have to change a constant in the code to select the iambic mode.

Have fun!

using System;
using System.Threading;
using System.Collections;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;

namespace iambic_keyer
{
    public class Program
    {
        static OutputPort pin_led = new OutputPort(Pins.ONBOARD_LED, false);
        static PWM pin_buzzer = new PWM(Pins.GPIO_PIN_D10);
        static InputPort pin_left = new InputPort(Pins.GPIO_PIN_D11, false, Port.ResistorMode.PullUp);
        static InputPort pin_right = new InputPort(Pins.GPIO_PIN_D12, false, Port.ResistorMode.PullUp);
        static AnalogInput pin_speed = new AnalogInput(Pins.GPIO_PIN_A0); 

        static bool both_paddles_old;
        static bool both_paddles_released;

        enum TIMER_STATE
        {
            IDLE1, IDLE2, IDLE3, ON1, ON2, OFF
        };
        static TIMER_STATE timer_state;

        static long dit_time;
        static long duration;

        enum KEYER_MODE
        {
            IAMBIC_A, IAMBIC_B
        };
        static KEYER_MODE keyer_mode;

        static long element_stop_time;
        static long char_stop_time;
        static long word_stop_time;

        static uint buzzer_period;

        static int mchar;
        static int mchar_elements;

        static Hashtable mcode_tab = new Hashtable();


        
        public static void Main()
        {
            setup();

            while (true)
            {
                loop();
            }
        }



        static void setup()
        {

            mcode_tab[0x0201] = "A";  // 01
            mcode_tab[0x0408] = "B";  // 1000
            mcode_tab[0x040A] = "C";  // 1010
            mcode_tab[0x0304] = "D";  // 100
            mcode_tab[0x0100] = "E";  // 0
            mcode_tab[0x0402] = "F";  // 0010
            mcode_tab[0x0306] = "G";  // 110
            mcode_tab[0x0400] = "H";  // 0000
            mcode_tab[0x0200] = "I";  // 00
            mcode_tab[0x0407] = "J";  // 0111
            mcode_tab[0x0305] = "K";  // 101
            mcode_tab[0x0404] = "L";  // 0100
            mcode_tab[0x0203] = "M";  // 11
            mcode_tab[0x0202] = "N";  // 10
            mcode_tab[0x0307] = "O";  // 111
            mcode_tab[0x0406] = "P";  // 0110
            mcode_tab[0x040D] = "Q";  // 1101
            mcode_tab[0x0302] = "R";  // 010
            mcode_tab[0x0300] = "S";  // 000
            mcode_tab[0x0101] = "T";  // 1 
            mcode_tab[0x0301] = "U";  // 001
            mcode_tab[0x0401] = "V";  // 0001 
            mcode_tab[0x0303] = "W";  // 011 
            mcode_tab[0x0409] = "X";  // 1001 
            mcode_tab[0x040B] = "Y";  // 1011 
            mcode_tab[0x040C] = "Z";  // 1100 

            mcode_tab[0x050F] = "1";  // 01111 
            mcode_tab[0x0507] = "2";  // 00111
            mcode_tab[0x0503] = "3";  // 00011 
            mcode_tab[0x0501] = "4";  // 00001 
            mcode_tab[0x0500] = "5";  // 00000 
            mcode_tab[0x0510] = "6";  // 10000 
            mcode_tab[0x0518] = "7";  // 11000 
            mcode_tab[0x051C] = "8";  // 11100 
            mcode_tab[0x051E] = "9";  // 11110
            mcode_tab[0x051F] = "0";  // 11111 

            mcode_tab[0x0511] = "=";  // 10001
            mcode_tab[0x0615] = ".";  // 010101
            mcode_tab[0x0605] = "SK"; // 000101
            mcode_tab[0x0516] = "KN"; // 10110
            mcode_tab[0x0512] = "/";  // 10010
            mcode_tab[0x060C] = "?";  // 001100

            both_paddles_old = false;
            both_paddles_released = false;
            timer_state = TIMER_STATE.IDLE3;
            duration = 1; // 1 = dit, 3 = dah

            mchar = 0;
            mchar_elements = 0;

            pin_speed.SetRange(5, 50);  // wpm: 5 .. 50

            buzzer_period = 1000000 / 800; // buzzer frequency: 800 Hz

//            keyer_mode = KEYER_MODE.IAMBIC_A;
            keyer_mode = KEYER_MODE.IAMBIC_B;

        }




        static void loop()
        {
            int wpm = pin_speed.Read();
            bool right_paddle = !pin_right.Read();
            bool left_paddle = !pin_left.Read();
            long milliseconds = Utility.GetMachineTime().Ticks / System.TimeSpan.TicksPerMillisecond;

            dit_time = 1022 / wpm; // dit duration in milliseconds

            bool both_paddles = left_paddle && right_paddle;
            both_paddles_released = both_paddles_released || (both_paddles_old && !both_paddles);
            both_paddles_old = both_paddles;

            switch (timer_state)
            {
                case TIMER_STATE.IDLE1:
                    if (milliseconds >= char_stop_time)
                    {
                        int mcode;
                        mcode = mchar_elements << 8 | mchar;
                        object x = mcode_tab[mcode];
                        if (x != null)
                        {
                            Debug.Print(x.ToString());
                        }
                        mchar_elements = 0;
                        mchar = 0;
                        timer_state = TIMER_STATE.IDLE2;
                    }
                    goto ts_idle3;

                case TIMER_STATE.IDLE2:
                    if (milliseconds >= word_stop_time)
                    {
                        Debug.Print(" ");
                        timer_state = TIMER_STATE.IDLE3;
                    }
                    goto ts_idle3;

                case TIMER_STATE.IDLE3:
                ts_idle3:
                    if (keyer_mode == KEYER_MODE.IAMBIC_B && both_paddles_released)
                    {
                        both_paddles_released = false;
                        duration = duration == 1 ? 3 : 1;
                        timer_state = TIMER_STATE.ON1;
                    }
                    else if (left_paddle && !right_paddle)
                    {
                        duration = 1; // dit
                        timer_state = TIMER_STATE.ON1;
                    }
                    else if (!left_paddle && right_paddle)
                    {
                        duration = 3; // dah
                        timer_state = TIMER_STATE.ON1;
                    }
                    else if (left_paddle && right_paddle)
                    {
                        duration = duration == 1 ? 3 : 1;
                        timer_state = TIMER_STATE.ON1;
                    }
                    break;

                case TIMER_STATE.ON1:
                    element_stop_time = milliseconds + dit_time * duration; // dit or dah
                    pin_buzzer.SetPulse(buzzer_period, buzzer_period / 2); // Buzzer on
                    pin_led.Write(true); // LED on
                    timer_state = TIMER_STATE.ON2;
                    break;

                case TIMER_STATE.ON2:
                    if (milliseconds >= element_stop_time)
                    {
                        element_stop_time = milliseconds + dit_time; // space after dit or dah
                        pin_buzzer.SetPulse(0, 0); // Buzzer off
                        pin_led.Write(false); // LED off
                        timer_state = TIMER_STATE.OFF;
                    }
                    break;

                case TIMER_STATE.OFF:
                    if (milliseconds >= element_stop_time)
                    {
                        if (mchar_elements < 8)
                        {
                            switch (duration)
                            {
                                case 1:
                                    mchar = mchar << 1;  // dit = 0
                                    mchar_elements++;
                                    break;
                                case 3:
                                    mchar = (mchar << 1) + 1;  // dah = 1
                                    mchar_elements++;
                                    break;
                            }
                        }
                        char_stop_time = milliseconds + dit_time * 3; // gap between letters
                        word_stop_time = milliseconds + dit_time * 7; // gap between words
                        timer_state = TIMER_STATE.IDLE1;
                    }
                    break;

            }

        }

    }
}




Attached Files



#2 Jim Davies

Jim Davies

    Advanced Member

  • Members
  • PipPipPip
  • 48 posts
  • LocationBrighton & Hove, UK

Posted 18 December 2010 - 06:23 PM

Nice - thanks for posting it. No replies - not many Morse Code users out there, I guess! When I was an active 'G8', I used 'phone' myself - one day I'll get back to amateur radio... Jim

#3 Bob W.

Bob W.

    New Member

  • Members
  • Pip
  • 2 posts

Posted 30 December 2010 - 02:05 PM

Thanks for posting this, I am new to this Netduino, C# and .net stuff. In fact I just recieved my Netduino today and all ready have it sending (blinking) CQ cq cq de ne7ne ne7ne k. I don't fully yet understand what all you are doing and just figured out how your hash table works, I haven't figured out your timeing yet but like I said, I am still a newbee at this. I have some experince with assembly level (ie 8051,Z80, ect)old basic and turbobasic and a little pascal is the highest I had ever gotten in programing. So far I am likeing C# and as I play with it sort of starting to get the hang of it. Anyway one of my projects is building a 4 button contest keyer with canned messages. with the 4 or 5 buttons and useing hex numbers will have any one of 15 messages that could be sent. I hope to try and use your speed control into the program. I am still working on how i can use interupts frome the buttons for selecting corisponding messages. On another project, I am in the process of duplacating an old 8051 project I built several years ago. The problem I have is how to output a byte to a 4 pin port. In assy I could send a byte to a port address and output the whole byte, But the only way I see right now is to take the byte and do 4 right bit shifts, each shift send the lsb to each of the corisponding output pins. My other idea was to output on 1 pin to a 8 bit shift register and then output the 4 bit byte from the external shift register. I see tho that I will still need 4 or 5 pins with a clock pulse pin, a data pin. a chip select pin, a clear register pin and a r/w pin to the shift register. Each of the 4 output lines will key optoisolaters that key triacs that switch 4 120 volt outlets. Any better Ideas on other ways to send out a 4 bit byte on 4 pins would be of great help. Thanks and 73's Bob W. NE7NE Thanks again I like your project tho my IC706 MK llg all ready has an iambic keyer built in. Now for that memory keyer.

#4 HamGeek

HamGeek

    New Member

  • Members
  • Pip
  • 1 posts

Posted 20 January 2011 - 07:32 PM

I just stumbled upon this project. While I don't really need a keyer for the radio, I'm thinking this project might be a good starting point for a project I have in mind. I'm thinking it might be entertaining to use CW to send status updates to Twitter via CW. I saw another project using an Arduino as well as a Processing script running on a PC to accomplish it. I think they did it that way because the Arduino lacked the horse power and/or memory to handle decoding the CW and sending the status update. I'm thinking maybe the Netduino might perform better in that respect and using the Netduino+, or regular Netduino with an ethernet shield, it might not even need a computer beyond uploading the program and configuring the Twitter login/password.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.