Morse Code using an LED with an array and the millis function

Hi guys!
I am a new to not only Arduino but programming anything! I am a old Harley Mechanic, at least that is what we use to be called now we are Technicians, LOL! I bought a Arduino Starter kit for my young daughter about a year ago trying to get her interested in something other than her phone and what an absolute failure that was! After doing the projects in the Arduino projects book with my daughter, and boy was that miserable, it peaked my interest and I came to a realization of the potential of an Arduino! I have made a couple of projects but that is all the experience I have! The only reason I am writing all this is to let you know I am very inexperienced! OK, so here is what I am trying to do. I wrote a sketch that gets a high signal when a button is depressed. I have a String in all caps that is used to traverses an array to find the corresponding Morse Code for a letter stored in the array and out put the code to an LED then move to the next letter in the String. I can get it to work with the delay function but I was trying to use the millis function. I have been using the Serial monitor to try and debug but that hasn't helped at all. I have been beating my head against a wall for more than a couple weeks and have run out of ideas on what is going on. I have tried creating a class that will just do the blinking, a function that will just do the blinking, making flags then trying to blink it at the end of the main loop before it reiterates, I think that's the word, restarts at the beginning of the main loop. My sketch now does nothing to the LED, unless it is happening so fast I can't see it, and doesn't print out code in the Serial monitor line by line like I had it with the delays it just prints the entire code for the String all at once. I to uploaded the sketch, like the forum rules say but it said new users can't upload, so I just copied and pasted!

int charCounter = 0;
int SwitchState = LOW;
int buttonState = 0;
const int arraySize =36;

const byte outPut1 = 10;
const byte InputPin = 4;

String morseAlphabet[arraySize] = {"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--..", 
"1.----","2..---","3...--","4....-","5.....","6-....","7--...",
"8---..","9----.","0-----"};


unsigned long dotInterval = 200;
unsigned long dashInterval = 600;
unsigned long previousMillis = 0;

bool ledState = false;


/******************************************************************************/
String myWord = "SOS"; //HAS TO BE IN UPPER CASE LETTERS OR IT WON'T WORK!!
/******************************************************************************/


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

  pinMode(outPut1, OUTPUT);
  pinMode(InputPin, INPUT);

  digitalWrite(outPut1, LOW);
  digitalWrite(InputPin, LOW);  

}

 void loop(){

  unsigned long currentMillis = millis();

  SwitchState = digitalRead(InputPin);

  if(SwitchState == HIGH  && charCounter == 0){
    
    for(int i =0; i< arraySize; i++){
            
      if(morseAlphabet[i].charAt(0) == myWord.charAt(charCounter)){
        for(int j = 0; j < morseAlphabet[i].length(); j++){

          
          if(morseAlphabet[i].charAt(j) == '.'){
                         

            if(SwitchState == HIGH){
              Serial.print(" . ");

              if((unsigned long)(currentMillis = previousMillis)>= dotInterval){
                ledState = !ledState;
                digitalWrite(outPut1, ledState);

                previousMillis = millis();
              }
              

            }            
          }
           
          if(morseAlphabet[i].charAt(j) == '-'){
            

            if(SwitchState == HIGH){
              Serial.print(" - ");
              
              if((unsigned long)(currentMillis = previousMillis)>= dashInterval){
                ledState = !ledState;
                digitalWrite(outPut1, ledState);

                previousMillis = millis();
              }
            }
          }
        }     
        i =-1;    
        Serial.println();
        charCounter++;
      }
    }
  }
  if(SwitchState == HIGH){
     for(int a = 0; a < 600; a++){
     delay(1);
     }
  }
  charCounter = 0;
}     

I hate to say it but ... It looks like code that originally used delay() for timing and then someone just took out all the delays and pasted something involving millis() in place of the delays without really understanding how to do that. Sorry, I didn't mean to be cruel!

Rewriting code to work without delay() really does involve just that: re-writing it. You can't just take the old code and replace A with B, you really do have to re-think the whole structure of the code. This code is not a nice simple example to practice on!

Hi.

quickly:

put that in the very end of your setup(), here, the counter is "reset" at each loop() and will never give you an elapsed time to light led or print anything.

First question: why are you doing this? Is there a reason why you must do it, or is it an exercise to help you improve your coding skills?

I'll be a heretic and say sometimes delay() is the answer. If you don't have anything else to do while waiting why complicate things?

Full disclosure: I use both delay() and millis() where I think appropriate.

Not cruel, that is exactly what I did. It is my code except the actual millis functions, i got that off the bald engineers site. obviously I don't understand something or it would work. I thought the millis() just received the time from the internal clock then as long as the difference between current time and a stored time is greater than the desired duration it would continue to loop in the for loop?? what am I missing?? I can pull it out of the for loop and the millis function works fine, can it not be used inside a for loop, much less nested for loops???

I was trying to get rid of the delays because if I have a really long word when i let off the button sometimes it responds instantly and other times it takes a few milliseconds. I could just use the delays but I am trying to get a better grasp on the millis function.

Thanks pete I will give it a shot!

took me a while to see that you're simply trying to output SOS. i was able to run your code with pin changes on a multiFunction shield and checking for a LOW button pressed state

buttons are typically connected between the pin and ground, the pint configures as INPUT_PULLUP to use the internal pullup resistor which pulls the pin HIGH and when pressed, the button pulls the pin LOW.

it also seems unnecessary to check for SwitchState == HIGH inside your loops

i replaced your code using millis() with following, for example, to see what the code is intended to do

                            digitalWrite (outPut1, On);
                            delay        (dashInterval);
                            digitalWrite (outPut1, Off);
                            delay        (dotInterval);

but i don't see where millis() has a use, unless you entered something in from the serial monitor

If that line is in setup(), then it won't be in the same scope for loop(). The previousMillis global variable is the one that should persist across calls to loop().

Thank you for your reply. I was originally using dealy but instead of SOS if I use some really long random string sometimes it responds almost instantanously and other times i would take a few seconds to respond. It's more to just try and figure out mills and why it isn't working?

guessing you weren't seeing what you expected because you simply toggled the led state

rather than turn it on for the appropriate time and then off. The LED should always come on whether a dot/dash

what do you expect it to do?

you're absolutely right, and I assumed it was not necessary to tell.

(the kind of mistakes in my own code I usually fix AFTER an error message from compiler :grin:)

I was trying to get it to write a digital HIGH on my output pin by pulling the internal clock then taking the difference form the current time and the previous time then the same thing except to have a digital LOW to dicern the difference between the dot and dash outputs.

It never gets past here:

if (SwitchState == HIGH && charCounter == 0) {

consider
also Morse Code Timing

int charCounter = 0;
int SwitchState = LOW;
int buttonState = 0;
const int arraySize =36;

#if 1
const byte outPut1 = 10;
const byte InputPin = 4;
#else
const byte outPut1  = 13;
const byte InputPin = A1;
#endif

#if 0
String morseAlphabet [arraySize] = {
    "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--..",   
    "1.----",   "2..---",   "3...--",   "4....-",   "5.....",
    "6-....",   "7--...",   "8---..",   "9----.",   "0-----"
};
#else
const char *morseAlphabet [] = {
    ".-",   "-...",   "-.-.",  "-..",   ".",    "..-.",   
    "--.",  "....",   "..",    ".---",  "-.-",  ".-..",
    "--",   "-.",     "---",   ".--.",  "--.-", ".-.",
    "...",  "-",      "..-",   "...-",  ".--",  "-..-",
    "-.--", "--..",   
    ".----",   "..---",   "...--",   "....-",   ".....",
    "-....",   "--...",   "---..",   "----.",   "-----"
};
#endif

unsigned long dotInterval   = 200;
unsigned long dashInterval  = 3 * dotInterval;
unsigned long spaceInterval = dotInterval;

bool ledState = false;

enum { Off = HIGH, On = LOW };

char buf [80];

// -----------------------------------------------------------------------------
void
output (
    char *s )
{
    Serial.println (s);

    for (unsigned i = 0; i < strlen (s); i++)  {
        Serial.print (s [i]);
        if (' ' == s [i])  {
            delay (7*dotInterval);
            continue;
        }

        const char *code = morseAlphabet [s[i] - 'a'];
        for (unsigned j = 0; j < strlen (code); j++)  {
            Serial.print (code [j]);
            digitalWrite (outPut1, On);
            delay ('.' == code [j] ? dotInterval : dashInterval);
            digitalWrite (outPut1, Off);
            delay (dotInterval);
        }
        delay (dotInterval);
    }
}

// -----------------------------------------------------------------------------
void loop ()
 {
    if (Serial.available ())  {
        int n = Serial.readBytesUntil ('\n', buf, sizeof(buf)-1);
        buf [n] = '\0';
        output (buf);
    }
}


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

    pinMode (outPut1,  OUTPUT);
    pinMode (InputPin, INPUT_PULLUP);

    digitalWrite (outPut1, Off);
}
1 Like

OK?? Sorry, I am very new at all this what are the "#" in front of the if and else?????

Should be:

unsigned long spaceInterval = 3*dotInterval;
unsigned long interwordgapInterval = 7*dotInterval;

Ref:

Thank you for the reference on Morse Code. I was a was aware of the timing MORSE code i was just trying to get the sketch to work before I even incorporated anything else or worried about the t timing of the flash, dash, space, or new words.