pulsante che si accita dopo 3 pressioni

Salve ragazzi, sto cercando di realizzare con arduino un circuito col seguente funzionamento: premendo 3 volte un pulsante mi si deve eccitare un rele per circa 1 o 2 secondi per poi essere rilasciato, ma attenzione (qui viene il difficile che mi sta facendo scervellare) se non completo le 3 pressioni entro 2 secondi il circuito si deve resettare ripartendo da zero. Spero di essermi spiegato.

Ciao...cioè dalla prima pressione entro 2 secondi devi farne altre due...oppure tra una pressione e la successiva non devono esserci più di 2 secondi?...e se premi una quarta cosa deve fare?

PS: una presentazione meno spiccia sarebbe ben gradita

Ok dal momento che inizio con la prima pressione, devo completare il ciclo con altre 2 pressioni entro tot tempo (per esempio 2 secondi) altrimenti si resetta, alla terza pressione, se effettuata nel tempo stabilito si eccita il rele per tot secondi (ad esempio 1 sec), se premo una quarta o quinta volta il processo verrà ignorato perchè quando si ecciterà il relè dopo il suo rilascio del rele (diseccitato) potrà essere inserito un tempo un tempo di attesa (2, 3 secondi) prima che l'automatismo venga riabilitato. Spero di essere stato chiaro...

Ok.
Di questo cosa sai fare? Hai già scritto/trovato un programma?

>csalvo1570: Ti pregherei di "modificare" (utilizzando il bottone More -> Modify che si trova in basso a destra del tuo post) la tua "presentazione" spiegando bene quali conoscenze hai di elettronica e di programmazione per i motivi spiegati al punto 16.7 del REGOLAMENTO ... che, comunque, ti consiglio di leggere tutto con molta attenzione. Grazie :slight_smile:

Guglielmo

Conti il tempo con millis()

x gpb01: ok scusami, ho rettificato la presentazione.

x silente: si ho buttato giù qualcosa ma come vedi non credo sia sufficiente:

int ch = 0;

void setup() {
pinMode(13, OUTPUT); // Inizializzazione pin 13 in uscita:
pinMode(2, INPUT); // Inizializzazione pin 02 in entrata:
}

void loop() {

p1:
if (digitalRead(2)==HIGH) { // Controlla se il pin 2 è al livello alto ed esegue istruzione altrimenti passa a ELSE
delay(500);
ch = +1;
goto p2;
}else if (ch<1);
ch = 0;
goto p1; // ricomincia da loop p1

p2:
if (digitalRead(2)==HIGH) { // Controlla se il pin 2 è al livello alto ed esegue istruzione altrimenti passa a ELSE
delay(500);
ch = +1;
goto p3;
}else if (ch<2);
ch = 1;
goto p1; // ricomincia da loop p1

p3:
if (digitalRead(2)==HIGH) { // Controlla se il pin 2 è al livello alto ed esegue istruzione altrimenti passa a ELSE
ch = +1;
digitalWrite(13,HIGH);
delay(2000);
digitalWrite(13,LOW);
goto p1;
}else if (ch<3);
ch = 0;
goto p1; // ricomincia da loop p1
}

Ciao,

il GOTO, salvo rare occasioni, è meglio evitarlo...rende il codice poco leggibile.
Consiglio...se devi gestire/controllare eventi in un tempo stabilito NON usare delay() ma usa millis()...millis() ti restituisce una uint32_t, con millisecondi come unità di misura, che indica il tempo trascorso dall'accensione di arduino; quindi va gestito come "confronto" con qualche cosa (qui nel forum se ne parla in continuazione).
Altro suggerimento...dato che, salvo presenza di delay(), il loop() gira a "manetta" e quindi nel tempo che tu premi un pulsante di cicli ne ha fatti tanti...per questo ti consiglio un controllo sul rilascio del pulsante...cioè premuto/verificato una volta non lo consideri più fino al suo rilascio.
altro suggerimento...sicuramente dovrai usare un debounce per essere sicuro della unicità della pressione del pulsante...questo può essere software o hardware...dato che devi controllare sequenza pigiate in un range di tempo ti consiglio quello hardware dato che quello software "consuma tempo".

Ho visto nella sezione software un problema simile al tuo, prova a vedere se lo trovi. A lui hanno proposto un codice che, con le opportune modifiche, potrebbe fare al caso tuo.

ho buttato giù qualcosa ma come vedi non credo sia sufficiente

Discorso goto a parte (se non sbaglio anche Visual basic permette la programmazione strutturata senza goto), il problema maggiore è la logica rigidamente controllata dall'ordine delle istruzioni: un passo, delay, un altro passo, un altro delay e così via. Un codice strutturato così non può mai tenere conto di più di una cosa alla volta che accade nel tempo reale (ad esempio dei timer che scadono, dei pulsanti premuti con tempistiche differenti ecc).

Seconda cosa, leggendo il livello del pulsante invece che la sua variazione, il codice diventa più complesso, perché bisogna prevedere anche il controllo sul rilascio. Immagino che quei delay da mezzo secondo servano idealmente a questo, ma costringono a premere il pulsante con la cadenza esatti, che è una cosa non accettabile nell'uso reale... ad esempio nessuno vorrebbe usare una luce che si accende solo se si preme il pulsante tra 1 e 1.5 secondi, e si spegne solo se si preme tra 2 e 2.5

La logica corretta da adottare in qualsiasi sistema di controllo è quella di lasciar girare il più liberamente possibile il loop, usare delle variabili per tenere conto della fase in cui ci si trova, e ad ogni giro di loop tenere conto della fase attuale per decidere se compiere oppure no delle azioni in base a eventi che si verificano (timer che scadono, pulsanti che vengono premuti o rilasciati ecc).

Ad esempio a me sembra che ci siano almeno quattro fasi di funzionamento ben distinte: riposo, conteggio, acceso, e pausa spento.

Cosa farei nella fase riposo?

  • Controllerei se il pulsante è stato premuto,
  • se lo è allora:
    --- imposterei il contatore pressioni a uno
    --- farei partire la misura del tempo (dipende dal metodo scelto)
    --- imposterei la fase a conteggio

Cosa farei nella fase conteggio?

  • Controllerei se il tempo è scaduto,
  • se lo è allora:
    --- ritorno alla fase riposo
  • altrimenti se il pulsante è stato premuto
    --- incremento contatore
    --- se il contatore ha raggiunto 3:
    ------ accendo uscita
    ------ rifaccio partire misura del tempo
    ------ passo alla fase acceso

eccetera per tutte le fasi...

La misura del tempo trascorso può avvenire sia contando i giri di loop (se il loop gira a una velocità nota), sia usando il contatore a 32 bit di Arduino leggibile con la funzione millis.

Un'alternativa, quando si parla di logiche così semplici, è simulare a software uno schema a relé e contatti, così si tratta solo di scrivere le espressioni dei contatti:

Ad esempio: w1 = !x & !t1 & !q & (p | w1);

ORSO2001:
Ciao,

il GOTO, salvo rare occasioni, è meglio evitarlo...rende il codice poco leggibile.
Consiglio...se devi gestire/controllare eventi in un tempo stabilito NON usare delay() ma usa millis()...millis() ti restituisce una uint32_t, con millisecondi come unità di misura, che indica il tempo trascorso dall'accensione di arduino; quindi va gestito come "confronto" con qualche cosa (qui nel forum se ne parla in continuazione).
Altro suggerimento...dato che, salvo presenza di delay(), il loop() gira a "manetta" e quindi nel tempo che tu premi un pulsante di cicli ne ha fatti tanti...per questo ti consiglio un controllo sul rilascio del pulsante...cioè premuto/verificato una volta non lo consideri più fino al suo rilascio.
altro suggerimento...sicuramente dovrai usare un debounce per essere sicuro della unicità della pressione del pulsante...questo può essere software o hardware...dato che devi controllare sequenza pigiate in un range di tempo ti consiglio quello hardware dato che quello software "consuma tempo".

quando intendi debounce, intendi il resistore tra pin e massa? di che valore deve essere precisamente?
dove trovo un file per gestione pulsante fatto bene in modo da poterlo studiare?

X CLAUDIO_FF: non sto così avanti con la programmazione, mi sto avvicinando adesso ad arduino...

Girando sul forum trovai questo, relativo a debounce hw.
Hai provato a vedere "combinazione pulsanti" in software? Credo possa esserti utile, almeno come un idea.

debouncing_hw.pdf (22.8 KB)

A mio avviso se sei un principiante dovresti cominciare a studiare l'uso della funzione millis(), gioia e dolore per chi, me compreso, si è inoltrato nel mondo di Arduino.

Il primo programma da studiare è l'esempio BlinkWithoutDelay, che trovi nell'IDE di Arduino in File->Esempi->02.Digital->BlinkWithoutDelay e anche qui spiegato in dettaglio.

Nell'esempio si vede come nel LOOP si continui a testare se è passato un certo intervallo:

if (currentMillis - previousMillis >= interval)

e, nel caso sia passato, a cambiare lo stato del led per farlo lampeggiare.

Ma ciò che non salta subito agli occhi è che se quella condizione NON È VERA il programma salta tutta quella parte ed esegue le istruzioni che si trovassero dopo di queste

    digitalWrite(ledPin, ledState);
  }

DENTRO AL LOOP. Solo che in questo esempio NON CI SONO ALTRE ISTRUZIONI e il programma torna a testare se è passato un certo intervallo.
Se tu inserisci altre istruzioni in quella parte del LOOP (evita il delay!) le istruzioni verranno eseguite, mentre il led continuerà tranquillamente a lampeggiare. Prova ad inserire un pulsante e UN ALTRO led con le istruzioni per tenerlo acceso finché il pulsante resta premuto. Vedrai che il secondo led si accende e spegne a seconda che il pulsante sia premuto o rilasciato, mentre il primo continua il suo lampeggio.

Buono studio.

Ciao,
P.

csalvo1570:
X CLAUDIO_FF: non sto così avanti con la programmazione, mi sto avvicinando adesso ad arduino...

Era per dare un'idea generale delle cose da considerare e spunti su cosa studiare.

Poi in realtà se si pensa il programma come detto, la traduzione in codice è praticamente uno a uno con la procedura espressa in italiano, ad esempio:

if (fase == RIPOSO)             // se sono nella fase riposo
{
    if (premuto)                // se il pulsante e` stato premuto
    {
        ch = 1;                 // contatore pressioni = 1 (prima pressione)
        t = millis();           // annoto tempo attuale
        fase = CONTEGGIO;       // passo a fase conteggio
    }
}
else if (fase == CONTEGGIO)     // altrimenti se sono nella fase conteggio
{
    if ((millis() - t) >= 2000) // se trascorsi 2 sec dall'annotazione
    {
        fase = RIPOSO;          // torno a riposo
    }
    else if (premuto)           // altrimenti se pulsante e` stato premuto
    {
        ch = ch + 1;            // incremento contatore pressioni
        if (ch == 3)            // se è arrivato a 3
        {
            digitalWrite(USCITA, ONLEVEL); // accendo
            t = millis();                  // annoto tempo attuale
            fase = ACCESO;                 // passo a fase acceso
        }
    }
}
else if .......

NOTA: le varie parole in maiuscolo sono etichette (o nomi di comodo) corrispondenti a valori, servono per non riempire il codice di numerini facili da confondere, e per leggere le istruzioni più chiaramente. Le etichette si possono definire all'inizio del codice con:

#define ETICHETTA valore

NOTA: anche HIGH, LOW, INPUT, OUTPUT sono etichette predefinite.

Rimane da leggere il pulsante. Per il debounce bastano gli schemi postati da Silente (anche se il condensatore lo metterei da non meno di 100nF), per riconoscere l'istante di pressione invece serve un po'di codice che "lasci passare" il livello premuto solo una volta anche se il pulsante resta premuto a lungo:

premuto = (digitalRead(PULSANTE) == PRESSLEVEL);

if      (!premuto)   { giapremuto = false; }
else if (giapremuto) { premuto = false;    }
else                 { giapremuto = true;  }

Questa parte di codice va messa immediatamente prima della logica vista all'inzio, e il tutto all'interno del loop.

Io farei così:
(è la versione aggiornata con tutte le correzioni)

unsigned long t1=0;
byte p1=0;
byte pronto2=0;
byte p2=0;
byte pronto3=0;
byte p3=0;

void setup() 
{
pinMode(13,OUTPUT);   // Inizializzazione I/O 13 in uscita:
pinMode(2,INPUT_PULLUP);   // Inizializzazione I/O 2 in entrata:
}

void loop()
{
if(digitalRead(2)==LOW && p1==0) {p1=1; t1=millis();}
if(p1==1)
  {
    if(millis()-t1<2000)
      {
      if(digitalRead(2)==HIGH) pronto2=1;
      else if(pronto2 && digitalRead(2)==LOW) p2=1;
      else if(p2 && digitalRead(2)==HIGH) pronto3=1;
      else if(pronto3 && digitalRead(2)==LOW) p3=1;
      }
    else {t1=0; p1=0; pronto2=0; p2=0; pronto3=0; p3=0;}
  }

if(p3)  // ACCESO!
  {
  t1=0; p1=0; pronto2=0; p2=0; pronto3=0; p3=0;
  digitalWrite(13,HIGH);
  delay(2000);
  digitalWrite(13,LOW);
  }
}

Considera, però, che se c'è il bootloader, all'accensione il pin 13 manda degli impulsi. Ti conviene usare un altro pin I/O.
Buona notte! :slight_smile:

6/1/19:

  • In due if avevo messo = anziché ==: ho corretto.
  • Dovevo anche chiudere una parentesi graffa.
    Perdonatemi: quando scrivo al telefonino non è proprio come scrivere nell'IDE al computer... :slight_smile:
  • Ho aggiunto degli else. Concettualmente ci vanno, ma il loro effetto è solo un minimo aumento della velocità di esecuzione.
    7/1/19: Ho chiuso anche l'altra parentesi graffa.

x datman:

unsigned long t1=0;
byte p1=0;
byte pronto2=0;
byte p2=0;
byte pronto3=0;
byte p3=0;

void setup()
{
pinMode(13,OUTPUT); // Inizializzazione I/O 13 in uscita:
pinMode(2,INPUT_PULLUP); // Inizializzazione I/O 2 in entrata:
}

void loop()
{
if(digitalRead(2)==LOW && p1==0) {p1=1; t1=millis();}
if(p1==1)
{
if(millis()-t1<2000)
{
if(digitalRead(2)==HIGH) pronto2=1;
if(pronto2 && digitalRead(2)=LOW) p2=1;
if(p2 && digitalRead(2)==HIGH) pronto3=1;
if(pronto3 && digitalRead(2)=LOW) p3=1; ERRORE!!!!!!!!!
}
else {t1=0; p1=0; pronto2=0; p2=0; pronto3=0; p3=0;}

if(p3) // ACCESO!
{
t1=0; p1=0; pronto2=0; p2=0; pronto3=0; p3=0;
digitalWrite(13,HIGH);
delay(2000);
digitalWrite(13,LOW);
}

Ho voluto provare il tuo file e mi da errore al 4 if e non riesco a capire il bug...

  1. per favore edita il post inserendo il codice nei tag code
  2. potrebbe essere utile a tutti conoscere il testo dell'errore che da
unsigned long t1=0;
byte p1=0;
byte pronto2=0;
byte p2=0;
byte pronto3=0;
byte p3=0;

void setup()
{
pinMode(13,OUTPUT);   // Inizializzazione I/O 13 in uscita:
pinMode(2,INPUT_PULLUP);   // Inizializzazione I/O 2 in entrata:
}

void loop()
{
if(digitalRead(2)==LOW && p1==0) {p1=1; t1=millis();}
if(p1==1)
  {
    if(millis()-t1<2000)
      {
      if(digitalRead(2)==HIGH) pronto2=1;
      if(pronto2 && digitalRead(2)=LOW) p2=1;
      if(p2 && digitalRead(2)==HIGH) pronto3=1;
      if(pronto3 && digitalRead(2)=LOW) p3=1;            ERRORE!!!!!!!!!
      }
    else {t1=0; p1=0; pronto2=0; p2=0; pronto3=0; p3=0;}

if(p3)  // ACCESO!
{
t1=0; p1=0; pronto2=0; p2=0; pronto3=0; p3=0;
digitalWrite(13,HIGH);
delay(2000);
digitalWrite(13,LOW);
}

Ogni volta che tra le tonde di if vedi un singolo uguale mettine due. Se vedi if (...a=b...) rendilo of (... a==b...).
Poi la prima riga sotto if (p3)è inutile

Silente:
Poi la prima riga sotto if (p3)è inutile

Se la procedura si deve "riarmare" allora i vari flag sono da (ri)azzerare sia se scade il tempo, sia se vengono rilevate le tre pressioni.