Morse Code Translator

I am trying to create a project that converts a String that is entered at the top of the code into a series of on and off flashes of an LED (plugged into the D13 pin). The IDE does not report any syntax errors, but the code does not work.

#include "Morse.h"

String stuff = "Hello, world";
//Note: use "*" for apostrophe

byte pinNo = 13;
int dit = 750;

void morseWrite(char letter) {
  
  String thatLetter = MDefault;
  
  switch (letter) {
    case 'a':
      thatLetter = Ma;
      break;
    case 'b':
      thatLetter = Mb;
      break;
    case 'c':
      thatLetter = Mc;
      break;
    case 'd':
      thatLetter = Md;
      break;
    case 'e':
      thatLetter = Me;
      break;
    case 'f':
      thatLetter = Mf;
      break;
    case 'g':
      thatLetter = Mg;
      break;
    case 'h':
      thatLetter = Mh;
      break;
    case 'i':
      thatLetter = Mi;
      break;
    case 'j':
      thatLetter = Mj;
      break;
    case 'k':
      thatLetter = Mk;
      break;
    case 'l':
      thatLetter = Ml;
      break;
    case 'm':
      thatLetter = Mm;
      break;
    case 'n':
      thatLetter = Mn;
      break;
    case 'o':
      thatLetter = Mo;
      break;
    case 'p':
      thatLetter = Mp;
      break;
    case 'q':
      thatLetter = Mq;
      break;
    case 'r':
      thatLetter = Mr;
      break;
    case 's':
      thatLetter = Ms;
      break;
    case 't':
      thatLetter = Mt;
      break;
    case 'u':
      thatLetter = Mu;
      break;
    case 'v':
      thatLetter = Mv;
      break;
    case 'w':
      thatLetter = Mw;
      break;
    case 'x':
      thatLetter = Mx;
      break;
    case 'y':
      thatLetter = My;
      break;
    case 'z':
      thatLetter = Mz;
      break;
    case '0':
      thatLetter = M0;
      break;
    case '1':
      thatLetter = M1;
      break;
    case '2':
      thatLetter = M2;
      break;
    case '3':
      thatLetter = M3;
      break;
    case '4':
      thatLetter = M4;
      break;
    case '5':
      thatLetter = M5;
      break;
    case '6':
      thatLetter = M6;
      break;
    case '7':
      thatLetter = M7;
      break;
    case '8':
      thatLetter = M8;
      break;
    case '9':
      thatLetter = M9;
      break;
    case '.':
      thatLetter = MPer;
      break;
    case ',':
      thatLetter = MCom;
      break;
    case '?':
      thatLetter = MQues;
      break;
    case '*':
      thatLetter = MApos;
      break;
    case '!':
      thatLetter = MExc;
      break;
    case '/':
      thatLetter = MSlsh;
      break;
    case '(':
      thatLetter = MPar;
      break;
    case ')':
      thatLetter = MClos;
      break;
    case '&':
      thatLetter = MAmp;
      break;
    case ':':
      thatLetter = MCol;
      break;
    case ';':
      thatLetter = MSemi;
      break;
    case '=':
      thatLetter = MEqu;
      break;
    case '+':
      thatLetter = MPls;
      break;
    case '-':
      thatLetter = MMin;
      break;
    case '_':
      thatLetter = MUnd;
      break;
    case '"':
      thatLetter = MQuot;
      break;
    case '

Also, I created a library (Morse.h) that converts each character to a series of 0s (dots), 1s (dashes), 2s (these are filler characters), and 3s (to denote a space). This is below:

#define Ma "012222"
#define Mb "100022"
#define Mc "101022"
#define Md "100222"
#define Me "022222"
#define Mf "001022"
#define Mg "110222"
#define Mh "000022"
#define Mi "002222"
#define Mj "011122"
#define Mk "101222"
#define Ml "010022"
#define Mm "112222"
#define Mn "102222"
#define Mo "111222"
#define Mp "011022"
#define Mq "110122"
#define Mr "010222"
#define Ms "000222"
#define Mt "122222"
#define Mu "001222"
#define Mv "000122"
#define Mw "011222"
#define Mx "100122"
#define My "101122"
#define Mz "110022"
#define M0 "111112"
#define M1 "011112"
#define M2 "001112"
#define M3 "000112"
#define M4 "000012"
#define M5 "000002"
#define M6 "100002"
#define M7 "110002"
#define M8 "111002"
#define M9 "111102"
#define MPer "010101"
#define MCom "110011"
#define MQues "001100"
#define MApos "011110"
#define MExc "101011"
#define MSlsh "100102"
#define MPar "101102"
#define MClos "101101"
#define MAmp "010002"
#define MCol "111000"
#define MSemi "101010"
#define MEqu "100012"
#define MPls "010102"
#define MMin "100001"
#define MUnd "001101"
#define MQuot "010010"
#define MDol "0001001"
#define MAt "011010"
#define MSpac "322222"
#define MDefault "222222"

Finally, I was wondering if it is necessary for the massive switch statement to exist or if there is a more efficient way to perform the same task.:
      thatLetter = MDol;
      break;
    case '@':
      thatLetter = MAt;
      break;
    case ' ':
      thatLetter = MSpac;
      break;
    default:
      thatLetter = MDefault;
  }
 
  for(byte b = 1; b > 7; b++) {
    char beep = thatLetter.charAt(b);
   
    switch (beep) {
      case '0':
        digitalWrite(pinNo, HIGH);
        delay(dit);
        digitalWrite(pinNo, LOW);
        delay(dit);
        break;
      case '1':
        digitalWrite(pinNo, HIGH);
        delay(dit3);
        digitalWrite(pinNo, LOW);
        delay(dit);
        break;
      case '3':
        delay(dit
4);
    }
   
    delay(dit*3);

}
}
 
void setup() {
  pinMode(13, OUTPUT);
}
 
void loop() {
 
  stuff.toLowerCase();
  byte length = stuff.length();
  length += 1;
 
  for(int i = 1; i < length; i++) {
    char daLetter = stuff.charAt(i);
    morseWrite(daLetter);
  }
}


Also, I created a library (Morse.h) that converts each character to a series of 0s (dots), 1s (dashes), 2s (these are filler characters), and 3s (to denote a space). This is below:

§DISCOURSE_HOISTED_CODE_1§


Finally, I was wondering if it is necessary for the massive switch statement to exist or if there is a more efficient way to perform the same task.

You could simplify an enormous amount by making an array.

Could you give me an example of where the array would be used?

Consider rewriting your code using an array. Also using the string type is not efficient. In addition to that, there is no need to embed fillers in the definitions so you could use 0 for dots and 1 for dashes which can be coded binary into 1 byte/character but that will require you handle the length differently.

Your encoding system has me confused, frankly. Shouldn't it just consist of dots and dashes? And since you are using strings, why confuse everyone with 0, 1, 2?

#define Ma "012222"

Why not:

#define Ma ".-"   // A is dot dash

Consider rewriting your code using an array.

Are you saying that I should have one array with the list of characters and one with the corresponding series of dots and dashes?

you could use 0 for dots and 1 for dashes which can be coded binary into 1 byte/character

As far as I can tell, I cannot convert the dots and dashes into binary because I would lose the 0s at the beginning of some of the numbers, which are vital to Morse Code. For example, 01 (.-) in binary would just be 1, and the dot would become lost. Due to this, A (.-), T (-), U (..-), and V (...-) would have the same value.

Shouldn't it just consist of dots and dashes? And since you are using strings, why confuse everyone with 0, 1, 2?

I have no idea why I made these 1s and 0s. The 2s, though, were used so that every character would be 6 characters long. I'm sure there is a more efficient way to do this.

I looked around on Google for some Morse Code sketches, but as far as I could see they only confused the situation more. Try this which I just wrote:

// Morse code generator
// Author: Nick Gammon
// Date: 8th November 2014
// Version: 1


const byte LED = 13;

char * letters [26] = {
   ".-",     // 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
};

char * numbers [10] = 
  {
  "-----",  // 0
  ".----",  // 1
  "..---",  // 2
  "...--",  // 3
  "....-",  // 4
  ".....",  // 5
  "-....",  // 6
  "--...",  // 7
  "---..",  // 8
  "----.",  // 9
  };
  
const unsigned long dotLength = 100;  // mS
const unsigned long dashLength = dotLength * 3;  
const unsigned long wordLength = dashLength * 2; 

// get a line from serial
//  forces to upper case
void getline (char * buf, size_t bufsize)
{
byte i;
  
  // discard any old junk
  while (Serial.available ())
    Serial.read ();
    
  for (i = 0; i < bufsize - 1; )
    {
    if (Serial.available ())
      {
      int c = Serial.read ();
      
      if (c == '\n')  // newline terminates
        break;
      
      buf [i++] = toupper (c);
      } // end if available
    }  // end of for
  buf [i] = 0;  // terminator
  Serial.println (buf);  // echo what they typed
  }     // end of getline

void dot ()
  {
  digitalWrite (LED, HIGH);
  delay (dotLength);
  digitalWrite (LED, LOW); 
  delay (dotLength);
  }  // end of dot

void dash ()
  {
  digitalWrite (LED, HIGH);
  delay (dashLength);
  digitalWrite (LED, LOW); 
  delay (dotLength);
  }  // end of dash
    
void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  pinMode (LED, OUTPUT);
  }  // end of setup

void loop ()
  {
  Serial.println ("Enter your message ...");
  char buf [60];
  getline (buf, sizeof buf);
  
  
  // for each letter
  for (char * p = buf; *p; p++)
    {
    char c = *p;
    char * sequence = NULL;
    
    if (c >= 'A' && c <= 'Z')
      sequence = letters [c - 'A'];
    else if (c >= '0' && c <= '9')
      sequence = numbers [c - '0'];   
    else if (c == ' ')
      {
      delay (wordLength);   // gap between words
      continue;
      }
    
    // ignore not in table
    if (sequence == NULL)
      continue;
      
    // output sequence for one letter
    for (char * s = sequence; *s; s++)
      {
      if (*s == '.')
        dot ();
      else if  (*s == '-')
        dash (); 
      }
    // now a gap
    delay (dashLength);   
    }  // end of for each letter
      
  }  // end of loop

This code worked perfectly!
Thank you for your time. This also helped me to finally figure out how to use serial communication, which will be invaluable in the future.
I will edit the code to include punctuation marks soon.

Glad I could help.

Disclaimer: I typed in the Morse from Wikipedia and checked it once. You might want to check it twice (like Santa does) just to be sure.

I just double checked. The morse is correct. Thanks again!

This is the way i would have done it. I used excel to generate the constant arrays. Apologies for using the dealy(), this is just a quick example.

#include <ctype.h>

const int LEDPIN = 13;
const byte MinASCII = 34;//lowest character code
const byte MaxASCII = 90;//highest character code
const byte RANGE = MaxASCII-MinASCII+1;
const byte length[RANGE] = {6, 0, 0, 0, 0, 6, 6, 0, 0, 0, 6, 6, 6, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 0, 0, 0, 0, 6, 0, 2, 4, 4, 3, 1, 4, 3, 4, 2, 4, 3, 4, 2, 2, 3, 4, 4, 3, 3, 1, 3, 4, 3, 4, 4, 4};
const byte code[RANGE] = {18, 0, 0, 0, 0, 30, 45, 0, 0, 0, 51, 33, 42, 0, 31, 30, 28, 24, 16, 0, 1, 3, 7, 15, 7, 0, 0, 0, 0, 12, 0, 2, 1, 5, 1, 0, 4, 3, 0, 0, 14, 5, 2, 3, 1, 7, 6, 11, 2, 0, 1, 4, 8, 6, 9, 13, 3};
const int tUnit = 300;//ms for 1 time unit, dot or space

void setup(){
  pinMode(LEDPIN, OUTPUT);
  Serial.begin(115200);
  sendMorseStr("HELLO WORLD");
}//setup()

void loop(){}//loop()

void sendMorseChar(byte c){
  c = toupper(c);//only uppercase letters
  if(c<MinASCII) return;//out of range
  if(c>MaxASCII) return;//out of range
  c -= MinASCII;//c is valid, create index
  byte l = length[c];//get number of symbols
  if(l==0) return;//no code for this char
  byte s = code[c];//get code byte
  for(int i = 0; i<l; i++){//process bits in code byte
    digitalWrite(LEDPIN, 1);//turn led on
    delay(tUnit);//leave onfor at least 1 tUnit
    if(s&1) delay(2*tUnit);//symbol was dash, leave on for another 2 tUnits
    digitalWrite(LEDPIN, 0);
    s >>= 1;//shift bits
    delay(tUnit);//space between symbols
  }//for(i)  
  delay(2*tUnit);
}//sendMorseChar()

void sendMorseStr(char* s){
  while(byte c = *s++){//process all chars until c==\0
    if(c==' ') delay(5*tUnit);//space equals 5 tUnits
    else sendMorseChar(c);
  }//while(c)
}//sendMorseStr()

Hi,

[soapbox]
Its not dots and dashes, it di and dahs, as it sounds when it is sent.
[/soapbox]

Tom.... :slight_smile:

The International Morse Code encodes the ISO basic Latin alphabet, some extra Latin letters, the Arabic numerals and a small set of punctuation and procedural signals as standardized sequences of short and long signals called "dots" and "dashes", or "dits" and "dahs".

I think I am permitted to refer to them as dots and dashes. They got first billing.

Hi, it depends where ya was brung up.
Wikipedia!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Tom....... :slight_smile:

Any time that I have seen morse code written out, it is with dots and dashes. As they are being written out in Nick's code as dots and dashes, I would say that he has a right to call them dots and dashes.

nilton61:
Consider rewriting your code using an array. Also using the string type is not efficient. In addition to that, there is no need to embed fillers in the definitions so you could use 0 for dots and 1 for dashes which can be coded binary into 1 byte/character but that will require you handle the length differently.

Don't have to handle lengths differently.. just add a start bit.

// Typing on the fly -- I may be off in a few small details

unsigned char test = 0b00011101;  // Q with start bit. (--.-)

int len = 8;
while (bit8(test) == 0) {
  //Shift till we come to the start bit.
  test << 1;
  len--;
}

test << 1;

// Now handle the data
while (len > 8)  {
  mode = bit8(test);
  if (mode) {
    // send dash
  } else {
    // send dit
  }
  len--;
  // And shift the bits.
  test << 1;
}

awdairnner:
I would say that he has a right to call them dots and dashes.

Indeed, but it indicates conclusively that he is not a radio operator or telegraphist. :grin:

Likewise the matter of flashing a light with Morse Code - it can be done, but is very rarely used as it is not very practical.

Likewise the matter of flashing a light with Morse Code - it can be done, but is very rarely used as it is not very practical.

That is so. Now that I have a working code, I will replace the LED and digitalWrite functions with a small speaker and tone/noTone functions.

Likewise the matter of flashing a light with Morse Code - it can be done, but is very rarely used as it is not very practical.

If you are 15 years old and learning morse code during 'rest hour' at summer camp it works very well. Don't ask how I know.

Don

Now that I have a working code, I will replace the LED and digitalWrite functions with a small speaker and tone/noTone functions.

In spite of my previous post I have to agree that listening to a speaker is a better technique. At some point you will graduate from translating dits and dahs (or dots and dashes) into characters to recognizing the sound patterns themselves and translating those patterns into characters.

Don