Problema con un pulsante

Ciao a tutti, ho bisogno del vostro aiuto (sono un principiante).
Vorrei utilizzare un pulsante come interruttore per fare accendere e spegnere un led. Premendo il pulsante, se il led è spento, si deve accendere e deve rimanere acceso; viceversa premendo il pulsante, se il led è acceso, si deve spegnere e rimanere spento.
Per quanto riguarda il circuito, ho inserito l'apposita resistenza di pull down ma penso di avere il problema del rimbalzo (il comportamento del led non è sempre prevedibile).
Ho usato il seguente codice

const int p = 9, led1 = 11, led2 = 12;

void setup() {
 pinMode(p, INPUT);
 pinMode(led1, OUTPUT);
 pinMode(led2, OUTPUT);
}

void loop() {
 if (digitalRead(p) == HIGH) {
   digitalWrite(led1, !digitalRead(led1));
   while(digitalRead(p) == HIGH){
   }
 }

}

Grazie per l'aiuto.

Non devi controllare lo stato del pulsante. Quello viene fatto ogni pochi µs.
Devi controllare il combio di stato e aggiungere un ritardo per il debounce.

https://www.arduino.cc/en/tutorial/switch

Ciao Uwe

>Ale_S: 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).

Grazie,

Guglielmo

https://www.arduino.cc/en/tutorial/switch

L'esempio di quella pagina mi sembra sbagliato per diversi motivi.

Intanto usa variabili tempo long invece di unsigned long.

Poi non filtra la pressione, un breve disturbo impulsivo viene accettato subito come buono.

Filtra i rimbalzi successivi alla pressione (quando ha già dato l'ok, come dire no a pressioni troppo ravvicinate).

Filtra anche i rimbalzi del rilascio... PURCHÉ il rilascio avvenga entro il tempo di debounc, altrimenti il primo rimbalzo del rilascio viene preso come nuova pressione valida (per questo ha dovuto scrivere un tempo di debounch così lungo)...

In sostanza è inutilizzabile se si volesse implementare il riconoscimento di un long-press o per la lettura di un contatto/interruttore che apre/chiude con tempi lunghi.

Lo riscriverei senz'altro in questo modo, le variazioni sia alla pressione che al rilascio sono sempre subordinate alla permanenza stabile del livello in input per almeno DEBOUNCE millisecondi (ne bastano 40..50):

#define PRESSED_LEVEL ...  // livello ingresso con pulsante premuto
#define IN_PIN          2  // the number of the input pin
#define OUT_PIN        13  // the number of the output pin
#define DEBOUNCE       40  // the debounce time, increase if the output flickers
#define OFF_LEVEL     ...  // livello uscita spenta

byte previous = !PRESSED_LEVEL; // the previous reading from the input pin
unsigned long time = 0;         // the last time the output pin was toggled
byte state = OFF_LEVEL;         // the current state of the output pin


void setup() 
{
    pinMode(IN_PIN, INPUT);
    pinMode(OUT_PIN, OUTPUT);
}


void loop() 
{
    byte reading = digitalRead(IN_PIN);

    if (reading == previous)
        time = millis();

    else if (millis() - time > DEBOUNCE) 
    {
        previous = reading;
        if (reading == PRESSED_LEVEL)
            state = !state;
    }

    digitalWrite(OUT_PIN, state);
}

NOTA: Le costanti PRESSED_LEVEL e OFF_LEVEL definite all'inizio del programma vanno impostate a 1 o 0 in base al collegamento hardware che si realizza (verso Vcc o verso massa).

Poi ancora meglio separare completamente la logica di lettura/debounce da quella di controllo/comando, ad esempio fare in modo che la lettura/debounce produca solamente una variabile pulita, stabile, filtrata, che rappresenta lo stato ideale premuto/rilasciato. In questo modo sono sicuro che 'pressed' vale 1 (o HIGH o true) quando il pulsante è premuto e 0 (o LOW o false) quando non lo è. Nel resto del programma uso questa variabile come "ingresso ideale" privo di rimbalzi.

    byte reading = digitalRead(IN_PIN);
    if (reading == previous)
        time = millis();
    else if (millis() - time > DEBOUNCE)
        previous = reading;
    byte pressed = (previous == PRESSED_LEVEL);

Salve,
So che dovrei aiutare, ma non so come fare. Non so neanche mettere giusti i codici e me ne dispiaccio (per di più sono sul cel...)
Comunque questo programmino DOVREBBE funzionare. Potrebbe confermafe?
//led e pulsante
const byte PINLED=13;//led gua installato su Arduino 1
const byte PINTASTO=2;//si può non usare la resistenza pulldown, Arduino lo permette.
// Di contro il tasto va tenuto premuto per TEMPO ms per evitare false pressioni
unsigned long tp;
const int TEMPO=100;
byte stato=0;
int tt;
void setup(){
pinMode (PINLED, OUTPUT);
pinMode (PINTASTO, INPUT_PULLUP);//É proprio il comando PULLUP che, in logica negata, inserisce automaticamente il risultato pulldown.
}
void loop()
{
if (!digitalRead (PINTASTO) &&stato==0)
{
tp=millis();
stato++;
}
if (digitalRead (PINTASTO) && stato ==1)
{
if (millis()<tp)
{
stato=0;
}
else
{
tt=millis()-tp;
stato=0;
}
}
if (tt>TEMPO)
{
digitalWrite (PINKED, !digitalRead (PINLED));
}
}

giovepluvio: 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).

Guglielmo

PS: ... e usa l'apposita funzione dell'IDE per formattare il codice, che ... così non si può guardare!!!

Grazie per le risposte... mi scuso per non aver postato il codice nella maniera corretta.
Siccome sono un principiante sto imparando molto dalle videolezioni che si trovano su youtube. Il problema del rimbalzo l'ho scoperto proprio guardando una videolezione in cui veniva spiegato che può essere risolto con il codice che ho postato. Stamattina ho provato a cambiare il pulsante (probabilmente ieri ne utilizzavo uno a lamelle e oggi ne ho inserito uno a membrana) ed ho inserito in parallelo alla resistenza di pull down (10kOhm) un condensatore da 10nF e tutto sembra funzionare perfettamente. Raramente capita di avere una falsa lettura, ma in linea di massima ora sembra funzionare bene. Chiaramante, ho notato, che se si tiene il dito sul pulsante Arduino rimarrà sempre nel while e non eseguirà più niente, ma penso che a nessuno venga in mente di fare ciò. Posso lasciarlo così o mi consigliate di agire in qualche altro modo? Tra l'altro ho visto che esistono delle librerie che gestiscono il controllo del rimbalzo, potrebbero funzionare?
L'obiettivo finale, ma penso sia molto lontano, è quello di realizzare una piccola casa domotica (plastico) comandando le luci (led) sia tramite bluetooth che tramite i pulsanti posti all'interno della casa.

Ale_S:
... librerie che gestiscono il controllo del rimbalzo ...

... (condensatore da 100n o simile fra pin e massa) + (resistenza da 100 ohm o simile fra pin e pulsante) = problema rimbalzi risolto senza librerie, senza parti extra di sketch, e senza impazzirci su :wink:

Claudio_FF:
L'esempio di quella pagina mi sembra sbagliato per diversi motivi.

Sí hai ragione. A prima vista sembra essere giusto ma poi ci sono quei 2 errori che descrivi.
L' ho indicato fidandomi che era un progetto ufficiale arduino.cc senza controllarlo.

@giovepluvio á bello che vuoi aiutare ma..
Il Tuo sketch é difficile da leggere perché non é formattato (tutto all inizio riga) e perché usi delle variabili criptiche. Oltre che non funziona.

if(..) tp=millis();
...
}
if (...)
{
if (millis()<tp)

Millis() é solo dopo parecchi giorni (dopo un overflow) piú piccolo di un millis() preso prima .
Inoltre nel Tuo sketch il pulsante deve essere rilasciato perché il led si accende/spenga.

Apri un nuovo tread col Tuo codice e vediamo insieme a eliminare gli errori.

Ciao Uwe

Vi allego lo schema che sto utilizzando adesso, mantenendo il codice che ho postato all'inizio.

Ale_S:
Grazie per le risposte... mi scuso per non aver postato il codice nella maniera corretta.

... si, ok, ma effettua la correzione del tuo primo post come ti ho spiegato nel mio post #2. Grazie.

Guglielmo

Ciao a tutti, vi racconto come ho cercato di risolvere il problema anche se non ci sono riuscito del tutto.
Innanzitutto vi allego lo schema di un antirimbalzo hardware che ho trovato sul forum, nel mio caso ho usato lo schema con resistenza di pull-down.
Il codice che utilizzo per gestire il pulsante è il seguente:

if (digitalRead(pulsanteCucina) == HIGH) {
    locale = 'c';
    digitalWrite(releCucina, !digitalRead(releCucina));
    while (digitalRead(pulsanteCucina) == HIGH);
}

Le false letture non sono tante però ci sono ancora. Cosa potrei fare per risolvere definitivamente?
Il pulsante serve per comandare, tramite Arduino, un relè.

Ale_S:
Cosa potrei fare per risolvere definitivamente?

Andare a studiarti QUESTO e sostituire quella routine o, molto meglio, mettere una rete R/C (debouncing hardware, vd. allegato) e non ci pensi più.

Guglielmo

debouncing_hw.pdf (22.8 KB)

Grazie Guglielmo.
Nel caso volessi usare un anti-rimbalzo hardware (mi sembra di capire che lo preferisci rispetto a quello software), che codice dovrei usare visto che il mio non va bene? Se riesci magari a darmi una dritta

Ale_S:
Nel caso volessi usare un anti-rimbalzo hardware (mi sembra di capire che lo preferisci rispetto a quello software), che codice dovrei usare visto che il mio non va bene? Se riesci magari a darmi una dritta

Nessun codice aggiuntivo :smiley: ...
... solo la digitalRead() visto che, a pulire i rimbalzi, ci pensa la rete esterna R/C :wink:

Guglielmo

Infatti quel codice è giusto e funziona, a patto che:

  1. sia tutto adeguatamente filtrato lato hardware

  2. non vi sia la minima possibilità di captare un impulso spurio

Se il comando meccanico (pulsante / interruttore / contatto) è degradato / ossidato / impreciso ecc, allora il filtro hardware potrebbe dover essere dimensionato meglio, ad esempio bassa impedenza (resistenze di pull-qualcosa non maggiori di 1k) e condensatore da almeno 10µF.

La bassa impedenza riduce anche la possibilità di captare disturbi, che comunque sono sempre in agguato per collegamenti superiori a poche decine di cm, per cui si potrebbe dover pensare ad optoisolatori (dal micro non dovrebbero mai partire linee lunghe che vanno in giro).

Per il resto quel codice ha due punti deboli:

  1. basta un breve impulso (anche di pochi µs ma coincidente con il momento della digitalRead) per far commutare l'uscita, mentre una strategia di debounce prevede di controllare la stabilità di un segnale per tot millisecondi prima di darlo per buono (banalmente anche solo rileggere l'ingresso dopo 30..40ms dal primo evento, ma sarebbe meglio leggerlo più volte a distanza di pochi ms e darlo buono quando per N volte è rimasto stabile).

  2. per tutto il tempo in cui l'ingresso rimane attivo (pulsante premuto) il programma è bloccato nel while di attesa, e quindi non può fare nient'altro. Va bene per controllare UNA funzione... e basta, se i pulsanti fossero due (o più) comandati grosso modo nello stesso momento ci sarebbero "malfunzionamenti".

... usare i due if consecutivi con la flag ? ... vero, per ogni pulsante "sprechi" due if ed una variabile byte ... pero' non blocca nulla, anche se tieni premuti uno o piu pulsanti, tutto il resto continua a funzionare ...