Arduino, delay() vs millis()

by luca
8 comments

Uno degli errori più frequenti di chi inizia a scrivere sketch per Arduino è l’uso eccessivo della funzione delay().

Tale funzione, come spiegato nella reference ufficiale, mette in pausa il programma per il numero di millisecondi indicato come parametro. Vediamo un semplice esempio: abbiamo collegato ad Arduino un pulsante e un led: alla pressione del pulsante, il led deve essere acceso per 3 secondi.

Lo sketch in esecuzione su Arduino potrebbe essere (modificando quello presentato nel tutorial ufficiale):

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);  
  } 
}

Aggiungiamo ora un secondo pulsante e un secondo led e proviamo a modificare lo sketch per gestire entrambi, sempre utilizzando delay():

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);
  } 
}

Provando il circuito su breadboard

delay-fritzing

ci accorgiamo di un problema: quando uno dei due led è acceso, non è possibile accendere anche l’altro! Il motivo è semplice: mentre il led è acceso, lo sketch è fermo e non può controllare lo stato dell’altro pulsante…

MILLIS

Per risolvere il problema, possiamo sfruttare la funzione millis(): tale funzione restituisce i millisecondi trascorsi dall’avvio dello sketch.

Per sapere quindi quando sono trascorsi 3 secondi possiamo:

[checklist]

  • memorizzare in una variabile il tempo zero, ovvero l’istante di inizio (il momento in cui abbiamo acceso il led)
  • controllare periodicamente (ad esempio ad ogni esecuzione del loop) la differenza tra l’istante attuale e quello iniziale

[/checklist]

Ecco lo sketch modificato:

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;
    }
}

Ho dichiarato quattro nuove variabili: due (led1OnTime e led2OnTime) per memorizzare l’istante in cui i led vengono accesi e due (led1On e led2On) per memorizzare lo stato (acceso/spento ovvero true/false) di ogni led.

Quando viene premuto un tasto, lo sketch accende il led, memorizza l’istante di accensione e cambia la variabile di stato.

Infine se un led è acceso (la sua variabile di stato ha valore true), viene ciclicamente verificato se tale led è acceso da più del tempo previsto (30 secondi ovvero 30000 millisecondi): in tal caso il led viene spento.

Related Posts

8 comments

Bob Green 23 luglio 2015 - 19:15

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 27 luglio 2015 - 08:27

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

Reply
Mark 16 marzo 2016 - 04:31

Great write up and easy to understand

Reply
luca 16 marzo 2016 - 14:18

Thanks for sharing!

Reply
Guido 17 aprile 2016 - 07:59

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 18 aprile 2016 - 19:31

grazie per il suggerimento Guido!

Reply
diego 7 marzo 2017 - 12:53

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 7 marzo 2017 - 16:55

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

quattro × 4 =