comandare indipendentemente 3 led con un pulsante

salve a tutti,
come da titolo avrei bisogno di accendere a scelta uno di 3 led utilizzando un solo pulsante e di farlo restare acceso per un paio di secondi.
i led saranno sempre accesi singolarmente e mai in coppia,
avrei pensato di far leggere ad arduino il numero di pressioni del tasto nell'arco di un paio di secondi (o meglio ancora stabilire un intervallo in cui resta ad aspettare una ulteriore pressione del tasto) ed in base al valore finale di pressioni accendere il relativo led.

finora ho provato modificando qualche sketch trovato in rete ma nessuno prevede un "meccanismo" come quello descritto sopra.
ho anche spulciato il forum ma non ho trovato nulla di utile.

potete indicarmi gli argomenti da approfondire?
che funzionalità serviranno per lo sketch?
avevo pensato di allegare il dodice che so fare ma mi rendo conto sia relativo solo a const e pinmode

potete indicarmi la via? :blush:

Devi usare millis per crearti un timeout e poi usare while per uscire dal ciclo in cui leggi e conti le pressioni del pulsante.
L'uscita dal while deve avere un doppio controllo, sul tempo trascorso e sul numero di pressioni, in modo da uscire trascorso il tempo di lettura ed al raggiungimento del numero massimo di letture ammesse (nel tuo caso 3).
Una struttura in pseudo codice potrebbe essere questa:

1- leggo lo stato del pulsante
2- se non è premuto torno al punto 1
3- se è premuto, memorizzo il valore attuale di millis ed azzero il contatore delle pressioni del pulsante
4- do
5- leggo il pulsante
6- se è premuto, incremento il contatore delle pressioni
7- while: (controllo se la differenza fra millis ed il valore memorizzato è maggiore di un certo intervallo) and (controllo se il pulsante è stato premuto 3 volte)
8- se nessuna delle due condizioni è vere, torno al punto 4
9- accendo il led in base alle pressioni contate.

Devi ricordarti di mettere un piccolo debounce software per la lettura del pulsante, per evitare false letture dovute ai "rimbalzi".

Eviterei i pulsanti classici con gli interrupt, avrebbe centinaia di false letture ogni millisecondo. Evitare proprio.

ok è da stamattina che sto approfondendo la materia XD,
intanto due domande:

  • suppongo di dover mettere anche un debounce per evitare le false letture, quello posso inserirlo anche a logica sketch ultimata o influenza il funzionamento del tutto?

  • in giro ho trovato la libreria clickbutton: Google Code Archive - Long-term storage for Google Code Project Hosting. può fare al caso mio o meglio scrivere qualche riga in più e definire tutto a mano (anche a livello di apprendimento)?

uber:

  • suppongo di dover mettere anche un debounce per evitare le false letture, quello posso inserirlo anche a logica sketch ultimata o influenza il funzionamento del tutto?

  • in giro ho trovato la libreria clickbutton: Google Code Archive - Long-term storage for Google Code Project Hosting. può fare al caso mio o meglio scrivere qualche riga in più e definire tutto a mano (anche a livello di apprendimento)?

Senza debounce, se usi i classici switch, ne risente l'esecuzione dello sketch perché leggerti false pressioni.
Non metterti ad usare librerie strane, si tratta di mettere solo 2 letture intercalate da un delay per evitare false letture. Puoi anche crearti una piccola funzione, così da riclicare il codice.

leo72:
Eviterei i pulsanti classici con gli interrupt, avrebbe centinaia di false letture ogni millisecondo. Evitare proprio.

Mmm... se si ha bisogno di interrupt per la gestione di pulsanti non è necessario del tutto evitarli, ne abbiamo parlato in un topic sul forum internazionale :wink: anch'io prima ero convinto che per i pulsanti tradizionali andrebbero usati o i codici di debouncing o i classici circuiti antirimbalzo e invece e possibile anche usare interrupt con le dovute aggiunte software

ok cercherò di farlo il più semplice possibile,
il fatto è che sto traendo spunto da codici già fatti integrando le mie lacune con le reference dal sito e ogni tanto mi viene il dubbio su quale strada seguire :slight_smile:
per esempio ho notato che si potrebbe usare anche la funzione "switch", giusto?

cmq il codice scritto finora è questo, potete correggermi eventuali errori?
ad esempio le variabili definite con "byte" non le conoscevo fino a 10 minuti fa :P, vanno bene per l'utilizzo che devo farne? pur non avendo problemi di dimensioni mi piacerebbe sapere se tale utilizzo mi fa risparmiare memoria nel codice rispetto ad una "int" classica.

const int bottone = 2;        //pin bottone
const int led1 = 11;                //pin del led 1
const int led2 = 12;              //pin del led 2
const int led3 = 13;              //pin del led 3

byte statobottone = 0;          //variabile per la lettura del bottone
byte contatore = 0;                 //variabile per il conteggio

void setup()
{
  pinMode(led1, OUTPUT);          //definisce led1 come output
  pinMode(led2, OUTPUT);         //definisce led2 come output
  pinMode(led3, OUTPUT);         //definisce led3 come output
  pinMode(bottone, INPUT);     //definisce bottone come input
} 
  
void loop(){  
  if (statobottone == HIGH) {     //se il bottone è premuto
    ++contatore;                               //incrementa di 1 il contatore
  }

[...]

al momento sto approfondendo la parte della conta totale delle pressioni ENTRO il determinato tempo,
ovvero funzione millis,
se ho ben capito millis registra il tempo da cui è acceso arduino e con altre costanti/variabili devo fargli leggere il pulsante e incrementare il contatore?

piccolo aggiornamento:

con il seguente codice che manca ancora della funzione millis

// this constant won't change:
const int buttonPin = 2;      // the pin that the pushbutton is attached to
const int led1Pin = 11;       // the pin that the LED is attached to
const int led2Pin = 12;       // the pin that the LED is attached to
const int led3Pin = 13;       // the pin that the LED is attached to

// Variables will change:
byte buttonPushCounter = 0;   // counter for the number of button presses
byte buttonState = 0;         // current state of the button
byte lastButtonState = 0;     // previous state of the button

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(led3Pin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  buttonState = digitalRead(buttonPin);

  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes:  ");
      Serial.println(buttonPushCounter);
    } 
    else {
      Serial.println("off");
    }
  }
  
  lastButtonState = buttonState;

  if (buttonPushCounter == 1) {
    digitalWrite(led1Pin, HIGH);   // accende il led 1
    delay(2000);                   // aspetta 2 secondi
    digitalWrite(led1Pin, LOW);    // spenge il led 1
  }
  if (buttonPushCounter == 2) {
    digitalWrite(led2Pin, HIGH);   // accende il led 2
    delay(2000);                   // aspetta 2 secondi
    digitalWrite(led2Pin, LOW);    // spenge il led 3
  }
  if (buttonPushCounter == 3) {
    digitalWrite(led3Pin, HIGH);   // accende il led 3
    delay(2000);                   // aspetta 2 secondi
    digitalWrite(led3Pin, LOW);    // spenge il led 3
  }
  if (buttonPushCounter >= 4) {
    buttonPushCounter = 0;
  }
}

ho un risultato strano, in poche parole contrariamente a quanto mi aspettavo i led si accendono sequenzialmente ma restano accesi fino a nuova pressione, in poche parole si passa dal led 1 al 2 ecc...
ho inserito la funziona serial per vedere come leggesse il tasto ed ho notato che a video effettivamente il valore "off" compare esattamente 2 secondi dopo l'accensione del led, però fisicamente il resta sempre acceso.
tra le altre cose ho anche delle difficoltà nel passare da un led all'altro, come se molte letture andassero a vuoto.

cosa mi sono perso?

ok h modificato il loop così e pare funzionare

void loop() {
  buttonState = digitalRead(buttonPin);

  if (buttonState == HIGH) {
      counter++;
   }
  
  if (counter == 1) {
    digitalWrite(led1Pin, HIGH);   // accende il led 1
    Serial.println("on");
 }
 
   if (counter != 0) {
    delay(2000);                   // aspetta 2 secondi
    digitalWrite(led1Pin, LOW);   // accende il led 1
    Serial.println("off");
  }
counter = 0;
}

MasterPi:
Mmm... se si ha bisogno di interrupt per la gestione di pulsanti non è necessario del tutto evitarli, ne abbiamo parlato in un topic sul forum internazionale :wink: anch'io prima ero convinto che per i pulsanti tradizionali andrebbero usati o i codici di debouncing o i classici circuiti antirimbalzo e invece e possibile anche usare interrupt con le dovute aggiunte software

Allora, entrando nel dettaglio, posso dirti che ho usato anch'io una tecnica di debounce per una lettura di un pulsante dall'interno di un interrupt: faccio la conta di diverse decine di chiamate alla ISR e solo dopo una certa soglia eseguo il codice della ISR. In pratica la ISR viene chiamata un sacco di volte. Se non è proprio un'applicazione critica, eviterei l'uso dei pulsantini tramite interrupt.

Quando si vogliono usare gli interrupt con i pulsanti ... un bel circuito di debouncing hardware è praticamente obbligatorio.

Una interessante guida QUI

Guglielmo

IO coi debounce hardware non mi sono trovato bene. O sbaglio qualcosa io oppure il circuito antidebounce mi prende per le mele :stuck_out_tongue_closed_eyes:
Preferisco mettere un piccolo delay e... via col vento! :wink:

leo72:
.... O sbaglio qualcosa io oppure il circuito antidebounce mi prende per le mele :stuck_out_tongue_closed_eyes:

Buona la ... PRIMA :smiley: :smiley: :smiley: :smiley:

Guglielmo

leo72:

MasterPi:
Mmm... se si ha bisogno di interrupt per la gestione di pulsanti non è necessario del tutto evitarli, ne abbiamo parlato in un topic sul forum internazionale :wink: anch'io prima ero convinto che per i pulsanti tradizionali andrebbero usati o i codici di debouncing o i classici circuiti antirimbalzo e invece e possibile anche usare interrupt con le dovute aggiunte software

Allora, entrando nel dettaglio, posso dirti che ho usato anch'io una tecnica di debounce per una lettura di un pulsante dall'interno di un interrupt: faccio la conta di diverse decine di chiamate alla ISR e solo dopo una certa soglia eseguo il codice della ISR. In pratica la ISR viene chiamata un sacco di volte. Se non è proprio un'applicazione critica, eviterei l'uso dei pulsantini tramite interrupt.

Si in effetti anch'io cerco di evitare tramite hardware o col codice debouncing, ma mi è successo di non poter fare a meno degli interrupt per gestire dei pulsanti e allora mi sono dovuto arrangiare in quel modo che è pur sempre efficiente :wink:

visto il doc ma è un po' troppo avanzato per me :cold_sweat:
ho riletto le prime risposte e porvato a scrivere il codice seguendo i punti indicati

const int bottone = 2;        // the pin that the pushbutton is attached to
const int led1 = 11;          // the pin that the LED is attached to
const int led2 = 12;          // the pin that the LED is attached to
const int led3 = 13;          // the pin that the LED is attached to
unsigned long tempo = millis();
unsigned long inizioattesa = 0;
byte contatore = 0;           // counter for the number of button presses
byte stato = 0;               // current state of the button
byte statoprecedente = 0;     // previous state of the button

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

void loop() {
  stato = digitalRead(bottone);
  if (stato != statoprecedente) 
  {
    if (stato == HIGH) 
    {
      inizioattesa = tempo;
      contatore = 0;
    }
  }
  stato = digitalRead(bottone);
  if (stato == HIGH)
  {
    contatore++;
  }
    while (tempo - inizioattesa > 2000)
  {
  if (contatore == 1) {
    digitalWrite(led1, HIGH);      // accende il led 1
  }
  if (contatore == 2) {
    digitalWrite(led2, HIGH);      // accende il led 2
  }
  if (contatore == 3) {
    digitalWrite(led3, HIGH);      // accende il led 3
  }
    delay(2000);                   // aspetta 2 secondi
    digitalWrite(led1, LOW);       // spenge il led 1
    digitalWrite(led2, LOW);       // spenge il led 3
    digitalWrite(led3, LOW);       // spenge il led 3
  }
}

ho avuto delle difficoltà con i punti 7 ed 8 ed ho dovuto semplificare...

...ovviamente non funziona, lo compila, lo carica ma poi premendo il pulsante non fa nulla =(

Allora uber non ho ben capito cosa intendi fare; per come ho interpretato io il problema desideri comandare tre led attraverso la pressione di un solo pulsante, ossia: se premo il pulsante buttonpin una volta si accende il led1 per tot secondi, se premo buttonpin due volte accendo il led2 per totsecondi e se premo buttonpin tre volte allora si accende il led3 per tot secondi, giusto?
Allora da questa prospettiva è facile capire che bisogna "dire" al programma entro quando tempo l'utente deve selezionare il led da accendere, ossia se si deve aspettare il comando entro tre, quattro o cinque secondi. Supponiamo di voler creare un programma che entro tre secondi dalla prima pressione del pulsante riceve la pressione dei pulsanti( e nel frattempo incrementerà la rispettiva variabile count) e al termine di essi seleziona il led da accendere in base al valore di count. Usando un pulsante è indubbiamente necessario l'utilizzo di un debouncing(antirimbalzo) o hardware(con un trigger di Shcmitt o con due porte logiche) o software(attraverso la funzione millis()). Noi scegliamo solitamente l'utilizzo software che è più rapido e funziona sempre(o quasi xD).
Allora il codice approssimativamente dovrebbe essere questo:

int count = 0;
unsigned long wait = 0;
#define INTERVALLO 3000 //intervallo di tempo per dare il comando a partire dalla prima pressione del pulsante
const int buttonPin = 5; //bottone
const int led1 = A3;
const int led2 = A2;
const int led3 = A1;
int buttonState;             
int lastButtonState = LOW;   
unsigned long lastDebounceTime = 0;  
long debounceDelay = 50;   

void setup() {
pinMode (buttonPin,INPUT_PULLUP);
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
digitalWrite(led3, LOW);
}

void loop() {
int reading = digitalRead(buttonPin);
  if (reading != lastButtonState) 
    lastDebounceTime = millis();
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;

      if (buttonState == HIGH) 
        count++;
    }
  }
  lastButtonState = reading;

wait = millis();
while ( count > 0 && count <= 3 && (millis() < wait + INTERVALLO)) {
int reading = digitalRead(buttonPin);
  if (reading != lastButtonState) 
    lastDebounceTime = millis();
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;

      if (buttonState == HIGH) 
        count++;
    }
  }
  lastButtonState = reading;
}

if ( count > 0) {
if (count > 3) count = 3;
switch(count) {
  case 1:
              digitalWrite(led1, HIGH);
              digitalWrite(led2, LOW);
              digitalWrite(led3, LOW);
              delay(2000);
              digitalWrite(led1, LOW);
              digitalWrite(led2, LOW);
              digitalWrite(led3, LOW);
              break;
  case 2:
              digitalWrite(led1, LOW);
              digitalWrite(led2, HIGH);
              digitalWrite(led3, LOW);
              delay(2000);
             digitalWrite(led1, LOW);
              digitalWrite(led2, LOW);
              digitalWrite(led3, LOW);
              break;
  case 3:
              digitalWrite(led1, LOW);
              digitalWrite(led2, LOW);
              digitalWrite(led3, HIGH);
              delay(2000);
              digitalWrite(led1, LOW);
              digitalWrite(led2, LOW);
              digitalWrite(led3, LOW);
              break;
              
}
count = 0;
}
}

si hai capito a pieno quello che mi serviva,
sto provando e sono commosso FUNZIONA!!! GRAZIE!!!!!!!!

spiego nel dettaglio l'utilizzo:
visto che giro in scooter e che durante il tragitto metto sempre lo zaino con le chiavi nel sottosella, ho pensato di usare un vecchio telecomando mezzo scassato collegato ad arduino e questo programma da tenere nel bauletto o nelle carene vicino al clason per comandare tramite un solo pulsante (di serie sullo scooter e quindi impermeabile) tutti i tasti delle aperture.
nell'applicazione pratica al posto dei led ci sono 3 transistor che chiudono il circuito con i relativi tasti del box (cancello esterno + 2 serrande).
il progetto torna molto utile soprattutto in caso di pioggia, risparmio tempo, evito di dover scendere per prendere le chiavi e soprattutto di dover aprire per frugare nel sottosella sotto il diluvio.

senza dimenticare la parte di stuio di programmazione, l'idea iniziale era sostituire completamente il telecomando ma come hai potuto vedere ne so troppo poco per avventurarmi in un progetto del genere con moduli radio ecc...
non mollo cmq, intanto approfondisco questo sketch, non appena sarò più confidente proverò la versione completa :wink:

grazie ancora! :slight_smile:

Cerca soprattutto di approfondire la parte relativa alla funzione millis() e a coma gestire intervalli di tempo senza l'ausilio della funzione delay(). Magari cerca un po' tra i tutorial di arduino, o magari leggi la struttura su questo sito stesso così inizi a farti un'idea :wink:

Il codice è indentato maluccio, non si capiscono le nidificazioni dei vari blocchi :sweat_smile: :sweat_smile:
A parte questo, un primo errore palese è il controllo del while:

while (tempo - inizioattesa > 2000)

In questo modo tu dici:
mentre (tempo - inizio attesa > 2000) fai...

La condizione è falsa subito e non entri neanche nel while. Devi invertire il segno di confronto:

while (tempo - inizioattesa < 2000)

Poi non ho guardato il resto.

Il codice funziona alla perfezione :wink: come hogià detto prima, Leo, il mio obiettivo era quello di far partire il ricevimento del comando a partire dalla prima pressione del pulsante. Così ho messo una prima pressione del pulsante che incrementava count, dopodicchè ho fatto questo while:

wait = millis();
while(count > 0 && count <= 3 && (millis() < wait + INTERVALLO)) {...}

Che cosa dice questo ciclo? se la variabile count è maggiore di zero(ossia c'è stata la prima pressione del pulsante) e se è minore o uguale a 3(che è il comando massimo) e inoltre si deve rimanere nel ciclo fino a quando non sono trascorsi INTERVALLO secondi dall'inizio del ciclo e ce lo dice l'ultima condizione(nota che wait è stato inizializzato poco prima con millis()).
Quindi non capisco qual è il problema che dici, anche perchè il codice che hai scritto non c'entra nulla, bo forse sto impallato io xD