Problema con la funzione millis

Salve, siccome sto imparando da poco la funzione millis, vorrei un aiuto per risolvere un problema con il mio sketch.

Questo sketch mi permette di simulare, per il mio modellino, l'accensione della luce interna dell'abitacolo all'apertura degli sportelli e contemporaneamente l'accensione delle frecce direzionali (dopo ogni 8 secondi si accendono prima le quattro frecce, poi solo quelle di sinistra ed infine solo quelle destra. poi dovrebbe ripartire da capo).
All'inizio ho utilizzato la funzione "for", ma la luce dell'abitacolo si accendeva dopo che aveva terminato il ciclo delle frecce direzionali.
Ora studiano un po' la funziona millis, l'ho utilizzata e ora va meglio, però sorge un problema, cioè una volta che tutto il ciclo dell'accensione delle frecce direzionali è terminato, ovviamente non si accendono più, poiché quel dato tempo è passato.
Ora vi chiedo come posso fare per azzerare il millis() e far ricominciare da capo il ciclo delle frecce direzionali?

Grazie.

Questo è lo sketch che ho creato:

/*
  sketch che permette di simulare l'accensione della luce nell'abitacolo
* all'apertura degli sportlli e contemporaneamente simulare l'accensione 
* delle frecce direzionali alternando: quattrofrecce - frecce SX - frecce DX.
*/

int frecceSX=13; //frecce direzionali sinistra
int frecceDX=12; //frecce direzionali destra
unsigned long intervalloSX=400;
unsigned long intervalloDX=400;
int statoFreSX=LOW;
int statoFreDX=LOW;
unsigned long precedente_millisSX=0;
unsigned long precedente_millisDX=0;
unsigned long tempo=0;
int lampada=11; //luce abitacolo
int sportello_SX_DX=10; //accende e spegne la lampada all'apertura degli sportelli

//inizializzazione
void setup()
  {
  pinMode(frecceSX, OUTPUT);
  pinMode(frecceDX, OUTPUT);
  pinMode(lampada, OUTPUT);
  pinMode(sportello_SX_DX, INPUT_PULLUP); //con la pullup ho attivato la resistenza interna dell'Arduino
  }

//loop

void loop()
  {
   Frecce();
   Lampada();
  }
  
  //Accende e spegne la luce abitacolo tramite le portiere SX e DX
 void Lampada(){
 
  int lampada_interna = digitalRead(sportello_SX_DX);
    
    if (lampada_interna==HIGH) {
      digitalWrite(lampada, HIGH);
  }
    else {
      digitalWrite(lampada, LOW);
  } 
 }
  
 //Frecce direzionali  
void Frecce() 
  {
  tempo=millis();
  
//accende contemporaneamente le frecce direzionali destra e sinistra per 8 secondi e poi le spegne
 if(tempo<10000){
  if(millis()-precedente_millisDX > intervalloDX){
    precedente_millisDX=millis();
    statoFreDX ^= 1;
    digitalWrite(frecceDX, statoFreDX);
  }
 
  if(millis()-precedente_millisSX > intervalloSX){
    precedente_millisSX=millis();
    statoFreSX ^= 1;
    digitalWrite(frecceSX, statoFreSX);
  }
 }

 //accende la freccia sinistra per 8 secondi e poi la spegne

  if((tempo>9000)&&(tempo<18000)){
  if(millis()-precedente_millisSX > intervalloSX){
    precedente_millisSX=millis();
    statoFreSX ^= 1;
    digitalWrite(frecceSX, statoFreSX);
  }
 }
 
 //accende la freccia destra per 8 secondi e poi la spegne

  if((tempo>18000)&&(tempo<29000)){
  if(millis()-precedente_millisDX > intervalloDX){
    precedente_millisDX=millis();
    statoFreDX ^= 1;
    digitalWrite(frecceDX, statoFreDX);
  }
 }
}

milvus:
Ora vi chiedo come posso fare per azzerare il millis() e far ricominciare da capo il ciclo delle frecce direzionali?

La funzione millis si azzera in automatico dopo circa 50 giorni di attività, non si influisce mai con il suo aggiornamento, per tanto viene resa pubblica una funzione e non la variabile in se, cosi si può definire questa in sola lettura.
La funzione millis va solo adoperata in operazioni differenziali (valoreAttuale-valoreDiInizio) in questo modo contano le differenze tra la seconda lettura di millis e la prima e il valore assoluto di millis non rappresenta altro che un riferimento. Adoperata in questo modo puo creare problemi solo in caso di reset, ma raramente un applicazione Arduino rimane attiva per più di 50 giorni, e se anche questo dovesse capitare in molti casi non causa che errori minimali, negli altri si prendono precisi (e semplici) provvedimenti.

Il valore di millis() si puó azzerare ma non é necessario.

Se crei la differenza come dice RobertoBochet funziona bene anche nel caso di roll-over dopo ca 49,5 giorni.

L' unica limitazione é che non puoi avere in questo modo intervali maggiorni di 49,5 giorni.

Ciao Uwe

uwefed:
L' unica limitazione é che non puoi avere in questo modo intervali maggiorni di 49,5 giorni.

Beh con qualche accorgimento. Ma sono veramente rari casi in cui servano intervalli maggiori di 49,5 giorni, in casi del genere forse è meglio affidarsi ad un RTC.

Quindi per far si che il ciclo riparta nuovamente, quale istruzione dovrei inserire per risolvere il problema? Grazie.

Al termine di loop questa viene re invocata. Gestisci bene le funzioni Frecce e lampade con degli intervalli relativi(a millis).
Generalmente devi tener conto di:
-Salvare il tempo di millis all'inizio del "timer"

long unsigned firstMillis = millis();

-Ad ogni ciclo eseguire una verifica data dalla sottrazione del valore iniziale di millis al nuovo millis e confrontarlo con l'intervallo richiesto.

if(millis()-firstMillis >= 1000)//Esempio con un intervallo richiesto di 1000ms

-Se la condizione si verifica allora il "timer" è scaduto e mi comporto di conseguenza, in caso contrario non faccio nulla per bloccare il programma.

Cerca di non introdurre elementi che blocchino il micro per molto tempo, se no potresti ottenere dei tempi superiori a quelli da te richiesti.

RobertoBochet:
Al termine di loop questa viene re invocata. Gestisci bene le funzioni Frecce e lampade con degli intervalli relativi(a millis).
Generalmente devi tener conto di:
-Salvare il tempo di millis all'inizio del "timer"

long unsigned firstMillis = millis();

-Ad ogni ciclo eseguire una verifica data dalla sottrazione del valore iniziale di millis al nuovo millis e confrontarlo con l'intervallo richiesto.

if(millis()-firstMillis >= 1000)//Esempio con un intervallo richiesto di 1000ms

-Se la condizione si verifica allora il "timer" è scaduto e mi comporto di conseguenza, in caso contrario non faccio nulla per bloccare il programma.

Cerca di non introdurre elementi che blocchino il micro per molto tempo, se no potresti ottenere dei tempi superiori a quelli da te richiesti.

Ok, grazie. Ora provo.

ciao, ho inserito le due istruzioni da te suggerito, ma non si accendono proprio i led.
ho provato a posizionarle subito dopo "tempo=millis();" e non andava, poi ho sostituito "if(tempo<10000)" con quella tua e non va ugualmente. Sicuramente non ho ben capito come modificare il mio sketch.
Perdonami ma sono alle prime armi con arduino.

Grazie.

Devi a studiarti come si usa la millis() prima QUI, poi QUI ed infine leggi anche QUI e QUI ... vedrai che ti sarà tutto più chiaro :wink:

Guglielmo

Purtroppo per te Guglielmo ha ragione, dovresti studiarti quelle "guide". Senza capirne la logica è difficile applicare quelle poche righe di codice da me postate.

I link suggeriti da gpb01 li avevo già consultati in passato, ovviamente li dovrò rileggere per capire meglio la funzione millis(). Però pensavo che per risolvere il problema al mio sketch si sarebbe risolto con una piccola istruzione, ma non è stato così! Pazienza, mi tocca rileggere tutto e capire ancora meglio.

Grazie ugualmente a tutti.

Magari basta una riga di codice, magari serve ridefinire la struttura del programma, in entrambi i casi senza capire il metodo di impiego della funzione millis diventa impossibile tirare inpiedi una soluzione solida. Cerca solo di capire il sistema d'impiego e la soluzione la troverai in un attimo. Se hai qualche domanda non esitare a chiedere.

Comunque in questi giorni ho approfondito un po' sull'argomento della funzione millis(), capendo qualcosa in più; però ancora non riesco a risolvere il problema del "riavvio" del ciclo.
Le ho provate tante (per le mie limitate conoscenze!). Forse è un qualcosa che non si può fare.
Quindi al momento ci rinuncio e riproverò quando ne saprò di più.

Comunque vi ringrazio per il tempo concessomi.

milvus:
... il problema del "riavvio" del ciclo.

Perdona, cosa intendi esattamente con "il problema del riavvio del ciclo" ?

Guglielmo

gpb01:
Perdona, cosa intendi esattamente con "il problema del riavvio del ciclo" ?

Guglielmo

Dopo che sono passati i 29 secondi non riparte il ciclo.

lo sketch dovrebbe fare quanto segue: lampeggiare il led di destra per meno di 10 secondi e poi si spegne, successivamente deve lampeggiare il led di sinistra anche questo per meno di 10 secondi e poi si spegne, infine devono lampeggiare entrambe i led (sinistra e destra) per circa 11 secondi e poi si spegne.
Poi dovrebbe ripartire da capo, ma non lo fa, poiché millis() va avanti con i millisecondi e quando deve rifare il loop, ovviamente sarà sempre superiore ai valori che io ho impostato nello sketch e così non fa il ciclo.
Ho provato in tutti i modi, per quello che so di arduino e del suo linguaggio, ma non riuscendoci per la mia limitata conoscenza.
Avevo aperto questo topic, credendo che bastava una piccola correzione o suggerimento per risolvere il problema.
Forse è un qualcosa che non si può fare?

Ho migliorato un po' lo sketch:

/*
*sketch che permette di simulare l'accensione della luce nell'abitacolo
* all'apertura degli sportlli e contemporaneamente simulare l'accensione 
*delle frecce direzionali alternando: quattrofrecce - frecce SX - frecce DX.
*/

long tempo_4_frecce = 9000; //tempo in millisecondi del lampeggio delle frecce
long tempo_SX_frecce = 18000;
long tempo_DX_frecce = 29000;

int frecceSX=13; //frecce direzionali sinistra
int frecceDX=12; //frecce direzionali destra
unsigned long intervalloSX=400;
unsigned long intervalloDX=400;
int statoFreSX=LOW;
int statoFreDX=LOW;
unsigned long precedente_millisSX=0;
unsigned long precedente_millisDX=0;

unsigned long i=0; //dichiaranto questa variabile di tipo "long", il ciclo non riparte

int lampada=11; //luce abitacolo
int sportello_SX_DX=10; //accende e spegne la lampada all'apertura degli sportelli

//inizializzazione
void setup()
  {
    Serial.begin(9600);
  pinMode(frecceSX, OUTPUT);
  pinMode(frecceDX, OUTPUT);
  pinMode(lampada, OUTPUT);
  pinMode(sportello_SX_DX, INPUT_PULLUP); //con la pullup ho attivato la resistenza interna dell'Arduino
  }

//loop

void loop()
  {
   Frecce();
   Lampada();
  }
  
  //Accende e spegne la luce abitacolo tramite le portiere SX e DX
 void Lampada(){
 
  int lampada_interna = digitalRead(sportello_SX_DX);
    
    if (lampada_interna==HIGH) {
      digitalWrite(lampada, HIGH);
  }
    else {
      digitalWrite(lampada, LOW);
  } 
 }
  
 //Frecce direzionali  
 void Frecce() 
  {
 i=millis();
 
//accende contemporaneamente le frecce direzionali destra e sinistra per 8 secondi e poi le spegne
 if(i <= tempo_4_frecce){
  
  if(millis()-precedente_millisDX > intervalloDX){
    precedente_millisDX=millis();
    statoFreDX ^= 1;
    digitalWrite(frecceDX, statoFreDX);
  }
 
  if(millis()-precedente_millisSX >= intervalloSX){
    precedente_millisSX=millis();
    statoFreSX ^= 1;
    digitalWrite(frecceSX, statoFreSX);
  }
  Serial.println(i);
 }

 //accende la freccia sinistra per 8 secondi e poi la spegne

  if((i > tempo_4_frecce)&&(i <= tempo_SX_frecce)){
    
  if(millis()-precedente_millisSX > intervalloSX){
    precedente_millisSX=millis();
    statoFreSX ^= 1;
    digitalWrite(frecceSX, statoFreSX);
  }
  Serial.println(i);
 }
 
 //accende la freccia destra per 8 secondi e poi la spegne

  if((i > tempo_SX_frecce)&&(i <= tempo_DX_frecce)){
      
  if(millis()-precedente_millisDX > intervalloDX){
    precedente_millisDX=millis();
    statoFreDX ^= 1;
    digitalWrite(frecceDX, statoFreDX);
  }
  Serial.println(i);
 }
}

Secondo me devi ricominciare da capo, semplificando la cosa e tendendo in mente DUE concetti :

  1. A te occorrono TRE stati logici
    1.1 lampeggia LED di destra per X secondi
    1.2 lampeggia LED di sinistra per Y secondi
    1.3 lampeggiano entrambi i LED per Z secondi
    ... dopo di che devi ricominciare da capo.

  2. Non devi MAI usare il valore così come è della millis() (... lo si fa in casi veramente eccezionali, ma non certo in questo), ma SEMPRE la differenza tra il valore attuale ed un valore precedentemente salvato ... il che ti da il tempo passato da quando hai salvato il valore.

Quindi ...
... ti occorrerà una variabile, che puoi chiamare "stato" che varrà 1, 2 o 3 a seconda della fase in cui ti trovi, dopo di che, in funzione di detta variabile, devi solo fare dei confronti del tempo trascorso.

Lo so, detto così può sembrare complesso, ma ti assicuro che è molto semplice. Inizia per gradi, prevedi tre blocchi che esegui a secondo del valore di "stato" ... vediamo se riesci ad impostare la base (... senza tanti fronzoli, e senza includere la gestione dei LED, per ora dobbiamo solo creare il loop delle fasi), poi andiamo avanti passo passo ...

Guglielmo

ok ci provo, tanto ho più tempo a disposizione tra oggi e domani. :slight_smile:
Ti ringrazio per la tua disponibilità. :wink:

milvus:
Ti ringrazio per la tua disponibilità. :wink:

... di nulla, figurati. Mi raccomando ... per piccoli passi, che se cerchiamo di mettere tutto assieme subito ... non arriviamo più alla fine :smiley:
Quindi ... cominciamo ad avere le TRE fasi che si alternano senza fine cercando di dargli i tempi desiderati :wink:

Guglielmo

Ho inserito le seguenti istruzioni ad ogni inizio di stato logico:

 if(millis()-val_pre < tempo){
   val_pre=millis();
   stato=millis();
  if(stato>=tempo){
   stato=0;
   val_pre=0;}
  else{
...

La variabile, di tipo unsigned long, "tempo" gli ho dato un valore di 10000 millisecondi, per far si che ogni stato logico duri meno di 10 secondi.

Purtroppo non va, infatti completa solo il primo stato (cioè entrambe i LED) poi non fa più nulla.

Suggerimento (non troppo, altrimenti gpb01 mi sgrida :P)

stato e' un flag, non devi dargli il valore di millis o di tempo, ma usarlo tu per dire allo sketch cosa fare ... controllandolo insieme alla variabile che usi per salvare il valore di millis di volta in volta ...

tipo, molto genericamente, usando 3 cicli if consecutivi ...

se lo stato e' 1, allora controlli millis usando il tempo numero 1, e quando e' passato, dopo le tue operazioni metti stato a 2
se lo stato e' 2, allora controlli millis usando il tempo numero 2, e quando e' passato, dopo le tue operazioni metti stato a 3
se lo stato e' 3, allora controlli millis usando il tempo numero 3, e quando e' passato, dopo le tue operazioni rimetti stato a 1 (in modo che il ciclo possa ricominciare da capo)

all'interno di ogni ciclo if, ci metti tutto quello che devi fare nei vari diversi stati, inclusi gli altri cicli if che potranno essere necessari (tanto se lo stato non e' quello, tutto cio che si trova all'interno del relativo ciclo if verra' ignorato, quindi di volta il volta lo sketch eseguira' solo quello che c'e' all'interno del ciclo if relativo al valore che ha stato in quel momento ;))