Problema con funzione millis

Ciao, sto provando a compilare uno sketch che mi perfette di avviare un ciclo passati 5 secondi. In questo ciclo deve accendersi un dispositivo e dopo 2 secondi accendersi un altro senza bloccare nulla nel codice. Il problema è che non riesco a farlo funzionare, parte il primo dispositivo ma poi il ciclo sembra bloccarsi.
Lo sketch è il seguente:

#include <Time.h>
#include <TimeAlarms.h>
#include <DS3231.h>
#include <DS3232RTC.h>
#include <Wire.h> 
DS3231  rtc(SDA, SCL);
Time t;

unsigned long time;
unsigned long previusMillis;

#define stereo   2
#define led_sopra   3
#define giradischi   4
#define led_tv   5
#define ventola   6
#define router   7
#define voip   8
#define pc   9
#define wii   12
#define lampada   13

void setup(){
  Serial.begin(115200);
  rtc.begin();

  
  pinMode(pc, OUTPUT);
  pinMode(stereo, OUTPUT);
  pinMode(led_sopra, OUTPUT);
  pinMode(wii, OUTPUT);
  pinMode(led_tv, OUTPUT);
  pinMode(ventola, OUTPUT);
  pinMode(giradischi, OUTPUT);
  pinMode(lampada, OUTPUT);
  pinMode(router, OUTPUT);
  pinMode(voip, OUTPUT);

  analogWrite(led_sopra, LOW );
  analogWrite(led_tv, LOW );
  analogWrite(ventola, LOW );
  digitalWrite(pc, HIGH );
  digitalWrite(stereo, HIGH );
  digitalWrite(wii, HIGH );
  digitalWrite(giradischi, HIGH );
  digitalWrite(lampada, HIGH );
  digitalWrite(router, HIGH );
  digitalWrite(voip, HIGH );
  
  
}

void loop(){
  t = rtc.getTime();

if (millis()>5000) {Avvio_Router_Voip();}
if (rtc.getTemp()<25)  {digitalWrite(ventola, LOW);}
if (rtc.getTemp()>25)  {digitalWrite(ventola, HIGH);}

if (t.hour == 00 && t.min == 48 && t.sec == 40) { digitalWrite(pc, LOW ); digitalWrite(stereo, LOW ); digitalWrite(wii, LOW ); digitalWrite(giradischi, LOW );}
if (t.hour == 00 && t.min == 48 && t.sec == 20) { Avvio_Router_Voip();}

}

void Avvio_Router_Voip(){
  
  digitalWrite(router, LOW);
  previusMillis = millis();
 if(millis() - previusMillis  >= 2000){ digitalWrite(voip, LOW);}

}

La parte che non riesco a funzionare è quella relativa al void Avvio_Router_Voip()
Sbaglio qualcosa nella funzione millis?

Non stai usando correttamente millis() per gestire delle cose, non si usa cosi millis().

Ci sono esempi e descrizione di come usare millis() per gestire qualcosa, tu dovrai usare millis() per verificare il trascorrere di un intervallo di tempo passato il quale esegui qualcosa, se questa cosa deve essere eseguita una sola volta avrai bisogno di un flag, di una variabile messa a zero esegue il codice, una volta eseguito il codice la variabile viene messa a 1 uno, e non lo esegue più.

Qui un esempio di esecuzione di compiti con mills(), in questo esempio non è presente una variabile flag, quindi il codice viene ripetuto continuamente al passare del tempo, ma da qui puoi capire l'uso di millis() per programmare compiti senza "bloccare" il programma.

http://www.leonardomiliani.com/2013/programmiamo-i-compiti-con-millis/

Ciao Davide,

se non sbaglio...

if (millis()>5000) {Avvio_Router_Voip();}

questa condizione sarà sempre vera dopo 5 secondi che arduino è avviato.

invece la tua funzione:

void Avvio_Router_Voip(){
  
  digitalWrite(router, LOW);
  previusMillis = millis();
 if(millis() - previusMillis  >= 2000){ digitalWrite(voip, LOW);}

}

in pratica ogni volta che richiami questa funzione che, dato la condizione descritta sopra, sarà ad ogni ciclo dopo 5 secondi dall'avvio...gli dici che previusMillis è uguale a millis...e subito dopo se uno meno l'altro >= 2000....che invece sarà sempre 0 e quindi il pin voip non andrà mai LOW.

Comunque guarda quanto suggerito da Torn24

Penso che tu voglia fare qualcosa di particolare "esattamente quello che vuoi fare non lo hai spiegato", quindi il tuo non sarà un uso comune di millis() per programmare compiti ad intervalli di tempo regolari.

E' inutile volerti fare esempi di codice, perché ho quasi la certezza che il tuo scopo sia particolare e per questo richieda un codice pensato su misura "certamente con l'uso di millis()" ma usata in modo specifico, probabilmente con variabili flag.

Quindi se vuoi spiega esattamente e dettagliatamente cosa vorresti fare, in modo da pensare a una soluzione "personalizzata".

Grazie dell'aiuto, la funzione millis da sola ho capito come funziona ma non riesco ad integrarla nel mio sketch. Da quello che ho capito l'errore che ho commesso è che passati 5 secondi, essendo quel if sempre verificato, il ciclo viene sempre eseguito in loop perché non c'è nulla che lo blocchi, quindi devo inserire una variabile che faccia eseguire quel ciclo una sola volta passato un certo periodo di tempo e lo blocchi alla fine, giusto?

Spiego bene cosa devo fare: --- passati 5 secondi dall'accensione di arduino deve essere eseguito per una sola volta un ciclo così composto: - accensione router - attesa di 20 secondi - accensione voip - attesa di 20 secondi - controllo se LED in ingresso è acceso - se il LED è acceso finisce il ciclo, se è spento il ciclo deve ripartire da capo (da accensione router) - se il ciclo fallisce 5 volte si deve accendere un LED

Tutto questo deve avvenire senza bloccare il resto del codice.

Ciao,

non ho capito come verificherai se router e voip hanno eseguito quanto richiesto (per il momento gestisci uscite arduino)…io farei così per gestire millis() e funzione (inserisco solo la parte mia):

unsigned long previusMillisIF;
unsigned long previusMillisFUNC;
byte counter;
boolean funcON;

#define LEDallarme 14 // ATTENZIONE HO MESSO UN PIN A CASO!!!

void loop() {

  if (couter < 5) { // verifica se entro i 5 tentativi
    if (millis() == previusMillisIF + 5000 && !funcON ) { //verifica se passati 5 secondi dall'ultimo previusMillisIF = millis();
      previusMillisFUNC = millis(); // salvo il valore di millis() attuale per la verifica nella funzione Avvio_Router_Voip()
      FuncON = true; //abilito l'esecuzione della funzione Avvio_Router_Voip()
    }
  }
  else {  // se oltre i 5 tentativi...e fai quello che vuoi
    digitalWrite (LEDallarme, HIGH); // se counter >= 5 attiva LEDallarme
  }

  if (funcON) {
    Avvio_Router_Voip();
  }
}

void Avvio_Router_Voip() {

  if (digitalRead(router) == HIGH && millis() == previusMillisFUNC + 20000) { // verifico stato router che è, dopo il setup, HIGH; 
                                                                              // verifico se sono passati 20 sec
    digitalWrite(router, LOW); // cambio stato uscita
    previusMillisFUNC = millis(); // salvo di nuovo milli() per confronto prossimo if
  }

  // qua ELSE IF/ELSE per verificare se eseguito o meno inserendo:
  // counter++;
  // funcON = false;
  // previusMillisFUNC = millis();

  if (digitalRead(router) == LOW && millis() == previusMillisFUNC + 20000) { // se sono riuscito a cambiare stato all'inizio funzione e nuovo confronto se trascorsi 20 secondi
      digitalWrite(voip, LOW); // cambio stato uscita
    
  }

  // qua ELSE IF/ELSE per verificare se eseguito o meno inserendo:
  // counter++;
  // funcON = false;
  // digitalWrite(router, HIGH);
  // previusMillisFUNC = millis();
}

spero sia chiaro il concetto…almeno quello che ho in testa io…

Quello che ti serve è una macchina a stati finiti. Nella pratica è una variabile che assume più stati finiti, un numero di valori prestabiliti, in base al valore della variabile esegue qualcosa di particolare.

Ecco un esempio per il tuo caso, il codice ovviamente non è stato provato e quindi possono esserci degli errori, ma la logica di una macchina a stati finiti è questa.

Esempio di macchina a stati finiti, in italiano:
http://www.lucadentella.it/2013/04/30/macchina-a-stati-finiti-e-arduino/

Esempio di programma modificato

/*
 * 
 * 
 * Spiego bene cosa devo fare: 
--- passati 5 secondi dall'accensione di arduino deve essere eseguito per una sola volta un ciclo così composto:
- accensione router
- attesa di 20 secondi
- accensione voip
- attesa di 20 secondi
- controllo se LED in ingresso è acceso
- se il LED è acceso finisce il ciclo, se è spento il ciclo deve ripartire da capo (da accensione router)
- se il ciclo fallisce 5 volte si deve accendere un LED



 */

unsigned long tempoCorrente;
unsigned long tempo=0;
unsigned long intervallo=5000;
int stato=0;
int contatore=0;
int flag=1;
void setup() {
  // put your setup code here, to run once:
  tempoCorrente=millis();
  tempo=millis();
}



void loop() {
  // put your main code here, to run repeatedly:
  tempoCorrente=millis();
  if(tempoCorrente-tempo>=intervallo && flag==1){
      stato=1;
      flag=0; // il codice viene eseguito una sola volta flag zero e la condizione non è più vera
  }
  if(stato==1){
      digitalWrite(router,HIGH);
      stato=2;
      
      tempo=millis();
  }
  else if (stato==2){
    
    if(tempoCorrente-tempo>=20){
        digitalWrite(voip,HIGH);
        stato=3;
        
        tempo=millis();
    }

  }
  else if(stato==3){

     if(tempoCorrente-tempo>=20){
        if(digitalRead(led,HIGH){
          stato=0;
        }
        else{
            contatore++;// conta + 1
            stato=1; // Ripete da router
          
        }
     }
  }
   if(contatore>=5){

        stato=0;//non fa più niente
        digitalWrite(led5volte,HIGH);

   }
   
}

Ho provato ad modificare il tuo codice per cercare di capirlo e vedere se funziona pezzo per pezzo, ho così scritto:

#include <Time.h>
#include <TimeAlarms.h>
#include <DS3231.h>
#include <DS3232RTC.h>
#include <Wire.h> 
DS3231  rtc(SDA, SCL);
Time t;

unsigned long time;
unsigned long tempoCorrente;
unsigned long tempo=0;
int stato=0;
int contatore=0;
int flag=1;

#define stereo   2
#define led_sopra   3
#define giradischi   4
#define led_tv   5
#define ventola   1
#define router   7
#define voip   8
#define pc   9
#define wii   10
#define lampada 0  

#define pulsante_lampada A0
int statopulsante_lampada = 0; 




void setup(){
  rtc.begin();
  tempoCorrente=millis();
  tempo=millis();
  
  pinMode(pc, OUTPUT);
  pinMode(stereo, OUTPUT);
  pinMode(led_sopra, OUTPUT);
  pinMode(wii, OUTPUT);
  pinMode(led_tv, OUTPUT);
  pinMode(ventola, OUTPUT);
  pinMode(giradischi, OUTPUT);
  pinMode(lampada, OUTPUT);
  pinMode(router, OUTPUT);
  pinMode(voip, OUTPUT);

  pinMode(pulsante_lampada, INPUT);

  analogWrite(led_sopra, LOW );
  analogWrite(led_tv, LOW );
  analogWrite(ventola, LOW );
  digitalWrite(pc, HIGH );
  digitalWrite(stereo, HIGH );
  digitalWrite(wii, HIGH );
  digitalWrite(giradischi, HIGH );
  digitalWrite(lampada, HIGH );
  digitalWrite(router, HIGH );
  digitalWrite(voip, HIGH );
  
  
}

void loop(){
       t = rtc.getTime();
       if (rtc.getTemp()<25)  {digitalWrite(ventola, LOW);}
       if (rtc.getTemp()>25)  {digitalWrite(ventola, HIGH);}
     if (t.hour == 14 && t.min == 54 && t.sec == 40) { digitalWrite(pc, LOW ); digitalWrite(stereo, LOW ); digitalWrite(wii, LOW ); digitalWrite(giradischi, LOW );}
     if (t.hour == 00 && t.min == 48 && t.sec == 20) { }
     
statopulsante_lampada = digitalRead(pulsante_lampada);
if (statopulsante_lampada == HIGH) {digitalWrite(lampada, LOW);} else {digitalWrite(lampada, HIGH);}

tempoCorrente=millis();
  if(tempoCorrente-tempo>=2000 && flag==1){Avvio_Router_Voip();}
  
  }

  
void Avvio_Router_Voip() {

stato=1;
flag=0; 
  
if (stato==1) {digitalWrite(router,LOW); stato=2; tempo=millis();}
else if (stato==2 && tempoCorrente-tempo>=20) {digitalWrite(voip,LOW); stato=3; tempo=millis();}}

Funziona tutto tranne l’ultimo if che non viene eseguito. Cosa sbaglio?

PS: Vi prego di non denunciarmi nel caso avessi scritto bestemmie…

Ho provato a mettere insieme il tuo codice con quello che ti ho mostrato, devi mettere solo
nella funzione Avvio_Router_Voip() l’accensione del voip, dovrebbe bastare un digitalWrite(), non mettere nessun if() e nessun stato, metti solo l’accensione il resto lo fa la funzione loop();

/*
 * 
 * 
 * Spiego bene cosa devo fare: 
--- passati 5 secondi dall'accensione di arduino deve essere eseguito per una sola volta un ciclo così composto:
- accensione router
- attesa di 20 secondi
- accensione voip
- attesa di 20 secondi
- controllo se LED in ingresso è acceso
- se il LED è acceso finisce il ciclo, se è spento il ciclo deve ripartire da capo (da accensione router)
- se il ciclo fallisce 5 volte si deve accendere un LED



 */

#include <Time.h>
#include <TimeAlarms.h>
#include <DS3231.h>
#include <DS3232RTC.h>
#include <Wire.h> 
DS3231  rtc(SDA, SCL);
Time t;

unsigned long time;
unsigned long tempoCorrente;
unsigned long tempo=0;
int stato=0;
int contatore=0;
int flag=1;

#define stereo   2
#define led_sopra   3
#define giradischi   4
#define led_tv   5
#define ventola   1
#define router   7
#define voip   8
#define pc   9
#define wii   10
#define lampada 0  

#define pulsante_lampada A0
int statopulsante_lampada = 0; 




void setup(){
  rtc.begin();
  tempoCorrente=millis();
  tempo=millis();
  
  pinMode(pc, OUTPUT);
  pinMode(stereo, OUTPUT);
  pinMode(led_sopra, OUTPUT);
  pinMode(wii, OUTPUT);
  pinMode(led_tv, OUTPUT);
  pinMode(ventola, OUTPUT);
  pinMode(giradischi, OUTPUT);
  pinMode(lampada, OUTPUT);
  pinMode(router, OUTPUT);
  pinMode(voip, OUTPUT);

  pinMode(pulsante_lampada, INPUT);

  analogWrite(led_sopra, LOW );
  analogWrite(led_tv, LOW );
  analogWrite(ventola, LOW );
  digitalWrite(pc, HIGH );
  digitalWrite(stereo, HIGH );
  digitalWrite(wii, HIGH );
  digitalWrite(giradischi, HIGH );
  digitalWrite(lampada, HIGH );
  digitalWrite(router, HIGH );
  digitalWrite(voip, HIGH );
  tempoCorrente=millis();
  tempo=millis();;
  
}



void loop() {

  t = rtc.getTime();
  if (rtc.getTemp()<25)  {digitalWrite(ventola, LOW);}
  if (rtc.getTemp()>25)  {digitalWrite(ventola, HIGH);}
  if (t.hour == 14 && t.min == 54 && t.sec == 40) { digitalWrite(pc, LOW ); digitalWrite(stereo, LOW ); digitalWrite(wii, LOW ); digitalWrite(giradischi, LOW );}
  if (t.hour == 00 && t.min == 48 && t.sec == 20) { }
     
  statopulsante_lampada = digitalRead(pulsante_lampada);
  if (statopulsante_lampada == HIGH) {digitalWrite(lampada, LOW);} else {digitalWrite(lampada, HIGH);}

  
  
  tempoCorrente=millis();
  if(tempoCorrente-tempo>=50000 && flag==1){ // PASSATI 5 SECONDI INIZIA L'ACCENSIONE
      stato=1;
      flag=0; // il codice viene eseguito una sola volta flag zero e la condizione non è più vera
  }
  if(stato==1){ // if() DENTRO ACCENDE IL ROUTER MODIFICARE DIGITALWRITE
      digitalWrite(router,HIGH);
      stato=2;
      
      tempo=millis();
  }
  else if (stato==2){ // PASSATI 20 SECONDI AVVIA LA FUNZIONE AVVIO_ROUTER_VOIP()
    
    if(tempoCorrente-tempo>=20000){
        Avvio_Router_Voip();// 
        stato=3;
        
        tempo=millis();
    }

  }
  else if(stato==3){ // PASSATI ALTRI 20 SECONDI CONTROLLA IL LED MODIFICARE PIN DIGITALWRITE
	 
     if(tempoCorrente-tempo>=20000){
        if(digitalRead(led,HIGH){ // SE IL LED è ACCESO ESCE DAL CICLO
          stato=0;
        }
        else{ // SE IL LED è SPENTO CONTA 1, E RIPETE DA INIZIO ACCENSIONE ROUTER
            contatore++;// conta + 1
            stato=1; // Ripete da router
          
        }
     }
  }
   if(contatore>=5){// SE CI SONO STATI 5 TENTATIVI  STATO=0 NON FA NIENTE, SE FOSSE STATO=1
                           // RIPARTE DA ROUTER 
        stato=0;//non fa più niente
        digitalWrite(led5volte,HIGH); // ACCENDE UN LED DOPO 5 TENTATIVI

   }
   
}


void Avvio_Router_Voip() {

        digitalWrite(voip,HIGH); // mettere il pin giusto

}

Grazie mille adesso funziona. Se provo a scrivere il codice (la parte dopo il primo if che da l'avvio) in un loop separato ad esempio "void Avvio_Router_Voip()" si blocca alla prime istruzione, come mai? Mi sarebbe piaciuto inserirlo in un void separato perchè dovrò utilizzare più volte quei comandi e sarebbe più comodo richiamare semplicemente la funzione.

La funzione loop() viene eseguita continuamente ciclicamente, finisce e ricomincia da capo.
La funzione setup() e tutte le funzioni che crei tu e richiami vengono eseguite un unica volta, per cui certe cose sono possibili sono nella funzione loop() come controllare il tempo che passa e eseguire qualcosa trascorso un certo tempo, perché essendo sempre eseguita legge il tempo con millis() e poi lo confronta sempre con le strutture di controllo if().

Per fare qualcosa del genere in una funzione bisognerebbe creare un ciclo finito o infinito while(1), ma bloccherebbe il programma, anche un ciclo finito per le volte che è eseguito bloccherebbe il restante programma while(i<200) i++; per il tempo che è eseguito bloccherebbe il programma.

Quindi certe cose necessitano di farle fare al loop() :wink:

Chiarissimo, grazie mille!!

Se vuoi rendere la funzione loop() più pulita, puoi crearti una funzione dove metti il “ciclo” e richiamarla continuamente nella funzione loop(), in questo modo è come se fosse nel loop() e mantieni il tutto più organizzato e più pulito.

Ma come detto è necessario richiamarla nel loop() perché è l’unica funzione che viene eseguita continuamente senza bloccare il programma.

Esempio:

/*
 * 
 * 
 * Spiego bene cosa devo fare: 
--- passati 5 secondi dall'accensione di arduino deve essere eseguito per una sola volta un ciclo così composto:
- accensione router
- attesa di 20 secondi
- accensione voip
- attesa di 20 secondi
- controllo se LED in ingresso è acceso
- se il LED è acceso finisce il ciclo, se è spento il ciclo deve ripartire da capo (da accensione router)
- se il ciclo fallisce 5 volte si deve accendere un LED



 */

#include <Time.h>
#include <TimeAlarms.h>
#include <DS3231.h>
#include <DS3232RTC.h>
#include <Wire.h> 
DS3231  rtc(SDA, SCL);
Time t;

unsigned long time;
unsigned long tempoCorrente;
unsigned long tempo=0;
int stato=0;
int contatore=0;
int flag=1;

#define stereo   2
#define led_sopra   3
#define giradischi   4
#define led_tv   5
#define ventola   1
#define router   7
#define voip   8
#define pc   9
#define wii   10
#define lampada 0  

#define pulsante_lampada A0
int statopulsante_lampada = 0; 




void setup(){
  rtc.begin();
   
  pinMode(pc, OUTPUT);
  pinMode(stereo, OUTPUT);
  pinMode(led_sopra, OUTPUT);
  pinMode(wii, OUTPUT);
  pinMode(led_tv, OUTPUT);
  pinMode(ventola, OUTPUT);
  pinMode(giradischi, OUTPUT);
  pinMode(lampada, OUTPUT);
  pinMode(router, OUTPUT);
  pinMode(voip, OUTPUT);

  pinMode(pulsante_lampada, INPUT);

  analogWrite(led_sopra, LOW );
  analogWrite(led_tv, LOW );
  analogWrite(ventola, LOW );
  digitalWrite(pc, HIGH );
  digitalWrite(stereo, HIGH );
  digitalWrite(wii, HIGH );
  digitalWrite(giradischi, HIGH );
  digitalWrite(lampada, HIGH );
  digitalWrite(router, HIGH );
  digitalWrite(voip, HIGH );
  tempoCorrente=millis();
  tempo=millis();;
  
}



void loop() {

  t = rtc.getTime();
  if (rtc.getTemp()<25)  {digitalWrite(ventola, LOW);}
  if (rtc.getTemp()>25)  {digitalWrite(ventola, HIGH);}
  if (t.hour == 14 && t.min == 54 && t.sec == 40) { digitalWrite(pc, LOW ); digitalWrite(stereo, LOW ); digitalWrite(wii, LOW ); digitalWrite(giradischi, LOW );}
  if (t.hour == 00 && t.min == 48 && t.sec == 20) { }
     
  statopulsante_lampada = digitalRead(pulsante_lampada);
  if (statopulsante_lampada == HIGH) {digitalWrite(lampada, LOW);} else {digitalWrite(lampada, HIGH);}

  ciclo();
  
}
  
  
   



void Avvio_Router_Voip() {

}


void Ciclo(){ // Viene sempre richiamata dalla funzione loop() è come se fosse all'interno del loop()
  tempoCorrente=millis();
  if(tempoCorrente-tempo>=5000 && flag==1){
      stato=1;
      flag=0; // il codice viene eseguito una sola volta flag zero e la condizione non è più vera
  }
  if(stato==1){
      digitalWrite(router,HIGH);
      stato=2;
      
      tempo=millis();
  }
  else if (stato==2){
    
    if(tempoCorrente-tempo>=20000){
        Avvio_Router_Voip();// 
        stato=3;
        
        tempo=millis();
    }

  }
  else if(stato==3){
	 
     if(tempoCorrente-tempo>=20000){
        if(digitalRead(led,HIGH){
          stato=0;
        }
        else{
            contatore++;// conta + 1
            stato=1; // Ripete da router
          
        }
     }
  }
   if(contatore>=5){

        stato=0;//non fa più niente
        digitalWrite(led5volte,HIGH);
	contatore=0;

   }

}