Arduino Iambic Keyer and Side Tone Generator

The latest issue of QEX (March/April) came this week with an article about an open-source keyer (pages 25-31).   This article referenced an earlier QEX article from Sept/Oct 2009 using an Arduino.    Since I have a Bencher paddle lying around, but no practice oscillator, I decided to investigate a bit further.   An Iambic keyer needs logic for creating its output, so the Arduino seemed like a shoe-in, especially since they’re widely available, even through Radio Shack (SparkFun, and AdaFruit are other good suppliers).

Finding back-issue articles of QEX turned out to be nigh impossible, so a few quick searches on the ‘net  turned up programs that could be easily adapted.   The first was a tone generation tutorial.   The next was a simple, but functional program that signalled through the LED.  I trimmed the fluff from this program and added tonal output (source code below). Here’s what the basic parts look like:

Keyer components

Plus the paddle (of course).  The two 4.7K resistors on the 1/8″ jack are pull-up resistors and the paddle “shorts” them to ground when contact is made.  Note the 100 ohm resistor in series with the speaker.   Some folks have used speakers from “singing” greeting cards. Wired-up, the completed simple keyer looks like:

wired keyer

The speaker is wired between digital pin 4 and ground.  The paddle wires connect to pins 2 and 5.  The red and black wires to the 1/8″ connector (resistors, barrel) are +5V and ground (respectively).

The code to run is as follows (download the Arduino software here):

///////////////////////////////////////////////////////////////////////////////
//
//  Iambic Morse Code Keyer Sketch
//  Copyright (c) 2009 Steven T. Elliott
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details:
//
//  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
//  Boston, MA  02111-1307  USA
//
//  http://openqrp.org/?p=343
//
//  "Trimmed" by Bill Bishop - wrb[at]wrbishop.com
//
///////////////////////////////////////////////////////////////////////////////
//
//                         openQRP CPU Pin Definitions
//
///////////////////////////////////////////////////////////////////////////////
//
// Digital Pins
//
int         tonePin  = 4;       // Tone output pin
int         LPin     = 5;       // Left paddle input
int         RPin     = 2;       // Right paddle input
int         ledPin   = 13;      //
//
////////////////////////////////////////////////////////////////////////////////
//
//  keyerControl bit definitions
//
#define     DIT_L      0x01     // Dit latch
#define     DAH_L      0x02     // Dah latch
#define     DIT_PROC   0x04     // Dit is being processed
#define     PDLSWAP    0x08     // 0 for normal, 1 for swap
#define     IAMBICB    0x10     // 0 for Iambic A, 1 for Iambic B
//
////////////////////////////////////////////////////////////////////////////////
//
//  Library Instantiations
//
////////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////
//
//  Global Variables
//

unsigned long       ditTime;                    // No. milliseconds per dit
unsigned char       keyerControl;
unsigned char       keyerState;

#define NOTE_D5  587	// "pitch.h" at http://arduino.cc/en/Tutorial/Tone

///////////////////////////////////////////////////////////////////////////////
//
//  State Machine Defines

enum KSTYPE {IDLE, CHK_DIT, CHK_DAH, KEYED_PREP, KEYED, INTER_ELEMENT };

///////////////////////////////////////////////////////////////////////////////
//
//  System Initialization
//
///////////////////////////////////////////////////////////////////////////////

void setup() {

    // Setup outputs
    pinMode(ledPin, OUTPUT);      // sets the digital pin as output

    // Setup control input pins
    pinMode(LPin, INPUT);        // sets Left Paddle digital pin as input
    pinMode(RPin, INPUT);        // sets Right Paddle digital pin as input
    digitalWrite(ledPin, LOW);   // turn the LED off

    keyerState = IDLE;
    keyerControl = IAMBICB;      // Or 0 for IAMBICA
    loadWPM(15);                 // Fix speed at 15 WPM
}

///////////////////////////////////////////////////////////////////////////////
//
//  Main Work Loop
//
///////////////////////////////////////////////////////////////////////////////

void loop()
{
  static long ktimer;
  int debounce;

  // Basic Iambic Keyer
  // keyerControl contains processing flags and keyer mode bits
  // Supports Iambic A and B
  // State machine based, uses calls to millis() for timing.

  switch (keyerState) {
    case IDLE:
        // Wait for direct or latched paddle press
        if ((digitalRead(LPin) == LOW) ||
                (digitalRead(RPin) == LOW) ||
                    (keyerControl & 0x03)) {
            update_PaddleLatch();
            keyerState = CHK_DIT;
        }
        break;

    case CHK_DIT:
        // See if the dit paddle was pressed
        if (keyerControl & DIT_L) {
            keyerControl |= DIT_PROC;
            ktimer = ditTime;
            keyerState = KEYED_PREP;
        }
        else {
            keyerState = CHK_DAH;
        }
        break;

    case CHK_DAH:
        // See if dah paddle was pressed
        if (keyerControl & DAH_L) {
            ktimer = ditTime*3;
            keyerState = KEYED_PREP;
        }
        else {
            keyerState = IDLE;
        }
        break;

    case KEYED_PREP:
        // Assert key down, start timing, state shared for dit or dah
        digitalWrite(ledPin, HIGH);         // turn the LED on
        tone( tonePin, NOTE_D5 );
        ktimer += millis();                 // set ktimer to interval end time
        keyerControl &= ~(DIT_L + DAH_L);   // clear both paddle latch bits
        keyerState = KEYED;                 // next state
        break;

    case KEYED:
        // Wait for timer to expire
        if (millis() > ktimer) {            // are we at end of key down ?
            digitalWrite(ledPin, LOW);      // turn the LED off
            noTone( tonePin );
            ktimer = millis() + ditTime;    // inter-element time
            keyerState = INTER_ELEMENT;     // next state
        }
        else if (keyerControl & IAMBICB) {
            update_PaddleLatch();           // early paddle latch in Iambic B mode
        }
        break;

    case INTER_ELEMENT:
        // Insert time between dits/dahs
        update_PaddleLatch();               // latch paddle state
        if (millis() > ktimer) {            // are we at end of inter-space ?
            if (keyerControl & DIT_PROC) {             // was it a dit or dah ?
                keyerControl &= ~(DIT_L + DIT_PROC);   // clear two bits
                keyerState = CHK_DAH;                  // dit done, check for dah
            }
            else {
                keyerControl &= ~(DAH_L);              // clear dah latch
                keyerState = IDLE;                     // go idle
            }
        }
        break;
    }
}

///////////////////////////////////////////////////////////////////////////////
//
//    Latch dit and/or dah press
//
//    Called by keyer routine
//
///////////////////////////////////////////////////////////////////////////////

void update_PaddleLatch()
{
    if (digitalRead(RPin) == LOW) {
        keyerControl |= DIT_L;
    }
    if (digitalRead(LPin) == LOW) {
        keyerControl |= DAH_L;
    }
}

///////////////////////////////////////////////////////////////////////////////
//
//    Calculate new time constants based on wpm value
//
///////////////////////////////////////////////////////////////////////////////

void loadWPM (int wpm)
{
    ditTime = 1200/wpm;
}

This keyer program has few options which must be configured at compile-time.   This isn’t always convenient.  So, while gratifying, I still desired more functionality.   But, before I dug in adding features, I did a few more searches and found this absolutely brilliant project:

http://radioartisan.wordpress.com/arduino-cw-keyer/

It has tons of features (including memories) and can be implemented by adding a few more wires and components.   Most of the features are accessible by simply adding a push-button switch, which allows commands to be specified via the paddle. I chose to add a 10K ohm potentiometer, and enable remote speed control by un-commenting the line:

//#define FEATURE_POTENTIOMETER

Which has the keyer looking like:

Adding switching outputs and even a PS2 keyboard are among the many features.   Take some time to look around the site, there are many hidden treasures.   As K3NG points out, enabling many of the features at-once may cause “memory overflow” on the ATMega328P.   It’s been my experience that simply moving to the Arudino Mega 2560, with its larger memory space,  resolves this issue.      Don’t get discouraged waiting for the compile & download to the Arduino; it took about 1 and 1/2 minutes (small sketches take only seconds).

NOTE: that you don’t need to use an entire Arduino module to execute on a project.   Once the project is working, you can pop-out the Atmel chip from the Arduino (usually a ATMega328P) and wire directly up to the pins on a perf or proto-board (or an Evil Mad Scientist Target board).   The chips themselves are only four bucks and some change.   Be sure to buy ones that have the boot loader already installed, or else you’ll need to buy a separate programmer.   See this article for more details and clock parts.

Wrapping up, the project needs boxed & painted, more switches & potentiometers added,  and a power supply.   What did I end up doing?  I put the word out to the local radio artisans, and picked up a used MFJ-492X for a good price.

Additional Links:

Using an Iambic Paddle.
Keyers, Paddles, & More.
Voice & Morse Keyers.

This entry was posted in Amateur Radio. Bookmark the permalink.

6 Responses to Arduino Iambic Keyer and Side Tone Generator

  1. Ray says:

    Thanks for the interesting article. I just built it and I like it better than the “low-cost dot memory keyer” that I built from an article in qst in 1978. I know its a simple setup but it wouldn’t hurt to sketch out the wiring, I was unsure for a while about the V+ to connect to the resistors. I used the 5v output, would the 3v be ok?

  2. Howard says:

    Bill – Thanks for this. It runs fine but I want to add a pot for speed control, and I’m just learning Arduino. I’ve added int sensorPin=0; int sensorValue; sensorValue = analogRead(sensorPin);
    However, your sketch is based on specific e.g. 20 (wpm) so I can’t simply replace the number with sensorValue.
    I don’t know how to convert the analog output from pin 0 to a number like that. Any suggestions? Thanks

    • Bill says:

      Hello Howard,

      Be sure to set your sensorPin to “A0″, not just “0”. A simple tutorial can be found at “http://www.arduino-tutorials.com/arduino-analog-input/”. To change the speed, simply add the line: “loadWPM( (int)(analogRead( sensorPin ) * (20./1023.) + .5) )” — the analog input will vary from 0 to 1023. The “constant” 20./1023. will then scale the analog read value (with rounding) to between 0 and 20 WPM. Change the 20. to be whatever top speed you desire.

      You can put this into your setup() routine if you only care about changing speeds when you start-up/reset the Arduino, or inside the main “loop()” if you want to vary it on-the-fly.

      Obviously, you can optimize when the calculations are being done, and only perform them when the input value changes, but I’ll leave that up to you.

      Best regards,

      ->Bill

  3. Brian says:

    Boy I had a lot of fun with this one… Built it on a Maker Shield and added the speed control through the maker shield pot. Increased max speed to 30wpm and wow! what a blast! Thanks for sharing this.

    Here’s how I added the speed pot: (add somewhere after “Main Work Loop”)

    I’ll be looking for other ways to add to this – Thanks!!

  4. Reg says:

    Thank you for this project. It makes a simple and straightforward keyer for next to no money. I got it working first time, but had to puzzle out the red and black wires – red to resistors and 5Volts and black to ground the socket for attaching the keyer. A bit of thought and it suddenly made sense….. a little circuit diagram would have helped. In the comments left by different people is the way to adjust the speed…. perfect !

    I can play with this and then build it up to the advanced version.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>