[RISOLTO]problemi con debounce + chiarimento switch case per gestione menu

Buona sera a tutti, sto sperimentando un modo per crearmi un menu che abbia un carattere speciale che mi faccia da cursore per selezionare le varie voci del menu, non riesco a far funzionare correttamente il debounce sul pulsante up,
sul down mi funziona perfettamente. cosa dovrei modificare?
Grazie

// include the library code:
#include <LiquidCrystalFast.h>

// initialize the library with the numbers of the interface pins
LiquidCrystalFast lcd(23, 25, 27, 29, 31, 33, 35, 37);
         // LCD pins: RS RW  E1  E2 D4 D5 D6 D7

// costanti per assegnazione pin 
const int pulsanteUp = 1;     // pin pulate Up
const int pulsanteDown = 2;   // pin pulsante Down
const int pulsanteLeft = 3;   // pin pulsante Left
const int pulsanteRight = 4;  // pin pulsante Right
const int pulsanteEnter = 5;  // pin pulsante Enter
const int pulsanteEsc = 6;    // pin pulsante Esc

// variabili per lettura pulsanti
int statopulsanteUp = 0;
int statopulsanteDown = 0;
int statopulsanteLeft = 0;
int statopulsanteRight = 0;
int statopulsanteEnter = 0;
int statopulsanteEsc = 0;

// variabili per debouncing
int lastButtonState = LOW;
boolean flag = false;
long lastDebounceTime = 0;
long debounceDelay = 30;
//variabili lettura colonne e righe lcd
int numRighe = 0;
int numColonne = 0;

//carattere speciale per cursore di selezione menu
byte boccale[8] = {B00000, B01000, B11100, B11111, B11101, B11111, B11100, B00000,};


void setup() {
  //inizializza pin input dove sono collegati i pulsanti
  pinMode(pulsanteUp, INPUT);
  pinMode(pulsanteDown, INPUT);
  pinMode(pulsanteLeft, INPUT);
  pinMode(pulsanteRight, INPUT);
  pinMode(pulsanteEnter, INPUT);
  pinMode(pulsanteEsc, INPUT);
  

//creazione carattere speciale
  lcd.createChar(0, boccale);
   
// set lcd numero colonne e righe: 
  lcd.begin(40, 4);

}

void loop() {
// lettura stato dei vari pulsanti
  statopulsanteUp = digitalRead(pulsanteUp);
  statopulsanteDown = digitalRead(pulsanteDown);
  statopulsanteLeft = digitalRead(pulsanteLeft);
  statopulsanteRight = digitalRead(pulsanteRight);
  statopulsanteEnter = digitalRead(pulsanteEnter);
  statopulsanteEsc = digitalRead(pulsanteEsc);
 // prova menu principale
  lcd.setCursor (numColonne, numRighe); lcd.write(0);
  lcd.setCursor (1, 0); lcd.print ("voce 1");
  lcd.setCursor (1, 1); lcd.print ("voce 2");
  lcd.setCursor (1, 2); lcd.print ("voce 3");
  lcd.setCursor (1, 3); lcd.print ("voce 4");
  lcd.setCursor (21, 0); lcd.print ("voce 5");
  lcd.setCursor (21, 1); lcd.print ("voce 6");
  lcd.setCursor (21, 2); lcd.print ("voce 7");
  lcd.setCursor (21, 3); lcd.print ("voce 8");
  
  {       
   if ((statopulsanteDown != lastButtonState) && (flag==false))
     {
     flag=true;
     lcd.clear();
     if (statopulsanteDown==HIGH)// se premo Down sposto verso il basso il cursore per selezionare una voce del menu
     numRighe--; lcd.setCursor (numColonne, numRighe); lcd.write(0);
     lastDebounceTime = millis();
     if (numRighe == -1 && numColonne == 0){numRighe = 3; numColonne = 20;}
     if (numRighe == -1 && numColonne == 20){numRighe = 3; numColonne = 0;lcd.clear();}
     
     }    
         if (millis() > (lastDebounceTime + debounceDelay))
      flag = false;
      lastButtonState = statopulsanteDown;
      }
  
 {
  if ((statopulsanteUp != lastButtonState) && (flag==false))
     {
     flag=true;
     lcd.clear();
     if (statopulsanteUp==HIGH)// se premo Up sposto il cursore verso l'alto per selezionare una voce del menu
     numRighe++; lcd.setCursor (numColonne, numRighe); lcd.write(0);
     lastDebounceTime = millis();
     if (numRighe == 4 && numColonne == 0){numRighe = 0; numColonne = 20;}
     if (numRighe == 4 && numColonne == 20){numRighe = 0; numColonne = 0;lcd.clear();}

     }
        if (millis() > (lastDebounceTime + debounceDelay))
      flag = false;
      lastButtonState = statopulsanteUp;
   
    }  
  }

La via più semplice è mettere 2 letture del pin a cui hai collegato il pulsante separate da un piccolo delay. Senza stare ad usare codici più complessi.

if (digitalRead(pin) == stato) {
  delay(30);
  if (digitalRead(pin) == stato) {
    ....
  }
}

Funzionamento garantito al 99% :wink: (l'1% è per scaramanzia)

Nulla.. direi che sono capitato nell' 1% :slight_smile:

forse c'è qualcosa di sbagliato nel codice?

peppe123:
Nulla.. direi che sono capitato nell' 1% :slight_smile:

forse c'è qualcosa di sbagliato nel codice?

Beh, intanto tu non usi il metodo che ti ho detto io ma uno basato sugli intervalli misurati con millis(). :wink:

E poi mi pare che tu condivida la variabile lastButtonState per entrambi i pulsanti. Prova a mettere lastButtonStateDown e lastButtonStateUp perché ho come il timore che il problema sia qui.

Ci sono molte semplici librerie che gestiscono i buttons con molte funzioni oltre al deboncing.
Io suggerisco, per semplicità e robustezza, la seguente

http://playground.arduino.cc/Code/Buttons

Io non voglio passare da quello che pontifica a destra e manca però dico e domando: se puoi fare una cosa con un semplice delay messo fra 2 letture successive perché andare a scomodare una libreria? :sweat_smile:

leo72:

peppe123:
Nulla.. direi che sono capitato nell' 1% :slight_smile:

forse c'è qualcosa di sbagliato nel codice?

Beh, intanto tu non usi il metodo che ti ho detto io ma uno basato sugli intervalli misurati con millis(). :wink:

E poi mi pare che tu condivida la variabile lastButtonState per entrambi i pulsanti. Prova a mettere lastButtonStateDown e lastButtonStateUp perché ho come il timore che il problema sia qui.

risolto :slight_smile:
ho messo lastButtonStateDown.. Up.. Left.. etc.. ora va perfettamente

grazie!

Bravo.
Ora arrivano le tiratine d'orecchie ]:smiley:

const int pulsanteUp = 1;     // pin pulate Up
const int pulsanteDown = 2;   // pin pulsante Down
const int pulsanteLeft = 3;   // pin pulsante Left
const int pulsanteRight = 4;  // pin pulsante Right
const int pulsanteEnter = 5;  // pin pulsante Enter
const int pulsanteEsc = 6;    // pin pulsante Esc

// variabili per lettura pulsanti
int statopulsanteUp = 0;
int statopulsanteDown = 0;
int statopulsanteLeft = 0;
int statopulsanteRight = 0;
int statopulsanteEnter = 0;
int statopulsanteEsc = 0;

// variabili per debouncing
int lastButtonState = LOW;
boolean flag = false;
long lastDebounceTime = 0;
long debounceDelay = 30;
//variabili lettura colonne e righe lcd
int numRighe = 0;
int numColonne = 0;

Sai queste dichiarazioni di variabili di tipo "int" quanto consuma in RAM?
30 byte! 30 byte quando ne potresti consumare 15 semplicemente usando il typo "byte".
A meno che tu non voglia ad esempio leggere il pulsante -15000 :wink:
I pin del microcontrollore non arriveranno mai a 255, basta un tipo "byte"

Altra cosa.

long lastDebounceTime = 0;
long debounceDelay = 30;

è sbagliato. Ammettiamo che il tuo Arduino resti acceso per più di 25 giorni. Arriverai ad avere problemi con la gestione del valore restituito da millis() perché quest'ultimo è di tipo "unsigned long", non "long". Per cui quando arriverai a memorizzare un valore più grande di 2147483647 ti ritroverai con numeri negativi e non saprai perché :stuck_out_tongue:

Grazie, praticamente sono al mio primo progetto, critiche e consigli sono ben accetti.
riposto il codice, magari mi aiutate a capire come posso creare un menu con sottomenu.. sto provando utilizzando solo switch case ma dal case 0 riesco a passare al case 1 poi sono bloccato li. come faccio a saltare da un case all'altro?
grazie

// include the library code:
#include <LiquidCrystalFast.h>

// initialize the library with the numbers of the interface pins
LiquidCrystalFast lcd(23, 25, 27, 29, 31, 33, 35, 37);
         // LCD pins: RS RW  E1  E2 D4 D5 D6 D7

// costanti per assegnazione pin 
const int pulsanteUp = 1;     // pin pulate Up
const int pulsanteDown = 2;   // pin pulsante Down
const int pulsanteLeft = 3;   // pin pulsante Left
const int pulsanteRight = 4;  // pin pulsante Right
const int pulsanteEnter = 5;  // pin pulsante Enter
const int pulsanteEsc = 6;    // pin pulsante Esc

// variabili per lettura pulsanti
byte statopulsanteUp = 0;
byte statopulsanteDown = 0;
byte statopulsanteLeft = 0;
byte statopulsanteRight = 0;
byte statopulsanteEnter = 0;
byte statopulsanteEsc = 0;

// variabili per debouncing
int lastButtonStateUp = LOW;
int lastButtonStateDown = LOW;
int lastButtonStateLeft = LOW;
int lastButtonStateRight = LOW;
int lastButtonStateEnter = LOW;
int lastButtonStateEsc = LOW;


boolean flag = false;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 30;
//variabili lettura colonne e righe lcd
int numRighe = 0;
int numColonne = 0;

//variabile menu
unsigned int cont_pag = 0;

//carattere speciale per cursore di selezione menu
byte boccale[8] = {B00000, B01000, B11100, B11111, B11101, B11111, B11100, B00000,};


void setup() {
  //inizializza pin input dove sono collegati i pulsanti
  pinMode(pulsanteUp, INPUT);
  pinMode(pulsanteDown, INPUT);
  pinMode(pulsanteLeft, INPUT);
  pinMode(pulsanteRight, INPUT);
  pinMode(pulsanteEnter, INPUT);
  pinMode(pulsanteEsc, INPUT);
  

//creazione carattere speciale
  lcd.createChar(0, boccale);
   
// set lcd numero colonne e righe: 
  lcd.begin(40, 4);

}

void loop() {
// lettura stato dei vari pulsanti
  statopulsanteUp = digitalRead(pulsanteUp);
  statopulsanteDown = digitalRead(pulsanteDown);
  statopulsanteLeft = digitalRead(pulsanteLeft);
  statopulsanteRight = digitalRead(pulsanteRight);
  statopulsanteEnter = digitalRead(pulsanteEnter);
  statopulsanteEsc = digitalRead(pulsanteEsc);
  
     
  
  
switch (cont_pag) {

  case 0:
  lcd.setCursor (numColonne, numRighe); lcd.write(0);
  lcd.setCursor (1, 0); lcd.print ("voce 1");
  lcd.setCursor (1, 1); lcd.print ("voce 2");
  lcd.setCursor (1, 2); lcd.print ("voce 3");
  lcd.setCursor (1, 3); lcd.print ("voce 4");
  lcd.setCursor (21, 0); lcd.print ("voce 5");
  lcd.setCursor (21, 1); lcd.print ("voce 6");
  lcd.setCursor (21, 2); lcd.print ("voce 7");
  lcd.setCursor (21, 3); lcd.print ("voce 8");
  
   if ((statopulsanteDown != lastButtonStateDown) && (flag==false))
     {
     flag=true;
     lcd.clear();
     if (statopulsanteDown==HIGH)//premo Down per spostare il cursore char
     numRighe--; lcd.setCursor (numColonne, numRighe); lcd.write(0);
     lastDebounceTime = millis();
     if (numRighe == -1 && numColonne == 0){numRighe = 3; numColonne = 20;lcd.clear();}
     if (numRighe == -1 && numColonne == 20){numRighe = 3; numColonne = 0;lcd.clear();}
     
     }    
         if (millis() > (lastDebounceTime + debounceDelay))
      flag = false;
      lastButtonStateDown = statopulsanteDown;
      
   
 
  if ((statopulsanteUp != lastButtonStateUp) && (flag==false))
     {
     flag=true;
     lcd.clear();
     if (statopulsanteUp==HIGH) //sposto verso l'alto il cursore char per selezionare voci del menu principale
     numRighe++; lcd.setCursor (numColonne, numRighe); lcd.write(0);
     lastDebounceTime = millis();
     if (numRighe == 4 && numColonne == 0){numRighe = 0; numColonne = 20;}
     if (numRighe == 4 && numColonne == 20){numRighe = 0; numColonne = 0;lcd.clear();}

     }
        if (millis() > (lastDebounceTime + debounceDelay))
      flag = false;
      lastButtonStateUp = statopulsanteUp;
      
   
     break;
   
   
        case 1:
  lcd.setCursor (1, 0);lcd.print("menu2");
  if ((statopulsanteRight != lastButtonStateRight) && (flag==false))
     {
     flag=true;
     lcd.clear();
     if (statopulsanteRight==HIGH)
     cont_pag = 2;
     lastDebounceTime = millis();
     lcd.clear();
     if (millis() > (lastDebounceTime + debounceDelay))
      flag = false;
      lastButtonStateRight = statopulsanteRight;
      
     }
  
     break;
     
     case 2:
  lcd.setCursor (1, 0);lcd.print("menu3");
     if ((statopulsanteLeft != lastButtonStateUp) && (flag==false))
     {
     flag=true;
     lcd.clear();
     if (statopulsanteLeft==HIGH)
     cont_pag = 0;
     lastDebounceTime = millis();
     lcd.clear();
     if (millis() > (lastDebounceTime + debounceDelay))
      flag = false;
      lastButtonStateLeft = statopulsanteLeft;
     
     } 
  
     break;
     
  }

 }

Secondo me stai sbagliando la logica del tuo programma.
cont_pag, che è la variabile controllata dallo switch, devi modificarla esternamente allo switch stesso, altrimenti non ha senso come hai fatto tu: mi pare di vedere che tu entri in uno case, lì dentro cambi il valore di cont_pag e poi il tuo programma riparte e rientra nello switch e da lì nel case impostato in precedenza, dove poi ricambi ancora cont_pag ecc....

La logica alla base dello switch..case è

var = imposto_la_variabile_con_qualcosa; 
switch (var) {
  case 0:
    ...
    break;
  case 1:
    ..
    break;
  ecc....
}

Tu lo usi come una specie di salto, alla fine, per passare da un case all'altro.

@leo72
nessun problema, pontifica pure :-). La libreria serve se peppe, come mi è sembrato e come conferma negli ultimi post, vuole avere una gestione un po più sofisticata dei button senza inserire delay.

@peppe123
se può interessarti guarda anche la mia libreria per la gestione dei menu. Ho visto chè è stata scaricata da qualche centinaio di utenti. La dovrei anche migliorare, tempo permettendo:

(vai sul download per scaricare tutto). Se non ti spaventi per topics molto lunghi vai anche su Arduino Forum

brunialti:
@leo72
nessun problema, pontifica pure :-). La libreria serve se peppe, come mi è sembrato e come conferma negli ultimi post, vuole avere una gestione un po più sofisticata dei button senza inserire delay.

@peppe123
se può interessarti guarda anche la mia libreria per la gestione dei menu. Ho visto chè è stata scaricata da qualche centinaio di utenti. La dovrei anche migliorare, tempo permettendo:

GitHub - brunialti/MENWIZ: ARDUINO LCD menu library: short user code to manage complex menu structures
(vai sul download per scaricare tutto). Se non ti spaventi per topics molto lunghi vai anche su Arduino Forum

Grazie, intanto sperimento qualcosa senza usare librerie, puo' essere che imparo qualcosa :slight_smile:

leo72:
Secondo me stai sbagliando la logica del tuo programma.
cont_pag, che è la variabile controllata dallo switch, devi modificarla esternamente allo switch stesso, altrimenti non ha senso come hai fatto tu: mi pare di vedere che tu entri in uno case, lì dentro cambi il valore di cont_pag e poi il tuo programma riparte e rientra nello switch e da lì nel case impostato in precedenza, dove poi ricambi ancora cont_pag ecc....

La logica alla base dello switch..case è

var = imposto_la_variabile_con_qualcosa; 

switch (var) {
  case 0:
    ...
    break;
  case 1:
    ..
    break;
  ecc....
}



Tu lo usi come una specie di salto, alla fine, per passare da un case all'altro.

praticamente io utilizzo un cursore per spostarmi nelle varie voci del menu: quindi se il cursore si trova nella riga x e nella colonna x con la funzione if premo enter per accedere a quel sottomenu.
per ora lo sto sperimentando con successo, nel senso che sembra fare il suo dovere. magari il codice diventa un po' contorto pero' non sono riuscito a trovare una strada alternativa.
a quanto pare in questo modo con un unico switch riesco a fare menu e sottomenu.
che ne pensate?
potrei incontrare qualche difficolta' nell'implementazioni di ulteriori funzioni nei case visto che dovro' gestire temperature rele e funzioni timer?

@peppe:
non mi sono messo ad analizzare a fondo il tuo codice.

per ora imposto menu e sottomenu.. poi vediamo piu avanti quali complicanze di presenteranno :smiley:

in questo momento sono alle prese con una parte del menu che deve avere una decina di voci, una per ogni riga.. come posso fare per far comportare il mio lcd (posseggo un lcd 40, 4) come se fosse un 40x12 scorrendo come se fossero continue.

non so se ho reso l'idea :slight_smile:

grazie