Accensione led con timeout

Buongiorno a tutti,

è il mio primo post quindi spero di essere nella sezione giusta ed ho gia fatto la presentazione.
Detto questo passiamo al mio dilemma.

Ho un progetto dove ci sono due led (rosso e verde) ed un pulsante. Il led verde è sempre acceso, nel momento in cui premo il pulsante si spegne il led verde e si accende il led rosso per un tempo che ho preimpostato (nello sketch 10sec). al termine di questi 10 secondi il led rosso si spegne e si riaccende il verde.
e fino a qui tutto ok.

il mio problema è che vorrei nello stesso progetto far funzionare in totale 5 pulsanti ognuno con i propri led rosso e verde, ma che funzionino tutti indipendentemente.

Chi mi aiuta? allego qui il mio attuale sketch (che comunque ho trovato nel web ed ho solo modificato alcune cose)

led_e_pulsante_timer.ino (1.78 KB)

Ciao,
per risolvere problemi di quel tipo, devi studiarti come si usa la funzione millis(), prima QUI, poi QUI e QUI e QUI e tutti gli articoli che sono in QUESTA pagina ... vedrai che ti sarà tutto più chiaro :wink:

Guglielmo

P.S.: Dopo lo studio dei link qui sopra, QUESTO post potrà essere utile a capire ancora meglio ...

Grazie Guglielmo, ho letto tutto ed in effetti sono riuscito ad agganciare anche il secondo pulsante e quindi credo di riuscire a farlo per infinite volte.

Il problema è che adesso mi funziona un pulsante alla volta, e mentre il pulsante 1 sta facendo il suo conto alla rovescia, il pulsante 2 non risponde. e viceversa

Posta il codice che hai sviluppato e vediamo dove può essere il problema.

const byte INPUT_BUTTON = 7;
const byte led_rosso = 13;
const byte led_verde = 12;
const byte BUTTON = 6;
const byte led_bianco = 11;
const byte led_blu = 10;

//global variables
const unsigned long timeout = 10000; //the timeout in milliseconds

//setup
void setup() {
    //pins configuration
    pinMode(led_rosso, OUTPUT); //LED pin as output
    pinMode(led_verde, OUTPUT);
    pinMode(led_bianco, OUTPUT);
    pinMode(led_blu, OUTPUT);
    pinMode(INPUT_BUTTON, INPUT); //button pin as input w/internal pull-up
    pinMode(BUTTON, INPUT);
}


//loop
void loop() {
    digitalWrite(led_verde, HIGH);
    //has the user pressed the button?
    if (!digitalRead(led_rosso)) {
        //wait for a while, to avoid bounces
        delay(30);
        if (!digitalRead(INPUT_BUTTON)) { //the button is still pressed, it wasn't a bounce
          iteractWithUser();
        }
    }
    digitalWrite(led_blu, HIGH);//has the user pressed the button?
    if (!digitalRead(led_bianco)) {//wait for a while, to avoid bounces
        delay(30);
        if (!digitalRead(BUTTON)) { //the button is still pressed, it wasn't a bounce
          iteractWithUser1();
}
}
}
//iteraction with user
void iteractWithUser() {
    unsigned long startingTime; //used to store the starting moment
    
    //wait for the release of the button
    while(!digitalRead(INPUT_BUTTON));
    digitalWrite(led_verde, LOW);
    digitalWrite(led_rosso, HIGH); //LED on
    //loop
    startingTime = millis();
    do {
        //has the user pressed the button?
        if (!digitalRead(INPUT_BUTTON)) {
            //debounce
            delay(30);
            if (!digitalRead(INPUT_BUTTON)) {
                //the user has pressed the button
                //fast blinking for confirmation
                digitalWrite(led_rosso, LOW);
                delay(100);
                digitalWrite(led_rosso, HIGH);
                delay(100);
                startingTime = millis(); //we reset the initial time
                digitalWrite(led_verde, LOW);
            }
        }
    } while ((millis() - startingTime) < timeout);
    //this loop will only end when the it goes in timeout

    //light off the LED
    digitalWrite(led_verde, HIGH);
    digitalWrite(led_rosso, LOW);
}

void iteractWithUser1() {
    unsigned long startingTime; //used to store the starting moment
    
    //wait for the release of the button
    while(!digitalRead(BUTTON));
    digitalWrite(led_blu, LOW);
    digitalWrite(led_bianco, HIGH); //LED on
    //loop
    startingTime = millis();
    do {
        //has the user pressed the button?
        if (!digitalRead(BUTTON)) {
            //debounce
            delay(30);
            if (!digitalRead(BUTTON)) {
                //the user has pressed the button
                //fast blinking for confirmation
                digitalWrite(led_bianco, LOW);
                delay(100);
                digitalWrite(led_bianco, HIGH);
                delay(100);
                startingTime = millis(); //we reset the initial time
                digitalWrite(led_blu, LOW);
            }
        }
    } while ((millis() - startingTime) < timeout);
    //this loop will only end when the it goes in timeout

    //light off the LED
    digitalWrite(led_blu, HIGH);
    digitalWrite(led_bianco, LOW);
}

PaoloConca89:
Il problema è che adesso mi funziona un pulsante alla volta, e mentre il pulsante 1 sta facendo il suo conto alla rovescia, il pulsante 2 non risponde. e viceversa

Scusa, ma qui:

...
    if (!digitalRead(led_rosso)) {
        //wait for a while, to avoid bounces
        delay(30);
        if (!digitalRead(INPUT_BUTTON)) { //the button is still pressed, it wasn't a bounce
          iteractWithUser();
        }
    }
    digitalWrite(led_blu, HIGH);//has the user pressed the button?
    if (!digitalRead(led_bianco)) {//wait for a while, to avoid bounces
        delay(30);
        if (!digitalRead(BUTTON)) { //the button is still pressed, it wasn't a bounce
          iteractWithUser1();
...

non dovresti leggere due volte i bottoni? Che è quel "digitalRead(led_rosso)"?

Messo così tu leggi lo stato del LED mica del bottone!

Non ho analizzato il resto ma intanto (tralascio anche il discorso sul debounce) dovresti cambiarlo in:

...
    if (!digitalRead(INPUT_BUTTON)) {
        //wait for a while, to avoid bounces
        delay(30);
        if (!digitalRead(INPUT_BUTTON)) { //the button is still pressed, it wasn't a bounce
          iteractWithUser();
        }
    }
    digitalWrite(led_blu, HIGH);//has the user pressed the button?
    if (!digitalRead(BUTTON)) {//wait for a while, to avoid bounces
        delay(30);
        if (!digitalRead(BUTTON)) { //the button is still pressed, it wasn't a bounce
          iteractWithUser1();
...

Grazie per questa prima correzione, chiedo scusa sono proprio un novellino

Vorrei proporre un metodo alternativo al solito problema di "attesa".

Con la funzione millis() interrogo in modo cadenzato una serie di timer software che decrementano una semplice variabile.
Al raggiungimento dello 0 attiva il codice relativo.

Nel caso specifico non è nemmeno richiesto l'inserimento di ulteriori attese per evitare fenomeni di rimbalzo dei tasti.

Led_Timeout.ino (3.39 KB)

Ciao Lelebum,

sembra un lavoro perfetto!!! e penso proprio sia quello che mi serve.

Provo a metterlo in pratica gia stasera e ti faccio sapere.

Intanto grazie mille

lelebum:
Vorrei proporre un metodo alternativo al solito problema di "attesa".

Con la funzione millis() interrogo in modo cadenzato una serie di timer software che decrementano una semplice variabile.
Al raggiungimento dello 0 attiva il codice relativo.

Nel caso specifico non è nemmeno richiesto l'inserimento di ulteriori attese per evitare fenomeni di rimbalzo dei tasti.

ho provato il tuo codice m purtroppo non fa quello di qui ho bisogno, premendo e tenendo premuto il pulsante il led passa da rosso a verde e appena lo rilascio torna rosso.

Mi serviva invece che: premendo (1 click) il pulsante da rosso passa a verde per X secondi e poi torna rosso

Il codice l'ho buttato giù al volo...
Appena ho due minuti do una controllata e ti so dire

Vorrei provare a dare una mano anch'io, cominciando con lo schema logico che deve precedere sempre la scrittura del codice.

Supponendo che i pin siano a INPUT_PULLUP e i pulsanti siano connessi fra i pin di input e la massa (attivi LOW), ia prima scrittura potrebbe essere questa

LOOP
  SE pulsante1 == LOW
    ledVerde1 = LOW
    ledRosso1 = HIGH
  SE sono trascorsi 10 secondi dalla pressione del pulsante1
    ledVerde1 = HIGH
    ledRosso1 = LOW
  SE pulsante2 == LOW
    ledVerde2 = LOW
    ledRosso2 = HIGH
  SE sono trascorsi 10 secondii dalla pressione del pulsante2
    ledVerde2 = HIGH
    ledRosso2 = LOW
END LOOP

Semplice, no? Adesso occorre vedere come implementare la frase "sono trascorsi 10 secondi". La prima soluzione è utilizzare "delay(10000);", ma questo impedisce di poter utilizzare i pulsanti contemporaneamente, perché alla pressione del pulsante1 il ciclo si ferma per 10s prima di consentire la pressione di uno dei due pulsanti; e lo stesso se si preme il pulsante2 per primo.

Quindi occorre "vedere che ore sono" alla pressione del pulsanteX e "tenere d'occhio" l'orologio per lo scadere dei 10 secondi. Il codice si modifica così

LOOP
  SE pulsante1 == LOW
    ledVerde1 = LOW
    ledRosso1 = HIGH
    cheOreSono1 = oraAdesso
  SE oraAdesso >= cheOreSono1 + 10s
    ledVerde1 = HIGH
    ledRosso1 = LOW
  SE pulsante2 == LOW
    ledVerde2 = LOW
    ledRosso2 = HIGH
    cheOreSono2 = oraAdesso
  SE oraAdesso >= cheOreSono2 + 10s
    ledVerde2 = HIGH
    ledRosso2 = LOW
END LOOP

Notiamo che i 10s potrebbero essere diversi per i due pulsanti. Mettiamoli in due variabili. Il codice diventa:

SETUP
  secondi1 = <TEMPO PULSANTE1>
  secondi2 = <TEMPO PULSANTE2>
END SETUP
LOOP
  SE pulsante1 == LOW
    ledVerde1 = LOW
    ledRosso1 = HIGH
    cheOreSono1 = OraAdesso
  SE oraAdesso >= cheOreSono1 + secondi1
    ledVerde1 = HIGH
    ledRosso1 = LOW
  SE pulsante2 == LOW
    ledVerde2 = LOW
    ledRosso2 = HIGH
    cheOreSono2 = oraAdesso
  SE oraAdesso >= cheOreSono2 + secondi2
    ledVerde2 = HIGH
    ledRosso2 = LOW
END LOOP

Sembra tutto a posto. Invece NO! Perché adesso il codice "gira" in continuazione velocissimo, quindi tutte le volte che trova un pulsante X LOW inizializza nuovamente la variabile cheOreSonoX. La conseguenza è che il tempo "secondiX" comincia a trascorrere non quando il pulsante è PREMUTO (=>LOW), ma quando il pulsante è RILASCIATO (=>HIGH).

Consiglierei a PaoloConca di tentare di scrivere il codice equivalente. Magari aggiungendo altri pulsanti e led. Dovrebbe essere facile a questo punto. In seguito si può vedere come fare per tempi di accensione dei led comparabili a quelli della pressione di un pulsante

Ciao,
P.

Molto semplicemente, farei così (che è molto simile all'idea di pgiagno, ma ci ho pensato prima di leggere la sua risposta):

  • Scansione dei 5 pulsanti
  • Appena ne premo uno, se on1...on5==0 memorizzo millis() in t1...t5 e commuto i LED relativi, segnandomi on1...on5=1 per non ripetere inutilmente l'azione
  • Quando millis()-t1...t5 supera i 10 secondi, commuto nuovamente i due LED, riportando on1...on5 a 0.

Ho trovato il problema, nella fretta ho fatto confusione con due modalità di codice ...

Devi semplicemente modificare SOLAMENTE la seconda riga che segue "void Timersoft" come da listati seguenti:

Codice iniziale con errore

 void TimerSoft(){
  if ((millis() - cntMillis) < kTimerSoft) return;   // non trascorso intervallo
  cntMillis = millis()+ kTimerSoft;                  // ricarica x nuovo intervallo

Codice corretto funzionate

 void TimerSoft(){
  if ((millis() - cntMillis) < kTimerSoft) return;   // non trascorso intervallo
  cntMillis += kTimerSoft;                           // ricarica x nuovo intervallo

Sketch già testato su arduino e quindi non dovresti avere problemi
Fammi sapere l'esito

lelebum:
Ho trovato il problema, nella fretta ho fatto confusione con due modalità di codice ...

Devi semplicemente modificare SOLAMENTE la seconda riga che segue "void Timersoft" come da listati seguenti:

Codice iniziale con errore

 void TimerSoft(){

if ((millis() - cntMillis) < kTimerSoft) return;  // non trascorso intervallo
  cntMillis = millis()+ kTimerSoft;                  // ricarica x nuovo intervallo





Codice corretto funzionate



void TimerSoft(){
  if ((millis() - cntMillis) < kTimerSoft) return;  // non trascorso intervallo
  cntMillis += kTimerSoft;                          // ricarica x nuovo intervallo




Sketch già testato su arduino e quindi non dovresti avere problemi
Fammi sapere l'esito

Ciao Lelebum,

ora funziona!!!!!! ma ho dovuto modificare i valori, perché in origine il led rimaneva acceso per parecchio tempo. ora rimane 10secondi. sai dirmi perché?

[/code] unsigned long cntMillis; // contatore dei millis()
const unsigned long kTimerSoft = 1000; // periodo del timer cntMillis

word tmrKey0; // timer x timeout x tasto 0
const word tKey0=11; // periodo in 1/50" (equiv. 10")

word tmrKey1; // timer x timeout x tasto 1
const word tKey1=200; // periodo in 1/50" (equiv. 10") [/code]

La mia idea era questa (5 pulsanti, 5 LED verdi e 5 LED rossi), che non avevo sviluppato per lasciarlo fare a te, facendoti imparare qualcosa anziché darti subito la soluzione:

#define P1 2
#define P2 3
#define P3 4
#define P4 5
#define P5 6
#define LEDV1 7
#define LEDR1 8
#define LEDV2 9
#define LEDR2 10
#define LEDV3 11
#define LEDR3 12
#define LEDV4 15 // Analog 0
#define LEDR4 16 // Analog 1
#define LEDV5 17 // Analog 2
#define LEDR5 18 // Analog 3

unsigned long t1; // Prende millis() alla pressione di P1.
unsigned long t2; // Prende millis() alla pressione di P2.
unsigned long t3; // Prende millis() alla pressione di P3.
unsigned long t4; // Prende millis() alla pressione di P4.
unsigned long t5; // Prende millis() alla pressione di P5.
uint8_t on1=0;
uint8_t on2=0;
uint8_t on3=0;
uint8_t on4=0;
uint8_t on5=0;


void setup()
{
pinMode(P1, INPUT_PULLUP);
pinMode(P2, INPUT_PULLUP);
pinMode(P3, INPUT_PULLUP);
pinMode(P4, INPUT_PULLUP);
pinMode(P5, INPUT_PULLUP);
pinMode(LEDV1, OUTPUT);
pinMode(LEDR1, OUTPUT);
pinMode(LEDV2, OUTPUT);
pinMode(LEDR2, OUTPUT);
pinMode(LEDV3, OUTPUT);
pinMode(LEDR3, OUTPUT);
pinMode(LEDV4, OUTPUT);
pinMode(LEDR4, OUTPUT);
pinMode(LEDV5, OUTPUT);
pinMode(LEDR5, OUTPUT);

digitalWrite(LEDV1, HIGH);
digitalWrite(LEDV2, HIGH);
digitalWrite(LEDV3, HIGH);
digitalWrite(LEDV4, HIGH);
digitalWrite(LEDV5, HIGH);
}

void loop() {
if(!digitalRead(P1) && !on1)
  {
  t1=millis();
  on1=1;
  digitalWrite(LEDV1, LOW);
  digitalWrite(LEDR1, HIGH);
  }
if(!digitalRead(P2))
  {
  t2=millis();
  on2=1;
  digitalWrite(LEDV2, LOW);
  digitalWrite(LEDR2, HIGH);
  }
if(!digitalRead(P3))
  {
  t3=millis();
  on3=1;
  digitalWrite(LEDV3, LOW);
  digitalWrite(LEDR3, HIGH);
  }
if(!digitalRead(P4))
  {
  t4=millis();
  on4=1;
  digitalWrite(LEDV4, LOW);
  digitalWrite(LEDR4, HIGH);
  }
if(!digitalRead(P5))
  {
  t1=millis();
  on5=1;
  digitalWrite(LEDV5, LOW);
  digitalWrite(LEDR5, HIGH);
  }
if(millis()-t1>10000 && on1)
  {
  on1=0;
  digitalWrite(LEDR1, LOW);
  digitalWrite(LEDV1, HIGH);  
  }
if(millis()-t2>10000 && on2)
  {
  on2=0;
  digitalWrite(LEDR2, LOW);
  digitalWrite(LEDV2, HIGH);  
  }
if(millis()-t3>10000 && on3)
  {
  on3=0;
  digitalWrite(LEDR3, LOW);
  digitalWrite(LEDV3, HIGH);  
  }
if(millis()-t4>10000 && on4)
  {
  on4=0;
  digitalWrite(LEDR4, LOW);
  digitalWrite(LEDV4, HIGH);  
  }
if(millis()-t5>10000 && on5)
  {
  on5=0;
  digitalWrite(LEDR5, LOW);
  digitalWrite(LEDV5, HIGH);  
  }
}

Le variabili on1...5 servono per non far ripetere le operazioni, risolvendo il problema rilevato da pgiagno.

P.S.: la sintassi
SE oraAdesso >= cheOreSono2 + 10s
non va usata, poiché nasce il problema dell'overflow di millis(). La sintassi da usare è:
SE (oraAdesso-tempoMemorizzato2 >= 10000)

Da quanto vedo hai modificato kTimerSoft da 20 a 1000 e pertanto hai allungato il tempo di cadenza.
Visto che la tua applicazione richiede tempi relativamente lunghi ti consiglio di assegnare a kTimerSoft =100 pari ad una cadenza 100ms e quindi alla variabile tKey0=100.
Quindi 0.1" x 100 uguale a 10" come da richiesto.
In ogni caso kTimerSoft, una volta scelta la cadenza, non devi più modificarla

Bastano tante uscite quanti pulsanti

Collegando i due led uno al + e uno al - hai fatto la loro mutua esclusione
Una sola uscita 2 led

Sì, si può fare. Bisogna solo fare attenzione a mettere comunque due resistenze, altrimenti i due LED si trovano direttamente in serie fra +5 e massa!

Bello sarebbe poter anche usare un solo pin sia per ingresso che per uscita su due led
Mi sa difficile, una cosa degna di entrare nel mio post "per un pugno di bottoni"