usare millis per due uscite relè a tempo

Buongiorno, scusate ma proprio non riesco ad usare millis .
La mia necessità è la seguente:
Devo controllare un ingresso di tensione su pin a1 (questa parte di sketch funziona) , e quando supero una certa soglia deve attivarsi una uscita per pilotare un relè che deve essere mantenuto per un certo tempo (ex 60 minuti) , nel frattempo se il valore di tensione controllato non è ancora disceso nei limiti prefissati devo attivare un secondo relè ,il quale deve rimanere attivato anche lui per un tempo prefissato (ex 60minuti).
Dato che devo tenere sempre sotto controllo il valore di tensione non posso usare delay ,in quanto bloccherebbe il programma fino al rilascio del primo relè , quindi ho optato per millis (in realtà potrei bypassare il problema usando due altre schede nano da usare come timer , una per relè ma...... ).
Ho letto e riletto il tutorial di millis ma tutti gli esempi riportano solo il lampeggio del led e già con quello ho dei seri problemi , complice anche l'età che mi rallenta i neuroni ,a comporre lo sketch .
Grazie infinite per qualsiasi aiuto e consiglio.

Ciao, crapa

E' semplice!
Supponiamo che tu debba fare il caffè per il tiramisù, cuocere la pasta e cuocere un ciambellone: accendi il fuoco sotto alla macchinetta, metti la pasta nell'acqua bollente e inforni il ciambellone, quindi segni l'ora: t_inizio=millis();
Poi controlli l'orologio, supponendo che tu debba togliere il caffè dopo 5 minuti, scolare la pasta dopo 8 minuti e togliere il ciambellone dopo 40 minuti:

if(millis()-t_inizio>=5*60*1000) {Togli il caffè}
if(millis()-t_inizio>=8*60*1000) {Scola la pasta}
if(millis()-t_inizio>=40*60*1000) {Sforna il ciambellone}

P.s.: per i neuroni: Gingko Biloba, Maca e Hocus Focus (MyVitamins)! :slight_smile:
N.B.: la maca è eccitante, più o meno come un caffè.

Sei già troppo avanti, ho capito solo ora con il tuo esempio che 5601000 sono 5 minuti .
Io sono ancora fermo alle dichiarazioni per millis , ora sto provando con la libreria timer.h ma francamente ho difficoltà a interpretare le righe.Ma ogni esempio che leggo mi devia dal percorso .
ad esempio il comando t.update() messo alla fine cosa significa? e in generale le due parentesi vuote che significato hanno.
Non sono un programmatore e molti esempi letti non risolvono il mio problema penso che risolverò con altri due nano usando il delay.
Grazie comunque per la collaborazione .

Per cose semplici, può essere più difficile usare una libreria che fare da soli... :slight_smile:

Le parentesi sono messe per contenere i parametri da inviare alla funzione. Se non devono essere inviati parametri, le parentesi restano vuote. Void, invece, vuol dire che la funzione non deve restituire un valore. Esempio:

void scriviNumero(int num)
{
lcd.setCursor(0,0);
lcd.print(num);
}

void setup() {}

void loop()
{
scriviNumero(42);
}

Funzioni

Grazie per la solerte risposta , infatti sto abbandonando anche la libreria, stavo provando questa strada perchè in una discussione che ho letto veniva indicata questa soluzione in quanto la persona era come me un" utilizzatore" e non un programmatore.
Ritorno sui miei passi e avendo necessità di risolvere in breve tempo il problema o uso due nano (che spreco) oppure risolvo con due timer utilizzando due ne555 . Anche perchè ho paura che essendo questa scheda accesa 24 ore al giorno per 365 giorni all'anno non vorrei incorrere nel problema di overflow che non mi è ben chiaro.
Comunque potresti cortesemente postarmi uno sketch completo dove con un pulsante utilizzo millis? cioè premo pulsante e quando rilascio rimane il led acceso per un tempo prefissato? in modo di avere una base di partenza quando riprenderò il discorso.
Grazie mille .

In questo esempio, il LED si accende appena premi il pulsante (che deve essere collegato verso massa, in quanto attivo la resistenza di pull-up interna) e resta acceso per 3 secondi da quando lo lasci:

const byte INGRESSO=2; // Pulsante su I/O 2. IL PULSANTE DEVE ESSERE CONNESSO VERSO MASSA.
const byte USCITA=13; // Uscita su I/O 13, a cui è collegato anche il LED presente sulla scheda.
unsigned long t_premuto;

void setup()
{
pinMode(INGRESSO, INPUT_PULLUP);
pinMode(USCITA, OUTPUT);
}

void loop()
{
if(!digitalRead(INGRESSO)) t_premuto=millis();

if(millis()-t_premuto<3000) digitalWrite(USCITA,HIGH);
else digitalWrite(USCITA,LOW);
}

E' fatto nel modo più semplice possibile, attivando o disattivando l'uscita a ogni giro di loop anche se è già attiva o non attiva. Non c'è niente di male, ma concettualmente è sbagliato. Prima di attivarla o disattivarla bisognerebbe controllare se è già stata impostata in quello stato.

Il problema dell'overflow di millis() non sussiste se usi la forma corretta, che è:
if(millis()-t_inizio > durata)

può anche essere >= o < o <=, secondo le esigenze. Evita di usare == per millis(), perché l'uguaglianza dura solo 1ms e il flusso del programma potrebbe non passare per quell'if nel momento in cui l'uguaglianza è verificata.

Grazie, veramente ad ognuno il suo mestiere, ho letto il tuo sketch e ho già visto diverse cose che non conoscevo , const byte ,pullup ..
Sono andato a vedere le spiegazioni, penso di essere un cavernicolo che sta cercando di creare la ruota.
Grazie infinite per il tempo dedicatomi il suo sketch è molto chiaro e mi permette di capire molte cose ,ora ho finalmente una direzione da seguire , .
Grazie .

Prego :slight_smile:
A presto

crapa:
ora ho finalmente una direzione da seguire

Considera che è possibile scrivere/organizzare/progettare i programmi secondo diversi "paradigmi", compreso emulare porte logiche, contatti, ladder ecc. Personalmente ho visto che per compiti semplici ragionare in ladder o a blocchi funzionali (anche se scrivendo in C) permette notevoli semplificazioni (anche se non rende il codice molto chiaro/intuitivo). Il gradino successivo è la macchina a stati (sempre come metodo di progetto), che permette di elaborare sequenze arbitrariamente complesse anche in parallelo con altre, senz'altro il migliore investimento di tempo studio. Il passo successivo sarebbe la programmazione "classica", quella degli esempi, che porta subito ad infognarsi con la logica, magari portata avanti con temporizzazioni bloccanti realizzate con delay, e qui il principiante si blocca subito perché non trova intuitivo ragionare nei due modi precedenti (che secondo me, visto che siamo su un piccolo microcontroller per semplici automazioni, e non su un PC alle prese con un gestionale, dovrebbero essere "insegnati" per primi). Ma conoscendoli tutti e tre si può trarre vantaggio da ogni approccio quando risulta più valido per lo specifico problema (in un programma si possono anche adottare tutti e tre assieme, tanto sono solo modi di progettare/impostare la logica o parti di essa).

Finito il pippone teorico, un esempio in stile PLC (con qualche difficoltà in più):

void loop()
{
    // lettura ingressi con espressione relazionale per avere logica positiva
    in = digitalRead(INGRESSO) == PRESSLEVEL;   // in==1 quando premuto

    // temporizzatore tipo Toff retriggerabile realizzato con millis
    if (in) { t1out=1; t1=millis(); } else if (millis()-t1 >= TEMPO) t1out=0;

    // scrittura uscite con operatore ternario
    digitalWrite(USCITA, t1out ? ONLEVEL : OFFLEVEL);
}

I vari nomi in maiuscolo sono costanti o definizioni di comodo per descrivere l'hardware all'inizio del programma (livelli di ingresso uscita, collegamenti ecc), ad esempio:

#define INGRESSO    ...  <-- terminale ingresso Arduino
#define PRESSLEVEL  ...  <-- livello letto a pulsante premuto

#define USCITA      ...  <-- terminale uscita Arduino
#define ONLEVEL     ...  <-- livello uscita attiva
#define OFFLEVEL    ...  <-- livello uscita disattiva

#define TEMPO       ...  <-- millisecondi temporizzatore

La cosa secondo me interessante di questo approccio (parametrizzazione iniziale) è che si possono modificare i collegamenti, o la logica positiva o negativa di ingressi/uscite, senza minimamente toccare la logica del programma, nel loop infatti non si fa riferimento esplicito ad alcun terminale o livello, e si lavora sempre nella più intuitiva logica positiva.

Altro esempio, relé passo passo:

void loop()
{
    in = digitalRead(INGRESSO) == PRESSLEVEL;   // in==1 quando premuto

    q ^= in & !inPrec;                          // rele' passo passo
    inPrec = in; 

    digitalWrite(USCITA, q ? ONLEVEL : OFFLEVEL);
}

Risposta perfetta e completa, @Claudio_FF for president! ;D