I had previously built a small Morse code keypad using a binary tree[1], at the time I saw that I could re-purpose the code to output Morse rather than input it.
I had also built a project where I selected and read from a txt file on an SD card [2].
I thought it would be nice to combine code from both those projects to be able to select a txt file from an SD card and output that text in Morse code.
One way of learning Morse is using a dichotomic search table, part of which is shown in Figure 1.
You start with either a dot or dash and keep adding until you spell out the letter you want. For example, if you want the letter a, you send a dot then a dash.
Figure 2 shows a binary tree, it looks remarkably similar to our dichotomic search table. A binary tree is implemented as an array; we ignore array position 0 and use the numbers on each node as the index for the data (Figure 3).
We can traverse the indexes of a binary tree using simple math either forward or, in our case, backwards.
To traverse backwards from a left hand node you divide its index number by 2.
To traverse backwards from a right hand node you subtract 1 from the index number then divide by 2.
So how do you know you are at a left or right node?
Easy, all the left hand nodes have even number indexes and all the right odd, which leads us to the question of how to determine if a number is odd or even?
With binary numbers there is a neat trick, consider 1, 2, 3, 4, 5 in binary0001,
0010,
0011,
0100,
0101,
Note that in all of the odd numbers the far right bit is 1 so if we bitwise AND (&) our index with 1 the result will be 1 for an odd number.
Example:
0101
0001&
----
0001<- So an odd number
----
The initial plan was that we locate the required letter in the array and move backwards through the nodes sounding a dot if our backwards jump was even and a dash if odd. Using the tree of Figure 1 this presents us with a problem.
Consider the letter I (dot, dot) this letter in Morse is a palindrome so reads the same both forward and back but on the other hand letter a (dot, dash) reads backwards as dash dot the code for the letter n, we can solve this by the use of a stack.
As we traverse back through the tree we push each index value onto our stack stopping when we reach index 0.
Then if we pop each element from the stack we have our character elements in the correct order so we sound a dot for the even and a dash for the odd indexes.
A second way of solving this problem is to rearrange the Morse tree to allow for direct translation as, partly, shown in Figure 4. (You should see that the letters a and n have swapped position and that now if you work backwards from either letter the dots/dashes will sound in the correct order.)
I did this by writing another small program which, for each character, stored the character then worked back through the tree recording each jump (left or right) in a queue until it reached index 0. Then, using a new empty array, each jump was de queued and used to move the index in the new array. When the queue is empty we are at the right array index for the character to be placed.
Hardware:The hardware consists of an Arduino UNO, a Deek robot data logging shield v1.0 and an LCD shield with buttons.
The UNO, data logging shield and LCD shield are layered together like a cake.
I have removed pin 10 from my LCD shield this pin is used for the LCD backlight but has been implemented incorrectly on some shields [3], such as the one I bought, this is no loss in the context of this project as the data logging shield uses this pin as chip select.
I soldered the piezo sounder to the logging shield using the spare contact pads provided adding in a couple of pins for a jumper so that it could be disconnected when I use the shield in other projects.
The connections for the sounder are:
+To Digital pin 3
GND to GND via 2*0.1 inch pins with jumper
All other connections are made by plugging the shields into each other.
Software:The Morse code function:
In the end I coded up the Morse sounder function both ways and decided to post both sets of code.
1– Work backwards through the binary tree until reaching index 1 pushing each index onto a stack. At this point pop each item from the stack sounding a short beep for the dots and a long beep for the dashes. (If an index is an odd number then it is sounded as a dash, an even index is sounded as a dot.). You will need to install the stack library to run this code, details are in the code file.
2– Rearranged binary tree version, work your way back through the tree sounding the dots and dashes without the need for a stack.
Selecting and reading the file:
I checked out Arduino reference for listing the files on an SD card [4] and another on reading and writing files [5] I tried out the code from both pages and set about melting them together.
For the display I went the model view controller (MVC) route.
Rather than set the Morse speed at compile time I set up 3 of the Analog pins as inputs and enabled their internal pullups, just take the required pin to ground to select the speed you require, you do have to reset the Arduino after altering the jumper wire. (The default is5 words per minute - A1= 5 wpm, A2= 13 wpm, A3= 20wpm)
Library's used:
SPI
SD
LiquidCrystal
and additionally for the stack version
StackArray [ https://playground.arduino.cc/Code/StackArray/ ]
Additionally:
We make node 1 a null value and start filling in the letters from node 2 on-wards; otherwise we would lose the first dot, dash transition and the branch increments through the table will not work correctly.
We must add in null values at the positions where there are no letters;you cannot compact a binary tree by removing the empty spaces and still have it work correctly.
We deal with the space character by creating an additional delay (no sound) before the next letter.
Constraint:When reversing through the array our final position is at array index 1.
USE:On start up you will be informed that the SD card is being initialized and if that fails.
You will then be presented with the dialog "Select file" with the first filename from the card shown on the display line below.
You will be informed if the file fails to open.
Use the down key to cycle through the list of files on the SD card and press select when you have found the file you want to play.
The dialog will change to "Opt-> and the filename" and translation to Morse will begin. The Morse character being sounded will be shown on the second line of the display.
The file names must be a maximum of 8 characters (followed by the.txt).
Note there is no stop option.
Morse speed:Morse speeds are somewhat complex to work out so I refer anyone interested in the detail to the reference [6]:
Limitations:I saw no easy way of differentiating the digraph ch from ch as in chip.The Wikipedia Morse code page states that S with caron is also denoted by its combination so I went with that [7].
I added in the other special characters as best I could but cannot be certain all are correct.
References (checked /April/2020):[1]Morse keyboard https://create.arduino.cc/projecthub/Mr_Glenn/morse-keyboard-f8a59f?ref=user&ref_id=183831&offset=6
[2]GymGeneral https://create.arduino.cc/projecthub/Mr_Glenn/gymgeneral-a-sports-interval-timer-7a63f8?ref=user&ref_id=183831&offset=5
[3]Backlight pin problems https://forum.arduino.cc/index.php?topic=96747.0
[4]SD card list files https://www.arduino.cc/en/Tutorial/listfiles
[5]SD card read/write files https://www.arduino.cc/en/Tutorial/ReadWrite
[6]Morse speed http://www.kent-engineers.com/codespeed.htm
[7]Wikipedia Morse code https://en.wikipedia.org/wiki/Morse_code
Comments