Morse Code translator

I have a functioning Morse code translator, that me an some colleagues put together. It works great, but you have to input the text into the sketch through a function.

Now we are trying to be able to input text through the serial monitor, then get it to translate.

In the original one we used strings for the dots and dashes. Ill just post the sketch, so you can get a look at it.

Im looking for a little advice on whether I should just start from scratch. or if it is possible to make some slight changes to the original.

And what changes I could consider trying. Thanks

This is the functional sketch:

/*
  Convert Morse code from a character string
*/

#define debug true


int ledPin = 13;
int speakerPin = 9;
int pitch1 = 140;      //tone frequency
int pitch2 = 140;
int dot = 50;          // 250mS length of one dot
int dash = 3 * dot;     // Dash is equal to 3 dots in duration

// Morse code for Alphabet 
String morseATable[] = { ".-", "-...", "-.-.", "-..", ".", "..-.", "--.",   // A-G
                        "....", "..", ".---", "_._", ".-..", "--", "-.",    // H-N
                        "---", ".--.", "--.-", ".-.", "...", "-", "..-",    // O-U
                        "...-", ".--", "-..-", "-.--", "--.."               // V-Z
                      };

// Morse code for Numbers
String morseNTable[] = { "-----", ".----", "..---", "...--", "....-",       // 0-4
                        ".....", "-....", "--...", "---..", "----."         // 5-9
                      };

                     
void setup()
{
  pinMode(ledPin, OUTPUT);                    // set ledPin as OUTPUT
  pinMode(speakerPin, OUTPUT);                // set speakerPin to POUTPUT
  Serial.begin(9600);                         // Initialze Serial Monitor
  if (debug)
    Serial.println("\nBegin of Morse Code");    // "\n" produces a "newline" character

  convert2Morse("this is what it translates");               // String to be converted
}

void loop ()
{
     
  
  
}

/* 
  Description: Converts characters in a string to the Morse code sequence
  Argument:    String to be converted
  Returns:     nul
  Calls:       morseLED
*/ 
void convert2Morse(String message)
{
  for (int pos = 0; pos < message.length(); pos++)
    { 
      if (debug) 
        {
          Serial.print(message.charAt(pos));
          Serial.print("   ");
        }
      if ((message.charAt(pos) >= 97) && (message.charAt(pos) <= 122))    // Check for a lower case character
        morseLED(morseATable[message.charAt(pos)-97]);
      if ((message.charAt(pos) >= 48) && (message.charAt(pos) <= 57))     // Check for a number
        morseLED(morseNTable[message[pos]-48]);  
      if (message.charAt(pos) == 32)                                      // Check for a space
        morseLED("       ");                                              // A space is equal to seven dots
      if (debug)
        Serial.println();
    }
}

/* 
  Description: Passes individual dots and dashes (and Spaces) to flashLED handler
  Argument:    Morse Code sequence
  Returns:     nul
  Calls:       flashLED
*/ 
void morseLED(String sequence)
{  
  for (int pos = 0; pos < sequence.length(); pos++)
    flashLED(sequence.charAt(pos));
  delay(2 * dot);                                                             // Space between two letters is equal to three dots (one dash)
}

/* 
  Description: Produces a dot, dash or space flash on the LED
  Argument:    Individual dot or dash
  Returns:     nul
  Calls:       none
*/ 
  
void flashLED(char dotOrDash)
{
  if (debug)
    Serial.print(dotOrDash);
  if (dotOrDash == 46)              // ASCII code for a dot is 46
    {
      digitalWrite(ledPin, HIGH);   // turn the LED on
      tone (speakerPin, pitch1);
      delay(dot);                   // wait for period of the dot
      digitalWrite(ledPin, LOW);    // turn the LED off 
      noTone(speakerPin); 
      delay(dot);
    }
  else
    if (dotOrDash == 45)
      {                               // ASCII code for a dash is 45
        digitalWrite(ledPin, HIGH);   // turn the LED on 
        tone (speakerPin, pitch2);
        delay(dash);                  // wait for period of the dash
        digitalWrite(ledPin, LOW);    // turn the LED off 
        noTone(speakerPin); 
        delay(dot);
      }
    else
      if (dotOrDash == 32)            // ASCII code for a space is 32
        delay(dot);                   // No flashes, just a time delay
}

Hi welcome on the arduino forum.

Please use the # button when you post code. It provides tags to make the code more readable. You can modify your post (upper right) select the code and press the # button.

some tips:
if ((message.charAt(pos) >= 97) && (message.charAt(pos) <= 122)) // Check for a lower case character
==>
if ((message.charAt(pos) >= 'a') && (message.charAt(pos) <= 'z')) // no comments needed

same for checking digits and space

for the input question check - Serial.available() - Arduino Reference -

and check - avr-libc: Modules - can all be used in Arduino

partial code (not tested) to get you started

#include <ctype.h>  // !!

void setup()
{
  // ....
}

void loop ()
{
  if (Serial.available() > 0)
  {
    char c = Serial.Read();
    void convert2Morse(char c);
  }
}

void convert2Morse(char c)
{
  if (isalpha(c))     // see - http://www.nongnu.org/avr-libc/user-manual/group__ctype.html - can all be used in Arduino
  { 
    c = tolower(c) - 'a';
    morseLED(morseATable[c]);
  }
  // isdigit() and isspace() sort of similar 
}

So for example you want to type in
DQ DX DQ DX
and have it output
..-. ..- -.- --.- or similar?

I'd be tempted to do this:

if (Serial.available()>0){  // something entered?
incomingByte = Serial.read(); // read it
switch(incomingByte){
case 'D':
Serial.print ("-.--");
Serial.print(" ");
break;
case 'Q':
Serial.print ("-.-");
Serial.print(" ");
break;
//etc.
}

But this does really call for an array.

all the numbers have a value from 48 to 57
all the capital letters have a value from 65 to 90
all the lower case letters have value from 97 to 122
punctuation is in 2 groups, starting at 32
So subtract 32 from everything, and the morse data for each character in an array with 0,1 representing the dots & dashes.
so read the incoming byte, look up the byte in the array, and interpret on fly to make your dot & dash tones.
Each character is 1 to 5 "bits", use the upper 3 bits of the byte to indicate if you have 1,2,3,4, or 5 dots/dashes to send out.

if (Serial.available()>0){  // something entered?
incomingByte = Serial.read(); // read it
valueLookup = incomingByte - 32;
// do some checking to make sure characters are acceptable, then
outputCode = lookupArray[valueLookup];
//cycle thru the byte
numberBits = (outputCode>>5) & 0x07;  // get the upper three bits
outputCode =  outputCode & 0x1F; // get the data bits
maskBit = 1; // set for 1st  bit
for (x = 1; x<(numberBits+1); x=x+1){ // loop thru 1,2,3,4,5 "bits"
outputBit = outputCode & maskBit;  // determine if bit is 0/1
if (outputBit >0){
// call the dash function
}
else {
// call the dot function
}
// call the dot-dash time gap function
maskBit = maskBit<1; // set for next bit
}  // end character send
//call the character time gap function
} // end read character from serial

so something along these lines.

Nice variation !

FYI, I have a full-fledged Morse code translator project here:

Just search on the page for Morse. You can enter code on the on-board keypad or serial monitor if you modify one line of code.

Thanks robtillaart - I thought it was pretty clever, gets away from strings, and takes full advantage of the byte being stored.
Just hope it doesn't turn out to be a blatant ripoff of something that already exists!
Have been thinking about this kind of scheme to modify my matrix scrolling code to do similar, and then be able to store 1000+ character messages in EEPROM; pull the next byte from EEPROM, look up the font in a progmen array, and plop it onto the end of the displayArray, ready to be scrolled across the matrix.

@CrossRoads
text = 26 chars +10 digits + some ,.;'[] < 64 chars so fit in 6 bits ==> 4 chars in 3 bytes... 512 bytes eeprom can contain 682 chars already.

Here's another Morse example to steal code from: this one is morse-enabled Bitlash:

You can use commands at the serial terminal to send morse:

…optionally...
> wpm(15);   // set words per minute, default 15
> freq(800);  // set sidetone frequency, default 800 hz

…then...
> printm("Uptime is %d", millis);    // send output in morse

There's a PTT output, a tone output, and a continuous output for running LED's; you can change these in the example code. The morse transmission is non-blocking so your Bitlash can be doing other things while sending buffered morse output.

Here's a function that sends station ID every 10 minutes:

function stationid {printm("W1AW");}
function startup {run stationid, 10*60*1000;}

Install Bitlash, then in the IDE: File -> Examples -> bitlash -> morse2, then Upload.

-br

Edit… the encoding for the morse table might be worth a look.

//////////
//
//	The Morsetab
//
// Each value in the table is bit-encoded as lllddddd, where:
//	lll is 3 bits of length [0..7] and 
//	ddddd is 5 bits of morse data, 1==dah and 0==dit
// since there are only 5 bits for morse data we handle 6 element symbols as a special case
// they are stored with a length of zero and an index into 'm6codes', an exception array below

robtillaart:
@CrossRoads
text = 26 chars +10 digits + some ,.;'[] < 64 chars so fit in 6 bits ==> 4 chars in 3 bytes... 512 bytes eeprom can contain 682 chars already.

Not sure what you're saying here. For my use, I have font 'library' in progmen, will accept text message from serial monitor, interpret that as lookup into the font library, store that look in eeprom. For playback, pull the pointer from eeprom, read the character from the font library, write that data to the max7219.
fonts are 5 bytes each, so if ABCDE came in for example and they were at library locations 0,5,10,15,20 would store 0,1,2,3,4
and at playback time would pull bytes (0,1,2,3,4) for A, (5,6,7,8,9) for B, (10,11,12,13,14) for C. That way I could store a 1022 character message in EEPROM. I currently use addresses 0,1 to store the length of the message.
For morse code use, not sure how bit packing helps, loses the length info which I think you need to know so you can tell how many dots-dashes are in the letter.

I really do appreciate all of the insight and ideas. I have some homework to do though since, I've kinda just started at this stuff.

thanks again for pointing me in the right direction.