Feedback on second project: reading morse code

Hi all,

My second Arduino project is about reading morse code. I have a board with a tactile switch which turns a LED on when pressed. An LDR connected to the same board reads the amount of light it receives. When LED is off pin 0 receives analogRead value < 100, when it's on (tactile switch pressed) it's well above it (300-400).

When the LDR is above the 100 treshold (switch is pressed and LED is on) it starts counting the number of milliseconds until the treshold gets below that (switch is released, LED is off). When the number milliseconds is lower than 700 ms, it's a short signal (dit), when it's between 700 and 1200 ms it's a long signal (dah), when it's above 1200 ms it's a space (signaling the start of the next letter).

This is all working fine, and I see that it gets correctly printed to the Serial, but when I want to compare the char array to convert morse (like "LLL") back to a alphabetic letter ("O" in this example) it doesn't work. The evaluation never returns true.

This is my code:

/* Receiving morse code - v0.1 */
 
int photocellPin = 0;     // LDR connected to pin 0
int photocellReading;     // the analog reading from LDR
long onmillis = 0;        // reset onmillis
long timeon = 0;          // reset timeon
  
void setup(void) {
  Serial.begin(9600);   
}

char morse[4];
 
void loop(void) {
  photocellReading = analogRead(photocellPin);  
 
  if (photocellReading < 100) {
    // off (no signal coming in)

    if (onmillis > 0) {
      // signal was coming in, we just lost it (count time spent and determine whether it's a S, L or space (end of letter))
      
      timeon = millis() - onmillis;
      if (timeon <= 700) {
        
        Serial.print("S");
        strcat(morse,"S");
        
      } else if (timeon > 700 && timeon <= 1200) {
        
        Serial.print("L");
        strcat(morse,"L");
        
      } else { // larger than 1200: space detected, which means the end of this letter
        Serial.print(" ");
        
        Serial.print("Full morse code of this letter: _"); Serial.print(morse); Serial.println("_");
        Serial.print("Translated from morse to letter: "); 
        if (morse == "SSS") {
          Serial.println("S");
        } else if (morse == "LLL") {
          Serial.println("O");
        } else {
          Serial.print("unknown, but morse was: "); Serial.println(morse);
        }

      }
      onmillis = 0;
    }
    
  } else {
    
    // on (receiving signal)
    if (onmillis == 0) {
      // we just started receiving signal
      onmillis = millis();
    }
    
  }
  
  delay(10); // read every 10 ms.
}

All help is very much appreciated.

Kind regards,
Vincent

You can't compare two character arrays with '=='. That will compare the address of the first characters of the two strings and since the strainga are at different addresses they will never be 'equal'.

Try using the String object. It understands '==' so you compare character strings. You can also use '+=' for concatenate.

String morse;
 
void loop(void) {
  photocellReading = analogRead(photocellPin);  
 
  if (photocellReading < 100) {
    // off (no signal coming in)

    if (onmillis > 0) {
      // signal was coming in, we just lost it (count time spent and determine whether it's a S, L or space (end of letter))
      
      timeon = millis() - onmillis;
      if (timeon <= 700) {
        
        Serial.print("S");
        morse += "S";
        
      } else if (timeon > 700 && timeon <= 1200) {
        
        Serial.print("L");
        morse += "L";
        
      } else { // larger than 1200: space detected, which means the end of this letter
        Serial.print(" ");
        
        Serial.print("Full morse code of this letter: _"); Serial.print(morse); Serial.println("_");
        Serial.print("Translated from morse to letter: "); 
        if (morse == "SSS") {
          Serial.println("S");
        } else if (morse == "LLL") {
          Serial.println("O");
        } else {
          Serial.print("unknown, but morse was: "); Serial.println(morse);

        morse = ""; //  Important to clear the string so the signals don't just continue to accumulate!

        }

      }
      onmillis = 0;
    }
    
  } else {
    
    // on (receiving signal)
    if (onmillis == 0) {
      // we just started receiving signal
      onmillis = millis();
    }
    
  }
  
  delay(10); // read every 10 ms.
}

Thank you very much, this is working very well :slight_smile:

I had two subsequent questions:

  1. Is there any way to have this functionality when using a char* instead of string? Or should I always use a string when comparing?
  2. How can I put this in an array where the morsecode ("LLL") is the key and the alphabetic letter ('O') is the value? That would be cleaner than having 26 if-else statements I think.

Kind regards,
Vincent

s there any way to have this functionality when using a char* instead of string?

You mean "String".
Yes, you can use "strcmp" to compare "char*" strings.

How can I put this in an array where the morsecode ("LLL") is the key and the alphabetic letter ('O') is the value?

I showed you how in the thread you started for the encoder.

Thank you for your reply.

I showed you how in the thread you started for the encoder.

Yes, but that was the other way around. From a letter to morse. Now I want to do the opposite: from morse to letter. Is there a function to get the key for a value? Sorry for the n00b questions, I'm really new to C++.

Is there a function to get the key for a value? Sorry for the n00b questions

No, but there's a for loop.
Just use a "for" loop and "strcmp"

Thanks, I'll use that.

Morse code can be thought of as Short, Long, and Space to mark End Of Character, a 3-state code.
You could use 2 binary bits to record that, one bit high to mark a tap and the next to mark short/low or long/high.

Last I remember there are no Morse code letters that use more than 4 taps. However with digits and punctuation there can be six, woo-hoo, which would require unsigned int.

Letters only, 'O' would be binary 00111111 where 'S' would be binary 00101010

Translation to ASCII would only require a switch-case statement and be very fast.

Great idea! Thanks :slight_smile:

My second Arduino project is about reading morse code.

This was my second project with embedded processors as well, but that was back in 1975.

The way I encoded morse was to use a 0 for dot and 1 for dash. Of course that meant you didn't know how long a morse character was so the end of the character was marked with a 1. So an S for example ( three dots ) would show as 00001000, Then all you need to do to convert it into ASCII is to use a lookup table in an array. All morse characters can be encoded in a single byte.

This was my second project with embedded processors as well, but that was back in 1975.

Hehe, cool how so much and so little can change at the same time :slight_smile:

Thank you for your suggestion, makes a lot of sense.

I did automatic speed tracking as well. If I timed a signal and it was greater than the threshold then it was treated as a dash. Then the threshold was set at half what I timed the dash to be. If it was less than the threshold and so a dot I adjusted the threshold to be twice what I timed. In that way the receiver always locked onto any speed signal.