Sto provando a convertire una funzione che usa delay con millis, ma non ci riesco.
Non capisco dov'è l'errore.
funzione con delay() che funziona ( attiva il rele per 30 secondi)
int pinRele = 13;
int statorele = HIGH;
unsigned long ritardo = 30000;
unsigned long previousTimeRele = 0;
void rele() {
digitalWrite(pinRele, LOW); //rele attivo
delay(ritardo); //tempo in millisecondi durata suono sirena
digitalWrite(pinRele, HIGH);
}
funzione con millis() che NON fuziona, il rele rimane sempre attivo
int pinRele = 13;
int statorele = HIGH;
unsigned long ritardo = 30000;
unsigned long previousTimeRele = 0;
void rele() {
digitalWrite(pinRele, LOW);
if (millis() - previousTimeRele > ritardo){
previousTimeRele = millis();
if (statorele == HIGH)
statorele = LOW;
digitalWrite(pinRele, statorele);
//per debug
Serial.println(statorele);
delay(2000);
}
}
Posta tutto il codice, non può essere solo quello.
Non si capisce dove fai la chiamata alla funzione rele(). Potrebbe essere anche solo chiamata dalla loop() ma molto probabilmente tutta la tua logica è errata nel caso di uso con millis()
Ovvero, quando chiami la rele() nel primo caso stai eseguendo le tre istruzioni della rele() una dopo l'altra (e nel fratempo NON puoi fare nulla di altro e rimani nella rele), mentre nella seconda versione ... quando chiami rele() hai un semplice if, mica un ciclo while quindi non rimani li dentro. Ma per sapere di più bisogna vedere il resto del codice. (P.S. NON sto dicendo che devi usare while nella rele() altrimenti quella funzione diventa bloccante come con un delay)
void pir1 (){ //leggo sensore PIR am312 e se rilevo il movimento attivo il relè
if (digitalRead(pinPIR1)) rele();
//debug
Serial.print("pinPIR1 :"); Serial.println(digitalRead(pinPIR1));
}
la funzione pir1() viene chiamata direttamente dalla funzione loop() .
Ma perchè vuoi cambiare quella delay() con millis() ? Qualche necessità reale o solo per studio ?
Se vuoi una cosa non bloccate, secondo me non puoi farla semplicemente cambiando il contenuto della rele().
L'ideale è strutturare il programma secondo una macchina a stati.
Già quello che ti ha postato @cotestatnt è una specie di macchina a stati
Che va bene, però la funzione 'rele' invece viene chiamata solo quando... viene chiamata. Mentre per funzionare come processo non bloccante dovrebbe essere chiamata sempre come la funzione 'pir'.
Se non sbaglio quello che vorresti fare è un timer che accende un relé quando viene rilevato movimento:
Per tutti e due i motivi. Supponi debba attivare una ventola per 3/5 minuti, ma in alcuni casi debba disattivarla prima del tempo impostato, come faccio se il sistema è bloccato dal delay ?
Ho visto la risposta di @cotestatnt e proverò appena possibile.
mi errore sulla istruzione digitalWrite. nel manuale di riferimento la sintassi è: digitalWrite(pin, value). c'è un parametro in più, quello del tempo.
forse non intendevi un'altra istruzione ?
... sono solo DUE parametri, il primo è 'pinRele' ed il secondo è il risultato, negato, della funzione 't1' alla quale, a sua volta, vengono passati due parametri ... conta le parentesi
Aggiungo inoltre che il ritardo di attivazione del relè inizia nel momento in cui il segnale del PIR torna sul livello LOW (cosa che del resto fa anche l'esempio che avevo fatto io).
______
PIR ___________| |_________________________________
_________________
Relé __________________| T |_______________
Forse in questi casi è più opportuno che inizi sul fronte di salita?
______
PIR ____________| |_________________________________
_________________
Relé _____________| T |_____________________
Si, ma comunque nel mio codice l'uscita del timer si attiva subito al fronte di salita. Se si vuole che anche i 30 secondi decorrano già dalla salita, allora serve un timer di tipo TP:
// timer tipo TP (t pulse) non retriggerabile
bool t1(bool in, uint32_t pt) {
static bool inp = 0;
static bool q = 0;
static uint32_t t;
if (in && !inp && !q) {
q = 1;
t = millis();
}
else if (q && millis()-t >= pt) {
q = 0;
}
inp = in;
return q;
}
Se poi lo si vuole retriggerabile ad ogni fonte di salita anche se non è scaduto il tempo, basta togliere &&!q dalla condizione di attivazione.
Si, inp (contrazione di: ingresso precedente) serve per il riconoscimento del fronte (edge detect). E per questo nel disegno ho indicato la freccia. Le linee tratteggiate indicano che ai fini del comportamento dell'uscita non importa quanto lunghi siano quei livelli.
E questo porta alla domanda seguente, che ha due risposte molto diverse a seconda che ti interessi usare il fronte o il livello del segnale di ingresso, e in quale condizione debba (se lo deve) tornare a zero l'uscita.
In sostanza il grafico potrebbe essere tutto li, fronte e dopo N secondi si alza Q e resta alta per sempre. Oppure il grafico potrebbe essere solo una parte, e l'uscita Q in realtà deve tornare a zero... ma quando?
potrebbe anche rimanere sempre alta , perché avrei intenzione di un ulteriore controllo con una variabile globale dell'istruzione nel loop (digitalWrite..).
Guardando il progetto su wokwi postato qui da cotestant.
Io vedo due dispositivi: Il pir e il relay.
Pir è un sensore, la sua uscita diventa alta quando rileva un movimento e poi torna bassa dopo t tempo. Serve un ingresso per rilevare lo stato del pir.
Relay è un attuatore. Serve una uscita per comandare il relay.
Di chi è la responsabilità di:
comandare il relay eccitandolo?
comandare il relay diseccitandolo?
monitorare lo stato del sensore pir?
Rimanendo astratti posso immaginare il gestore del relay che prende in ingresso lo stato del sensore pir. Per cui la responsabilità di eccitare il relay ricade su questo gestore. Però potrei anche assegnare la responsabilità al gestore del pir.
Gestore del relay
Il gestore è composta da una o più funzioni oppure è un oggetto di classe GestoreRelay. La responsabilità di eccitarsi in ritardo di 30s rispetto al segnale di trigger ricade su questo gestore.
Gestore del pir
Il gestore monitora lo stato e quando l'uscita del pir diventa alta si appunta il tempo di millis e aspetta 30s dopo di che l'uscita del gestore da bassa diventa alta. La responsabilità ricade su questo gestore.
Se ad esempio al relay c'è collegata una sirena allora se il suo stato è OFF, devo monitorare il/i sensore/i. Mentre se il suo stato è ON non monitoro i sensori.