Devo tenere attratto un relè per un certo tempo quando si presenta un soggetto ( la mia gatta) davanti a un sensore IR classico.
Ho pensato di utilizzare la funzione Millis().
Uso un Wemos Lolin D1mini.
Il sensore IR vede la presenza del corpo davanti a sé, infatti mi accende il led ma poi anche se il programma esegue la funzione Eroga() non riesco a far attrarre il relè per il tempo che ho impostato nella "#define Rele_TIME 10000".
Lo sketch è il seguente:
/**************** *************
Test_sensore_IR
22 gennaio 2022
Ruotando il trimmer in senso orario
aumenta la distanza fino a circa 15 cm
*****************************************/
#define Rele_TIME 10000 // tempo di attrazione del relè
unsigned long lastMillis;
int LED = 13; // Utilizzo del led LOLIN D1
int Rele = 14; // Pin D5 = GPIO14 del LOLIN
int isObstaclePin = 2; // Pin D4 = GPIO2 del LOLIN
int isObstacle = HIGH; // non trovato ostacolo
void setup(){
pinMode(LED, OUTPUT);
pinMode(Rele, OUTPUT);
pinMode(isObstaclePin, INPUT);
Serial.begin(9600);
lastMillis = 0;
}
void Ostacolo(){
isObstacle = digitalRead(isObstaclePin);
if (isObstacle == LOW){
Serial.println("Presenza ");
Eroga();
}
else{
Serial.println("Libero ");
digitalWrite(LED, LOW);
}
}
void Eroga(){
unsigned long now = millis();
if(now - lastMillis < Rele_TIME){
digitalWrite(LED, HIGH);
digitalWrite(Rele, HIGH); // rele attivo
lastMillis = now;
}
digitalWrite(Rele, LOW); // rele disattivo
}
void loop(){
Ostacolo();
delay(1000);
}
Facciamo conto che al tempo t0 millis() è uguale a 1000;
nel momento in cui entri nella funzione Eroga() quello che accade è che now diventa uguale a 1000, lastMillis non è stata ancora assegnata quindi è uguale a 0
1000 - 0 < 10000 -> vero
Accende il led ed attiva il relé
lastMillis == 1000
esce dall'if e rimette subito il pin del relé su LOW.
Al giro successivo l'if non sarà più vero ed il relè rimane su LOW, ammesso che il sensore IR rimanga impegnato perché in caso contrario la funzione non viene nemmeno eseguita.
Quando lo attivi, prendi il tempo t_on, facendolo una sola volta;
poi, se millis()-t_on>=tempo, spegni.
Puoi aggiungere qualcosa per non ripetere lo spegnimento a ogni giro se è già stato effettuato, ma ripeterlo è solo un minimo rallentamento.
@anon67178573 un altro possibile approccio è quello di assegnare ad una variabile il valore di millis() + ritardo 10 secondi quando il sensore viene attivato.
Poi nel loop controlli se millis() == variabile (con la dovuta tolleranza per non mancare il momento esatto nel caso la MCU fosse impegnata in atro quando esegui l'if).
In questo modo eviti di dover gestire il fronte del segnale con un ulteriore variabile.
Se assegni il valore di now a ogni loop questo non potrà mai soddisfare la condizione now - lastMillis < Rele_TIME . Il momento dell'assegnazione dei valori alle variabili deve essere quindi controllato dalle opportune condizioni:
void Ostacolo() {
if ((lastMillis == 0) && (!Flag)) isObstacle = digitalRead(isObstaclePin);
if (!isObstacle) {
Serial.println("Presenza ");
Flag = true;
Eroga();
}
else {
Serial.println("Libero ");
digitalWrite(LED, LOW);
}
}
Per prima cosa leggi lo stato del pin una sola vola per periodo di attivazione, alla lettura di una presenza attivi il Flag che permetterà la scrittura della variabile lastMillis nell'altra funzione
Il flag impostato true attiva l'aggiornamento della variabile lasMillis e riporta a faslo il flag, anche isObstacole viene riportato allo stato iniziale per inibire la riattivazione del flag prima che sia trascorso il tempo di attivazione.
Trascorso il tempo di attivazione anche lastMillis viene riportato a zero e la condizione ((lastMillis == 0) && (!Flag)) è nuovamente verificata dando il via a un nuovo ciclo.
Grazie per l'aiuto sopratutto pratico.
Appena rientro a casa riprendo a fare esperienza con i vostri consigli.
Per quanto riguarda "l'overflow" non c'è problema, visto che il marchingegno che ho pensato di fare, rimane attivo per un massimo di 4 o cinque giorni.
L'overflow mi pare che arrivi dopo circa 49 giorni o giù di li.
Non avevo mai preso in considerazione questa cosa, i controlli che uso con millis gestiscono intervalli inferiori al secondo che si attivano solo su richiesta dell'utente.
ho modificato il mio codice seguendo questo tuo suggerimento.
Se non ho capito male, quando millis() < t_on, millis() - t_on dovrebbe dare un negativo ma non accade perché millis() restituisce un unsigned long.
Si, ma se quegli intervalli ricadono proprio quando il contatore torna a zero, la logica del programma può sbarellare da una frazione di secondo a centinaia di ore (a seconda delle condizioni testate).
Invece calcolando la differenza tra due interi long senza segno ottieni sempre il valore corretto, con l'unico limite che il risultato non può comunque eccedere le 1192 e qualcosa ore, ma considerando l'imprecisione di millis (circa cinque secondi all'ora) se serve misurare tempi così lunghi meglio usare altro.