Morse Code Translator

Hi Arduino Forum. I have an Arduino Uno R3 and am trying to make the arduino translate some morse code from a string in the code. As well as I am also trying to have two LED’s blink when it is running and when it is not a green LED to light up to sort of signal that it is ready for new code or for a reset and/or that it is done “talking”. This is very frusterating as I have been working for a long time on it and it only works when one letter is imputed.
Code is attached.

MorseCodeTranslator.ino (8.76 KB)

  for (int i = 0; i < sizeof(stringToMorseCode) - 1; i++)

The function to determine the length of a string is NOT sizeof(). Most people use strlen().

dan0314:
This is very frusterating as I have been working for a long time on it and it only works when one letter is imputed.

Try this code, it can translate from ASCII text to MORSE code and the other way round:

/* Serial Morse Encoder and Decoder
 *  written by 'jurs' for Arduino forum
 *  Usage: 
 *  Use the serial monitor to send ASCII text ==> will be encoded to morse code
 *  Use the serial monitor to send dots and dashes ==> will be decoded to ASCII
 */

char morseCode[]={ // first bit set tells where morse encoding starts
  B00000101, // A = .-
  B00011000, // B = -...
  B00011010, // C = -.-.
  B00001100, // D = -..
  B00000010, // E = .
  B00010010, // F = ..-.
  B00001110, // G = --.
  B00010000, // H = ....
  B00000100, // I = ..
  B00010111, // J = .---
  B00001101, // K = -.-
  B00010100, // L = .-..
  B00000111, // M = --
  B00000110, // N = -.
  B00001111, // O = ---
  B00010110, // P = .--.
  B00011101, // Q = --.-
  B00001010, // R = .-.
  B00001000, // S = ...
  B00000011, // T = -
  B00001001, // U = ..-
  B00010001, // V = ...-
  B00001011, // W = .--
  B00011001, // X = -..-
  B00011011, // Y = -.--
  B00011100, // Z = --..
  B00111111, // 0 = -----
  B00101111, // 1 = .----
  B00100111, // 2 = ..---
  B00100011, // 3 = ...--
  B00100001, // 4 = ....-
  B00100000, // 5 = .....
  B00110000, // 6 = -....
  B00111000, // 7 = --...
  B00111100, // 8 = ---..
  B00111110, // 9 = ----.
};

#define NUMCHARS sizeof(morseCode)

char morseChars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

void sendMorseChar(char c)
{
  // invalid ASCII chars will not be encoded into anything
  // valid ASCII chars are encoded into morse dots/dashes
  // then finish with sending a space character ' '
  char* found= strchr(morseChars, c);
  if (found==NULL) return;
  else
  {
    byte morse= morseCode[found-morseChars];
    boolean firstBitFound=false;
    for (int i=7; i>= 0; i--) 
    {
      byte thisBit= bitRead(morse,i);
      if (firstBitFound)
      {
        if (thisBit) Serial.print('-');
        else Serial.print('.');
      }
      else if (thisBit) firstBitFound=true;
    }
  }
  Serial.print(" ");
}

char handleMorseInput(char c)
{ // char c==0 is for clearing internal receiveBuf
  // char c==1 is for evaluating internal receiveBuf
  static byte receiveBuf;
  if (c==0) 
  { 
    receiveBuf=1; // set 0-bit
    return 0;
  }
  else if (c==1)
  {
    for (int i=0;i<NUMCHARS;i++)
    {
      if (receiveBuf==morseCode[i])
      {
        return morseChars[i];
      }
    }
    return('*'); 
  }
  else if (c=='.') receiveBuf= receiveBuf<<1;
  else if (c=='-') receiveBuf= (receiveBuf<<1) | 1;
  return 0;
}

void handleSerialInput()
{
  static boolean isMorseInput=false;
  if (!Serial.available()) return;
  char c=Serial.read();
  // automatic detection of morse or ASCII input
  if (c=='.' || c=='-') 
  {
    if (!isMorseInput)
    { // switch to morse input and clear input buffer
      isMorseInput= true;
      handleMorseInput(0);
    }
    handleMorseInput(c);
  }
  else
  {
    if (isMorseInput)
    {
      char dc= handleMorseInput(1); // retrieve decoded char from input buffer
      if (dc>32) Serial.print(dc);
      isMorseInput=false;
    }
    else if (c=='\n') Serial.println();
    else if (c==' ')  Serial.print(' ');
    else if (c>32) // filter out control characters and international special characters
    {
      sendMorseChar(toupper(c));
    }
  }
}

void setup() {
  Serial.begin(9600);
}

void loop() {
  handleSerialInput();
}

Input is taken from the serial monitor.

If you want to see additional blinking LED output instead of output on Serial only, and if you want to set a busy/timeout flag/LED while sending, I think this can be added easily.

jurs:
Try this code, it can translate from ASCII text to MORSE code and the other way round:

/* Serial Morse Encoder and Decoder

*  written by ‘jurs’ for Arduino forum
*  Usage:
*  Use the serial monitor to send ASCII text ==> will be encoded to morse code
*  Use the serial monitor to send dots and dashes ==> will be decoded to ASCII
*/

char morseCode={ // first bit set tells where morse encoding starts
  B00000101, // A = .-
  B00011000, // B = -…
  B00011010, // C = -.-.
  B00001100, // D = -…
  B00000010, // E = .
  B00010010, // F = …-.
  B00001110, // G = --.
  B00010000, // H = …
  B00000100, // I = …
  B00010111, // J = .—
  B00001101, // K = -.-
  B00010100, // L = .-…
  B00000111, // M = –
  B00000110, // N = -.
  B00001111, // O = —
  B00010110, // P = .–.
  B00011101, // Q = --.-
  B00001010, // R = .-.
  B00001000, // S = …
  B00000011, // T = -
  B00001001, // U = …-
  B00010001, // V = …-
  B00001011, // W = .–
  B00011001, // X = -…-
  B00011011, // Y = -.–
  B00011100, // Z = --…
  B00111111, // 0 = -----
  B00101111, // 1 = .----
  B00100111, // 2 = …—
  B00100011, // 3 = …–
  B00100001, // 4 = …-
  B00100000, // 5 = …
  B00110000, // 6 = -…
  B00111000, // 7 = --…
  B00111100, // 8 = —…
  B00111110, // 9 = ----.
};

#define NUMCHARS sizeof(morseCode)

char morseChars=“ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”;

void sendMorseChar(char c)
{
  // invalid ASCII chars will not be encoded into anything
  // valid ASCII chars are encoded into morse dots/dashes
  // then finish with sending a space character ’ ’
  char* found= strchr(morseChars, c);
  if (found==NULL) return;
  else
  {
    byte morse= morseCode[found-morseChars];
    boolean firstBitFound=false;
    for (int i=7; i>= 0; i–)
    {
      byte thisBit= bitRead(morse,i);
      if (firstBitFound)
      {
        if (thisBit) Serial.print(’-’);
        else Serial.print(’.’);
      }
      else if (thisBit) firstBitFound=true;
    }
  }
  Serial.print(" ");
}

char handleMorseInput(char c)
{ // char c==0 is for clearing internal receiveBuf
  // char c==1 is for evaluating internal receiveBuf
  static byte receiveBuf;
  if (c==0)
  {
    receiveBuf=1; // set 0-bit
    return 0;
  }
  else if (c==1)
  {
    for (int i=0;i<NUMCHARS;i++)
    {
      if (receiveBuf==morseCode[i])
      {
        return morseChars[i];
      }
    }
    return(’*’);
  }
  else if (c==’.’) receiveBuf= receiveBuf<<1;
  else if (c==’-’) receiveBuf= (receiveBuf<<1) | 1;
  return 0;
}

void handleSerialInput()
{
  static boolean isMorseInput=false;
  if (!Serial.available()) return;
  char c=Serial.read();
  // automatic detection of morse or ASCII input
  if (c==’.’ || c==’-’)
  {
    if (!isMorseInput)
    { // switch to morse input and clear input buffer
      isMorseInput= true;
      handleMorseInput(0);
    }
    handleMorseInput(c);
  }
  else
  {
    if (isMorseInput)
    {
      char dc= handleMorseInput(1); // retrieve decoded char from input buffer
      if (dc>32) Serial.print(dc);
      isMorseInput=false;
    }
    else if (c==’\n’) Serial.println();
    else if (c==’ ‘)  Serial.print(’ ');
    else if (c>32) // filter out control characters and international special characters
    {
      sendMorseChar(toupper(c));
    }
  }
}

void setup() {
  Serial.begin(9600);
}

void loop() {
  handleSerialInput();
}



Input is taken from the serial monitor.

If you want to see additional blinking LED output instead of output on Serial only, and if you want to set a busy/timeout flag/LED while sending, I think this can be added easily.

Which pins are the speaker and LED’s connected too?

dan0314:
Which pins are the speaker and LED's connected too?

Currently: NONE

The code I posted above just handles input/output from and to Serial. No LEDs. No speakers.

If you want to drive external devices like LEDs or speakers, you'd have to change the function "sendMorseChar(char c)" a little bit.

For the speaker you also would have to know:
Is it an active piezo speaker driven by HIGH (sound ON) and LOW (sound OFF) setting?
Or is it a passive speaker that needs to be driven by the "tone()" function?

If you need assistance in modifying the "sendMorseChar" function, please let me know more about

  • which type of speaker you are using
  • the "dot time" you want to use for timing the Morse output

[Edit] I think I found the information in the source code of the file attachment you posted with the initial posting:

int dotLen = 100;     // length of the morse code 'dot'
int led12 = 12;      // blink an led on output 12
int led6 = 6;        // blink an led on output 6
// seems to be a passive speaker on pin-8 which is driven at 1000 Hz frequency using tone() function
int audio8 = 8;      // output audio on pin 8
int note = 1000;      // music note/pitch
// usage of pin-5 is not really clear to me
int led5 = 5;

Some of your calculations about durations seem to be wrong:

int Spaces = dotLen * 100;     // length of the spaces between characters
int wordPause = dotLen * 100;  // length of the pause between words

I think the correct values would be:

int Spaces = dotLen * 3;     // length of the spaces between characters
int wordPause = dotLen * 7;  // length of the pause between words

So as of now I have abandoned the “working light” and I have a working code. The only thing I am trying to do now is when I type “Star Wars” into the serial monitor then it will play the imperial march. So I have a list of defines and am trying to tone it but I just don’t know where I can put it. Code is once again attached.

ImperialMarch.ino (4.99 KB)

WorkingMorseCode.ino (6.04 KB)

dan0314:
So as of now I have abandoned the “working light” and I have a working code. The only thing I am trying to do now is when I type “Star Wars” into the serial monitor then it will play the imperial march.

I don’t quite understand.

You have a morse encoder, type in “Star Wars”, then this will be encoded to:

... - .- .-.  .-- .- .-. ...

and after the morse code has been sent you’d like to play a melody?

Your “working code” contains some very silly mistakes, like for example:

  char info; // this defines a single "char"
...
    info = Serial.read();  // read from Serial into a single "char"
...
   if (info >= 'Star Wars' && info <= 'z')  // compare a single char against a string or what?

BTW:
Single chars are contained in single quotes, like ‘a’ or ‘z’
Strings are contained in double quotes, like “Star Wars” or “Everything is going extremely well.”

jurs:

// the leftmost '1' bit marks the boundary of the morse sequence

// where a ‘0’ means a ‘.’ (dot) and  ‘1’ means a ‘-’ (line)
char morseCode={
 B00000101, // A   .-
 B00011000, // B   -…

 B01001100, // ?   …–…   enough bits even for the longest ones

};



...

I too made a morse generator (based on interrupts and timer1); in the first version, I stored the
morse map into an array of structures, like this:

{‘A’,".-"},
{‘B’,"-…"},

{’?’,"…–…"},

Normally, this ^ ‘by the book’ approach seemed logical back then… However, it turned out that
someone else already made a morse like that (long before me…) So, because I don’t want to be
accused of plagiarism, I tried to come up with something else; possibly smaller or faster…
So after squeezing my brain for inspiration, this (lousy) one came out :

{‘A’,2,B1 }, // The second (middle) value holds/indicates the “width” of the morse sequence.
{‘B’,4,B1000 },

{’?’,6,B1100 },

and then I slept well until I found your idea !

Dear jurs , PLEASE, will you let me use your <<the leftmost ‘1’ marks the boundary>> idea
in my Morselizer ? I will even name the array ‘jurs’ and mention why in the comments.

What say you?