Thursday, May 24, 2018

More Arduino: A Simple Iambic CW Keyer

I've been using the homebrew "TO" style keyer that I described in earlier posts for several months now, and I have to admit that I don't very much like it.  It also reminds me of why I ditched my Hallicrafters TO keyer over 30 years ago:  They don't support iambic mode and are a bit "fiddly" when compared to Curtis-based keyers.  So, I'm working on a replacement that uses the cheap (<$5.00) Arduino Pro-Mini board.

While scouring the web in search of ideas, I came across Ernest, PA3HCM's Simple Arduino Keyer project and was quite impressed, not only by how well it works but by how short and simple his code is.  Bravo, Ernest!

Of course, not being able to leave well enough alone, I had to add a few creature comforts: Adjustable weighting and sidetone frequency.  Ernest's code made that easy enough to do - the timing is based on the value read from the speed pot and the frequency of the sidetone, so with a little bit of manipulation, I was able to modify the code to add a "correction factor" that allows the weight or tone frequency to be changed without affecting the code speed.  It works well, and unlike the old-school "TO" based design, is very forgiving of operator induced timing errors.

Here's the code:

// Simple Iambic Keyer Mod0b3 by N8NM
// Based on the excellent work of Ernest PA3HCM
// May 23, 2018

/*
   Revision History:
   Mod0b0 - Initial build by Ernest PA3HCM with Arduino Pull-up resistors enabled
   Mod0b1 - Added PTT output functionality
   Mod0b2 - Added WEIGHT control, range: 2.25:1-3.75:1
   Mod0b3 - Added TONE control to adjust sidetone freq. Strap to 3.3V line if not using this feature.

   Controls:
   Speed: Sets element length (determines words per minute)
   Weight:Varies dash/dah ratio +/- 25%
   Tone: Adjusts sidetone frequency

*/

#define P_DOT     2                   // Connects to the dot lever of the paddle
#define P_DASH    3                   // Connects to the dash lever of the paddle
#define P_PTT    11                   // PTT output to radio
#define P_AUDIO  12                   // Audio output
#define P_CW     13                   // Output of the keyer, connect to your radio
#define P_SPEED  A0                   // Center pin of SPEED pot
#define P_WEIGHT A1                   // Center pin of WEIGHT pot
#define P_TONE   A2                   // Center pin of TONE pot (strap to 3.3V for fixed freq.)

int speed;                           // Declare variable used in reading speed pot
float weight;                        // Declare variable used in reading weight pot
int tone_val;                        // Declare variable used in reading tone pot
float loop_delay;                    // Declare variable used to calculate audio tone loop delay
unsigned long startMillis;           // Declare variable used to determine PTT delay
unsigned long currentMillis;         // Declare variable used to determine PTT delay
byte PTTSTATE = 0;                   // Declare variable used as PTT status flag

// Initializing the Arduino
void setup()
{
  Serial.begin(115200);               // open UART serial port at 115200 bps
  pinMode(P_DOT, INPUT_PULLUP);       // Enable DIT input with internal pull-up
  pinMode(P_DASH, INPUT_PULLUP);      // Enable DAH input with internal pull-up
  pinMode(P_AUDIO, OUTPUT);           // Enable output for sidetone audio
  pinMode(P_CW, OUTPUT);              // Enable output used to key radio's CW line
  digitalWrite(P_CW, LOW);            // Start with key up
  pinMode(P_PTT, OUTPUT);             // Enable output used to key radio's PTT line
  digitalWrite(P_PTT, LOW);           // Start with PTT off
}

// Main routine
void loop()
{
  speed = analogRead(P_SPEED) / 2;                     // Read the keying speed from potmeter
  weight = (analogRead(P_WEIGHT) * .001465) + 2.25;    // Read the character weight from potentiometer
  tone_val = analogRead(P_TONE) + 330;                 // Read the tone value from potentiometer
  if (!digitalRead(P_DOT))                             // If the dot lever is presssed..
  {
    keyAndBeep(speed);                                 // ... send a dot at the given speed
    delay(speed);                                      //     and add a space following the dit
  }
  if (!digitalRead(P_DASH))                            // If the dash lever is pressed...
  {
    keyAndBeep(speed * weight);                        // ... send a dash at the given speed
    delay(speed);                                      //     and add a space following the dah
  }
  if (PTTSTATE == 1)                                   // If PTT is active...
  {
    currentMillis = millis();                          // ... get the current time
    if (currentMillis - startMillis >= 1000)           // ... and if more than 1000ms has elapsed since last element was sent
    {
      PTTSTATE = 0;                                    // ... clear the PTT flag
      digitalWrite(P_PTT, LOW);                        // ... and release PTT
    }
  }
}

// Key the transmitter and sound a beep
void keyAndBeep(int speed)
{
  PTTSTATE = 1;                         // Set PTT flag
  digitalWrite(P_PTT, HIGH);            // Activate PTT
  digitalWrite(P_CW, HIGH);             // Key down
  for (float i = 0; i < (speed / (tone_val * .002)); i++) // Beep loop
  {
    digitalWrite(P_AUDIO, HIGH);        // Set audio pin HIGH for 1ms
    delayMicroseconds(tone_val);
    digitalWrite(P_AUDIO, LOW);         // Set audio pin LOW for 1ms
    delayMicroseconds(tone_val);
  }
  digitalWrite(P_CW, LOW);             // Key up
  startMillis = millis();              // Start PTT "hang timer"
}


And here's the circuit diagram:
The pot values aren't critical, though linear taper pots should be used for the speed and weight controls and audio (log) taper for the tone and volume.  I'm using 1K, since that's the first thing I grabbed.

Presently, I've got the keyer built on a solderless breadboard, and other than the occasional intermittent connection, it's been a joy to operate.  My Irish/Polish/Catholic ancestry has left me with no natural sense of rhythm, and it's able to mask most of my sending errors!

73 - Steve N8NM

No comments:

Post a Comment