I wanted a keyer that would take multiple inputs, paddles, straight key, and computer keyboard. The idea being you can send however you like, without needing to change around cables, just send.
There are many arduino morse projects around already. I suppose I could have just downloaded an existing one, dropped it in and ran with it. But I wanted to tackle the challenge of coding it myself.
Once I'd sorted the hardware out, I started to work on the software. It turned out to be a bigger challenge than I'd expected! It's tricky, the timing of reading the paddles and keying. I came up with an interesting solution to timing when it comes to keying the rig.
You see, the main loop in the arduino sketch can vary in how fast it executes, based on how much work it has to do. My solution was to use a timer in the chip and schedule an interrupt.
All processors have timers for this. They run at a set speed and can interrupt the processor, forcing it to execute a small routine at precise intervals. This is useful in robotics, for example, like in a flying drone. You would need adjustments to the motor speeds to be occurring fast and regularly.
So, in my keyer, there is an interrupt that is running a keying routine once every millisecond. That's 1000 times per second, precisely. That allows me to control the timing of the keying exactly and produce clean, consistent Morse out to the radio. This routing watches a buffer that I load with events using the characters period, dash, and comma. Period and dash are obviously the Morse elements, and the comma is a delay equivalent to a dot.
So to send an 'A' for example, you'd load the event buffer with .,-, dot comma dash comma. The interrupt routine would immediately key the transmitter with cleanly spaced precise Morse.
The hardware is extremely simple. The schematic is below. The pins chosen are arbitrary and can be changed to suit your build if needed. They are referenced in the first few lines of the code and you change those to reflect the pins you use. The keying is handled by a simple transistor switch. You could use an opto-isolator if you wish, but I've had no trouble keying modern rigs this way.
The software is below. Simply copy and paste it into the Arduino IDE to upload it to your board. Upon startup it will briefly key the radio for a fraction of a second and then be ready to accept input. I wired an additional input jack for a straight key. That was simply parallel to the keying output and done for convenience.
For keyboard input, the code monitors the serial input. When the Arduino is powered by a USB connection to your computer, it will create a serial port. Simply run a terminal program and connect it to that port to allow you to send keyboard CW. You want to set your terminal program to 9600 baud, no parity, 1 stop bit and 8 data bits. Type to send at the current speed. Simple.
The code is up to version 1.4. Paddle routines have been heavily worked on, 2 memories have been added. shft-alt-1 to load memory one, alt-1 to send memory one. Same for memory 2, shft-alt-2 and alt-2.
Code pasted below:
/*
Super Simple Keyer Ver 1.4 Arduino based keyer
Written Apr. 30, 2017 by Kevin Loughin, KB9RLW
Ver 1
Initial release, keyer working ok, not iambic
Ver 1.2
First iambic code, dependent on timing, not perfect
Ver 1.3
Rewrite of keyer logic, iambic and dot dash buffering working!
Ver 1.4
Tweaked paddles a bit. Added 2 memories. shft-alt-1 to set mem 1, shft-alt-2 to set memory 2
alt-1 to send memory 1, alt-2 to send memory 2.
Tweaked character spacing for keyboard decode. Added BT using the minus sign.
*/
// Pins we're going to use. Change as needed.
const int wpmraw = A7; //Pot sense for speed
const int dotin = 4; //dot sense pin
const int dashin = 2; //dash sense pin
const int keyout = 13; //keying output
// declaring variables we'll use in program
int delayval = 150; //delay between elements in milliseconds
String event = ""; //event contains the elements currently being sent, .=dot -=dash ,=space
String memory1 = ""; //user memory 1
String memory2 = ""; //user memory 2
String membuff; // buffer for memory sending
int dotlen = 150; //current dot length in milliseconds
int dashlen = (dotlen * 3); //dash length in milliseconds
int active = 0; //keying handler flag
int delaycount = 0; // delay counter in interrup handler
int dotstate = 1; // dot paddle input pins reading
int dotread = 0;
int dashstate = 1; //dash paddle input pins reading
int lastdotstate = 1;
int lastdashstate = 1;
int dashread;
int lastdashdebounce;
int lastdotdebounce;
int eventbuffer = 0; // event buffering
int dashhalf;
int lastchar = 1;
int c;
int dotbuffer;
int dashbuffer;
int dashdelay;
int dotdelay;
int flag;
void setup() {
// Turn off keying pin right away
pinMode (keyout, OUTPUT);
digitalWrite(keyout, LOW);
// setup input pins
pinMode (dotin, INPUT);
pinMode (dashin, INPUT);
digitalWrite(dotin, HIGH);
digitalWrite(dashin, HIGH);
Serial.begin(9600);
// setup the interrupt
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
}
// interrupt handler
SIGNAL(TIMER0_COMPA_vect)
{
if ( event.length() > 0 ) //do we have anything to do?
{
if ( !active ) //are we not currently sending?
{
if ( event.charAt(0) == '.') //do we need to send a dot?
{
digitalWrite(keyout, HIGH); //key on
delaycount = dotlen;
active = 1;
lastchar = 1;
}
if ( event.charAt(0) == '-' ) //do we need to send a dash?
{
digitalWrite(keyout, HIGH); //key on
delaycount = dashlen;
active = 1;
lastchar = 2;
}
if ( event.charAt(0) == ',' ) //do we need to delay between elements?
{
delaycount = dotlen;
digitalWrite(keyout, LOW); //key off
active = 1;
}
}
}
if ( active ) // we're already active, count and reset when done
{
delaycount--;
if ( !delaycount ) //are we done with this element?
{
active = 0;
digitalWrite(keyout, LOW); //just make sure the key is off
event.remove(0, 1);
}
}
}
void loop()
{
// read the speed and set timing
dotlen = ( 200 - (.146 * analogRead(wpmraw)) );
dashlen = ( 3 * dotlen );
// Read paddle inputs
// read the dash pin
dashread = digitalRead(dashin);
if ( dashread != lastdashstate )
lastdashdebounce = millis();
if ( (millis() - lastdashdebounce) > 40 )
{
if ( dashread != dashstate )
dashstate = dashread;
}
lastdashstate = dashread;
// read the dot pin
dotread = digitalRead(dotin);
if (dotread != lastdotstate)
lastdotdebounce = millis();
if ((millis() - lastdotdebounce) > 40)
{
if (dotread != dotstate)
dotstate = dotread;
}
lastdotstate = dotread;
// Keyer logic
while (!dotstate || !dashstate ) // if paddles pressed, do
{
if ( event.length() < 3 ) // don't bother unless there's no more than 1 chars in buffer
{
if ( !dotstate && !dashstate ) // both paddles?
{
if ( lastchar == 1 && ( delaycount < dashlen ))
{
event = String(event + "-,");
lastchar = 2;break;
}
if ( lastchar == 2 && ( delaycount < dashlen ))
{
event = String(event + ".,");
lastchar = 1;break;
}
}
if ( !dotstate && dashstate ) // only dot paddle
{
if ( lastchar == 1 && ( delaycount < dashlen) && event.length() < 1)
{
event = String(event + ".,");break;
}
if ( lastchar == 2 && event.length() < 3)
{
event = String(event + ".,");break;
}
}
if ( dotstate && !dashstate ) // only dash paddle
{
if ( lastchar == 1 && (delaycount < dotlen))
{
event = String(event + "-,");break;
}
if ( lastchar == 2 && (delaycount < (dotlen * 2)) & event.length() < 2)
{
event = String(event + "-,");break;
}
}
}
break;
}
delay(12);
// Keyboard routines
while(Serial.available())
{
c = Serial.read();
Serial.write(c);
// check for special keys
if ( c == 27 ) // alt key pressed
{
flag = 1;
c = Serial.read();
}
if ( flag && c == 33 ) // shft-alt-1, memory 1 define
{
loadmemory(1);
flag = 0;
c = 0;
}
if ( flag && c == 64 ) // shift-alt-2, memory 2 define
{
loadmemory(2);
flag = 0;
c = 0;
}
if ( flag && c == 49 ) // alt 1, send memory 1
{
sendmemory(1);
flag = 0;
c = 0;
}
if ( flag && c == 50 ) // alt 2, send memory 2
{
sendmemory(2);
flag = 0;
c = 0;
}
decoder(c); // call the keyboard decoder to load this event
}
}
void loadmemory(int num) // interact with user to load a memory
{
String inData;
char received;
Serial.println( "." );
Serial.print( "Enter the contents for memory " );
Serial.println( num );
Serial.println();
while ( received != 13 )
{
if (Serial.available())
{
received = Serial.read();
Serial.write(received);
inData += received;
}
}
if ( num == 1 ) // loading memory1
{
memory1 = inData;
}
if ( num == 2 ) // loading memory2
{
memory2 = inData;
}
Serial.println();
Serial.print( "Memory " );
Serial.print( num );
Serial.println( " loaded with" );
Serial.println( inData );
inData == "";
}
void sendmemory(int num) // send memory number num
{
if ( num == 1 )
membuff = memory1;
else
membuff = memory2;
Serial.println( "." );
Serial.println( membuff );
for(int leng = membuff.length(); leng > 0; leng--)
{
decoder(membuff.charAt( membuff.length() - leng ));
}
}
void decoder(int in) // function to decode char for sending and load send buffer
{
switch(in)
{
case ' ' : event = String(event + ",,"); break;
case 'a' : event = String(event + ".,-,,,"); break;
case 'b' : event = String(event + "-,.,.,.,,,"); break;
case 'c' : event = String(event + "-,.,-,.,,,"); break;
case 'd' : event = String(event + "-,.,.,,,"); break;
case 'e' : event = String(event + ".,,,"); break;
case 'f' : event = String(event + ".,.,-,.,,,"); break;
case 'g' : event = String(event + "-,-,.,,,"); break;
case 'h' : event = String(event + ".,.,.,.,,,"); break;
case 'i' : event = String(event + ".,.,,,"); break;
case 'j' : event = String(event + ".,-,-,-,,,"); break;
case 'k' : event = String(event + "-,.,-,,,"); break;
case 'l' : event = String(event + ".,-,.,.,,,"); break;
case 'm' : event = String(event + "-,-,,,"); break;
case 'n' : event = String(event + "-,.,,,"); break;
case 'o' : event = String(event + "-,-,-,,,"); break;
case 'p' : event = String(event + ".,-,-,.,,,"); break;
case 'q' : event = String(event + "-,-,.,-,,,"); break;
case 'r' : event = String(event + ".,-,.,,,"); break;
case 's' : event = String(event + ".,.,.,,,"); break;
case 't' : event = String(event + "-,,,"); break;
case 'u' : event = String(event + ".,.,-,,,"); break;
case 'v' : event = String(event + ".,.,.,-,,,"); break;
case 'w' : event = String(event + ".,-,-,,,"); break;
case 'x' : event = String(event + "-,.,.,-,,,"); break;
case 'y' : event = String(event + "-,.,-,-,,,"); break;
case 'z' : event = String(event + "-,-,.,.,,,"); break;
case 'A' : event = String(event + ".,-,,,"); break;
case 'B' : event = String(event + "-,.,.,.,,,"); break;
case 'C' : event = String(event + "-,.,-,.,,,"); break;
case 'D' : event = String(event + "-,.,.,,,"); break;
case 'E' : event = String(event + ".,,,"); break;
case 'F' : event = String(event + ".,.,-,.,,,"); break;
case 'G' : event = String(event + "-,-,.,,,"); break;
case 'H' : event = String(event + ".,.,.,.,,,"); break;
case 'I' : event = String(event + ".,.,,,"); break;
case 'J' : event = String(event + ".,-,-,-,,,"); break;
case 'K' : event = String(event + "-,.,-,,,"); break;
case 'L' : event = String(event + ".,-,.,.,,,"); break;
case 'M' : event = String(event + "-,-,,,"); break;
case 'N' : event = String(event + "-,.,,,"); break;
case 'O' : event = String(event + "-,-,-,,,"); break;
case 'P' : event = String(event + ".,-,-,.,,,"); break;
case 'Q' : event = String(event + "-,.,-,-,,,"); break;
case 'R' : event = String(event + ".,-,.,,,"); break;
case 'S' : event = String(event + ".,.,.,,,"); break;
case 'T' : event = String(event + "-,,,"); break;
case 'U' : event = String(event + ".,.,-,,,"); break;
case 'V' : event = String(event + ".,.,.,-,,,"); break;
case 'W' : event = String(event + ".,-,-,,,"); break;
case 'X' : event = String(event + "-,.,.,-,,,"); break;
case 'Y' : event = String(event + "-,.,-,-,,,"); break;
case 'Z' : event = String(event + "-,-,.,.,,,"); break;
case '0' : event = String(event + "-,-,-,-,-,,,"); break;
case '1' : event = String(event + ".,-,-,-,-,,,"); break;
case '2' : event = String(event + ".,.,-,-,-,,,"); break;
case '3' : event = String(event + ".,.,.,-,-,,,"); break;
case '4' : event = String(event + ".,.,.,.,-,,,"); break;
case '5' : event = String(event + ".,.,.,.,.,,,"); break;
case '6' : event = String(event + "-,.,.,.,.,,,"); break;
case '7' : event = String(event + "-,-,.,.,.,,,"); break;
case '8' : event = String(event + "-,-,-,.,.,,,"); break;
case '9' : event = String(event + "-,-,-,-,.,,,"); break;
case '.' : event = String(event + ".,-,.,-,.,-,,,"); break;
case '-' : event = String(event + "-,.,.,.,-,,,"); break;
case '/' : event = String(event + "-,.,.,-,.,,,"); break;
}
}
No comments:
Post a Comment