Introduction to Morse Code
Okay, today’s lesson will be on blinking morse code through Arduino. Morse code is an ancient tongue spoken by grey haired wizards named Tim. It predates the internet, telephones, and radio. Now you, too, can become a master in a few short minutes.
What you’ll need:
- Code for controlling your Arduino through the Serial Monitor
- Knowledge on Morse Code! (look below)
Morse Code
- short mark, dot or “dit” (·) — “dot duration” is one time unit long
- longer mark, dash or “dah” (–) — three time units long
- inter-element gap between the dots and dashes within a character — one time unit long
- short gap (between letters) — three time units long
- medium gap (between words) — seven time units long
and can be written out in binary like this
- short mark, dot or “dit” (·) — 1
- longer mark, dash or “dah” (–) — 111
- intra-character gap (between the dots and dashes within a character) — 0
- short gap (between letters) — 000
- medium gap (between words) — 0000000
Every letter and some punctuation marks have their own code. A is 10 or dot-dash. @ is 100101 or dot-dash-dash-dot-dash-dot.
First steps
Let’s take a look at the following code:
#define ONCE (200) #define LED (13) // turn the light on void on() { digitalWrite(LED,HIGH); } // turn the light off void off() { digitalWrite(LED,LOW); } // short mark, dot or "dit" (·) — "dot duration" is one time // unit long void dot() { on(); delay(ONCE); } // longer mark, dash or "dah" (–) — three time units long void dash() { on(); delay(ONCE*3); } // short gap between any combination of dots and dashes void nextElement() { off(); delay(ONCE); } // short gap (between letters) — three time units long void nextLetter() { off(); delay(ONCE*3); } // medium gap (between words) — seven time units long void nextWord() { off(); delay(ONCE*7); } void setup() { pinMode(LED,OUTPUT); } void loop() { // S dot(); nextElement(); dot(); nextElement(); dot(); nextLetter(); // O dash(); nextElement(); dash(); nextElement(); dash(); nextLetter(); // S dot(); nextElement(); dot(); nextElement(); dot(); nextWord(); }
Code sequences
From the same Wikipedia page I pulled the complete list of characters A-Z, 0-9, and some punctuation. I made a list of their codes.
// 0 10 20 30 40 50 // 0123456789012345678901234567890123456789012345678901234 // static const char *letters = // "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,?'!/()&:;=+-_\"$@"; // each code represents a character. // 1 represents a dot and 0 represents a dash. // the order of these codes matches the order of the characters in // *letters. static const char *codes[] = { "10", // A, codes[0] "0111", // B, codes[1] "0101", // C "011", // D "1", // E "1101", // F "001", // G "1111", // H "11", // I "1000", // J "010", // K "1011", // L "00", // M "01", // N "000", // O "1001", // P "0010", // Q "101", // R "111", // S "0", // T "110", // U "1110", // V "100", // w "0110", // x "0100", // y "0011", // z "00000", // 0 "10000", // 1 "11000", // 2 "11100", // 3 "11110", // 4 "11111", // 5 "01111", // 6 "00111", // 7 "00011", // 8 "00001", // 9 "101010", // . "001100", // , "110011", // ? "100001", // ' "010100", // ! "01101", // / "01001", // ( "010010", // ) "10111", // & "000111", // : "010101", // ; "01110", // = "10101", // + "01110", // - "110010", // _ "101101", // " "1110110", // $ "100101", // @, codes[54] };
Great! We’ve almost figured out Morse Code. Now all we need is a method that tells Arduino how to turn binary into LED pulsing dots and dashes.
// blink sequence codes[j] void blinkCodeSequence(int j) { int i; for( i=0; i < strlen(codes[j]); ++i ) { // put a gap between pulses and only between the pulses. if( i>0 ) nextElement(); // pulse the light if( codes[j][i] == '0' ) dash(); if( codes[j][i] == '1' ) dot(); } }
Here, strelen() refers to the length of a string. If “j” is 1, then codes[j] is for B, codes[j] is “0111” and strlen() says that the sequence for “0111” is 4. Codes[j][i] refers to getting the character “i” in a sequence of j codes. For B codes[j][0] is “0”; codes[j][1], codes[j][2], and codes[j][3] are “1”.
Translating letters to sequences
Okay, now let’s translate some letters into sequences.
// find j such that codes[j] is the sequence for // character c void morsifyLetter(char c) { int j; for( j=0; j<strlen(letters); ++j ) { if( letters[j] == c ) { // letters[j] ia a match! That means codes[j] is // the code to use. blinkCodeSequence(j); // stop looping, quit morsifyLetter() right now return; } } // didn't find it... Serial.print("** Not found **"); }
I’m using the same technique to move through each of the letters. It’s important to note that the order of the letters has to match the order of codes.
Processing whole messages
We can begin processing whole messages, like so:
void processMessage(char *message) { int i, first; // is this the first letter of word? first = 1; // go through the characters sent by the PC user one by one for( i=0; i<strlen(message); ++i ) { // find the character in the list of *letters char c = message[i]; if( c == ' ' ) { Serial.print(" "); nextWord(); first = 1; Serial.print("\n"); // new line continue; } if( first != 1 ) { nextLetter(); } Serial.print(c); Serial.print(' '); // try to turn it into morse. morsifyLetter(c); first = 0; Serial.print("\n"); // new line } }
Result
Nice! Let’s take a look at our final result:
** Start ** > H .... E . L .-.. L .-.. O --- W .-- O --- R .-. L .-.. D -.. >
Hello world! The very basics of programming.
Questions
- How does strelen() know when to stop counting?
Final Thought
Special thanks to Alexander Kennedy for suggesting this idea.
Definitions
digitalWrite(x, y) | Delivers either a High or Low y value to a pin at x. |
strlen(x) | Provides the length of string x. |
pinMode(x, mode) | Configures pin x to either be mode Input or Output. |