Updated 2/22/2020
(Back to the Projects page...)
This article describes a Morse Code device that can operate as a USB keyboard on a standard PC computer system. Using standard Morse code input from a keyer paddle (standard or iambic), along with some non-standard characters (pro-signs), most of the standard keyboard keys can be generated. The device depends on key-closure inputs, so it won’t “copy code” from an audio source, such as a receiver. While this project was spurred by a design challenge (QEX May-June 2019), my idea and some of the preliminary work predates the contest announcement by several years. The idea came to me while considering the conundrum of textual data input to a computer while driving. A standard keyboard is large and difficult to manage with one hand. Touch-typing would be a challenge to say the least. Touch screens are an abomination (my opinion, to be sure, but backed by some solid ergonomics) for pilots and drivers, as they (the touch screens, that is) DEMAND that one look and concentrate upon the input area (this is the primary reason that texting-while-driving is such a bad idea). Thus, the idea of using Morse code as an input media was born. In terms of ergonomics, it can offer a huge benefit to anyone who must multitask their hands and eyes. It is the penultimate “touch-type” interface (generally, LOOKING at a key-paddle doesn’t really help send code better, so you just don’t need to look at it). For visually impaired users, it can offer a huge benefit for the same reason. Pilots and drivers can use the interface without drawing on any visual or hand-eye acuity. Astronauts can use such a device (with an appropriate prosthetic) to improve their operation of computers (if you’ve never tried to operate a keyboard in zero-G, you might not appreciate the difficulty that Sir Newton imparts as you strike the keys). Finally, 20WPM is an easily achievable SENDING speed for most people – achieving that on a keyboard (without looking) can take a good deal of practice. This project aims to create a user-definable platform (electronics and software) that adheres to international Morse Code definitions while building on that standard to create Morse "characters" which correspond to a standard PC keyboard key or function. The platorm includes user-defined keys that may be configured and placed per the user's requirements without requiring the involvement of any other hardware (other than a mouse/trackball). |
Key to the conundrum of interfacing a Morse input device is the fact that a typical keyboard has many more keys than there are standard Morse Code characters. Control, shift, ALT, function keys, etc.., make for a real poser of a problem. I considered these issues and came to a quick realization: make non-Morse keys pro-signs! Several pro-signs are in use in over-the-air Morse code. /AR and /SK are two of the most popular. I simply extrapolated that to control codes such as /TAB and /ESC. While this works well for some combinations, it doesn’t lend itself to the infinite. First, there are a variety of three-letter combinations that result in the same sequence of Morse elements. Another observation of mine is that pro-signs are difficult to visualize if there is no change in the element type when bridging the gap between pro-sign characters. For example, /AR is (*-*-*). The “A” ends in a DAH, and the “R” begins with a “DIT”, so the pro-sign “makes sense”. A prosign such as /SIU is very difficult to visualize since is there is no change in the elements between the characters. This results in a string of 7 DITs ended with a DAH. /SIU doesn’t really help, and one is left just remembering “7 DITs plus a DAH” which is only remotely obvious from the mnemonic “SIU”. Some of my prosigns violate this approach, but I have tried to minimize these as best I can. The space bar should not be forgotten either. While humans have no significant problems with extraneous word space (in fact, it often helps many of us when receiving Morse code), this is not so easy to rectify in a computer without resorting to heuristic (e.g., complex) algorithms. This would also result in the need to buffer data or resort to using back-space so that the Morse interface could edit what was already sent to the computer. The "back-space" appraoch is further indicted when considering that there can be other inputs to the PC that can change the context of the input stream without communicating such to the other HIDs. My best stab at this is by no means perfect, but it does represent a bit of elegance. My solution: the space character, dit-dit-dah-dah (**--). While this is a defined character in the Some of the foreign Morse specifications, it is not defined in the ITU specification nor any of the American Morse tables I’ve encountered. It is also relatively short. Considering that it will be entered with every word, it would be nice to make it shorter, but all of those characters are taken. Thus, it represents the best compromise without resorting to modifications to the ITU Morse standard. While there is an "auto-space" mode available (it features a 14 DIT-space pause to generate a space), using the explicit character means that the Morse interface does not need to try to interpret what you mean by a space - you can specify it explicitly. |
Even if one were to design a custom PCB, as I have, the brunt of this project would still be software. Software is needed to configure and service the USB connection, button presses,
LED outputs, and key-paddle presses. Furthermore, software is needed to interpret the paddle-presses, and produce the element (dit or dah) timings. Finally, PWM interrupt software is used
to generate the side-tones using a DDS sine-wave tone generator algorithm.
The DDS sine-tone generator is derived from software I wrote in the 1990’s for my repeater controller product. While the ARM and its peripherals allow a greater deal of sophistication,
the basic algorithm is unchanged and worthy of its own article (for more detail, see: https://www.nxp.com/docs/en/application-note/AN1771.pdf).
I derived this algorithm from a TI appnote circa 1984 (to the best of my recollection, this is that appnote:
"Texas Instruments Application Note, Precision Digital Sine-Wave Generation with the TMS32010, February 1984" -- While this document currently
has a cover wrapper with a 1989 publish date and a 1997 copyright date, when one examines the document, it is clear that the original dates from 1984).
The keyer algorithm derives from a design I produced for a Motorola 8-bit MCU. Edge detection is used for the key inputs to allow for rollover and memory. This allows the full
iambic implementation to be realized (“A” or “B” type). The original design was in assembly language, so some translation and debugging was required. Since ARM designs generally
provide edge detection interrupts for all GPIO pins, there is some flexibility in the pin assignment process.
The keypad/button support is relatively simple. Using an interrupt timer, the code cycles through a 20-key row/column matrix and traps all closures in a matrix that mirrors the 5x4 keypad.
The timing matrix allows for debounce and the ability to trap multiple keypresses which is crucial to allow for multiple key-press combinations (like the venerable CTRL-ALT-DEL).
This scheme can’t allow just ANY combination, but it at least allows keys on the same row to be captured in combination.
LED outputs are used to indicate that a particular button feature is active (such as CAPS-Lock), and these buttons are implemented as press-to-toggle (press-to-toggle applies to the Morse entry of
these key codes as well). Each of the LEDs are controlled by a
PWM output which allows the brightness of the LEDs to be easily controlled (however, this is currently only implemented as a compile-time setting).
This leaves the USB interface. I must admit that I punted on this one. I can spell U-S-B on most days, and I’ve designed a number of PCBs that transfer USB signals, but I’ve never gotten
much into the protocol (at least, not enough to remember for any length of time). Fortunately, TI has a set of example projects that allow one to get started using USB without having to
know much about it. I’m not proud of taking this approach as I like to know more about how things work, but there are only so many hours in the day.
The TI examples are rather fractured. While I wanted to use the TM4C123GXL Launchpad, which features a TM4C123GH6PM microcontroller, the TI example for a keyboard USB-HID device (“usb_dev_keyboard”)
was written for a DK-TM4C123G which uses the TM4C123GH6PGE microcontroller (the evaluation board for this processor was no longer available at the time of this writing). Those couple of
digits at the end make a big difference in hoe the processor peripherals are arrayed. I made a half-dozen attempts to get the example to run on my board. In the end, I had to start with a
USB-gamepad example (written for the TM4C123GH6PM)
and gradually copy/edit-in the keyboard example.
That was bad enough, but porting my Morse-text code turned out to be a bigger chore than the USB example. While my code was written for another processor, it was in the same family AND it was written
by ME, so it should have been be easy-peasy, right? Turns out, there were several opportunities for pulled-hair before it was finally working on the "new" processor.
A painful example of how difficult it can be to port C code that is tightly coupled to a processor’s on-chip resources. I suppose that the use of the TivaWare kernal code might have made that task
simpler, but that horse is well out of the barn by now.
Overall, the code is divided into two main files, “usb_dev_hidkybd.c” and “morse.c”. “usb_dev_hidkybd.c” handles all of the initialization from reset, and then enters the main polling loop. This loop repeatedly
calls the USB and “morse.c” subroutines and processes the results if any. It does a fair job of self-documentation, but invariably can be found to be lacking once one starts to dig into the thing.
I needed to track the pro-sign characters that I was "inventing", so I created an Excel spreadsheet to convert DIT/DAH patterns into the format used by my code. To allow for extended morse
characters, I used a 16-bit array to hold the binary representation of
the supported Morse characters. "0" = DIT, and "1" = DAH, with the last element at the LSB. Another, 8-bit, array holds the length of each character. The location in the arrays correspond to
the ASCII code, with array element "0" being the ASCII code for [space]. The spreadsheet then combines the length and element pattern to produce a unique integer. With this unique representation,
the spreadsheet can automatically compare a given character to all other characters in the list to see if there were any duplicates. This proved to be very helpful as I entered all of the
characters and then added new ones. This screenshot illustrates the result:
The “CHAR-code”, bit-length, and “hex bitmap” data plug into the corresponding arrays in the “morse_lut.c” file which is where the look-up-tables reside. Most of the characters are positioned to match their corresponding ASCII code, this is how the code originated. However, lower-case characters have no place in this application, since the status of the SHIFT key controls the case. Because of this, a lookup table for the ASCII code for each Morse character was created: cw_text_map[]. The spreadsheet and the lookup tables are the primary means for adding or modifying custom Morse characters. The primary care directive is that the arrays are order sensitive. Thus, it is safest to add new characters to the end of the arrays. A note on the Morse encoding scheme that I used. With the last element of the character at the LSB of the character word, there is an appearance of oddity, even when I look back on this choice. It would seem to make more sense to order the elements in-step with the bit hierarchy. It portends a big-endian/lil-endian sort of war, er, debate. The reason for this seemingly odd ordering lies in how the elements are extracted. By using a bitmask that starts at the first element, then sliding that bitmask to the right (towards the lsb), the end of the process can be easily determined: once the bitmask becomes zero, the end of the character has been reached, so the software knows that it is finished. Reversing the order of the elements, while it may "look" more appropriate, would require a counter to determine when the process was finished. Extra code that was elliminated simply by changing the order of the data. While a small victory, this is how one writes code when their first experiences were with assembly code on a 2 MHz processor. While today's microcontrollers more resemble the super-computers of the 70's and 80's, I still think it is worthwhile to take this approach as it places one in the proper mind-set: while powerful, these devices do not feature infinite memory nor speed. One can still get into trouble relatively easily by not keeping this fact as a basic tennant of the software design. |
Originally, I purposefully omitted the ability to support straight-key input. It was out of (my) scope, and seemed to be a lot of extra effort for little gain. This changed once I had a few prototypes
out in the hands of different operators. One in particular suggested that he would benefit from using a straight-key rather than keyer paddles. The straight-key is a simpler mechanical interface,
and I generally prefer to assign some thought to the input of users before dismissing them, so I considered adding the feature.
As it turns out, the process of timing a single key input was not all that different from what I was already doing with the paddle code, so the change turned out to be fundamentally straightforward,
but still it required some effort. My approach was to buffer timings for each character's element until a "character-space" was encountered. Then, the code would scan the buffer and perform some
calculations to determine what threshold value to use to differentiate a DIT from a DAH. Finally, the threshold timing value was used to construct a character code word which could be applied to the same search
array that is used for the paddle code. Thus, the core of the application remained largely intact, and only a couple of functions and interrupt service routines required any changes. This made it relatively
simple to switch between modes.
There are several things to track to allow for variances that are ultimately human. REALLY good straight-key operators have a very good fist, which means that their timings are consistent
and closely match the 1:3 ratio that the Morse standard defines for DIT:DAH and inter-element timing. Folks like me don't do so well at that, so the software had to account for timing variations that
might occur within a given character. For characters that featured at least one DIT and one DAH, the algorithm was pretty good at decoding. For characters that were all of the same element,
the software had to maintain statistics, e.g.: a running average, of the calculated DIT timing. "Character space" is calculated from the running DIT average and thus is also adaptive.
Keeping the algorithm concise yet adaptive was challenge. Ultimately, the result is reasonable as it can copy my sorry fist well enough. A code switch and a new pro-sign, /SKS, was implemented
to allow switching between modes (as a toggle command). Overall, the effort to code and test, while considerable, was not as bad as I had feared. This part of the code likely needs work, but I
won't be able to push it much further without getting some field time in the hands of other operators.
|
All of the components for this project can be ordered from www.mouser.com or the electronic retailer of your choice. An e-mail to me is the current means by which to order PCBs (see the pdf documents referenced on this page for the e-mail address). Gerber files are provided, so you may order your own set from your favorite PCB FAB. The PCB features relatively large surface mount components such that most folks experienced at soldering should be able to assemble the board with minimal difficulty. PCBs are currently available in limited quantities. Lead-times are generally reasonable, but still expect 2 to 3 weeks to ship. For the moment, I can only say that PCBs should be available along with the data on this page, which includes a "Morse HID Operations Manual". Kits, partial or otherwise, are possible, but at the moment no firm plans are in place for this option. |
In addition to the hardware and software, there are ergonomic considerations that are important to consider. I have found that the incorporation
of a track-ball, 4 "buttons", and a set of Iambic paddles into a single, blended HID to be an optimal solution. It places all of the elements in one
spot, allowing one point of input. This is a view of my MARK-I attempt: Duct-tape is not as "stretchy" as electrical tape, so the electrical tape won out for this application. The track-ball is tilted slightly on a 3D printed base. The iambic paddles are worth their own page - I fashioned them on a principal similar to my mobile paddles but using cheaper methods and materials. The idea being that I wanted a very small, self-contained "switch-core" that could be made part of a larger assembly. The result was mostly good, but the sloppy nature of PVC caused some unforeseen complications. MARK-II came not long after: Here all of the elements are unified into one assembly. It is at this point where the utility of the Morse keyboard becomes fully realized. While the MARK-II lash-up is ungainly, it shows great promise. I now have no excuse NOT to use the device - anytime, anywhere. This has also allowed for more extensive testing across many PC applications which led to some recent SW changes to address a flaw in how the SHIFT and CNTL keys were implemented. The addition of 4 more buttons adds some complexity, but they are very useful for some of my CAD programs. Overall, this collection of interfaces makes for an appliance that is highly useful. Further integration and miniaturization is warranted and effort to this end will continue. At this writing, a 2nd spin of the PCB is enroute to my doorstep which incorporates the Tiva MCU and a 2-port USB hub onto a PCB that is only a bit larger than the first PCB. This will allow all of the electronics to be placed under the track-ball mouse reducing size and weight considerably. More news and pictures as they develop. Simulated 3D view of the Morse Keyboard with built-in USB HUB Here is a listing of the various project documents that are directly relevant to the Morse HID: |