Salve a tutti. Come da titolo avrei la necessità di calcolare il tempo che intercorre tra la pressione di un pulsante che accende un relè e la successiva pressione dello stesso pulsante che spegne il relè. Ho letto numerosi post e documenti dove si parla della funzione millis(), ma purtroppo sono agli inizi e non ho capito bene come implementrla nel mio sketch. In pratica all'avvio il programma si ferma in una specie di standby visualizzando un simbolo di accensione sul display. In questo punto premo un pulsante che mi accende un relè che comanda un circuito. Nel loop viene richiamata una funzione che calcola il tempo trascorso. Premendo nuovamente il pulsante, il relè si spegne e il programma ritorna in standby e salva (aggiorna) il tempo, ore e minuti nella EEprom. Io vorrei sapere quanto tempo è rimasto acceso il circuito. Purtroppo sono riuscito a metà nel mio intento perchè il conteggio continua anche durante lo standby. Potreste aiutarmi a disticarmi magari con un esempio. Per semplificare inserisco solo la parte di codice interessata. Grazie a tutti.
//funzione chiamata nel loop che calcola il tempo trascorso
void tempoTrascorso() {
tmp = (millis() / 1000);
secondi = tmp - (variabile * 10);
if (secondi > 60) {
secondi = secondi - 60;
}
if (secondi == 60) {
minuti = minuti + 1;
variabile = variabile + 6;
}
if (minuti >= 60) {
minuti = 0;
ore = ore + 1;
}
lcd.setCursor(0, 1);
lcd.print("TIMER ");
lcd.print(ore);
lcd.print(":");
lcd.print(minuti);
lcd.print(":");
lcd.print(secondi);
//pulsante accensione on/off
static void click1() {
//int ledOnOff = 0;
// EEPROM.update(0, ledIngressi);
// EEPROM.update(1, ore);
// EEPROM.update(2, minuti);
// delay(200);
digitalWrite(4, !digitalRead(4));
int ledOff = 0;
ledOff = digitalRead(4);
if (ledOff == LOW) {
digitalWrite(led_up, HIGH);
delay(500);
digitalWrite(led_up, LOW);
EEPROM.update(0, ledIngressi);
EEPROM.update(1, ore);
EEPROM.update(2, minuti);
delay(200);
}
}
type or paste code here
Dovresti usare almeno una flag, di tipo bool, per "dire" alla routine che conta quando deve contare e quando deve fermarsi ... ad esempio, nel momento in cui accendi, metti la flag ad 1 (o TRUE, il nome della flag puo essere qualsiasi cosa), e nel momento in cui spegni, la metti a 0 (o FALSE), poi nella parte che incrementa il tempo, lo incrementi solo quando la flag e' ad 1.
Potresti usare millis anche al posto di quel delay, se il programma deve poter fare altre cose, perche' il delay e' bloccante (mentre aspetta, il programma non fa altro, ne legge ingressi, nulla) ... se invece il programma non deve fare nient'altro, si possono usare anche i delay.
Grazie per la risposta. Potresti farmi un esempio di codice? Avevo provato ad utilizzare una if all'inizio della funzione del tipo " se il pin di uscita è HIGH fai il conteggio" e una if quando spengo "se il pin è LOW prendi i dati e aggiorna la memoria" . Visto che non funzionava ho provato a lavorare un po con la millis() e la differenza tra due variabili unsigned long che avevo chiamato tempoAvvio e tempoArresto ma non sono riuscito a trovare la giusta combinazione per inserirla nello sketch. Negli esempi che ho guardato viene sempre inserito un intervallo che io non ho considerato forse questo è uno degli errori che ho commesso.
Da solo pezzi di codice difficile capire cosa fai. Dal tuo codice sopra non si capisce cosa è "variabile" per calcolare ore/min/sec nella funzione tempoTrascorso
La millis() la devi memorizzare esempio in tempoAvvio e poi quando serve, in quel momento, "spezzi" i millisecondi delta in ore/min/sec:
unsigned long tempoDelta = (millis()-tempoAvvio) / 1000UL; // da millisecondi a secondi
int ore=tempoD/3600UL; // UL suffisso per forzare costante a unsigned long
tempoD=tempoDelta-(ore*3600UL);
int min=tempoDelta/60UL;
int sec=tempoDelta-(min*60UL);
Hai perfettamente ragione e mi scuso per la confusione. Ho fatto alcune modifiche al pulsante di accensione/spegnimento e sono riuscito a calcolare un intervallo di tempo tra l'accensione e lo spegnimento. Ora devo aggiornare la memoria. I minuti e le ore le ho dichiarate come int.
if (ledOff == HIGH) {
tempoAcceso = (millis()/1000);
Serial.println("Sistema acceso da ");
Serial.print(tempoAcceso);
Serial.print("secondi");
if (ledOff == LOW) {
tempoArresto = (millis()/1000);
tmp = (tempoArresto - tempoAcceso);
secondi = tmp - (variabile * 10);
if (secondi > 60) {
secondi = secondi - 60;
}
if (secondi == 60) {
minuti = minuti + 1;
variabile = variabile + 6;
if (minuti >= 60) {
minuti = 0;
ore = ore + 1;
}
}
Serial.println(tempoArresto);
Serial.println(" secondi");
Serial.println(tempoAcceso);
Serial.println(" secondi");
Serial.println("Intervallo");
Serial.println(tmp);
Serial.println(" secondi");
EEPROM.update(0, ledIngressi);
EEPROM.update(1, ore);
EEPROM.update(2, minuti);
delay(200);
}
Quel calcolo che fai mi pare un pò strano.
"variabile" la aumenti di 6 ma quando la usi nei calcoli (serve solo li?) la moltiplichi per 10...
tanto vale aumentarla di 60 e risparmiarti la moltiplicazione.
Non mi è chiaro poi, ma ore/min conteggiano solo tempo tra un accendi/spegni o devi "accumulare" i tempi per cui rimane acceso ?
EDIT: mi pare di capire che vuoi accumulare.
secondo me, se vuoi accumulare:
if (ledOff == LOW) {
// variabili locali temporanee
unsigned long tempoAcceso= tempoArresto-tempoAvvio; // se tempi già in sec non serve dividere / 1000UL
int tmpore=tempoAcceso / 3600UL; // UL suffisso per forzare costante a unsigned long
tempoAcceso=tempoAcceso-(tmpore*3600UL);
int tmpmin=tempoAcceso / 60UL;
int tmpsec=tempoAcceso % 60UL;
// accumulo
ore=ore+tmpore;
minuti=minuti+tmpmin;
Serial.print("Avvio:"); Serial.print(tempoAvvio); Serial.println(" sec");
Serial.print("Arresto:"); Serial.print(tempoArresto); Serial.println(" sec");
Serial.print("Intervallo:"); Serial.print(tempoAcceso); Serial.println(" sec");
Serial.print("Acceso:"); Serial.print(tmpore); Serial.print(":"); Serial.println(tmpmin);
Serial.print("Totale:"); Serial.print(ore); Serial.print(":"); Serial.println(minuti);
EEPROM.update(0, ledIngressi);
EEPROM.update(1, ore);
EEPROM.update(2, minuti);
delay(200);
}
Si devo accumulare i tempi di accensione. Infatti come avevo scritto io non aggiornava la memoria.
Grazie infinite! tempoArresto = millis() suppongo cioè quando premo il pulsante per spegnere.
Scusa, approfitto della tua cortesia. Non sono riuscito a provare il codice che mi hai scritto. Pensi che bisogna inserire un codice come questo (non so se è corretto) per il salvataggio in memoria
minuti = tmpmin + EEPROM.read(cellaminuti);
ore = tmpore + EEPROM.read(cellaore);
oppure dovrebbe sommarlo automaticamente?
Si. Dividi anche per 1000UL visto che lo consideri in sec
No. Finché Arduino è acceso accumuliamo il tempo.
Se però spegni o resetti allora bisognerebbe nella setup rileggere ore e min
(ma quando azzeri eeprom?? cioè avresti un accumulo infinito)
OCCHIO se non ricordo male i comandi eeprom scrivono e leggono 1 byte. Finché ore rimane minore di 255 okay, ma poi... overflow
EDIT: il mio codice fa un po schifo, non puoi accumulare min e ore, devi accumulare i secondi e poi solo quando stampi a serial e scrivi in eeprom dividi in ore e min
Se a primo giro ore=2 min=43
poi secondo giro ore=6 min=38
Avresti mintot=43+38 che non ha senso
// variabili globali
unsigned long tempoTotale=0;
// togliere variabili "ore" e "minuti"
if (ledOff == LOW) {
// variabili locali temporanee
unsigned long tempoAcceso= tempoArresto-tempoAvvio; // se tempi già in sec non serve dividere / 1000UL
// accumulo
tempoTotale=tempoTotale+tempoAcceso;
// calcolo tempo totale in hh:mm
uint16_t ore=tempoTotale / 3600UL; // UL suffisso per forzare costante a unsigned long
tempoTotale=tempoTotale-(ore*3600UL);
uint8_t min=tempoTotale / 60UL;
Serial.print("Avvio:"); Serial.print(tempoAvvio); Serial.println(" sec");
Serial.print("Arresto:"); Serial.print(tempoArresto); Serial.println(" sec");
Serial.print("Intervallo:"); Serial.print(tempoAcceso); Serial.println(" sec");
Serial.print("Totale:"); Serial.print(ore); Serial.print(":"); Serial.println(min);
EEPROM.update(0, ledIngressi); // sempre 1 byte
EEPROM.update(1, min); // sempre 1 byte
EEPROM.put(2, ore); // uint16_t occuperà 2 byte
delay(200);
}
Provato e funziona perfettamente. Ti ringrazio per l'aiuto.
Nel setup avevo già inserito la lettura della memoria. Nel caso avessi bisogno di valori superiori a 255 come dovrei fare? Questa funzione serve per controllare il tempo di funzionamento delle valvole di un amplificatore di cui io però non conosco la vita .
EEPROM.put e EEPROM.get invece di update e read
ovviamente devi scrivere con indirizzi non 0,1,2 che sono byte
Mi sembra che funziona. Ho provato a lasciarlo avviato per 3 minuti e il tempo in memoria rimane lo stesso (43 min). Premuto il pulsante ON dopo 3 minuti di accensione, spento e riacceso con il pulsante, in memoria leggo 46 min
Non ti fidare, con tempi oltre l'ora sbarella.
EDIT:
// variabili globali
unsigned long tempoTotale=0; // togliere variabile "minuti" !!
unsigned long tempoAcceso=0;
uint8_t min=0;
uint16_t ore=0;
...
if (ledOff == LOW) {
// tempi già in sec non serve dividere / 1000UL, lo devi fare quando leggi millis()
tempoAcceso=tempoArresto-tempoAvvio;
// accumulo
tempoTotale=tempoTotale+tempoAcceso;
// calcolo tempo totale in hh:mm
ore=tempoTotale / 3600UL; // UL suffisso per forzare costante a unsigned long
tempoTotale=tempoTotale-(ore*3600UL);
min=tempoTotale / 60UL;
Serial.print("Avvio:"); Serial.print(tempoAvvio); Serial.println(" sec");
Serial.print("Arresto:"); Serial.print(tempoArresto); Serial.println(" sec");
Serial.print("Intervallo:"); Serial.print(tempoAcceso); Serial.println(" sec");
Serial.print("Totale:"); Serial.print(ore); Serial.print(":"); Serial.println(min);
EEPROM.put(0, (uint8_t) ledIngressi); // sempre 1 byte
EEPROM.put(1, (uint8_t) min); // sempre 1 byte
EEPROM.put(2, (uint16_t) ore); // uint16_t occuperà 2 byte
delay(200);
}
Nel setup dovrai leggere ore e min e ricaricare il valore come secondi in "tempoTotale"
...
EEPROM.get(1, min);
EEPROM.get(2, ore);
tempoTotale = ore*3600UL+min*60UL;
Ho invertito in eeprom min e ore. Ricorda che ore userà 2 byte quindi se vuoi scrivere altro in eeprom dopo ore, devi partire da indirizzo 4, visto che ore usa 2 byte ad indirizzo 2 e 3
Grazie davvero, gentilissimo!!! Non avrei saputo proprio come fare.
Appena posso provo il tutto e ti aggiorno. Grazie ancora!
Ciao . Perdonami se torno a scocciarti. Ricevo questo errore di compilazione:
cannot bind non-const lvalue reference of type 'unsigned char&' to an rvalue of type 'uint8_t {aka unsigned char}'
Ho rinominato la variabile "minuti" in "minutimom".
Queste tutte come variabili globali:
unsigned long tempoAvvio;
unsigned long tempoAcceso;
unsigned long tempoArresto;
unsigned long tempoTotale;
uint8_t min =0;
uint16_t ore=0;
questo nel Setup
EEPROM.get(0, (uint8_t) ledIngressi);
EEPROM.get(1, (uint8_t) min); // 1 byte non servirebbe cast (uint8_t) male non fa
EEPROM.get(2, (uint16_t) ore); // 2 byte non servirebbe cast, ma male non fa , per sicurezza
tempoTotale = ore*3600UL+min*60UL;
if (ledOff == HIGH) {
tempoAvvio = (millis()/1000);
Serial.println("Sistema acceso da ");
Serial.print(tempoAvvio);
Serial.print(" secondi");
if (ledOff == LOW) {
tempoArresto=millis()/1000UL;
tempoAcceso= tempoArresto-tempoAvvio; // se tempi già in sec non serve dividere / 1000UL
//funzione accumulo
tempoTotale=tempoTotale+tempoAcceso;
// calcolo tempo totale in hh:mm
ore=tempoTotale / 3600UL; // UL suffisso per forzare costante a unsigned long
tempoTotale=tempoTotale+tempoAcceso / 3600UL; // UL suffisso per forzare costante a unsigned long
tempoTotale=tempoTotale-(ore*3600UL);
min=tempoTotale / 60UL;
Serial.print("Avvio:"); Serial.print(tempoAvvio); Serial.println(" sec");
Serial.print("Arresto:"); Serial.print(tempoArresto); Serial.println(" sec");
Serial.print("Intervallo:"); Serial.print(tempoAcceso); Serial.println(" sec");
Serial.print("Totale:"); Serial.print(ore); Serial.print(":"); Serial.println(min);
EEPROM.put(0, uint8_t ledIngressi);
EEPROM.put(1, (uint8_t) min);
EEPROM.put(2, (uint16_t) ore);
delay(200);