Arduino, delay() vs millis()

luca 21/07/2015 8

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:

  • 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

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.

8 Comments »

  1. Bob Green 23/07/2015 at 19:15 - Reply

    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.

    • luca 27/07/2015 at 08:27 - Reply

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

  2. Mark 16/03/2016 at 04:31 - Reply

    Great write up and easy to understand

    • luca 16/03/2016 at 14:18 - Reply

      Thanks for sharing!

  3. Guido 17/04/2016 at 07:59 - Reply

    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.

    • luca 18/04/2016 at 19:31 - Reply

      grazie per il suggerimento Guido!

  4. diego 07/03/2017 at 12:53 - Reply

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

    • luca 07/03/2017 at 16:55 - Reply

      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.

Leave A Response »

Questo sito usa i cookie per poterti offrire una migliore esperienza di navigazione maggiori informazioni

Questo sito utilizza i cookie per fonire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o clicchi su "Accetta" permetti al loro utilizzo.

Chiudi