millis()

ciao a tutti, mi sono affacciato da poco al mondo arduino e della programmazione, ho cercato nel forum e su alcuni siti come risolvere il mio problema ma non ho trovato nulla o non ho capito come fare, quindi ho deciso di scrivere qui.

In pratica io ho un pulsate ed un led, premendo il pulsante si deve accendere il led che poi si deve spegnere con un'isteresi di circa 2 secondi. se il pulsante si preme per più di 1 minuto il led si deve spegnere anche se il pulsante è ancora premuto e non deve più riaccendersi finchè il pulsante si rilascia.

Ho fatto il mio programmino utilizzando il delay ma mi crea dei problemi di tempo.

Ora arriva il mio problema, ho letto che si può utilizzare la funzione millis() per creare questi ritardi ma proprio non capisco come fare.

Ringrazio tutti anticipatamente.

Buonasera, essendo il tuo primo post, nel rispetto del regolamento, ti chiedo cortesemente di presentarti QUI ([u]spiegando bene quali conoscenze hai di elettronica e di programmazione[/u] ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con attenzione il su citato REGOLAMENTO ... Grazie.

Guglielmo

Dopo aver fatto quanto richiesto al post qui sopra …
… per studiarti come si usa la millis(), guarda prima QUI, poi QUI ed infine leggi anche QUI e QUI … vedrai che ti sarà tutto più chiaro :wink:

Guglielmo

chiedo scusa, mi sono presentato ora dove mi hai indicato.

Grazie della risposta Guglielmo, li ho già letti nel pomeriggio (avevo trovato una discussione nella quale li suggerivi), ma non ci ho capito molto (sono proprio alle prime armi, ora comunque li rileggo), o mi sfugge qualcosa... ... non riesco a capire come fare per far accedere il led per un tempo x dopo aver premuto il pulsante senza usare delay.

Giorgio

Dividi le cose ... ... con una parte del programma verifichi solo se il pulsante è stato premuto o meno. Quando viene premuto, attivi una "flag" per ricordardi la cosa (così eviti più pressioni), ti segni il tempo iniziale e accendi il LED ... in un altra, SE la flag è è attiva, controlli il tempo passato e se è passato il tempo x, spegni il LED e azzeri la "flag" così da permettere di nuovo l'accensione alla successiva pressione.

Guglielmo

P.S.: Ho dato per scontato che una volta premuto una volta il pulsante esso non deve più influenzare il conteggio fino a quando non è passato il tempo x .. se così non fosse, basta eliminare la "flag" ...

dopo aver riletto ciò che mi avevi consigliato, la mia mente contorta ha partorito questo sketch:

int in = A0;
int out = 2;
unsigned long interval = 1000; //intervallo in millisecondi 
unsigned long pushMillis;
int currentMillis = 0;

void setup() {
 // put your setup code here, to run once:
pinMode (in, INPUT);
pinMode (out, OUTPUT);

}

void loop() {
 // put your main code here, to run repeatedly:


if (digitalRead (in) == HIGH){ //controllo stato del pulsante
    for (pushMillis = millis(); pushMillis + currentMillis <= pushMillis + interval; currentMillis++){
      digitalWrite (out , HIGH);
      delay(1);
     }

 digitalWrite (out, LOW);
}
}

Ovviamente non va :slight_smile: (il led si accende una volta sola per poi non accendersi più) ma non capisco perchè?
il ciclo for ha senso secondo voi?

Grazie

Giorgio

  1. ti ricordo che in conformità al regolamento, punto 7, devi editare il tuo post (quindi NON scrivendo un nuovo post, ma utilizzando il bottone More → Modify che si trova in basso a destra del tuo post) e racchiudere il codice all’interno dei tag CODE (… sono quelli che in edit inserisce il bottone con icona fatta così: </>, tutto a sinistra).

  2. riprendi i link che ti ho indicato e STUDIALI ATTENTAMENTE … perché quello che hai scritto … ::slight_smile:

Guglielmo

Ciao, ti posto un'esempio, che avevo gia postato altre volte qui, per una delle possibili soluzioni di lettura di un pulsante condizionata al suo stato (in pratica significa solo che, una volta che lo premi, invece di leggertelo ogni volta che fa un loop, lo ignora finche' non lo rilasci e ripremi di nuovo) ... la logica presuppone che il pulsante sia chiuso verso il positivo quando viene premuto, se e' l'opposto basta invertirla ...

// st = ingresso (byte)
// stp = flag di controllo ingresso (byte)

   st = digitalRead(pin)
   if ((st != stp) && (stp == 0))
   {
     //operazioni da eseguire una sola volta
     //alla PRESSIONE del pulsante, anche se il
     //pulsante viene mantenuto premuto, 
     //viene poi ignorato finche' non viene 
     //rilasciato e premuto di nuovo
      stp = st;  
   }
   if ((st != stp) && (stp == 1))
   {
      stp = st;
   }

Non e' che lo devi usare per forza, ma puo darti un'idea di come realizzare il tuo sistema ... come vedi, usa una "flag", stp, che e' semplicemente una variabile byte utilizzata per memorizzare di volta in volta lo stato precedente del pulsante, in modo da eseguire le operazioni solo nel momento in cui la flag e lo stato sono diversi, cioe' quando st cambia stato ... in questo modo il pulsante viene letto sempre, ma quello che c'e' nel primo if viene eseguito una sola volta, poi lo stato viene ignorato finche' non rilasci il pulsante, a quel punto stp viene cambiato di nuovo, cosi la prossima volta che lo premi verra' eseguito di nuovo il contenuto del primo if ...

Nel tuo caso, dato che vuoi eseguire altre cose, dovrai usare due diverse flag da cambiare nell'if, la prima e' quella che memorizza lo stato del pulsante, la seconda (puoi chiamarle tutte come vuoi, ovviamente) sara' quella che dice al resto del tuo sketch che il pulsante e' stato premuto e che gli fa contare il tempo e fare il resto delle azioni, ed ovviamente NON andra' azzerata all'interno dell'else come la prima, ma solo dove servira' nel resto dello sketch ...

A proposito, il debounce e' sempre meglio farlo via hardware, primo perche' risparmia risorse, secondo perche' non richiede delay bloccanti, terzo perche' funziona meglio (soprattutto con molti pulsanti), e quarto, perche' altrimenti cosa li producono a fare condensatori e resistenze ? (:D) ... metti un condensatore da 100n fra il pin e massa, in parallelo alla resistenza di pull-down, ed una resistenza da 100 ohm in serie al pulsante ... e ti togli un sacco di problemi ...

Etemenanki: ... A proposito, il debounce e' sempre meglio farlo via hardware, primo perche' risparmia risorse, secondo perche' non richiede delay bloccanti, terzo perche' funziona meglio (soprattutto con molti pulsanti), e quarto, perche' altrimenti cosa li producono a fare condensatori e resistenze ?

... quinto ... perché in caso di ISR è l'unico modo [u]decente[/u] di farlo :grin: :grin: :grin:

Guglielmo

Grazie del supporto Gueglielmo ed Etemenanki, ho risolto il mio problema con un delay di 1 millisecondo ripetuto 1000 volte in un if così non è percettibile il problema :) .

comunque in questi giorni (dato che posso solo alcune sere dopo il lavoro) mi rileggo tutti i link segnalatomi da Guglielmo e mi studio lo sketch di Etemenanki (per me che da poco mi sono avvicinato alla programmazione anche uno sketch semplice risulta complesso) perchè importante è capire il senso dei comandi poi tutto va da se.

Grazie ancora, siete stati gentilissimi.

giorgio2379:
il ciclo for ha senso secondo voi?

No, in nessun senso possibile.

Millis serve per leggere il valore di un contatore interno che aumenta “da solo” costantemente. Per sapere quando sono passati N ms da un certo momento, si legge il valore ritornato da millis salvandolo in una variabile (di tipo unsigned long o uint32_t), e poi si continua a confrontare il valore ritornato da millis (che cresce continuamente) con quello salvato. Quando la differenza ha raggiunto il valore che ci interessa sappiamo che sono passati N millisecondi:

tempo = millis();                   // memorizzo valore tempo attuale
while (millis() - tempo < 2000);    // attendo differenza >= 2 sec

Ecco, questo funziona, ma è come scrivere direttamente delay(2000), non è per questo che si usa millis.

Millis si usa per permettere al programma di fare anche altre cose (e non restare bloccato nel ciclo while o nell’istruzione delay), per fare questo ci si salva sempre il valore del tempo di partenza dopo il quale si vuole eseguire qualche operazione, e si abilita il riconoscimento del timeout usando una variabile flag:

tempo = millis();                   // memorizzo valore tempo attuale
abilitato = true;                   // abilito riconoscimento timeout

Nel resto del programma si controlla periodicamente se siamo arrivati al timeout e, nel caso, si fa qualcosa. Il flag serve per poter eseguire “qualcosa” una sola volta allo scadere del tempo:

if (abilitato  &&  (millis() - tempo >= 2000)) { abilitato = false; fa_qualcosa(); }

Tutto questo serve per portare avanti più compiti contemporaneamente, verificando di tanto in tanto il verificarsi degli eventi e chiamando le opportune funzioni per gestirli.

Se non serve portare avanti nessuna attività in parallelo, allora anche una soluzione con delay è sufficiente, ad esempio una minimale:

void loop() {
    if (digitalRead(in_pin)) {        // se premuto
        digitalWrite(out, 1);         // LED acceso
        delay(2000);                  // attende 2 sec
        digitalWrite(out_pin, 0);     // LED spento
        while (digitalRead(in_pin));  // attende no premuto
        delay(100);                   // ritardo anti rimbalzo in rilascio
    }
}

giorgio2379: ... capire il senso dei comandi ...

Se intendi nel mio esempio, sono abbastanza semplici ... non sono un programmatore, ma cerco di spiegarlo ...

st e' una variabile byte che contiene lo stato del pulsante

stp e' una variabile byte usata come flag che contiene lo stato "precedente" del pulsante (precedente e' inteso al momento in cui viene eseguito il controllo)

all'inizio dello sketch quando le dichiari, le imposti entrambe a zero (se la logica del pulsante e' "pulldown verso massa, chiuso verso VCC, se e' invertita, vanno ovviamente invertite anche stato iniziale e logica di controllo nell'if) ... quindi in condizioni normali avrai st ed stp entrambe a zero, e dato che i due if consecutivi NON vengono eseguiti quando le due variabili sono uguali, vengono saltati ...

Ora premi il pulsante; st viene mandata ad uno dal digitalRead del pulsante, quindi la prima volta che il primo if viene eseguito, trova entrambe le condizioni soddisfatte ("if ((st != stp) && (stp == 0))" significa esegui solo se st e' diverso da stp E contemporaneamente stp e' zero), quindi esegue quello che contiene, e come ultima istruzione, mette stp uguale ad st, per cui anche se il pulsante rimane premuto, da adesso in poi st e' di nuovo uguale ad stp, quindi dopo la prima esecuzione, l'if viene di nuovo ignorato per tutto il tempo in cui rimane premuto ... nel momento in cui rilasci il pulsante, st viene riportato ad uno, quindi per una sola volta, come prima, viene eseguito il secondo if, che in questo caso non fa altro che resettare di nuovo stp rimettendolo uguale ad st, in modo che ad una successiva lettura del pulsante venga di nuovo eseguito il primo if (si puo anche usare il secondo if per eseguire, una sola volta, differenti istruzioni solo "al rilascio" del pulsante, invece che alla sua pressione, se e' necessario) ...

Grazie Etemenanki e Claudio,

Etemenanki lo sketch lo avevo studiato e capito, ma grazie di avermelo confermato.

vi faccio un'altra domanda (sempre con pulsanti e led così è più semplice da spiegare), mettiamo che io ho 2 pulsanti e 2 led (ogni pulsante accede un led), se premo un pulsante il led associato deve rimanere acceso 2 secondi per poi spegnersi, ma voglio poterli accendere in qualsiasi momento anche entrambi. se ne premo uno e uso delay (2000) per ritardare lo spegnimento in quei 2 sec non posso premere l'altro, corretto? Se è corretto come si può fare per poterli accendere entrambi insieme?

grazie dell'aiuto

Devi usare la funziona millis() per fare le temporizzazioni ... Guglielmo ti ha gia postato all'inizio alcuni link utili, ma puoi prendere esempio anche dal "blinkwithoutdelay" del reference, che mostra come far lampeggiare un led proprio usando millis .. se usi lo stesso principio in un paio di cicli if, puoi comandare anche due (o piu) led in modo indipendente, con tempi diversi, e senza che il programma si blocchi ... poi prendendo spunto da quello ed usando delle flag (una per led) che dicano allo sketch quando il led e' acceso, li puoi temporizzare come vuoi anche per accensioni singole ...

Etemenanki: ... Guglielmo ti ha gia postato all'inizio alcuni link utili, ma puoi prendere esempio anche dal "blinkwithoutdelay" del reference ...

Emmmm ... Etem ... è il terzo link che gli ho messo :D

Guglielmo

Scusa, non li avevo aperti e non mi ricordavo piu a memoria quali erano :roll_eyes: