Arduino, delay() vs millis()

by luca
8 comments

One of the most common errors when you start writing your sketches for Arduino is the excessive use of the delay() function.

This function, as explained in the official documentation, pauses the program for the specified number of milliseconds. Let’s see an example: you connected to your Arduino a button and a led: when you press the button, the led should be turned on for 3 seconds.

The sketch to implement the goal could be (I slightly modified the one in the official tutorial):

const int buttonPin = 2;
const int ledPin = 4;
 
void setup() {
 
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}
 
void loop() {
 
  if (digitalRead(buttonPin) == HIGH) {     
    digitalWrite(ledPin, HIGH);
    delay(3000);
    digitalWrite(ledPin, LOW);  
  } 
}

Now add a second button and a second led and try to update the sketch to control both the leds, again using the delay() function:

const int button1Pin = 2;
const int button2Pin = 3;
const int led1Pin = 4;
const int led2Pin = 5;
 
void setup() {
 
  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(button1Pin, INPUT);
  pinMode(button2Pin, INPUT);
}
 
void loop(){
 
  if (digitalRead(button1Pin) == HIGH) {     
    digitalWrite(led1Pin, HIGH);
    delay(3000);  
    digitalWrite(led1Pin, LOW);
  }
  if (digitalRead(button2Pin) == HIGH) {     
    digitalWrite(led2Pin, HIGH);
    delay(3000);  
    digitalWrite(led2Pin, LOW);
  } 
}

Let’s test the circuit on a breadboard

delay-fritzing

You may note a problem: when a led is turned on, you cannot turn on also the other one! The reason is simple: when the first led is on, your sketch is stuck and cannot check the state of the second button…

MILLIS

To solve the problem, you can use the millis() function: it returns the number of milliseconds since the sketch was started.

If you need to know when a given time has passed, you can:

[checklist]

  • store in a variable the time zero, that is the initial time (when you turn on the led)
  • periodically check (for example at every loop execution) the difference between the actual and the initial time

[/checklist]

Here’s the new sketch:

const int button1Pin = 2;
const int button2Pin = 3;
const int led1Pin = 4;
const int led2Pin = 5;
 
unsigned long led1OnTime;
unsigned long led2OnTime;
bool led1On;
bool led2On;
 
void setup() {
 
  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(button1Pin, INPUT);
  pinMode(button2Pin, INPUT);
 
  led1On = false;
  led2On = false;
}
 
void loop(){
 
  if (digitalRead(button1Pin) == HIGH) {     
    digitalWrite(led1Pin, HIGH);
    led1On = true;
    led1OnTime = millis();
  }
  if (digitalRead(button2Pin) == HIGH) {     
    digitalWrite(led2Pin, HIGH);
    led2On = true;
    led2OnTime = millis();
  }
 
  if(led1On) 
    if(millis() - led1OnTime > 30000) {
      digitalWrite(led1Pin, LOW);
      led1On = false;
    }   
  if(led2On) 
    if(millis() - led2OnTime > 30000) {
      digitalWrite(led2Pin, LOW);
      led2On = false;
    }
}

I defined four new variables: two (led1OnTime e led2OnTime) to save the time when the leds are turned on and two (led1On e led2On) to save the current status (on/off, in boolean true/false) of each led.

When a button is pressed, the sketch turns the led on, saves the actual time and changes the status variable.

If a led is on (its status variable is true), the sketch periodically checks if the on period is longer than the required (30 seconds, it’s 30000 milliseconds): if so, the led is turned off.

Related Posts

8 comments

Bob Green Thursday July 23rd, 2015 - 07:15 PM

Luca, it might be easier for ‘noobs’ to follow if you used symbols instead of &lt and &gt. I know you only used &gt on this occasion. Excellent description of millis() otherwise.

Reply
luca Monday July 27th, 2015 - 08:27 AM

Hi Bob! Thanks for your comment: it seems that something went wrong with my wordpress “code” plugin: I’ll fix it!

Reply
Mark Wednesday March 16th, 2016 - 04:31 AM

Great write up and easy to understand

Reply
luca Wednesday March 16th, 2016 - 02:18 PM

Thanks for sharing!

Reply
Guido Sunday April 17th, 2016 - 07:59 AM

E’ raro che possa accadere ma è da tenere in conto che la variabile millis() si azzera dopo circa 50 giorni; questo significa che se utilizzi questo metodo su un sistema che resta sempre acceso potrebbe, dopo 50 giorni, non funzionare più.
Se invece del confronto diretto tra Millis() e la variabile si confrontano i due valori assoluti il problema non si presenta.

Reply
luca Monday April 18th, 2016 - 07:31 PM

grazie per il suggerimento Guido!

Reply
diego Tuesday March 7th, 2017 - 12:53 PM

sono alle prime armi e ho modificato il tuo sketch per accendere un led 15 secondi dopo aver premuto il tasto. quando lo rilascio il led deve spegnersi. Invece dopo essersi acceso no si spegne piu… cosa sbaglio ? grazie

const int button1Pin = 2;
const int led1Pin = 4;
unsigned long led1OnTime;

bool led1On;
void setup() {
pinMode(led1Pin, OUTPUT);
pinMode(button1Pin, INPUT);
led1On = false;
Serial.begin (19200);
}

void loop(){

if (digitalRead(button1Pin) == HIGH) {
digitalWrite(led1Pin, HIGH);
led1On = true;
led1OnTime = millis();

if(led1On )
if(millis() – led1OnTime > 15000) {
digitalWrite(led1Pin, LOW);
led1On = false;
} } }

Reply
luca Tuesday March 7th, 2017 - 04:55 PM

ciao, devi chiudere la parentesi graffa dopo ed1OnTime = millis();, altrimenti tutto il blocco seguente – compreso lo spegnimento del led – viene eseguito solo quando il pulsante è premuto.

Reply

Leave a Comment

16 − three =