A better way to store Morse code alphabet

Hi, so i am relativiere new to this. I am trying to create a more code alphabet, so that in the loop i can just call letters e.g. void loop() {

a()
b()
}

etc.

But i dont want to create a function for every letter so i am using an array:

char * letters [26] = {
".-", // A1
"-...", // B2
"-.-.", // C3
"-..", // D4
".", // E5
"..-.", // F6
"--.", // G7
"....", // H8
"..", // I9
".---", // J10
"-.-", // K11
".-..", // L12
"--", // M13
"-.", // N14
"---", // O15
".--.", // P16
"--.-", // Q17
".-.", // R18
"...", // S19
"-", // T20
"..-", // U21
"...-", // V22
".--", // W23
"-..-", // X24
"-.--", // Y25
"--.." // Z26
};

char * numbers [10] =
{
"-----", // 0
".----", // 1
"..---", // 2
"...--", // 3
"....-", // 4
".....", // 5
"-....", // 6
"--...", // 7
"---..", // 8
"----.", // 9
};

how do i go about accesing individual letters and then making an led flash that many times
thx in advance

Using array subscripts.

Use bits (0 = dot, 1 = dash). 26 letters each taking 4 bits = 13 bytes, 10 numbers each taking 5 bits = 10 bytes.

in C# I use Dictionary<TKey, TValue>

        public static  Dictionary<char, String> morse = new Dictionary<char, String>()
            {
                {'A' , ".-"},
                {'B' , "-..."},
                {'C' , "-.-."},
                {'D' , "-.."},
                {'E' , "."},
                {'F' , "..-."},
                {'G' , "--."},
                {'H' , "...."},
                {'I' , ".."},
                {'J' , ".---"},
                {'K' , "-.-"},
                {'L' , ".-.."},
                {'M' , "--"},
                {'N' , "-."},
                {'O' , "---"},
                {'P' , ".--."},
                {'Q' , "--.-"},
                {'R' , ".-."},
                {'S' , "..."},
                {'T' , "-"},
                {'U' , "..-"},
                {'V' , "...-"},
                {'W' , ".--"},
                {'X' , "-..-"},
                {'Y' , "-.--"},
                {'Z' , "--.."},
                {'0' , "-----"},
                {'1' , ".----"},
                {'2' , "..---"},
                {'3' , "...--"},
                {'4' , "....-"},
                {'5' , "....."},
                {'6' , "-...."},
                {'7' , "--..."},
                {'8' , "---.."},
                {'9' , "----."},
            };

to find an element

          if (morse.ContainsKey(c))
                nextElement = morse[c];

in C probably use an array of struct

void OutputMorse(const char character)
{
  char * morse = 0;


  if (character >= 'A' && character <= 'Z')
  {
    morse = letters[character - 'A');
  }
  else if (character >= 'a' && character <= 'a')
  {
    morse = letters[character - 'a');
  }
  else if (character >= '0' && character <= '9')
  {
    morse = numbers[character - '0');
  }


  if (morse != 0)
  {
    while (*morse != '\0')
    {
      if (*morse == '.')
        Serial.print("dot ");
      if (*morse = '-')
        Serial.print("dash ");
      morse++;
    }
  }
}

morse = letters[character - 'A');

A wee typo which should be:

morse = letters[character - 'A'];

Also cut and pasted the error twice.
But otherwise, the way I've always generated morse code.

Pete

Using strings is a horrible way to store Morse code. Here's the way I use it:

char letterTable[] = {                 // Morse coding
  0b101,              // A
  0b11000,            // B
  0b11010,            // C
  0b1100,             // D
  // the rest follow the same pattern

The Morse code for the letter 'A' is ".-" (dit dah). In the array, the highest bit with a 1 is a sentinel value. When that is read, it and all higher bits are discarded, but all lower bits that follow define the letter. After discarding the sentinel marker bit for 'A', we are left with 01, where a 0 bit is interpreted as a dit and a 1 bit is a dah. The letter 'B' is dah-dit-dit-dit. The same approach may be used for numbers and punctuation.

This code locates the sentinel:

  for (i = 7; i >= 0; i--)    // Read from Most Significant bit to the first 1 bit then...
    if (code & (1 << i))     // ...leave the loop
      break;

From there it's easy to decode the letter. The advantage is twofold: 1) a single bytes is used for all Morse elements, and 2) numerics process faster than string functions.

1 Like

That's not particularly efficient on most Arduinos because of the number of shifts that are performed.

  for (i = 7; i >= 0; i--)
    if (code & (1 << i))

On average that will require about 22 shifts for each letter. The code in #4 will be faster.

Pete

testing the bit rather than shifting for the sentinel bit , does that come down to the same thing ? use the 3 higher order bit to define the number of bit ? either way i am with a single bit per letter, and time is not the resource that is scarce.

This is some old C code, but it should run fine on Arduino:

struct __attribute__((__packed__)) MORSE {
    unsigned char length : 3;
    unsigned char value : 5;
};

const MORSE MORSE_LETTERS[] = {
    {2, 0b00001}, //A
    {4, 0b01000}, //B
    {4, 0b01010}, //C
    {3, 0b01000}, //D
    {1, 0b00000}, //E
    {4, 0b00010}, //F
    {3, 0b00110}, //G
    {4, 0b00000}, //H
    {2, 0b00000}, //I
    {4, 0b00111}, //J
    {3, 0b00101}, //K
    {4, 0b00100}, //L
    {2, 0b00011}, //M
    {2, 0b00010}, //N
    {3, 0b00111}, //O
    {4, 0b00110}, //P
    {4, 0b01101}, //Q
    {3, 0b00010}, //R
    {3, 0b00000}, //S
    {1, 0b00001}, //T
    {3, 0b00001}, //U
    {4, 0b00001}, //V
    {3, 0b00011}, //W
    {4, 0b01001}, //X
    {4, 0b01011}, //Y
    {4, 0b01100}  //Z
};

const MORSE MORSE_DIGITS[] = {
    {5, 0b11111}, //0
    {5, 0b01111}, //1
    {5, 0b00111}, //2
    {5, 0b00011}, //3
    {5, 0b00001}, //4
    {5, 0b00000}, //5
    {5, 0b10000}, //6
    {5, 0b11000}, //7
    {5, 0b11100}, //8
    {5, 0b11110}  //9
};

void morse(MORSE code)
{
    for (int bit = code.length; bit > 0; bit--)
        if (code.value & (1 << (bit - 1))) Serial.print("dash ");
        else Serial.print("dot ");
    Serial.println();
}

void morse(char* message)
{
    while (*message > 0)
    {
        char cc = *message;
        if (cc == 32) Serial.println(); //New line as space
        else if ((cc >= 48) && (cc <= 57)) morse(MORSE_DIGITS[cc - 48]); //0..9
        else if ((cc >= 65) && (cc <= 90)) morse(MORSE_LETTERS[cc - 65]); //A..Z
        else if ((cc >= 97) && (cc <= 122)) morse(MORSE_LETTERS[cc - 97]); //a..z
        else Serial.println("?"); //Illegal character
        message++;
    }
}

void setup() {
  Serial.begin(9600);
  while (!Serial) ;
  morse("Hello World 123");
}

void loop() {}

It may not be the fastest, but it should be the least memory consuming :slight_smile:

Danois90:
It may not be the fastest,

...or the most legible :frowning:else if ((cc >= 48) && (cc <= 57))

Hint:else if ((cc >= '0') && (cc <= '9'))

el_supremo:
That's not particularly efficient on most Arduinos because of the number of shifts that are performed.

  for (i = 7; i >= 0; i--)

if (code & (1 << i))



On average that will require about 22 shifts for each letter. The code in #4 will be faster.

Pete

When using Morse code, speed is really not a factor. Any ham who can send/receive at 30 words per minute is probably in the top 5% in speed, which is about 400ms per character. I think a ROL instruction is one clock cycles, so even with 22 shifts, it's still going to be efficient enough to get the job done.

econjack:
Using strings is a horrible way to store Morse code

No, it isn't horrible.

The advantage is twofold: 1) a single bytes is used for all Morse elements,

Yes, it uses less memory.

  1. numerics process faster than string functions.

They aren't faster the way you did it.

Pete

It did a timing test for the Morse code translation. John's version is the fastest and does use strings. The second fastest is Danois90's approach, but it's almost 7 times slower than John's. It also has a problem in that punctuation in Morse is 6 characters, so you would have to do interpretation, perhaps saying if the top 3 bits are 0, it's a punctuation mark. My version came in dead last and was about 3 times slower that Danois90's. John's used the most memory and mine the least, but the difference is only 10% and all were less than 2.5K.

Conclusion: I was wrong...strings are not a horrible way to process text into Morse code.

However, you can make an improvement to your code which will significantly speed it up.

  uint8_t mark_bit = 0x80;
  for (i = 7; i >= 0; i--) {    // Read from Most Significant bit to the first 1 bit then...
    if (code & mark_bit)     // ...leave the loop
      break;
    mark_bit >>= 1;
  }

This might be a wee bit faster yet (not that it matters :slight_smile: ):

  for (uint8_t mark_bit = 0x80; mark_bit; mark_bit >>= 1) {    // Read from Most Significant bit to the first 1 bit then...
    if (code & mark_bit)     // ...leave the loop
      break;
  }

In either case the average number of shifts is now about 3.

Pete