Multitasking while flashing an LED using Using millis()

Hi all, Im trying to make a program that can multitask and detect button presses and other inputs while a set LED flashes a word in morse code. I am not using delay() but only millis() to create delays, but my program wont multitask (The "Test" string I want to print only prints before the morse runs, not during it as well). How can I use Millis diffrently to get this multitasking to work?

Thanks in Advance! Code below:

//How long the LED should be on for a Dot
const unsigned long intervalOfDot = 400;
//How long the LED should be on for a Dash
const unsigned long intervalOfDash = 1000;
//Interval of Off LED between dots / dashes
const unsigned long intervalBetweenDotDash = 300;
//Interval of Off LED between Letters
const unsigned long intervalBetweenLetters = 1250;
//Interval of OFF LED between the repeat of a word
const unsigned long intervalRepeat = 2750;
//Word I want to Morse
String message = "BISTRO";
//LED for Morse display
int port = 12;
//Variable used to grab a time at a point and then use to wait a certian interval.
unsigned long timeCheck;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(port, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  //Word printing here to see if multitasking works while flashing morse - problem is it only prints TEST once before the morse flash - doesnt repeat
  Serial.println("TEST");
  
  morse(message);
}
//for the dot of a morse

void smalls(int freq) {
  for(int i = 0; i<freq; i++){
    digitalWrite(port, HIGH);
    //Waits for interval of Dot time before turning LED off
    timeCheck = millis();
    while(!(millis() > intervalOfDot + timeCheck)){
        
     }
      digitalWrite(port,LOW);
       //Waits for interval of BetweemDotDash time before ending function - goes to next letter
    timeCheck = millis();
    while(!(millis() > intervalBetweenDotDash + timeCheck)){
        
      }
  }
  
}


//for the dash of a morse
void larges(int freq) {
  for(int i = 0; i<freq; i++){
  digitalWrite(port, HIGH);
   //Waits for interval of Dash time before turning LED off
  timeCheck = millis();
    while(!(millis() > intervalOfDash + timeCheck)){
        
     }
  digitalWrite(port,LOW);
    //Waits for interval of BetweemDotDash time before ending function - goes to next letter
  timeCheck = millis();
    while(!(millis() > intervalBetweenDotDash + timeCheck)){
        
      }
  }

}

//Method to get each character of the word and turn it into the proper morse dot/dash combonation
void morse(String code) {
  for(int i = 0; i <=code.length(); i++){
    //When i reaches the end of the word (equal to length), a timecheck is stored and a while loop is made to wait until current time passes
    
    if(i == code.length()){
      timeCheck = millis();
      while(!(millis() > intervalRepeat + timeCheck)){
        
      }
        }
      //Else, a Letter is taken and converted into Morse
    else{
      String section = code.substring(i,i+1);
    if(section.equals("A") || section.equals("a")){
      smalls(1);
      larges(1);
    }
    else if(section.equals("B") || section.equals("b") ){
      larges(1);
      smalls(3);
    }
    else if(section.equals("C") || section.equals("c") ){ 
      larges(1);
      smalls(1);
      larges(1);
      smalls(1);
    }
    else if(section.equals("D") || section.equals("d")){
      larges(1);
      smalls(2);
    }
    else if(section.equals("E") || section.equals("e")){
      smalls(1);
    }
    else if(section.equals("F") || section.equals("f")){
      smalls(2);
      larges(1);
      smalls(1);
    }
    else if(section.equals("G") || section.equals("g")){
      larges(2);
      smalls(1);
    }
    else if(section.equals("H") || section.equals("h")){
      smalls(4);
    }
    else if(section.equals("I") || section.equals("i")){
      smalls(2);
    }
    else if(section.equals("J") || section.equals("j")){
      smalls(1);
      larges(3);
    }
    else if(section.equals("K") || section.equals("k")){
      larges(1);
      smalls(1);
      larges(1);
    }
    else if(section.equals("L") || section.equals("l")){
      smalls(1);
      larges(1);
      smalls(2);
    }
    else if(section.equals("M") || section.equals("m")){
      larges(2);
    }
    else if(section.equals("N") || section.equals("n")){
      larges(1);
      smalls(1);
    }
    else if(section.equals("O") || section.equals("o")){
      larges(3);
    }
    else if(section.equals("P") || section.equals("p")){
      smalls(1);
      larges(2);
      smalls(1);
    }
    else if(section.equals("Q") || section.equals("q")){
      larges(2);
      smalls(1);
      larges(1);
    }
    else if(section.equals("R") || section.equals("r")){
      smalls(1);
      larges(1);
      smalls(1);
    }
    else if(section.equals("S") || section.equals("s")){
      smalls(3);
    }
    else if(section.equals("T") || section.equals("t")){
      larges(1);
    }
    else if(section.equals("U") || section.equals("u")){
      smalls(2);
      larges(1);
    }
    else if(section.equals("V") || section.equals("v")){
      smalls(3);
      larges(1);
    }
    else if(section.equals("W") || section.equals("w")){
      smalls(1);
      larges(2);
    }
    else if(section.equals("X") || section.equals("x")){
      larges(1);
      smalls(2);
      larges(1);
    }
    else if(section.equals("Y") || section.equals("y")){
      larges(1);
      smalls(1);
      larges(2);
    }
    else if(section.equals("Z") || section.equals("z")){
      larges(2);
      smalls(2);
    }
    else if(section.equals("0")){
      larges(5);
    }
    else if(section.equals("1")){
      smalls(1);
      larges(4);
    }
    else if(section.equals("2")){
      smalls(2);
      larges(3);
    }
    else if(section.equals("3")){
      smalls(3);
      larges(2);
    }
    else if(section.equals("4")){
      smalls(4);
      larges(1);
    }
    else if(section.equals("5")){
      smalls(5);
    }
    else if(section.equals("6")){
      larges(1);
      smalls(4);
    }
    else if(section.equals("7")){
      larges(2);
      smalls(3);
    }
    else if(section.equals("8")){
      larges(3);
      smalls(2);
    }
    else{
      larges(4);
      smalls(1);
    }
    //Once the letter is succesfully morsed, this statment waits the program in the loop until it is time to move on
      timeCheck = millis();
      while(!(millis() > intervalBetweenLetters + timeCheck)){
        
      }
    }
  }
    
}

You might want to read again the blink without delay example and explanations

this code is just like delay (if written right and not having the wrong way to handle rollover)…

use "if (millis() - previousMillis > interval)" like used in "Blink Without Delay" and keep track if you are in morsing one character or you have to jump to the next character.

google what others have done.
I bet this was done before - so not worth to invest lot of time to reinvent the wheel. https://www.google.com/search?q=arduino+morse+without+delay

Unless you actually want to understand wheels and maybe go on to invent the sort of wheel that no one has invented before.

1 Like

even then it is worth to see how others have solved the problem and try to figure out how it was done.

Well I would say only as a last resort if you can’t figure it out yourself.
Otherwise you end up like LEGO kits. The ones that only make one model.

In the early days of micro computers ( the mid 70s ), there was no resources and you had to figure everything out for yourself. It was useful practice for later life.

1 Like

It’s interesting to first try to solve it by yourself if you have the time but It’s also often useful to see others’ point of view. You’ll never know otherwise how what you figured out compares and you learn from that too

Look, I have no idea what your talking about with wheels and what not. But I've looked at what you sent me, and I've tried to make a smaller program that just uses dots. This is the best I can come up with, and I'm not sure where I'm going wrong even when looking at the tutorials. Any help would be appreciated.

const unsigned long intervalOfDot = 400;
//How long the LED should be on for a Dash
const unsigned long intervalOfDash = 1000;
//Interval of Off LED between dots / dashes
const unsigned long intervalBetweenDotDash = 300;
//Interval of Off LED between Letters
const unsigned long intervalBetweenLetters = 1250;
unsigned long timeCheck;
int LEDState = LOW;
//LED for Morse display
int port = 12;
void setup() {
  // put your setup code here, to run once:
Serial.begin(9600);
  pinMode(port, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
unsigned long currentTime = millis();
 Serial.println("TEST");
  smalls(4);
  digitalWrite(port, LEDState);
}

void smalls(int freq) {
 //Counter
  int i = 0;
  while(i<freq){
    timeCheck = currentTime;
   LEDState = HIGH;
    if(currentTime > timeCheck + intervalOfDot){
       timeCheck = currentTime;
       LEDState = LOW;
       if(currentTime > timeCheck + intervalBetweenDotDash){
        LEDState = LOW;
    }
    }   
   
  }
  
  
}

that variable currentTime is local to the loop. you can't expect to be able to use it in your smalls() function

you probably get

error: 'currentTime' was not declared in this scope

if you try to compile

Nothing changes freq or i within the while block. So if ever true, (i < freq) will mean you never escape the while loop.

It seems.

a7

Hi gold523,
you have lots of friends who give you good advice and help, great! this forum is great.

Your program is almost good, it's on the time management that you seem to have difficulties; but it's the most difficult to understand at the beginning, the micro never stops, and you have to put breakpoints in the program, but without stopping it completely!

I suggest you put aside for the moment your void morse(String code) {
and run a test example on your program structure in 'multitasking'.

You have to separate the management of the led (morse) and the rest of the program, so that one and the other don't block each other.

I made an array where I put the tempos of the dots, the dashes and the intervals. These tempos are decremented one after the other, and the led is turned on or off at the end of each tempo.
In the example, I send 2 dots, 3 dashes, and 2 terminal tempos.

The array has this structure, you can put in 5 dots/dashs (or less)

tempo[0] = interval dot or dash 1
tempo[1] = intervalBetweenDotDash 1

tempo[2] = interval dot or dash 2
tempo[3] = intervalBetweenDotDash 2

tempo[4] = interval dot or dash 3
tempo[5] = intervalBetweenDotDash 3

tempo[6] = interval dot or dash 4
tempo[7] = intervalBetweenDotDash 4

tempo[8] = interval dot or dash 5
tempo[9] = intervalBetweenDotDash 5

tempo[10] = intervalBetweenLetters
tempo[11] = intervalRepeat

The terminal-series (9600) shows the decrementing tempos of the array

#define intervalOfDot             40                   // How long the LED should be on for a Dot
#define intervalOfDash           100                   // How long the LED should be on for a Dash
#define intervalBetweenDotDash    30                   // Interval of Off LED between dots / dashes
#define intervalBetweenLetters   125                   // Interval of Off LED between Letters
#define intervalRepeat           275                   // Interval of OFF LED between the repeat of a word


//String message = "BISTRO";                           // Word I want to Morse
int port = 12;                                         // LED for Morse display
//unsigned long timeCheck;                             // Variable used to grab a time at a point and then use to wait a certian interval.


int tempo [12];                                        // array of dots, dashs and tempos (5 * 2) + 2
int pointertempo;                                      // pointer on that array
unsigned long previousmillis;                          // general tempo
boolean led;                                           // image of port (led)
boolean canprint = true;                               // we can print, after decrease tempo 

void setup() {                                         // put your setup code here, to run once:
  Serial.begin(9600);                                  // or 115200
  pinMode(port, OUTPUT);
}


void loop() {
  if (tempo[11] == 0) {                                // end of terminal tempo (==0), another morse code
    pointertempo = 0;	                               // 
    smalls(2);                                         // 2 dots  
    larges(3);                                         // 3 dashs
    intervalLetters();                                 // wait
    intervalMessages();                                // wait
  }  
   
  if (canprint) { 
	canprint = false;  
    for (byte i=0; i<12; i++) {
      Serial.print(tempo[i]);  
      Serial.print(" ");      
    } 
    Serial.println("");
  }

  DodotDashInterval();                                 // decrease dot, dash & interval tempos, and manage led
}


void smalls (int freq) {                               // set dot tempo in the array
  while  (freq) {
	tempo[pointertempo] = intervalOfDot;
	pointertempo++;
	tempo[pointertempo] = intervalBetweenDotDash;
	pointertempo++;	
	freq--;
  }
}  

  
void larges (int freq) {                               // set dash tempo in the array
  while  (freq) {
	tempo[pointertempo] = intervalOfDash;
	pointertempo++;
	tempo[pointertempo] = intervalBetweenDotDash;
	pointertempo++;	
	freq--;
  }
}    


void intervalLetters(void) {                           // set intervel tempo in the array
 tempo[10] = intervalBetweenLetters; 
	pointertempo++;	 
}
      

void intervalMessages (void) {                         // set intervel tempo in the array
 tempo[11] = intervalRepeat;      
 	pointertempo++;	
}

  
void DodotDashInterval (void) {                        // decrease tempos 
  if (millis() - previousmillis > 10) {                // 
    previousmillis = millis();                         // 

    if       (tempo[0]) { tempo[0]--; led = true; }    // dot or dash  1 
    else if  (tempo[1]) { tempo[1]--; led = false;}    // IntervalBetweenDotDash
    else if  (tempo[2]) { tempo[2]--; led = true; }    // dot or dash  2 
    else if  (tempo[3]) { tempo[3]--; led = false;}    // IntervalBetweenDotDash
    else if  (tempo[4]) { tempo[4]--; led = true; }    // dot or dash  3 
    else if  (tempo[5]) { tempo[5]--; led = false;}    // IntervalBetweenDotDash
    else if  (tempo[6]) { tempo[6]--; led = true; }    // dot or dash  4 
    else if  (tempo[7]) { tempo[7]--; led = false;}    // IntervalBetweenDotDash
    else if  (tempo[8]) { tempo[8]--; led = true; }    // dot or dash  5
    else if  (tempo[9]) { tempo[9]--; led = false;}    // IntervalBetweenDotDash                    

    else if  (tempo[10]) {tempo[10]--;led = false;}    // ** intervalBetweenLetters  **
    else if  (tempo[11]) {tempo[11]--;led = false;}    // ** intervalBetweenMessages  **                      

    digitalWrite(port, led);
 
    digitalWrite(13, led);	                       // for me
    canprint = true;                                   //  after decrease, we can print 
  }			
}
 

It is an analogy to illustrate the copying of some simple program or technique, against doing the work and trying to understand and solve the problem yourself.

I assume from this that English is not your native language.

Yes, Im not an native English speaker.

Hi gold523,

as an everyday example with easy to follow numbers
delay() is blocking. As long as the delay is "delaying" nothing else of the code can be executed.
Now there is a technique of non-blocking timing.
The basic principle of non-blocking timing is fundamental different from using delay()

You have to understand the difference first and then look into the code.

otherwise you might try to "see" a "delay-analog-thing" in the millis()-code which it really isn't
Trying to see a "delay-analog-thing" in millis() makes it hard to understand millis()
Having understood the basic principle of non-blocking timing based on millis() makes it easy to understand.

imagine baking a frosted pizza
the cover says for preparation heat up oven to 200°C
then put pizza in.
Baking time 10 minutes

You are estimating heating up needs 3 minutes
You take a look onto your watch it is 13:02 (snapshot of time)
You start reading the newspaper and from time to time looking onto your watch
watch shows 13:02. 13:02 - 13:02 = 0 minutes passed by not yet time
watch shows 13:03. 13:03 - 13:02 = 1 minute passed by not yet time
watch shows 13:04. 13:04 - 13:02 = 2 minutes passed by not yet time

watch shows 13:05 when did I start 13:02? OK 13:05 - 13:02 = 3 minutes time to put pizza into the oven

New basetime 13:05 (the snapshot of time)
watch 13:06 not yet time
watch 13:07 not yet time
watch 13:08 not yet time (13:08 - 13:05 = 3 minutes is less than 10 minutes
watch 13:09 not yet time
watch 13:10 not yet time
watch 13:11 not yet time
watch 13:12 not yet time
watch 13:13 not yet time
watch 13:14 not yet time (13:14 - 13:05 = 9 minutes is less than 10 minutes
watch 13:15 when did I start 13:05 OK 13:15 - 13:05 = 10 minutes time to eat pizza (yum yum)

You did a repeated comparing how much time has passed by
This is what non-blocking timing does

In the code looking at "How much time has passed by" is done

currentTime - startTime >= bakingTime

bakingTime is 10 minutes

13:06 - 13:05 = 1 minute >= bakingTime is false
13:07 - 13:05 = 2 minutes >= bakingTime is false
...
13:14 - 13:05 = 9 minutes >= bakingTime is false
13:15 - 13:05 = 10 minutes >= bakingTime is TRUE time for timed action!!

So your loop() is doing

void loop()
  // doing all kinds of stuff like reading the newspaper
  
  if (currentTime - previousTime >= period) {
    previousTime = currentTime; // first thing to do is updating the snapshot of time 
    // time for timed action
  }

it has to be coded exactly this way because in this way it manages the rollover from Max back to zero
of the function millis() automatically

And here is a demo code that shows the software-technique of state-machines

best regards Stefan

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.