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
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