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:

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

  1. short mark, dot or “dit” (·) — 1
  2. longer mark, dash or “dah” (–) — 111
  3. intra-character gap (between the dots and dashes within a character) — 0
  4. short gap (between letters) — 000
  5. 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.