Morse code keyboard
Have you switched your QSOs to PSK31 or some other digital mode, but miss the joy of sending CW with an iambic paddle? Then here’s a project for you.
Bill Ward (KD4ISF), Guy Dickenson (KD7TJJ) and Dave Clausen (W2VV) setup a club call sign N2YCR and a remotely operated computer station running fldigi for PSK31. The machine is in the backroom at NYCR and can be operated by members with general class amateur radio licenses from anywhere, using xvnc to display the fldigi application.
But operating it with a keyboard has none of the feel of an old fashioned QSO, so I designed an interface for Dave’s BY-1 Bencher Iambic Paddle. Iambic paddles are very different from straight keys in that they have two inputs: one for “dit” and one for “dah”. If an input is held in, it will repeat at a pre-programmed speed. If both are held in, they will alternate between the two symbols. Typically the dah is three time as long as the dit.
The paddle itself doesn’t generate the code; this is the responsibility of the radio, or in this case, of the Teensy 2.0. It runs a state machine that listens for
PIND4PINB4 to be pulled low to indicate a dah, or PIND5PINB5 to be pulled low for a dit. If neither is pulled low within the time of two dits, the current bit-stream is looked up in a table and if there is a valid USB keyboard event in the table it will be sent to the host computer.
With a straight key the computer would need to estimate the codes per minute and try to differentiate dits from dahs. But with the iambic paddle, the operator has input the separate symbols so there is no difficult step to determine the code. This makes it feasible to run the entire decoding stack in a small microcontroller like the ATMega32U4.
Sending CW without audio feedback is very hard, so a tone is produced via PWM on OC0A at 498 Hz, directly into a speaker scavenged from an old headset. Software controls the volume in a crude fashion by limiting the on-time of the square wave. If the input sequence is not a valid Morse code, the timer 0 is switched to a slower speed, which causes OC0A to be driven at half speed, or 249 Hz, which will indicate an error tone.
Full source for the Teensy is available for your own use. It should appear as a generic USB keyboard and work with any application.
The case is made using my laser cut box generator and available on Thingiverse. I cut it using 6mm clear acrylic at 100% power, 4% speed, and etched the raster at 100% power, 20% speed. The box is a friction fit and holds together without any screws or glue.
Some possible improvements are to have an external potentiometer to control the speed of the CW (currently it is a compile time constant), and to create a USB serial device that translates its input into audio so that you can have a full CW QSO with fldigi.
Possible to add a POT to adjust for WPM?
73 de NT1K
I use vanilla XUbuntu 11.10 and did the standard library installs recommended at PCJR, but some of the paths were not included in the build. I edited the Makefile as follows:
#AVRPATH = /Applications/Arduino.app/Contents//Resources/Java/hardware/tools/avr/bin/
#CC = $(AVRPATH)/avr-gcc
#OBJCOPY = $(AVRPATH)/avr-objcopy
#OBJDUMP = $(AVRPATH)/avr-objdump
#SIZE = $(AVRPATH)/avr-size
#AR = $(AVRPATH)/avr-ar rcs
#NM = $(AVRPATH)/avr-nm
#AVRDUDE = $(AVRPATH)/avrdude -C $(AVRPATH)/../etc/avrdude.conf
#AVRPATH bits do not exist, using . instead
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
AR = avr-ar rcs
NM = avr-nm
AVRDUDE = avrdude
Thanks for a cool code thing!
Just built this and started getting sound out and keypresses showing up in “vi” in a linux shell session.
For me, and as far as I can tell in the code, Teensy 2.0 pins B4 and B5 were the paddle inputs, rather than D4 and D5.
My next thoughts are about seeing what an extended Morse code character set would look like. I think some adaptive technology products, like puff-and-sip interfaces have dealt with the problem.
Thanks for posting this project!
Got it working too! Great job guys. FWIW I had to change around the make file a little to set paths and use the AVR ISPmkII, and change some variable from static to const to make avr-gcc happy.
What do you use for a space? Just keep one finger on the keyboard?
I was thinking of adding more keycodes for space, enter, backspace etc.
There’s an example of an extended morse char set at…
…which I’m thinking of modifying to at least match the existing official morse chars. I don’t think I could send a 4-dash M without wincing.
Let us know if anyone knows a more “official” extended set of morse chars!
Yeah, I wouldn’t want to send that either, as I’m using this to improve my CW skills in preparation for field day, and would not want to develop bad habits.
However, I see you have a disabled a section starting on line 270 that uses the last_send_time to determine if a space should be inserted. I enabled that and uncommented line 299, but could not figure out how to get it working. Maybe it was giving you trouble too, but that would be cool if you could get it working. I added in a potentiometer to lower the volume; so I don’t drive my office mates crazy.
Hi rfengr and all,
Just making sure you know: I’m not the author of this code, nor do I have check-in rights to the Mercurial repo for the code; I’m just altering my local copy on a Debian 6 and an Ubuntu 11 system. Here’s what I’ve done:
I didn’t bother with or change the automatic space insertion. I didn’t look at that section much. If it’s disabled, I just left it turned off and added an explicit space char.
I did add 3 chars to the end of make-alphabet:
ENTER .-.- BACKSPACE —- SPACE ..–
Note that in the original code the Morse for “=” clobbers the “x” and thus needs correction:
The names like “ENTER” correspond to key names like “KEY_ENTER” in usb_keyboard_debug.h . The shifted versions of keys are not specifically named, the code just ORs a bit.
If I run “make clean”, I find that wipes out “morse.h” . Regenerate with a
./make-alphabet > morse.h
I tried raising the speed by changing cpm in iambic.c and it seemed to work :
const uint8_t cpm = 25;
I already mentioned the use of B pins instead of D pins for the input.
I have had success programming the Teensy with the Teensy Loader from
and the PJRC-suggested udev rules.
Regards to everyone!
I’ve updated the post with the correct pins. Thanks for the correction.
If you send me a patch I’ll update the code tree with the additional characters. I need to fix the Makefile for regenerating the morse.h, too.
Hi Could you send me a hex file. I am unable to compile under windows7. I have files missing.
I am: [email protected]
——– begin ——–
process_begin: CreateProcess(NULL, /Applications/Arduino.app/Contents//Resources
/Java/hardware/tools/avr/bin//avr-gcc –version, …) failed.
make (e=2): The system cannot find the file specified.
make: *** [gccversion] Error 2
Sure, I haven’t worked on the project in 6 months, but if I can find the hex file I will send it your way. I haven’t had time to look at the code since then, so I’m not sure if my version is worth much to you.
Many thanks. 73 de Béla ha4bm
could you share the changed morse.h. 73 de Béla HA4BM
Question: I’m quite happy to have made it work, but x[-..-]
I’ve edited the make-alphabet and morse.h and recompiled, but cannot fix it! What more can I do?
edit:read above, trying… Thanks!
Cleaning out my dad’s apartment and have to be out by the end of June. Offering several boxes of ham and CB radio gear for free, just pick up. Address in Parkchester, Bronx NY. Call Alan at 917-499-8298.
I’f not picked up it has to get trashed or donated by this coming weekend.
I fixed the X key in morse.h by hand with
[0x0019] = KEY_X, // -..-
The Morse encoding in binary is read from left to right starting with a sentinel 1 bit. Dit is 0, Dah is 1 following the sentinel 1 so in binary X looks like 11001 which converts to 0x0019.
Is there a schematic? I see PINB4 to connect to dah, PINB5 to to connect to dit and common to ground on the key. Then OC0A into a speaker and the other side to VCC??
I first tried to load this and it didn’t work. I reloaded the HEX file a second time and it worked great after that. Not fun for a first go round… “What did I do wrong?” was all I could think.
I was a little confused, so let me leave this for others:
Paddle gets connected to B4, B5 and GND pins. B4 is dit, B5 is dah
Speaker gets plugged into VCC and pin OC0A which was labled B7 on the board. It was labeled as OC0A in the Teensy docs.
In the morse.h file the character X and = are mapped to the same morse code input. Change the line for = from [0x0019] to [0x0031]. This makes X -..- and = -…-. Recompile, re-download and you’re in business.
Hope that helps!