Problema con millis() e contatore ore

Salve, sto svolgendo un progetto che riguarda l'attivazione dei relé ogni 24 ore presi da una coda. Come contatore ho utilizzato millis() e una variabile tempo che viene eguagliata a millis() nel momento in cui un relé viene inserito in quella coda. Il problema è che dopo aver eseguito il ciclo while, la variabile tempo diventa zero, quando invece l'ho eguagliata a millis(). Quindi il ciclo while continua all'infinito perché millis()-tempo sarà sempre maggiore di giorno. Qualcuno sa spiegarmi questa cosa? Di seguito il codice e il Serial print del processo:

void contatoreGiorni(int tempo){
  while ((millis()-tempo)>giorno){    
      if (esecuzioneMN==0){ // controlla se è in esecuzione un'operazione di mantenimento
        Serial.println("PASSO UNO MANTENIMENTO DOPO 24 H");
        Serial.print("millis()-tempo: ");
        Serial.println(millis()-tempo);
        startTimeMN=millis();
        esecuzioneMN=1;
      }
      durataMN =0;     // inizializzo la durata a 0. Questo mi serve perché altrimenti ad ogni iterazione durataRC avrebbe un modulo sempre maggiore e non soddisferebbe mai il prossimo while
      byte elementoMN=scodaMN.pop();  // estraggo dalla coda il primo elemento e procedo col ciclo:
      int relayMN = elementoMN >> 6;
      while (durataMN < mantenimento && esecuzioneMN==1 && codaPR.isEmpty()==true && codaRC.isEmpty()==true /*&& scodaMN.isEmpty()==false*/){    // finchè il contatore non raggiunge il tempo di ricarica e finché la ricarica è attiva
        buttonVal = analogRead(buttonPin);    // continua a leggere il valore dei pulsanti per interrompere la ricarica in caso di selezione di modalità priorità
        switchPulsanti();
        durataMN = millis() - startTimeMN;
        digitalWrite(relay[relayMN], HIGH);
      }      
      Serial.println("FUORI IL CICLO WHILE MANTENIMENTO GIORNALIERO");
      digitalWrite(relay[relayMN], LOW);
      esecuzioneMN=0; 
      scodaMN.push(elementoMN);  //mette nella coda di mantenimento per eseguire il mantenimento ogni 24 ore 
      tempo=millis();
      Serial.println("Presa inserita nella coda a mantenimento ciclico (MNC)");
      Serial.print("Variabile tempo: ");
      Serial.println(tempo); 
      Serial.print("Variabile millis(): ");
      Serial.println(millis());   
    }
}

Serial print:
PASSO UNO MANTENIMENTO DOPO 24 H
millis()-tempo: 70073
FUORI IL CICLO WHILE MANTENIMENTO GIORNALIERO
Presa inserita nella coda a mantenimento ciclico (MNC)
Variabile tempo: 80127
Variabile millis(): 80231
PASSO UNO MANTENIMENTO DOPO 24 H
millis()-tempo: 80291
FUORI IL CICLO WHILE MANTENIMENTO GIORNALIERO
Presa inserita nella coda a mantenimento ciclico (MNC)
Variabile tempo: 90345
Variabile millis(): 90449
PASSO UNO MANTENIMENTO DOPO 24 H
millis()-tempo: 72399
...

Ma "tempo" l'hai dichiarata di tipo int, non va bene. Millis restituisce un unsigned long. Con un int non ce la fa contenere il valore restituito da millis().

Ho cambiato la dichiarazione da int in long, ma il problema persiste. :frowning: Non capisco davvero cosa c'è di sbagliato se la variabile la rinizializzo all'interno del ciclo while, al successivo suo controllo dovrebbe prendere quel valore, invece è come se tempo fosse 0...

Non "long" ma "unsigned long", altrimenti può venirti fuori un negativo.

Stesso problema:

PASSO UNO MANTENIMENTO DOPO 24 H
millis()-tempo: 70073
FUORI IL CICLO WHILE MANTENIMENTO GIORNALIERO
Presa inserita nella coda a mantenimento ciclico (MNC)
Variabile tempo: 80127
Variabile millis(): 80231
PASSO UNO MANTENIMENTO DOPO 24 H
millis()-tempo: 80291
FUORI IL CICLO WHILE MANTENIMENTO GIORNALIERO
Presa inserita nella coda a mantenimento ciclico (MNC)
Variabile tempo: 90345
Variabile millis(): 90449
PASSO UNO MANTENIMENTO DOPO 24 H
millis()-tempo: 71549
FUORI IL CICLO WHILE MANTENIMENTO GIORNALIERO
Presa inserita nella coda a mantenimento ciclico (MNC)
Variabile tempo: 100562
Variabile millis(): 100667

Questo problema si presenta a cicli di tre, cioè dopo tre volte che fa questa cosa si ferma per poi riprendere altre tre volte e così via...

Spiega per bene cosa vuoi fare perché forse è la logica che è sbagliata.

Ok, allora io ho 4 pulsanti, ciascuno attiva un relè. Può attivarlo per due, quattro ore o otto ore. Quindi scorro un menu e seleziono la voce che mi interessa. Il relè da attivare e il tempo vengono inseriti rispettivamente in 3 code di diverse priorità (quindi si può fare una sola operazione alla volta). Il relè è attivo per il tot tempo che ho deciso, dopodiché una volta finita l'operazione lo metto su un'altra coda che ogni 24 ore a partire dalla fine dell'operazione riprende questo relè e lo attiva per altre 2 ore, dopodiché lo rimette ancora nella stessa coda per poi processarlo dopo altre 24 ore e così via. Il problema e il codice riguardano appunto quest'ultima coda in cui gli elementi che prendo non vengono processati una sola volta ogni 24 ore, ma vengono processati diverse volte ogni 24 ore. Quello che mi serve è un mantenimento costante della carica di alcune batterie collegate ad arduino. Non so se sia più chiaro adesso... Non è molto facile da spiegare, lo ammetto...

Esatto, ma si comporta come se lo diventasse zero, perché se noti millis()-tempo dovrebbe essere un numero piccolo in modulo, proprio perché millis() e tempo in quel momento distano davvero poco in modulo.

A me pare che l'errore sia nel while iniziale:

while ((millis()-tempo)>giorno){

Esso dice:
mentre la differenza fra millis e tempo è maggiore di giorno, fai questo

Poi vedo che tempo lo cambi solo all'interno dell'altro ciclo while, che però ha un controllo di ingresso molto complesso:

 while (durataMN < mantenimento && esecuzioneMN==1 && codaPR.isEmpty()==true && codaRC.isEmpty()==true /*&& scodaMN.isEmpty()==false*/){

Devono essere vere tutte quelle condizioni affinché entri nel while che cambia tempo.
Sei sicuro della cosa? Dai print che fai io non vedo differenze fra millis e tempo molto piccole, sono tutte nell'ordine dei 70/90K.

Non sono molto piccole, ma se guardi i singoli valori di millis() e tempo dovrebbero esserlo

Variabile tempo: 80127
Variabile millis(): 80231
PASSO UNO MANTENIMENTO DOPO 24 H
millis()-tempo: 80291

in questo caso millis()-tempo dovrebbe essere 104. Comunque mi sono accorto che non è solo lì che cambio la variabile tempo... Il discorso è molto più complesso perché tempo è un parametro formale della funzione. In realtà quando richiamo la funzione prendo 3 tempi diversi... Per spiegarmi meglio incollo una parte più grande del codice:

void scorriCode(){

  //PRIORITA'
  while (codaPR.isEmpty()==false || codaRC.isEmpty()==false || codaMN.isEmpty()==false){  // finchè non sono tutte vuote...
    while (codaPR.isEmpty()==false){ //finchè la coda priorità non è vuota
      esecuzioneRC==0;
      esecuzioneMN==0;
      if (esecuzionePRY==0){ // controlla se è in esecuzione un'operazione di ricarica  
        Serial.println("PASSO UNO PRIORITA'");
        startTimePRY=millis();
        esecuzionePRY=1;
      }
      durataPRY =0;     // inizializzo la durata a 0. Questo mi serve perché altrimenti ad ogni iterazione durataRC avrebbe un modulo sempre maggiore e non soddisferebbe mai il prossimo while
      byte elementoPR=codaPR.pop();
      int relayPR = elementoPR >> 6;
      while (durataPRY < priorita && esecuzionePRY==1){    // finchè il contatore non raggiunge il tempo di ricarica e finché la ricarica è attiva
        //Serial.print("DENTRO IL CICLO WHILE ");
        buttonVal = analogRead(buttonPin);    // continua a leggere il valore dei pulsanti per interrompere la ricarica in caso di selezione di modalità priorità
        switchPulsanti();
        durataPRY = millis() - startTimePRY;
        digitalWrite(relay[relayPR], HIGH);
      }
      Serial.println("FUORI IL CICLO WHILE PRIORITA'");
      digitalWrite(relay[relayPR], LOW);
      esecuzionePRY=0;
      scodaMN.push(elementoPR);  //mette nella coda di mantenimento per eseguire il mantenimento ogni 24 ore   
      Serial.println("Presa inserita nella coda a mantenimento ciclico (PR)");
   tempoPR=millis();  // per il mantenimento ogni 24 h 
    }

    //RICARICA
    while (codaPR.isEmpty()==true && codaRC.isEmpty()==false){
      if (esecuzionePRY==0){ // coda vuota e nessuna ricarica o priorità in esecuzione 
        if (esecuzioneRC==0){ // controlla se è in esecuzione un'operazione di ricarica  
          Serial.println("PASSO UNO PRIMA RICARICA");
          startTimeRC=millis();
          esecuzioneRC=1;
        }
        durataRC =0;     // inizializzo la durata a 0. Questo mi serve perché altrimenti ad ogni iterazione durataRC avrebbe un modulo sempre maggiore e non soddisferebbe mai il prossimo while
        byte elementoRC=codaRC.pop();  // estraggo dalla coda il primo elemento e procedo col ciclo:
        int relayRC = elementoRC >> 6;
        //primo ciclo di ricarica
        while (durataRC < ricarica && codaPR.isEmpty() && esecuzioneRC==1){    // finchè il contatore non raggiunge il tempo di ricarica e finché la ricarica è attiva
          buttonVal = analogRead(buttonPin);    // continua a leggere il valore dei pulsanti per interrompere la ricarica in caso di selezione di modalità priorità
          switchPulsanti();
          durataRC = millis() - startTimeRC;
          digitalWrite(relay[relayRC], HIGH);
        }
        Serial.println("FUORI IL CICLO WHILE ");
        digitalWrite(relay[relayRC], LOW);
        esecuzioneRC=0;
        scodaRC.push(elementoRC);  
        Serial.println("AGGIUNTA PRESA A SECONDA RICARICA");} 
      while (codaPR.isEmpty()==true && codaRC.isEmpty()==true && scodaRC.isEmpty()==false){
        if (esecuzionePRY==0){ // coda vuota e nessuna ricarica o priorità in esecuzione 
        if (esecuzioneRC==0){ // controlla se è in esecuzione un'operazione di ricarica  
          Serial.println("PASSO UNO SECONDA RICARICA");
          startTimeRC=millis();
          esecuzioneRC=1;
        }
        durataRC =0;     // inizializzo la durata a 0. Questo mi serve perché altrimenti ad ogni iterazione durataRC avrebbe un modulo sempre maggiore e non soddisferebbe mai il prossimo while
        byte elementoRC=scodaRC.pop();  // estraggo dalla coda il primo elemento e procedo col ciclo:
        int relayRC = elementoRC >> 6;
        //primo ciclo di ricarica
        while (durataRC < ricarica && codaPR.isEmpty() && esecuzioneRC==1){    // finchè il contatore non raggiunge il tempo di ricarica e finché la ricarica è attiva
          buttonVal = analogRead(buttonPin);    // continua a leggere il valore dei pulsanti per interrompere la ricarica in caso di selezione di modalità priorità
          switchPulsanti();
          durataRC = millis() - startTimeRC;
          digitalWrite(relay[relayRC], HIGH);
        }
        Serial.println("FUORI IL CICLO WHILE ");
        digitalWrite(relay[relayRC], LOW);
        esecuzioneRC=0;
        scodaMN.push(elementoRC);   //mette nella coda di mantenimento per eseguire il mantenimento ogni 24 ore
        Serial.println("Presa inserita nella coda a mantenimento ciclico (RC)");
         tempoRC=millis();      // per il mantenimento ogni 24 h
      }
    }
   }

    //MANTENIMENTO
    while (codaPR.isEmpty()==true && codaRC.isEmpty()==true && codaMN.isEmpty()==false){
      if (esecuzioneMN==0){ // controlla se è in esecuzione un'operazione di mantenimento
        Serial.println("PASSO UNO MANTENIMENTO");
        startTimeMN=millis();
        esecuzioneMN=1;
      }
      durataMN =0;     // inizializzo la durata a 0. Questo mi serve perché altrimenti ad ogni iterazione durataRC avrebbe un modulo sempre maggiore e non soddisferebbe mai il prossimo while
      byte elementoMN=codaMN.pop();  // estraggo dalla coda il primo elemento e procedo col ciclo:
      int relayMN = elementoMN >> 6;
      while (durataMN < mantenimento && esecuzioneMN==1 && codaPR.isEmpty()==true && codaRC.isEmpty()==true ){    // finchè il contatore non raggiunge il tempo di ricarica e finché la ricarica è attiva
        buttonVal = analogRead(buttonPin);    // continua a leggere il valore dei pulsanti per interrompere la ricarica in caso di selezione di modalità priorità
        switchPulsanti();
        durataMN = millis() - startTimeMN;
        digitalWrite(relay[relayMN], HIGH);
      }
      Serial.println("FUORI IL CICLO WHILE MANTENIMENTO");
      digitalWrite(relay[relayMN], LOW);
      esecuzioneMN=0;
      scodaMN.push(elementoMN);  //mette nella coda di mantenimento per eseguire il mantenimento ogni 24 ore 
     tempoMN=millis();  // per il mantenimento ogni 24 h
     Serial.println(millis()-tempoMN);
    }
  }
  cicloMantenimento();
}

void cicloMantenimento(){
   //ogni 24 ore esegui il mantenimento
  if (millis()-tempoMN > giorno && codaPR.isEmpty()==true && codaRC.isEmpty()==true){
  contatoreGiorni(tempoMN);
  tempoMN=millis();
  }
  if (millis()-tempoRC > giorno && codaPR.isEmpty()==true && codaRC.isEmpty()==true){
  Serial.println("Passaggio intermedio");
  contatoreGiorni(tempoRC);
  //contatoreGiorni(tempoRC);
  tempoRC=millis();
  }
  if (millis()-tempoPR > giorno && codaPR.isEmpty()==true && codaRC.isEmpty()==true){
  contatoreGiorni(tempoPR);
  tempoPR=millis();
  }  
}

void contatoreGiorni(unsigned long tempo){
  while (/*codaPR.isEmpty()==true && codaRC.isEmpty()==true &&*/ (millis()-tempo)>giorno){
      if (esecuzioneMN==0){ // controlla se è in esecuzione un'operazione di mantenimento
        Serial.println("PASSO UNO MANTENIMENTO DOPO 24 H");
        Serial.print("millis()-tempo: ");
        Serial.println(millis()-tempo);
        startTimeMN=millis();
        esecuzioneMN=1;
      }
      durataMN =0;     // inizializzo la durata a 0. Questo mi serve perché altrimenti ad ogni iterazione durataRC avrebbe un modulo sempre maggiore e non soddisferebbe mai il prossimo while
      byte elementoMN=scodaMN.pop();  // estraggo dalla coda il primo elemento e procedo col ciclo:
      int relayMN = elementoMN >> 6;
      while (durataMN < mantenimento && esecuzioneMN==1 && codaPR.isEmpty()==true && codaRC.isEmpty()==true /*&& scodaMN.isEmpty()==false*/){    // finchè il contatore non raggiunge il tempo di ricarica e finché la ricarica è attiva
        buttonVal = analogRead(buttonPin);    // continua a leggere il valore dei pulsanti per interrompere la ricarica in caso di selezione di modalità priorità
        switchPulsanti();
        durataMN = millis() - startTimeMN;
        digitalWrite(relay[relayMN], HIGH);
      }      
      Serial.println("FUORI IL CICLO WHILE MANTENIMENTO GIORNALIERO");
      digitalWrite(relay[relayMN], LOW);
      esecuzioneMN=0; 
      scodaMN.push(elementoMN);  //mette nella coda di mantenimento per eseguire il mantenimento ogni 24 ore 
      tempo=millis();
      Serial.println("Presa inserita nella coda a mantenimento ciclico (MNC)");
      Serial.print("Variabile tempo: ");
      Serial.println(tempo); 
      Serial.print("Variabile millis(): ");
      Serial.println(millis());   
    }
}

Nessuno sa aiutarmi? :frowning: Ho provato a riformulare il codice ma il problema resta sempre lo stesso: alla prima iterazione, prendendo il tempo (con la funzione assegnaTempi) dell'ultima operazione fatta e sottraendolo a millis() non mi da quello che mi dovrebbe dare!!! Dove sbaglio?

void scorriCode(){
    //RICARICA
    while (codaPR.isEmpty()==true && codaRC.isEmpty()==false){
      if (esecuzionePRY==0){ // coda vuota e nessuna ricarica o priorità in esecuzione 
        if (esecuzioneRC==0){ // controlla se è in esecuzione un'operazione di ricarica  
        Serial.println("--------------------------------------------------------");
          Serial.println("PASSO UNO PRIMA RICARICA");
          startTimeRC=millis();
          esecuzioneRC=1;
        }
        durataRC =0;     // inizializzo la durata a 0. Questo mi serve perché altrimenti ad ogni iterazione durataRC avrebbe un modulo sempre maggiore e non soddisferebbe mai il prossimo while
        byte elementoRC=codaRC.pop();  // estraggo dalla coda il primo elemento e procedo col ciclo:
        int relayRC = elementoRC >> 6;
        //primo ciclo di ricarica
        assegnaTempi(relayRC);
        while (durataRC < ricarica && codaPR.isEmpty() && esecuzioneRC==1){    // finchè il contatore non raggiunge il tempo di ricarica e finché la ricarica è attiva
          buttonVal = analogRead(buttonPin);    // continua a leggere il valore dei pulsanti per interrompere la ricarica in caso di selezione di modalità priorità
          switchPulsanti();
          durataRC = millis() - startTimeRC;
          digitalWrite(relay[relayRC], HIGH);
        }
        Serial.println("FUORI IL CICLO WHILE ");
        digitalWrite(relay[relayRC], LOW);
        esecuzioneRC=0;
        scodaRC.push(elementoRC);
        assegnaTempi(relayRC);  
        Serial.println("--------------------------------------------------------");
        Serial.println("AGGIUNTA PRESA A SECONDA RICARICA");} 
      while (codaPR.isEmpty()==true && codaRC.isEmpty()==true && scodaRC.isEmpty()==false){
        if (esecuzionePRY==0){ // coda vuota e nessuna ricarica o priorità in esecuzione 
        if (esecuzioneRC==0){ // controlla se è in esecuzione un'operazione di ricarica  
        Serial.println("--------------------------------------------------------");
          Serial.println("PASSO UNO SECONDA RICARICA");
          startTimeRC=millis();
          esecuzioneRC=1;
        }
        durataRC =0;     // inizializzo la durata a 0. Questo mi serve perché altrimenti ad ogni iterazione durataRC avrebbe un modulo sempre maggiore e non soddisferebbe mai il prossimo while
        byte elementoRC=scodaRC.pop();  // estraggo dalla coda il primo elemento e procedo col ciclo:
        int relayRC = elementoRC >> 6;
        //primo ciclo di ricarica
        while (durataRC < ricarica && codaPR.isEmpty() && esecuzioneRC==1){    // finchè il contatore non raggiunge il tempo di ricarica e finché la ricarica è attiva
          buttonVal = analogRead(buttonPin);    // continua a leggere il valore dei pulsanti per interrompere la ricarica in caso di selezione di modalità priorità
          switchPulsanti();
          durataRC = millis() - startTimeRC;
          digitalWrite(relay[relayRC], HIGH);
        }
        Serial.println("FUORI IL CICLO WHILE ");
        digitalWrite(relay[relayRC], LOW);
        esecuzioneRC=0;
        Serial.println("Presa inserita nella coda a mantenimento ciclico (RC)");
        assegnaTempi(relayRC);
        cicloMantenimento(elementoRC);
        Serial.println("--------------------------------------------------------");
      }
    }
   }
   buttonVal = analogRead(buttonPin);    // continua a leggere il valore dei pulsanti per interrompere la ricarica in caso di selezione di modalità priorità
   switchPulsanti();
}

void cicloMantenimento(byte secondoMantenimento){  // controlla se si è superato il tempo di giorno dall'ultima azione di ogni presa     
  Serial.println(millis());
  unsigned long trascorso00=millis()-tempo00;
  unsigned long trascorso01=millis()-tempo01;
  unsigned long trascorso10=millis()-tempo10;
  unsigned long trascorso11=millis()-tempo11;
  //ogni 24 ore esegui il mantenimento
  if (trascorso00 > giorno && codaPR.isEmpty()==true && codaRC.isEmpty()==true){
  Serial.println(trascorso00);
  codaMN.push(secondoMantenimento);
  Serial.println("Ho inserito la presa di nuovo in mantenimento perché sono passate 24h");
  tempo00=millis();
  }
  else if (trascorso01 > giorno && codaPR.isEmpty()==true && codaRC.isEmpty()==true){
  Serial.println("Passaggio intermedio");
  Serial.println(trascorso01);
  codaMN.push(secondoMantenimento);
  Serial.println("Ho inserito la presa di nuovo in mantenimento perché sono passate 24h");
  tempo01=millis();
  }
  else if (trascorso10 > giorno && codaPR.isEmpty()==true && codaRC.isEmpty()==true){
  Serial.println(trascorso10);
  codaMN.push(secondoMantenimento);
  Serial.println("Ho inserito la presa di nuovo in mantenimento perché sono passate 24h");
  tempo10=millis();
  }  
  else if (trascorso11 > giorno && codaPR.isEmpty()==true && codaRC.isEmpty()==true){
  Serial.println(trascorso11);
  codaMN.push(secondoMantenimento);
  Serial.println("Ho inserito la presa di nuovo in mantenimento perché sono passate 24h");
  tempo11=millis();
  }  
}


void assegnaTempi(int presa){  // assegna i tempi di completamento ad ogni presa (per passarli a cicloMantenimento())
  switch (presa){
    case 0:
    tempo00=millis();
    Serial.println(tempo00);
    break;
    case 1:
    tempo01=millis();
    Serial.println(tempo01);
    break;
    case 2:
    tempo10=millis();
    Serial.println(tempo10);
    break;
    case 3:
    tempo11=millis();
    Serial.println(tempo11);
    break;
  }
}
--------------------------------------------------------
PASSO UNO PRIMA RICARICA
6443
FUORI IL CICLO WHILE 
18465
--------------------------------------------------------
AGGIUNTA PRESA A SECONDA RICARICA
--------------------------------------------------------
PASSO UNO SECONDA RICARICA
FUORI IL CICLO WHILE 
Presa inserita nella coda a mantenimento ciclico (RC)
30737
30744
Passaggio intermedio
30751
Ho inserito la presa di nuovo in mantenimento perché sono passate 24h