This is an open source Arduino based CW (Morse Code) keyer with a lot of features and flexibility, often rivaling commercial keyers. The code can be used with a full blown Arduino board or an AVR microcontroller chip can be programmed and used directly in a circuit. This keyer is suitable as a standalone keyer or for use permanently installed inside a rig, especially homebrew QRP rigs. It’s open source code so you can fully customize it to fit your needs and also perhaps learn from it or find coding ideas for other projects. A circuit board and parts kits called the nanoKeyer is available from DJ0MY, and Hamshop offers a kit called Open CW Keyer which uses this software. Support for this project is available in the Radio Artisan discussion group.
- CW speed adjustable from 1 to 999 WPM
- Up to six selectable transmitter keying lines
- Programming and interfacing via USB port (“command line interface”)
- USB or PS2 Keyboard Interface for CW keyboard operation without a computer (USB Support NEW)
- Logging and Contest Program Interfacing via K1EL Winkey 1.0 and 2.0 interface protocol emulation
- Optional PTT outputs with configurable lead, tail, and hang times
- Optional LCD Display – Classic 4 bit mode or Adafruit I2C RGB display
- Up to 12 memories with macros
- Serial numbers
- CW keyboard (via a terminal server program like Putty or the Arduino Serial program)
- Speed potentiometer (optional – speed also adjustable with commands)
- QRSS and HSCW
- Beacon / Fox mode
- Iambic A and B
- Straight key mode
- Ultimatic mode
- Bug mode
- CMOS Super Keyer Iambic B Timing (NEW!)
- Paddle reverse
- Hellschreiber mode (keyboard sending, memory macro, beacon)
- Farnsworth Timing
- Adjustable frequency sidetone
- Sidetone disable / sidetone high/low output for keying outboard audio oscillator
- Command mode for using the paddle to change settings, program memories, etc.
- Keying Compensation
- Dah to Dit Ratio adjustment
- Callsign receive practice
- Send practice
- Memory stacking
- “Dead Operator Watchdog”
- Wordspace Adjustment
- Pre-configured and Custom Prosigns
- Non-volatile storage of most settings
- Modular code design allowing selection of features and easy code modification
- Non-English Character Support
- CW Receive (EXPERIMENTAL)
- Rotary Encoder Speed Control
- Sleep Mode
- USB Mouse Support (NEW!)
Basic Schematic (Click to Enlarge)
Note: Ignore the numbers on the outside of the Arduino symbol and refer to the numbers within the box for pin connections (i.e. D2, D3, A0, etc.) All capacitor values are in microfarads (uF), unless otherwise stated. No values are super critical and typical tolerance components may be used.
Connecting the Keyer
Here are the main pins you need to connect up to get started:
- Left Paddle – pin 2 – connect to your left paddle (grounding will send dits)
- Right Paddle – pin 5 – connect to your right paddle (grounding will send dahs)
- Transmitter Key – pin 11 – goes high for key down; use to drive a transistor to ground the TX key
- Sidetone – pin 4 – this outputs square wave sidetone to drive a speaker (schematic coming out shortly for driving with a transistor). The sidetone can be deactivated on transmit for transmitters that generate their own sidetone.
- The command button – pin A1 and at least R7
- Memory buttons, up to 12. Add buttons and resistors R8, R9, R10, etc. (You can do just a few memory buttons, all 12, or none at all.
Additional pins you may be interested in for other functionality:
- PTT (push to talk) (described in more detail below)
- Additional TX Key lines for multi-transmitter capability
- Potentiometer Speed Control – pin A0 – connect one end of the pot to +5V, the other end to ground, and connect the wiper to pin A0
- Rotary Encode Speed Control – no default pins are defined; two pins are required, defined by rotary_pin1 and rotary_pin1.
All pins can be easily changed at the beginning of the code if desired, though note that if the PS2 keyboard functionality is used, the clock pin must remain at pin 3 due to interrupt requirements. Optional I2C functionality uses pins A4 and A5; avoid using these pins if you plan to add the Adafruit I2C LCD display now or in the future.
To enable the command buttons, uncomment this line at compile time:
Button 0 is the command button. Pressing it will put the keyer into command mode which is described in detail below. Holding down the command button and pressing the left or right paddles will increase or decrease the CW speed.
Buttons 1 through 12 will play memories when momentarily depressed. To have a memory autorepeat (such as for doing a repetitive CQ), hold down the memory button and tap the left paddle. Holding buttons 1 through 6 down for a half second will switch the transmitter (1 through 6), if multiple PTT lines are enabled.
Buttons are multiplexed on one analog line using a voltage divider. You do not have to install all the buttons, and you can actually configure the number of buttons by changing this compile time setting:
#define analog_buttons_number_of_buttons 4
Two other settings are used to define the voltage divider resitor values:
#define analog_buttons_r1 10
#define analog_buttons_r2 1
analog_buttons_r1 is the value of R7 in the schematic in K (kilo ohms), and analog_buttons_r2 is the value of the remaining resistors (R8, R9, R10, R11, R12, etc.) The code calculates the voltage values for each button at runtime based on the three settings above. If you decide to use other resistor values you can adjust these values in the code, just be sure to do the math and make sure the resistors you chose make reasonable voltages and currents.
To enter command mode, press button 0, the command button and you will hear a “boop beep”, after which you can enter various commands by sending character using the paddle. (Note that if you’re in bug or straight key mode, you will temporarily be switched to iambic in command mode.)
If you enter a bogus command or the keyer didn’t recognize the character you sent, it will send a question mark, upon which you can retry your command.
To exit command mode, send X in CW using the paddles or just press the command button again upon which you will hear “beep boop” and you’ll be back in regular sending mode.
A - Switch to Iambic A mode B - Switch to Iambic B mode D - Switch to Ultimatic mode F - Adjust sidetone frequency G - Switch to bug mode I - TX enable / disable J - Dah to dit ratio adjust N - Toggle paddle reverse O - Toggle sidetone on / off P# - Program a memory T - Tune mode V - Toggle potentiometer active / inactive W - Change speed X - Exit command mode (you can also press the command button (button0) to exit) Z - Autospace On/Off # - Play a memory without transmitting
The behavior of the P command (program memory) can be changed with the following compile time options:
#define OPTION_PROG_MEM_TRIM_TRAILING_SPACES – this option will remove all trailing spaces from memories programmed in command mode
#define program_memory_limit_consec_spaces 1 – this setting limits the number of consecutive wordspaces that will be written to a memory
Neither of these settings affect memory programming when using the command line interface (CLI).
Serial Command Line Interface (“CLI”) / CW Keyboard
The keyer has a serial command line interface using the built in Arduino USB port. Simply connect to your computer and use a terminal program such as the Arduino serial port program or Putty. If you use the Arduino program, it’s recommended that you set it for carriage return (lower right).
To use the CW keyer functionality, simply type in what you want to send. In the Arduino serial interface you will need to hit Enter to send the data to the keyer for it to start sending. Programs like Putty will immediately send the characters and the keyer will send the code immediately as well.
Commands are preceded with a backslash (” \ “), the key above your Enter key (at least on US PC keyboards). To see a help screen, enter backslash question mark ” \? ” (no quotes). The status command (\s) is a useful command for viewing various settings and seeing the contents of the memories. If you enter a double backslash (“\\”), all sending buffers will be cleared and any memory sending will stop (this includes sending invoked by the PS2 keyboard or Winkey interface protocol emulation features).
Command Line Interface Showing Status (\s) Command Output
\? Help \# Play memory # \a Iambic A mode \b Iambic B mode \c Switch to CW (from Hell) \d Ultimatic mode \e#### Set serial number to #### \f#### Set sidetone frequency to #### hertz \g Bug mode \h Switch to Hell sending \i Transmit enable/disable \j### Dah to dit ratio (300 = 3.00) \k Callsign receive practice \l## Set weighting (50 = normal) \m### Set Farnsworth speed \n Toggle paddle reverse \o Toggle sidetone on/off \p# Program memory # \q## Switch to QRSS mode, dit length ## seconds \r Switch to regular speed mode \s Status \t Tune mode \u Manual PTT toggle \v Toggle potentiometer active / inactive \w### Set speed in WPM \x# Switch to transmitter # \y# Change wordspace to # elements (# = 1 to 9) \z Autospace on/off \+ Create prosign \!## Repeat play memory \|#### Set memory repeat (milliseconds) \* Toggle paddle echo \^ Toggle wait for carriage return to send CW / send CW immediately \~ Reset unit \& Toggle CMOS Super Keyer Timing on/off \%## Set CMOS Super Keyer Timing % \. Toggle dit buffer on/off \- Toggle dah buffer on/off
To enable the CLI, you must uncomment two lines in the source code before compilation:
CW Speed Adjustment
The CW sending speed can be adjusted several ways:
- The W command in command mode
- The command line interface \w command
- The memory macro \w, or \y and \z for incremental increases or decreases
- Holding down the command button (button 0) and pressing the left and right paddles
- Potentiometer Speed Control
- Rotary Encoder Speed Control
Potentiometer Speed Control
Adjusting the speed pot will immediately change the CW speed during manual sending or memory playing, however its changes will not be written to non-volatile memory. If the speed is changed using other methods (command mode, command line interface, memory macro, command button shortcut) that will override the pot setting until the pot is adjusted again.
The potentiometer functionality is enabled by commenting out this line:
The pin the potentiometer wiper is connected to is defined here:
#define potentiometer A0
Only enable this functionality if you have a potentiometer connnected, otherwise stray voltage on the Arduino pin will cause erratic and unexpected speed changes.
Rotary Encoder Speed Control
Speed can also be controlled using an inexpensive rotary encoder. The functionality is enabled by uncommenting this line:
The pins for connecting the encoder are defined here:
#define rotary_pin1 0 // CW Encoder Pin
#define rotary_pin2 0 // CCW Encoder Pin
The center pin of the rotary encoder should be connected to ground.
In order to have the keyer go directly into beacon mode at power up or reset and stay in beacon mode, simply ground pin 2. This is useful for keyers that are dedicated to beacon or fox service.
To switch to Iambic A mode, use the A command in command mode or \a in the command line interface.
To switch to Iambic B mode, use the B command in command mode or \b in the command line interface.
(An explanation of Iambic A and B can be found here.)
Straight Key Mode
To go into straight key mode, hold down the right paddle when powering up or power resetting.
To go into bug mode, use the command mode G command or the command line \g command.
To go into Ultimatic mode, use the command mode D command or the command line \d command.
CMOS Super Keyer Timing
This is enabled by uncommenting this line:
And the default timing setting can be set here:
#define default_cmos_super_keyer_iambic_b_timing_percent 33
CMOS Super Keyer Timing applies only to Iambic B mode. Setting the timing percent to 0 (zero) is essentially pure Iambic B and 100 is pure Iambic A. This function can be turned on and off at runtime using the command line interface \& command, and the timing percentage can be set using the \% command. Settings for this are stored in nonvolatile memory.
The sidetone line normally outputs square wave sidetone for driving a speaker. Sidetone can be disabled on transmit using the command mode O command. This is for transmitters that generate their own sidetone.
The sidetone frequency can be adjusted using the F command in command mode.
PTT (“Push To Talk”)
The PTT pins go high whenever code is sent. If it’s desired to have the PTT line go high before code is sent or stay high for a period of time after code stops being sent, these lines can be adjusted:
#define initial_ptt_lead_time_tx1 10
#define initial_ptt_tail_time_tx1 10
#define initial_ptt_lead_time_tx2 10
#define initial_ptt_tail_time_tx2 10
#define initial_ptt_lead_time_tx3 10
#define initial_ptt_tail_time_tx3 10
#define initial_ptt_lead_time_tx4 10
#define initial_ptt_tail_time_tx4 10
#define initial_ptt_lead_time_tx5 10
#define initial_ptt_tail_time_tx5 10
#define initial_ptt_lead_time_tx6 10
#define initial_ptt_tail_time_tx6 10
The lead and tail times are in milliseconds, and these are set for each transmitter independently. This feature is useful for driving T/R switches or older transmitters than need a little more time to get keyed up, or FM fox transmitters that need to have PTT keyed and sidetone pumped into the microphone line.
Hang time can be set by modifying this line:
#define default_ptt_hang_time_wordspace_units 0.0
PTT tail time is invoked when sending code automatically, such as via a memory play, the CLI, the PS2 keyboard, or Winkey interface emulation. PTT hang time is invoked for manual sending using the paddle and is speed (wpm) dependent.
Note that if you activate PTT lead time, you should activate tail time as well, otherwise PTT lead time will be invoked before each dit or dah, significantly slowing down the sending speed.
Currently PTT lead, tail, and hang times can only be changed at runtime using the Winkey interface emulation. (Let me know if you would like CLI commands to do this.)
For testing purposes the PTT line can be manually toggled on and off using the \u CLI command.
If your CW transmitter keys up when the CW line is keyed (or you are not going to use multi-transmitter support), there is probably no need to use the PTT line.
If you do not need the PTT lines and wish to use the Arduino pins for another function such as a transmitter keying line, simply set the pin number to zero, as so:
#define ptt_tx_1 0
#define ptt_tx_2 0
QRSS (Slow Speed CW)
QRSS mode can be activated using the command line \q command or in memory macros using the \q macro. Both take the dit length in seconds (double digit number) as an argument. For example: \q09 would put the keyer in QRSS mode with nine second long dits (and 27 second long dahs).
The \r command will switch back to regular CW speed mode in both the command line and in memories.
HSCW (High Speed CW)
High speed CW can be accomplished by using the \w command line interface command or in memories as a macro. Whereas the command mode speed adjustment and the speed potentiometer allow the speed to go up to a maximum of 60 WPM, the \w command will let you take it up to 255 WPM.
The keyer will send Hellschreiber characters by placing it in Hellscreiber mode using the \h command in the serial command line interface or memory macros. In the command line interface \c will return the keyer to CW mode and the \l (as in lima) memory macro will change back to CW. While in Hellschreiber mode, the paddle will still send CW. The Hellschreiber mode is intended mainly for beacons but works just fine for direct keyboard sending.
Hellschreiber Copied From the Keyer Speaker into a Laptop (click to enlarge)
Memory Operation and Memory Macros
Memories can be manually played using buttons 1 through 5, or using the \# command in the command line interface (for example, \1 plays memory 1). In command mode the memories can be sent without transmitting by entering the number of the memory.
Memories are programmed using the command line interface \p# command or in command mode using the P command.
To program memory 1 with CQ CQ CQ DE K3NG, the command would be \p1CQ CQ CQ DE K3NG.
To program memory 1 using command mode, enter command mode by pressing the command button and sending the P command. After hearing a beep, send the CW code to be stored and when finished, hit the command button to exit programming. The keyer will then play back the memory. If the keyer didn’t recognize a character you sent it will send a question mark in its place.
Macros can be placed in memories to do cool things. Some macros include:
\# Jump to memory # (1 through 9)
\c Play serial number with cut numbers
\d### Delay for ### seconds
\e Play serial number, then increment
\f#### Set sidetone to #### hertz
\h Switch to Hell sending
\i# Insert memory # (This is different from \#, the jump macro. The insert memory macro plays another memory, then comes back to the memory the keyer was originally playing. The jump command jumps to the other memory and doesn’t come back.)
\l Switch to CW (from Hell mode)
\n Decrement serial number, do not send
\q## Switch to QRSS mode, dit length ## seconds
\r Switch to regular speed mode
\t### Transmit for ### seconds
\u Activate PTT
\v Deactivate PTT
\w### Set regular mode speed to ### WPM
\x# Switch to transmitter # (1, 2, 3, 4, 5, or 6)
\y# Increase speed # WPM
\z# Decrease speed # WPM
\+ Prosign the next two characters
(Note that both command line commands and CW memories are case insensitive.)
The number of memories is set at compile time using these lines:
#define number_of_memories 12
#define memory_area_start 20
#define memory_area_end 1023
Up to 12 memories can be configured, with some caveats. Nine memories are supported in the CLI and in memory macros, and the full 12 are supported with the PS2 keyboard.
Memory_area_start and memory_area_end define the starting and ending EEPROM locations for the entire bank of memory. The memory area is divided up evenly between the memories. The example settings above will result in 12 memories each with 83 bytes, or 83 characters.
CW Dah to Dit Ratio Adjust
The CW dash length to dot length ratio can be adjusted using the J command in command mode. Upon entering the J command you will hear a repeating dit dah. Use the left and right paddles to shorten or lengthen the dah. Squeeze both paddles to exit the weight adjust command. After that you can enter X or press the command button to exit command mode.
The ratio can also be adjusted in the command line interface using the \j command. \j300 sets the keyer for a normal 3:1 ratio, \j250 would set it for a 2.5:1 ratio, for example.
The command mode N command switches the left and right paddles. The equivalent function in the CLI is \n and using the PS2 keyboard it’s CTRL-N.
The command mode T command or command line interface \t command goes into tune up mode. In the PS2 keyboard, use CTRL-T.
TX Disable / Enable
The transmit line can be disabled and enabled using the \i CLI command or I command in command mode. The equivalent PS2 keyboard command is CTRL-I. This feature can be used for sending practice without keying the transmitter.
The autospace feature can be toggled on and off with the Z command in command mode, the \z command in the command line interface, and CTRL-Z using the PS2 keyboard. This feature “cleans up” manual sending a bit by automatically inserting a wordspace delay if the operator waits more than one dit after sending a dit or dah to paddle either paddle. The autospace feature is activated by uncommenting this line:
Wordspace is the key up time in between words. By default it is set for seven dit lengths by this line:
#define default_length_wordspace 7
This can be adjusted using the \y command line interface command.
Dit and Dah Buffer Control
The dit and dah buffers can be turned on and off by adding this feature:
The dit and dah buffers can be turned on and off using the command line interface \. and \- commands. The settings are stored in nonvolatile memory.
The keying compensation filter extends the time of both dits and dahs to compensate for older transmitters that are slow on the draw in QSK at higher speeds. The inter-element key up times are reduced a corresponding amount of time. The time in mS can be set here:
#define default_keying_compensation 0
Currently there is no command to adjust this at runtime, however the Winkey emulation will adjust this if it is set in the host application.
First Element Extension Time
This feature makes the first dit or dah sent longer to compensate for slow T/R switches in rigs. The time is set here:
#define default_first_extension_time 0
Currently there is no command to adjust this at runtime, however the Winkey emulation will adjust this if it is set in the host application.
Custom prosigns can be sent using \+ in the CLI or in memories as a macro. Several “hard wired” / common prosigns are available for various keys on the PS2 keyboard like =, -, &, etc. and the Sroll Lock key can be used to create custom prosigns on the fly.
If you are programming a memory in command mode using the paddle, the \+ macro can be used to create prosigns. The \ is six dahs ( – - – - – - ) and + is didahdidahdit ( . – . – . ). Common prosigns AR, BK, and SK are automatically recognized and do not need to be proceeded with a \+ macro, just send the prosign as you normally would (AR would be didahdidahdit, not didah didahdit).
Receive Callsign Practice
In the command line interface the \k goes into callsign receive practice. Random callsigns are sent, the user enters the received callsigns, and the keyer will tell the user if they were correct.
Currently this code produces only US callsigns. I’ll be working on enhancements later to add other country callsigns, allow various user settings and adjustments, and variable speed based on the user’s accuracy.
This feature requires this to be uncommented:
PS2 / USB Keyboard Interface
A common PS2 or USB PC keyboard can be interfaced with the keyer to create a computerless CW keyboard. The two types of keyboards use different libraries, so read carefully how to install and configure the proper libraries.
1. Download the modified PS2Keyboard library files from Sourceforge. Create a directory in your sketchbook library directory called PS2Keyboard (i.e. \Arduino Sketchbook\libraries\PS2Keyboard\)and place the two files in there. (If you get a duplicate case error upon compilation, you’re probably using the stock PS2Keyboard library files and not my modified version.)
2. Uncomment the following lines in the K3NG Arduino Keyer code:
#define FEATURE_PS2_KEYBOARD PS2
3. Connect up a PS2 keyboard to your Arduino. Details on the pinouts of a PS2 keyboard connector can be found here.
Note that the PS2 keyboard data line can be relocated to other pins if desired, but the keyboard clock line must remain at pin 3 as that pin has special functionality for interrupt operation which is required by the PS2 keyboard library code.
1. Download the Circuits@Home library on Github. Install the library in a new directory in your Sketchbook libraries directory called USB_Host_Shield_20.
2. In the keyer code, uncomment this:
3. Uncomment the generic USB support code block:
/* Uncomment this section if using FEATURE_USB_KEYBOARD or FEATURE_USB_MOUSE */
/* USB Library can be downloaded at https://github.com/felis/USB_Host_Shield_2.0 */
/* End of FEATURE_USB_KEYBOARD / FEATURE_USB_MOUSE section */
4. Uncomment the USB Keyboard support block:
/* Uncomment this section if using FEATURE_USB_KEYBOARD */
class KbdRptParser : public KeyboardReportParser
virtual void OnKeyDown (uint8_t mod, uint8_t key);
virtual void OnKeyUp (uint8_t mod, uint8_t key);
/* End of FEATURE_USB_KEYBOARD section */
In order to connect the USB keyboard, you need hardware with a host USB port. This can be accomplished several ways:
1. Get a Circuits@Home USB Shield.
2. Get an Arduino Mega ADK. (Note that if you use the Mega ADK board, you must uncomment “#define BOARD_MEGA_ADK” in the library avrpins.h file.)
3. Get a Sparkfun USB Host Shield.
4. Build your own USB host interface using the MAX3412 chip using any one of the above circuits as an example.
I personally use an Arduino Mega ADK, only because I mistakenly ordered one long before I intended to use a host USB port, but it works well. The Mega board has the added benefit of being able to run all of the keyer code with plenty of room to spare. Please show your support for the USB library and purchase a Circuits@Home USB shield if possible, as they developed the wonderful USB library used here.
PS2 / USB Special Key Assignments:
F1 through F12 – play memories 1 through 12
Up Arrow – Increase CW Speed 1 WPM
Down Arrow – Decrease CW Speed 1 WPM
Page Up – Increase sidetone frequency
Page Down – Decrease sidetone frequency
Right Arrow – Dah to Dit Ratio increase
Left Arrow – Dah to Dit Ratio decrease
Home – reset Dah to Dit Ratio to default
Tab – pause sending
Delete – delete the last character in the buffer
Esc – stop sending and clear the buffer
Scroll Lock – Merge the next two characters to form a prosign
Shift – Scroll Lock – toggle PTT line
CTRL-A – Iambic A Mode
CTRL-B – Iambic B Mode
CTRL-D – Ultimatic Mode
CTRL-E – Set Serial Number
CTRL-G – Bug Mode
CTRL-H – Hellschreiber Mode (requires FEATURE_HELL)
CTRL-I – TX Line Disable/Enable
CTRL-M – Set Farnsworth Speed (requires FEATURE_FARNSWORTH)
CTRL-N – Paddle Revers
CTRL-O – Sidetone On/Off
CTRL-T – Tune
CTRL-U – PTT Manual On/Off
CTRL-W – Set WPM
CTRL-Z – Autospace On/Off
SHIFT-F1, F2, F3… – Program memory 1, 2, 3…
ALT-F1, F2, F3… – Repeat memory 1, 2, 3…
CTRL-F1, F2, F3… – Switch to transmitter 1, 2, 3…
USB Keyboard Only
Keypad / - Dit Paddle
Keypad * – Dah Paddle
Keypad ENTER – Tune / Straightkey
Myself and others have experienced issues using just the Arduino USB client port +5V to power the Arduino and PS2 keyboard, with operation being erratic or the keyboard just not functioning at all. This is due to computer USB ports not being able to supply enough current. The solution is simple: power the Arduino board directly using the power connector.
Why connect a USB mouse to a keyer? Because you can!
I added this feature on a whim after getting the USB keyboard code completed because the USB library supports mice and I wondered what it would be like to send code with a mouse. The answer is it’s not great sending code with a mouse, however this feature code have a practical application. I connected a wireless mouse to the Arduino and was able to walk around the room sending code. Undoubtedly you could use the guts of an old wireless mouse (I seem to go through one of these a year) and build a wireless paddle by mounting the electronics on a respectable paddle.
To enable the USB mouse functionality, install the USB library as described above in the USB Keyboard section. Uncomment this line:
Uncomment the generic USB support code block as described above and uncomment the USB Mouse code block:
/* Uncomment this section if using FEATURE_USB_MOUSE */
class MouseRptParser : public MouseReportParser
virtual void OnMouseMove(MOUSEINFO *mi);
virtual void OnLeftButtonUp(MOUSEINFO *mi);
virtual void OnLeftButtonDown(MOUSEINFO *mi);
virtual void OnRightButtonUp(MOUSEINFO *mi);
virtual void OnRightButtonDown(MOUSEINFO *mi);
virtual void OnMiddleButtonUp(MOUSEINFO *mi);
virtual void OnMiddleButtonDown(MOUSEINFO *mi);
/* End of FEATURE_USB_MOUSE section */
That’s it. The left mouse button is dit, the right is dah, and the middle button is a straight key.
As of this writing I have not been able to get both the USB mouse and keyboard to work simultaneously in the code, however it is possible to run a PS2 keyboard and USB mouse, if you have a bigger Arduino, like the Mega, that has more memory to work with.
Interfacing to Logging and Contest Programs / K1EL Winkey 1 & 2 Interface Protocol Emulation
The keyer can be interfaced to logging and contest programs with the K1EL Winkey interface protocol emulation feature. To enable, uncomment the following line:
This defaults the code to Winkey 1 mode. To enable K1El Winkey 2 interface protocol emulation functionality, uncomment this line in addition to the one above:
If you want compile both the CLI and K1EL Winkey interface protocol emulation features and upload to a unit, uncommenting the line below will cause the unit to default to K1EL Winkey interface protocol emulation rather than the normal Command Line Interface mode at power up or reset.
With the K1EL Winkey interface protocol emulation feature enabled, if you hold down the command button (button 0) and reset or power up the unit, it will go into the non-default mode. (If the default is K1EL Winkey interface protocol emulation, it will go into Command Line Interface mode, and vice versa.)
You may need to disable some features to get both the CLI and K1EL Winkey interface protocol emulation features to fit into an Arduino Uno. Other larger Arduino variants like the Mega can hold all of the features and options.
In K1EL Winkey interface protocol emulation mode the USB port will be set for 1200 baud. The emulation is a 99.9% complete emulation, and it should work with most programs that support K1EL Winkey interfacing. The N1MM contesting program and Ham Radio Deluxe (HRD) have been tested and work with all features I’ve tried.
Currently the following functions are implemented:
- CW Sending (of course)
- Key Down
- Unbuffered and Buffer Speed Setting
- Iambic A & B / Bug Mode Settings
- Ultimatic in normal, dit priority, and dah priority modes
- Pointer Operations
- Sidetone Frequency Settings (Winkey 1, 2, and custom frequencies)
- Paddle Reverse
- Paddle Watchdog
- Keying Compensation
- Dit to Dah Ratio
- Contest Wordspace
- PTT Lead, Tail, and Hang Time
- Speed Pot Setup and Query
- First Extension
- Software Paddle
- Serial Echoback
- Dual transmitter keying lines
- Paddle Only Sidetone
- Memory button reporting
- Standalone Message Sending
K1EL Winkey 2 memory querying and setting via EEPROM upload and download is not implemented.
The emulation functionality translates the K1EL Winkey interface protocol to native K3NG keyer functionality. The K1EL Winkey “protocol” is a de facto standard and many programs support it, and developing an open standard protocol and getting all the major programs to support it would be a monumental undertaking. So it made sense to merely emulate the existing protocol everyone else is talking.
SO2R operation has been run successfully with the N1MM contest program.
I have found to have this emulation work reliably with programs other than N1MM, you should disable the Arduino Automatic Software Reset as described here. This is done by cutting the PC board trace labeled RESET-EN on the Arduino Uno board, or an alternate solution is to install a capacitor on the reset line. I have found when some programs, including HRD, connect to the COM port, errant bytes are interpreted or received by the Arduino which trips up the protocol conversation and the program and keyer will not connect. In this configuration the keyer will not reset when a program connects to the COM port and it will be “ready to talk” immediately when the program begins sending bytes.
If you do not disable Automatic Software Reset and are using Ham Radio Deluxe uncomment the following line:
This option will discard the first three bytes that arrive on the USB port. This hack works for my hardware, but your mileage may vary. The number of bytes discarded at start up can be set here:
#define winkey_discard_bytes_startup 3
A side effect of disabling Automatic Software Reset is that you will need to manually hit the reset button when uploading new software to the Arduino. The button should be pressed as soon as you “Binary sketch size: xxxxx bytes” message in the Arduino program.
In the current build of HRD I’m using, there is a bug in its Winkey interface implementation. If you rapidly change the dah to dit ratio in the graphical user interface or change the speed rapidly, HRD will send incomplete commands to the Winkey. This will cause errant characters to be sent by the keyer, but otherwise the keyer will continue to function.
Ham Radio Deluxe offers a very nice Winkey settings interface. Presumably one could use this interface in place of the keyer command mode or command line interface and control most of the functionality in this keyer.
If you attempt to use this emulation with other programs and have issues, please let me know and I’ll attempt to figure it out. Serial port sniffer captures are very helpful in troubleshooting these issues.
N1MM exhibits a minor bug in the Send CW (CTRL K) window. If you hit the Tab key, N1MM sends a 0×09 byte to the keyer which is actually the PinConfig command. The next keystroke that is sent will be interpreted as an argument for this command and will alter the pin configuration and sidetone operation. If you accidentally hit the Tab key and the keyer stops keying the transmitter, or the sidetone is operational is toggled, re-initialize the Winkey interface in N1MM to restore the keyer back to proper operation. There is a workaround in the code for this bug. Uncomment this line:
Note this option breaks SO2R functionality in N1MM, but if you’re only going to using one rig it should be fine.
Dead Operator Watchdog
This feature turns off the transmit line after 100 consecutive dits or dahs. It can be enabled by uncommenting this line:
EEPROM / NonVolatile Settings
Most settings are stored in non-volatile EEPROM memory. Memory macros which alter the CW speed are not stored to EEPROM as to avoid “wearing out” EEPROM locations, especially in beacon mode.
Reset to “Factory” Defaults
To reset the keyer to defaults, depress both the left and right paddles and do a reset or power reset. This will wipe out all memories and change all the settings back to defaults.
This keyer supports multiple transmitters that can be selected using the \x CLI command, the CTRL-F1, F2, etc. key combinations on the PS2 keyboard, or using the hardware buttons (button1 hold, button2 hold, button3 hold, etc.). Up to six transmitters can be configured, each with its own keying line and PTT line. PTT lines are optional. The configuration of the TX Key and PTT lines are here:
#define tx_key_line_1 11
#define tx_key_line_2 12
#define tx_key_line_3 13
#define tx_key_line_4 0
#define tx_key_line_5 0
#define tx_key_line_6 0
#define ptt_tx_1 0
#define ptt_tx_2 0
#define ptt_tx_3 0
#define ptt_tx_4 0
#define ptt_tx_5 0
#define ptt_tx_6 0
Setting a line to zero disables it. At the very least you need one TX Key line defined. Obviously, with the Arduino Uno, pins are at a premium and each features uses pins. Larger Arduino platforms like the Mega offer more pins and more compiled-in functionality due to the larger memory space.
Dit and Dah Pins
If you need separate pins to indicate dits and dah, the pins can be defined here:
#define tx_key_dit 0
#define tx_key_dah 0
Banked Memory Buttons
A method for banked memory switches was created by DL2SBA and can be implemented by uncommenting:
This keyer supports an LCD display, either the standard commonplace Hitachi LCD display in four bit mode, or the Adafruit LCD I2C RGB shield.
The LCD display provides visual feedback for manual sending, memory operation, PS2 keyboard sending, and command button usage. Adafruit RGB LCD module buttons are not support yet, but a menuing system using these buttons is in the works.
To configure the LCD display, follow these steps:
1. Enable the appropriate libraries in the code.
For a 4 bit LCD Display, uncomment this line:
For the Adafruit I2C display uncomment these lines:
2. Uncomment this line (this applies to both types of displays):
3. Uncomment one of the lines below, depending on your display type:
4. If you are using the 4 bit LCD display, setup the I/O pins you want to use:
#define lcd_rs A2
#define lcd_enable 10
#define lcd_d4 6
#define lcd_d5 7
#define lcd_d6 8
#define lcd_d7 9
Note that the Adafruit I2C display uses pins A4 and A5 by default for interfacing as these are the hardware I2C pins. No pin setup is required in the code when using this dispay.
5. Uncomment one of the following lines depending on the display:
LiquidCrystal lcd(lcd_rs, lcd_enable, lcd_d4, lcd_d5, lcd_d6, lcd_d7);
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
6. If you are using a display that does not have 16 columns and 2 rows, adjust these two lines:
#define lcd_columns 16
#define lcd_rows 2
7. If you have free memory, uncomment this line for more messages on the display:
At higher CW speeds, the sending speed may be impacted by I2C LCD displays. This can be rectified by
increasing the I2C bus speed in file twi.h (it will be in ….\arduino-1.0.1\libraries\Wire\utility). Alter this line:
#define TWI_FREQ 100000L
…and change the setting to something like 500000L. (Thanks to AD7KG for discovering this bug and testing the fix.)
To enable support for non-English characters (i.e. À, Ä, È, Ö, etc.), uncomment this line:
If you need to customize the characters for your locality or language, modify the code in functions send_char() and convert_cw_number_to_ascii(). This support was added in version 2012011701 and currently works only with the command line interface and the K1EL Winkey interface protocol emulation. Support for the PS2 keyboard is in the works.
Sleep mode will put the unit to sleep after a certain amount of inactivity, in order to preserve battery power. To enable the feature, uncomment these lines:
The inactivity timer is set here (the unit is minutes):
#define go_to_sleep_inactivity_time 10
To wake the keyer after it goes to sleep, simply hit the left (normally dit) paddle.
All of the features will not fit on an Arduino Uno simultaneously. If the compiled code goes over about 28.5K, the upload a stock Uno will fail. The Nano holds slightly more than a stock Arduino.
You can burn an alternate bootloader to your Uno called Optiboot which will free up an additional 1.5K of program space to stuff additional features on to your Uno.
The Arduino Mega will run the entire “nine yards” compiled and is a fun board.
Do not enable the potentiometer feature if you do not have a potentiometer connected, otherwise noise on the pin will falsely trigger speed changes. Also, do not enable FEATURE_BUTTONS unless you have the corresponding resistors on the button pin connected. If the analog button pin does not have +5 volts on it, the keyer will not start up as it thinks there is a button depression in progress.
Note that code version 2011081901 introduced analog line multiplexed buttons. ”Legacy” buttons (one per I/O line) are no longer supported in the current code, however older versions of code below can be used if legacy buttons are desired (at the expense of new features and bug fixes).
The K3NG Arduino Keyer will not work with Logger16, Logger32, or Logger64 (and probably Logger128 and Logger256). Attempts to interface this keyer to any version of Logger may result in your Arduino board smoking or perhaps bursting into flames. Contact the friendly support people on the Logger list for help with this issue.
The preferred way to get support for this project is to post a message on Radio Artisan group. Please note it’s sometimes difficult for me to answer direct emails in a timely manner due to work, family, etc. and I may wait to answer questions on the group in hopes others may be able to lend assistance. While I like helping others, I have limited time, and time spent supporting the code takes away from time to make more code and features.
I encourage you to experiment with the code, customize it, learn from it, and have fun. That’s what amateur radio is all about. However, if you’re a novice programmer, please don’t ask me to work on your hacked up code to implement some specific application you’re looking for. If you would like for me to develop code for a specific piece of hardware or a device, you may be able to interest me in doing this by sending me a piece of hardware (free) to develop with and use.
I am not an expert or professional programmer. I tend to write readable code with sufficient though often sparse comments. (Good code shouldn’t need a lot of comments.) I avoid complexities that some hotshot C or C++ programmers may do as the goal here is to have fun, not show off how obfuscated I can make my code. Undoubtedly there are better or more efficient ways to do some things in the code.
Hardware, Extensions, User Contributions, Other Support Sources, Etc.
Oscar, DJ0MY, offers a hardware kit, utilizing an Arduino Nano (user supplied). The kit features a speed potentiometer, opto-isolated TX and PTT outputs, three memory buttons, and a PS2 keyboard connector. Oscar provided testing, especially for the K1EL Winkey interface protocol emulation, and numerous development ideas.
Dietmar, DL2SBA, has created some extensions to the hardware, including button switch banks.
Marc, F6ITU, has created a Wiki page (En Francais) describing the keyer project and plans for an Arduino shield. There is also an alternate patch PS2 keyboard library for European/French(?) keyboard localization.
Hajos Kontrapunkte wrote about his experience building the keyer.
Stewart, VA3PID, wrote an article about the keyer in the Scarborough Amateur Radio Club newsletter, Wavelength.
Owen, VK1OD, has contributed code and chronicled his Arduino project using this code.
Gerd, DD4DA, maintains a port of the keyer code for the AVR Studio 6 IDE. Gerd has provided code tips, bug fixes, ideas, and feedback
Hamshop.cz offers an ATMega328 based keyer kit with the Arduino bootloader that runs this code.
Chris, K4FH, provided testing, ideas, code, and feedback
Chis, W0ANM, provided testing and feedback
Marcin, SP5IOU, provided code, testing, ideas, and feedback
Peter, NI6E, helped identify bugs and test features. Here’s his discrete component keyer on a breadboard:
I will donate keyer parts or assembled keyer units for DXpeditions. Please contact me at anthony dot good at gmail dot com to discuss if you’re organizing a DXpedition.
MJ/ON6NB DXpedition using the K3NG Keyer running on nanoKeyer hardware (Donated May 2013)
If you sell kits using my code for a profit, I ask that you send me a free kit as a courtesy. Share the love. :-)
Source code is located on SourceForge. The master tree contains the latest stable code and is what you should normally use.
Unstable / beta versions of code can be found in the unstable tree. Unstable versions of code may contain new features which are not fully tested and probably not documented (though code comments may have some documentation.) Do not run this code unless you’re willing to experiment and accept buggy operation. Unstable code may give you a rash and cause your equipment to inexplicably explode without warning.
Code contributions and help testing features are welcome!
Code History (Stable Releases)
fixed ps2_keyboard_program_memory compiler error
2013051901 – USB Keyboard and Mouse support, CMOS Super Keyer Timing, Dit and Dah Buffer Control. Also rewrote the EEPROM code to use the struct method used in my other projects.
2013040201 - OPTION_PS2_NON_ENGLISH_CHAR_LCD_DISPLAY_SUPPORT code donated by SP5IOU, fixed \Z memory macro bug, fixed CLI \! command bug
2013032901 – Memory macro \i, memory macro now plays memory 10, CLI command added to play memory 10, CLI command \p0 programs memory 10, Multi-transmitter PTT Lead Time and Tail Time
2013012901 – Added Rotary Encoder Speed Control support and Sleep Mode.
2012101701 – Fixed another bug in Iambic A mode where additional dah was sent after releasing a paddle squeeze. Optional non-English character support was added for the CLI and K1EL Winkey interface protocol emulation. The command mode P command now supports double digit memory numbers. Fixed a bug with transmitter switch announcements (“TX1″, “TX2″, etc.) not sent in sidetone.
2012090501 – Fixed bug in Iambic A mode where dits would be lost while holding the dah paddle and intermittently hitting the dit paddle
2012070201 – Current transmitter setting is now stored in EEPROM, fixed a minor bug with K1EL Winkey interface protocol emulation where <CR><LF> was sent with every memory button push, and added OPTION_N1MM_WINKEY_TAB_BUG_WORKAROUND
2012062501 – Added LCD support!
2012060301 – Fixed a bug that I encounter with a unit that previously didn’t have bypass capacitors on the paddle leads and experienced RFI, upon which I added caps and the keyer thought both paddle line were grounded on startup in the brief moment they were charging up. There’s now a slight delay on startup to allow bypass caps to fully charge before checking the paddle lines.
2012052101 Code – Multiple transmitter outputs added (no more needing to use PTT outputs to switch between transmitters) and K1EL Winkey 2 interface protocol and settings now supported. Performance with HRD improved. Also incorporated DL2SBA banked memory button code.
2012042101 Code – Code cleanup to eliminate compiler warnings and two syntax issues caused by changes introduced in Arduino 1.0. Also fixed two K1EL Winkey interface protocol emulation bugs.
2012032501 Code – Improved command mode memory wordspace detection, wordspace limiting, and trailing space trimming option. The potentiometer is now enabled by default now on the first compile, upload, and power up.
2011120201 Code – Minor bug fixes, OPTION_REVERSE_BUTTON_ORDER, and updated to compile with newly-released Arduino 1.0
2011100601 Code - Last Arduino 0022 code
2011081901 Code - Introduced analog multiplexed buttons, memory repeat (without having to use memory macros), beacon bug fix, better handling of Winkey interface protocol emulation paddle and memory interruption, much improved paddle echo, PS2 keyboard ALT key combinations
2011081301 Code - Hardware pin changes
2011080502 – Many new features in the PS2 Keyboard functionality, up to 12 memories now, up to six transmitter lines. Code now includes GNU license.
2011072801 – A bunch of Winkey interface protocol emulation commands, weighting, \j CLI command, \i CLI command, \u CLU command, \+ memory macro and CLI command, Ultimatic mode, \d CLI command, D command mode command
2011071801 – Bug fix correcting timing issue, more K1EL Winkey interface protocol emulation commands
2011071602 – Features: Dead Op Watchdog, eliminated M command in command mode (now 1-5 entered to play memory), numerous Winkey interface protocol emulation additions, autospace feature, wordspace adjust feature, Iambic A and B modes (was just Iambic B before), keying compensation feature
2011071002 – Interfacing to logging and contest programs
2011070801 – Improved preemption of memory sending by pressing button 0 or hitting PS2 keyboard escape key; added F command (sidetone frequency adjust) to command line interface and memory macros; added additional keyboard commands
2011061201 – Added PS2 Keyboard functionality and had to change the right paddle pin from pin 3 to pin 5 in order to accommodate it (only pins 2 or 3 on an Arduino can be used for interrupts). Also improved serial_send_buffer functionality.
2011050602 – Added the \\ command to the serial command line interface code which dumps buffered characters for sending. Also added functionality for backspace to delete characters out of the buffer. Slash commands now preempt any characters in the buffer, and are executed immediately.
2011032501 – Minor correction to potentiometer feature include code
2011031501 – Now the LED blinks rapidly in command mode and slowly in beacon mode.
2011031301 – Added speed potentiometer support
I would like to thank the staff of an unnamed contest logging program for refusing to provide any help in implementing a certain feature and prompting me to create a better technical solution with others who were willing to help.