Layout for a Morse Code Encoder Decoder

I've always wanted to do this project which will allow me to send a message in Morse code and in return, the output will be in English. Similarly, if I enter an English word/sentence, I want the output to be in dots and dashes.
I want to use an LCD display for the output.

Part 1 - Morse to English:
When I give a Morse code input, I want to do it with a button connected to a piezo buzzer. The buzzer sends a pulse signal, which is read by a sound sensor, and feeds the info to the Arduino.
When I press the button for a second, it will be a dot and if I press it for three seconds, it is a dash. I am assuming there is some way to record the time a button is pressed. If so, how to do it, is a major question for me.

Part 2 - English to Morse:
I want to feed a string into the serial window. I want the output to be coming from the piezo buzzer and I want the LCD to show me the same output simultaneously in dashes.
Now, I think I can make the piezo buzzer sing for me the way I want it to, but I'm not really sure how to receive an input string other than through a keyboard. I want this project​ to be fitted in a box, or to be made as compact as possible. Is there any way to achieve this?

So these are my queries.
In addition, I want to know if there is any way I can make the whole project a little more efficient. I know there are somethings I can compress with regard to the Morse to English bit. I'd be really happy if anyone guided me on this here.

Part 0 - detecting sound This is more difficult than you think. Leave this part until last unless it is a necessary component of your assignment. Make it work with a button directly connected to the Arduino.

Thanks for the really quick reply, MorganS. I think this is what you want me to do.
Press button once - dot
Long press - dash.
Am I understanding you correctly?

Yes, just hook up the button directly to the Arduino. Then you can test out any Morse libraries you might download and get the LCD working.

Getting the sound detection working is probably a nice-to-have, so do that last if you have time.

Oh, man. This just reduces my project into nothing. I was actually thinking this might be hard to do, and I might learn more stuff.
Learning that there's a library for this makes me feel really stupid.
At the risk of sounding annoying; if there was no library for Morse code, how do you think I should be doing this?
Again, I'm really sorry if I'm wasting your time here.

As for the sound detection, bit, I was thinking of using the BC 547 and a mic to detect sound (like the ones in the dancing led circuit) and then send the info the Arduino board.

Most of the sound detectors you might come across are beat detectors. They will "hear" the initial part of the sound and then have no response to the sustained dash or dot. Making something that will hold the digital input high while it detects an appropriate frequency is not too difficult but it would be the most difficult part of that project for most experimenters.

Without a library? Start coding a state machine or parser structure. It seems doable for the specification you have given us.

vivekvs97:
So these are my queries.
In addition, I want to know if there is any way I can make the whole project a little more efficient. I know there are somethings I can compress with regard to the Morse to English bit. I'd be really happy if anyone guided me on this here.

About two years ago I posted this small little program in this forum for another guy who asked about Morse encoding/decoding.

This piece of code is for the serial monitor only (no button, no piezo/speaker, no external hardware):

The Morse code table is stored in a very efficient way: Each character encoding is stored in a single byte, so the whole table of 26 letters and 10 digits is 36 bytes in array size only.

/* 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)
    {
      c= handleMorseInput(1); // retrieve decoded char from input buffer
      Serial.print(c);
      isMorseInput=false;
    }
    else if (c==' ') Serial.print(' ');
    else if (c=='\n') Serial.println();
    else if (c>32) // filter out control characters and international special characters
    {
      sendMorseChar(toupper(c));
    }
  }
}

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

void loop() {
  handleSerialInput();
}

This is the inner working: When sending plain English text from the serial monitor, it is translated to dots and dashes in Morse code, which aredisplayed in the serial monitor.

Or it can work the other way round: When sending dots and dashes in Morse code, they are translated back to plain English text, which is displayed in the serial monitor.

Is this a start for you?
Please let me know what you think!

Please feel free to ask in case you have questions about the code, or possible extending the code with handling piezo speaker for tone output or a push button for Morse input.

1 Like

Really sorry for the long delay between replies, guys.
@jurs: I tried the code, and it works perfectly well. I think this is the code I am going to use for my project.
@MorganS: Per your advice, I'll focus on sound detection last. The state machine is a great idea, I'll try it out.
Thanks for the help, guys. I'll let you know if there is any update

Hey, guys, sorry for the extremely long delay between updates. I have been learning Morse Code and at the same time I'm trying a piecemeal method of working with the individual components (LCD Display, Buzzer, Button debouncing). I also attended a small 3 day workshop on C++ where they covered a few basics.

@jurs: The code you sent me was incredibly helpful. However, I only vaguely understand the code. I don't understand why certain Boolean Variables are set to be true and why the handleMorseCInput() and handleSerialInput() functions work (I don't understand how the buffer is cleared in handleSerialInput function).

Moreover, I tried to tweak your code a little to see if I understood it.

This is what I tried:

 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 (thisBit) firstBitFound=true;

      if (firstBitFound)
      {
        if (thisBit) Serial.print('-');
        else Serial.print('.');
      }
    }
  }

While it shows the correct character whenever I enter a morse code, it does not do the reverse.

If I enter A, the output should be .-, but it shows me -.-

Basically, if I enter any character, the output shows me an extra - added to it's left. I know that's because of how each character is initialized in the array morseCode[].

So, what is the error in the logic I've used?