Aggiunta menu a più funzioni visualizzabile su lcd e navigabile da matrix keypad

Ciao a tutti ragazzi . Torno a scrivervi perchè sto trovando un pò di problemi ad inserire nel mio sketch ( molto incasinato ma funzionante ) una sorta di menu navigabile con le varie opzioni. Premetto che uso arduino uno dove ho collegato un display lcd 16x2 un buzzer 2 rele un sensore di passaggio oggetti e un matrix keypad 4x4. Facendo questo ho usato praticamente quasi tutti i pin di arduino ( rimangono solo 0 e 1 ) ed ho la seguente configurazione : si accende la macchina emette un paio di beep e a display appare una sorta di demo di benvenuto e loading , quindi a un certo punto compare la scritta ready e il buzzer emette un altro suono. dopodichè compare una scritta mobile , a questo punto ci sono un paio di funzioni che da seriale se riceve un certo segnale attiva il rele 1 ,effettua il conteggio con il sensore conta oggetti e si ferma al numero giusto di oggetti passati , mentre dal keypad se inserisco la password ( in questo caso 1982) e subito dopo un numero x del keypad , fa la stessa cosa . Sempre dal keypad se premo o la lettera b o la c mi attiva /disattiva il rele 2. tutti i pulsanti del keypad emettono un piccolo beep. Adesso io vorrei utilizzare la lettera A e la D per navigare in un menu , dove per esempio con la A ti muovi nelle varie funzioni del menu , con la D selezioni . Le funzioni del menu dovrebbero essere 2 non tantissime , ossia Abilitazione rele 2 ---- > ON OFF
Abilitazione rele 1 ---- > richiesta password ---- se ok ------ >inserire numero desiderato e confermare
se non e ok torna alla schermata principale.
Eventualmente avrei un altro piccolo problema, nell inserimento numero desiderato attualmente posso solo inserire un valore da 0 a 9 , non 10 100 ecc magari se riusciamo a correggerlo.
Vi posto lo sketch però vi prego di non dirmi nulla , non so neanche io come fa a funzionare , grazie mille

#include <LiquidCrystal.h>
LiquidCrystal lcd(13, 3, 4, 5, 6, 7);
#include <Keypad.h>
#include <Password.h>
Password password = Password( "1982" ) ;
const int optoPin = 12; //questo è il pin a cui è collegato l'optoswitch
const int relePin = 2;
const int buzzerPin = 8;
const int relePinRefill = 9;
int pin;
int value;
bool optoState; //stato dell'opto
bool lastOptoState; //stato precedente dell'opto
int coins = 0;
unsigned long startTime;

#define TIMEOUT 10000
#define BUZTIME 100

const byte ROWS= 4; //number of rows on the keypad i.e. 4
const byte COLS= 4; //number of columns on the keypad i,e, 3
char keys[ROWS][COLS]=
{
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS]= {10, 11, 19, 18}; //Rows 0 to 3
byte colPins[COLS]= {14, 15, 16, 17}; //Columns 0 to 3
// command for library forkeypad
//initializes an instance of the Keypad class
Keypad keypad= Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

void setup() {
  // imposta il numero di colonne e righe del display utilizzato:
  lcd.begin(16, 2);
  pinMode(buzzerPin, OUTPUT);
  pinMode(optoPin, INPUT_PULLUP);
  pinMode(relePin, OUTPUT);
  pinMode(relePinRefill, OUTPUT);
  digitalWrite(relePin, HIGH);
  digitalWrite(relePinRefill, LOW);
  Serial.begin(9600); //inizio comunicazione seriale
  optoState = digitalRead(optoPin);
  digitalWrite(buzzerPin, HIGH);
      delay(500);
      digitalWrite(buzzerPin, LOW);
  lcd.setCursor(0, 0);
      lcd.print("LaBucciata  Slot");
      delay(5000);
      lcd.setCursor(0, 1);
      lcd.print("Configurazione..");
      delay(500);
      lcd.begin(16, 2);
      lcd.setCursor(5, 0);
      lcd.print("Ready");
      delay(400);
      digitalWrite(buzzerPin, HIGH);
      delay(200);
      digitalWrite(buzzerPin, LOW);
      delay(200);
      digitalWrite(buzzerPin, HIGH);
      delay(200);
      digitalWrite(buzzerPin, LOW);
      lcd.begin(16, 2);
      keypad.addEventListener(keypadEvent); {
      for (char i=10; i<12; i++)
      pinMode(i, INPUT_PULLUP);
}
} 
  void loop() {
  checkForCredits1();
  releaseTokens();
  tokensCountDown();
  keypad.getKey();
 }
  void keypadEvent(KeypadEvent eKey){
  switch (keypad.getState()){
    case PRESSED:
  digitalWrite(buzzerPin, HIGH);
  delay(50);
  digitalWrite(buzzerPin, LOW);
  lcd.begin(16, 2);
  lcd.setCursor(5, 0);
  Serial.print("Pressed: ");
  lcd.println(eKey);
  switch (eKey){
    case '*': checkPassword(); break;
    case '#': password.reset(); break;
    case 'B': digitalWrite(relePinRefill, HIGH); break;
    case 'D': digitalWrite(buzzerPin, HIGH); break;
    case 'C': digitalWrite(relePinRefill, LOW); break;
    default: password.append(eKey);
     }
  }
}

void checkPassword(){
  checkForCredits2:
  char keypressed = keypad.getKey();
     if (keypressed != NO_KEY)
     lcd.begin(16, 2);
     lcd.setCursor(0, 3);
     lcd.print(keypressed);
     {
      coins = (keypressed) - 48;
      }
         if (Serial.available())
       {
          coins = Serial.read();
          Serial.flush();
          Serial.begin(9600);
          }
    if (password.evaluate()){
    lcd.begin(16, 2);
    lcd.setCursor(3, 0);
    lcd.print("Success");
     Serial.begin(9600);
     goto checkForCredits2;
     digitalWrite(buzzerPin, HIGH);
      delay(200);
      digitalWrite(buzzerPin, LOW);
     delay(1000);
    }else{
    lcd.begin(16, 2);
    lcd.setCursor(3, 0);
    lcd.println("Wrong");
    goto wrongbeep;
    wrongbeep:
     digitalWrite(buzzerPin, HIGH);
      delay(500);
      digitalWrite(buzzerPin, LOW);  
  }
}
void checkForCredits1() {
  while (Serial.available() > 0) {
    value = Serial.parseInt();
    if (Serial.read() == 'x') {
      if (value > 0) {
        coins ++;
        Serial.flush();
        Serial.begin(9600);
        }
        }
        }
        }
 void releaseTokens() {
  if (coins > 0) {
    // Se il motore è fermo, memorizzo il momento in cui parte.
    // In questo modo mi assicuro di impostare la variabile solo al fronte di salita di relePin
    if(digitalRead(relePin) == HIGH){
      startTime = millis();
    }
    digitalWrite(relePin, LOW); //rilascio gettoni
    optoState = digitalRead(optoPin);
    }
  else {
    digitalWrite(relePin, HIGH);//STOP rilascio gettoni
    lcd.setCursor(0, 0);
    lcd.print("LaBucciata Slot $ $ Buona Fortuna $ $");
    delay(400);
    for (int positionCounter = 0; positionCounter < 1; positionCounter++) {
      lcd.scrollDisplayLeft();
    }
   
   
  }
 
  // Solo se il motore è ancora acceso, dopo 10 secondi dall'attivazione, resetto l'uscita 
  if(digitalRead(relePin) == LOW){
    if(millis() - startTime > TIMEOUT ){
      digitalWrite(relePin, HIGH); //STOP rilascio gettoni
      digitalWrite(buzzerPin, HIGH);
      lcd.begin(16, 2);
      lcd.setCursor(5, 0);
      lcd.print("refill");
      digitalWrite(relePinRefill, HIGH);
      delay(3000);
      lcd.begin(16, 2);
      lcd.setCursor(0, 0);
      lcd.print("gettoni mancanti:");
      delay(1000);
      lcd.setCursor(13, 1);
      lcd.println(coins);
      coins = 0;
      digitalWrite(buzzerPin, LOW);
      lcd.begin(16, 2);
      }
  }
 
}
 
void tokensCountDown() {
  if (optoState != lastOptoState) { // se lo stato dell'opto è cambiato (cioè qualcosa sta passando, in entrata o uscita)
    lastOptoState = optoState;
    delay (100); //debounce "economico" (questo serve perchè nel passaggio di stato possono esserci fluttuazioni che portano a conteggi errati)
    if (lastOptoState == LOW && coins > 0) {
      coins = coins - 1 ; //decremento gettoni
      startTime = millis(); //reset timer
     
    }
  }
}

Io ti accontento, non ti dirò nulla :smiley:

meeeeeeee intendevo non mi dite nulla per l'incasinamento dello sketch aahhahaha

Quello che ti posso dire così al volo è che quando leggi da seriale e assegni alla variabile è normale che ti restituisca un solo "numero" perché se dai occhio al reference e leggi cosa ti restituisce vedrai che ti da 8 bit quindi un valore compreso tra zero e nove (okkeyyy non è proprio così comunque è per far capire al volo).
Per ottenere il risultato desiderato devi fare un ciclo (seguendo l'esempio del reference) per leggere tutti i byte presenti, quando li hai letti tutti allora potrai usare i vari valori letti per assegnare il giusto valore alla tua variabile "coins", oppure guardare meglio nel reference della Serial per vedere se c'è un metodo che fa ala caso tuo :wink:
Detto questo non vado nel merito delle altre richieste per il semplice motivo che secondo me con il codice così com'è non si va molto lontano, nel senso che è talmente mal strutturato che ti dovrei consigliare di rivedere la parte oggetto della richiesta in toto, mi spiace dirlo ma quando vedo i goto iniziano a sanguinarmi occhi, orecchie e anche altri orifizi e lo bollo come "codice sbagliato da rivedere da zero".
Altra cosa, quando posti il codice prima premi Ctrl+T nell'IDE in modo da formattarlo altrimenti è ancora peggio leggerlo e capire cosa fa un programma.

hai ragione me lo hanno detto più di una persona che il codice cosi come è messo fa abbastanza pietà, ma come ho anche dimostrato in molti video , almeno funziona. Il suo scopo è letteralmente un coin - hopper MAME . Per poter riuscire a fare questo ho dovuto usare arduino che comunica con mame attraverso un programma che "sniffa" gli output di mame e assegna una funzione che invia via seriale a arduino in questo caso per far accendere un led piuttosto che far vibrare un motorino. Da questa cosa insieme ad un altro ragazzo abbiamo tirato giu questa sorta di sketch da completi neofiti , non negando di aver letteralmente scopiazzato di qua e di la , molte parti di sketch senza a volte sapere nemmeno cosa significassero.
Adesso cosi come è messo a me non interessa se viene anche totalmente stravolto , almeno l'importante e che funzioni, magari puo servire come idea per qualche progetto a terzi , e magari è la volta buona che io comincio ad imparare davvero ... comunque grazie

hashmaker:
hai ragione me lo hanno detto più di una persona che il codice cosi come è messo fa abbastanza pietà, ma come ho anche dimostrato in molti video , almeno funziona.

Si ok ma a me stava venendo il mal di testa a leggerlo... Almeno indentalo per bene (premi Ctrl-T dall'IDE Arduino, te lo fa lui) e forse già in quel modo potresti visivamente capire se qualcosa non sta nel posto per te giusto. Una volta che lo avrai riformattato e gli avrai dato un'altra occhiata (comunque con attenzione), se hai ancora problemi posta il nuovo listato e vediamo.

PS per il discorso dei pin che hai quasi finito, prenditi un adattatore I2C (o un altro display 1602 con interfaccia I2C) e gestisci tramite quello il display, ti risparmi unabella manciata di pin già così.

Ok, allora ti suggerisco (vi suggerisco) di partire dal riprogettare il metodo checkPassword, prova a riscriverlo con una macchina a stati finiti eliminando l'uso dei goto e revisionando la lettura dei coins con il siggerimento del mio post precedente. In ultimo, anche se non inerente al funzionamento, visto che quel metodo non verifica la passowrd o quantomeno non fa solo quello, cambiargli il nome renderà il codice più facilmente manuntenibile/legginile in futuro.
Capisc che sia difficile avere un programma funzionante al 90% e dover iniziare nuovamente, ma se non riesci a adattare il codice la cosa giusta da fare è ripartire con un programma vuoto che fa solo una cosa, ad esempio la parte di analisi contenuta nel metodo checkpassword, farlo funzionare (sempre senza l'uso dei goto) e quando funziona importarlo nel programma completo.

docdoc:
Si ok ma a me stava venendo il mal di testa a leggerlo…

il fatto e che funziona tutto come ti ho detto avrei voluto solo aggiungere questo menu ma vi ripeto io non so neanche da dove cominciare per non fare casini.

#include <LiquidCrystal.h>
LiquidCrystal lcd(13, 3, 4, 5, 6, 7);
#include <Keypad.h>
#include <Password.h>
Password password = Password( "1982" );
const int optoPin = 12; //questo è il pin a cui è collegato l'optoswitch
const int relePin = 2;
const int buzzerPin = 11;
const int relePinRefill = 8;
int pin;
int value;
bool optoState; //stato dell'opto
bool lastOptoState; //stato precedente dell'opto
int coins = 0;
unsigned long startTime;

#define TIMEOUT 10000
#define BUZTIME 100

const byte ROWS = 4; //number of rows on the keypad i.e. 4
const byte COLS = 4; //number of columns on the keypad i,e, 3
char keys[ROWS][COLS] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {9, 10, 19, 18}; //Rows 0 to 3
byte colPins[COLS] = {14, 15, 16, 17}; //Columns 0 to 3
// command for library forkeypad
//initializes an instance of the Keypad class
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

void setup() {
  // imposta il numero di colonne e righe del display utilizzato:
  lcd.begin(16, 2);
  pinMode(buzzerPin, OUTPUT);
  pinMode(optoPin, INPUT_PULLUP);
  pinMode(relePin, OUTPUT);
  pinMode(relePinRefill, OUTPUT);
  digitalWrite(relePin, HIGH);
  digitalWrite(relePinRefill, LOW);
  Serial.begin(9600); //inizio comunicazione seriale
  optoState = digitalRead(optoPin);
  digitalWrite(buzzerPin, HIGH);
  delay(500);
  digitalWrite(buzzerPin, LOW);
  lcd.setCursor(0, 0);
  lcd.print("LaBucciata  Slot");
  delay(5000);
  lcd.setCursor(0, 1);
  lcd.print("Configurazione..");
  delay(500);
  lcd.begin(16, 2);
  lcd.setCursor(5, 0);
  lcd.print("Ready");
  delay(400);
  digitalWrite(buzzerPin, HIGH);
  delay(200);
  digitalWrite(buzzerPin, LOW);
  delay(200);
  digitalWrite(buzzerPin, HIGH);
  delay(200);
  digitalWrite(buzzerPin, LOW);
  lcd.begin(16, 2);
  keypad.addEventListener(keypadEvent);
  for (char i = 9; i < 13; i++) {   //Ingresso dei pulsanti
    pinMode(i, INPUT_PULLUP);
  }
}
void loop() {
  checkForCredits1();
  releaseTokens();
  tokensCountDown();
  keypad.getKey();
}
void keypadEvent(KeypadEvent eKey) {
  switch (keypad.getState()) {
    case PRESSED:
      digitalWrite(buzzerPin, HIGH);
      delay(50);
      digitalWrite(buzzerPin, LOW);
      lcd.begin(16, 2);
      lcd.setCursor(5, 0);
      Serial.print("Pressed: ");
      lcd.println(eKey);
      switch (eKey) {
        case '*': checkPassword(); break;
        case '#': password.reset(); break;
        case 'B': digitalWrite(relePinRefill, HIGH); break;
        case 'D': digitalWrite(buzzerPin, HIGH); break;
        case 'C': digitalWrite(relePinRefill, LOW); break;
        default: password.append(eKey);
      }
  }
}

void checkPassword() {
checkForCredits2:
  char keypressed = keypad.getKey();
  if (keypressed != NO_KEY)
    lcd.begin(16, 2);
  lcd.setCursor(0, 3);
  lcd.print(keypressed);
  {
    coins = (keypressed) - 48;
  }
  if (Serial.available())
  {
    coins = Serial.read();
    Serial.flush();
    Serial.begin(9600);
  }
  if (password.evaluate()) {
    lcd.begin(16, 2);
    lcd.setCursor(3, 0);
    lcd.print("Success");
    Serial.begin(9600);
    goto checkForCredits2;
    digitalWrite(buzzerPin, HIGH);
    delay(200);
    digitalWrite(buzzerPin, LOW);
    delay(1000);
  } else {
    lcd.begin(16, 2);
    lcd.setCursor(3, 0);
    lcd.println("Wrong");
    goto wrongbeep;
wrongbeep:
    digitalWrite(buzzerPin, HIGH);
    delay(500);
    digitalWrite(buzzerPin, LOW);
  }
}
void checkForCredits1() {
  while (Serial.available() > 0) {
    value = Serial.parseInt();
    if (Serial.read() == 'x') {
      if (value > 0) {
        coins ++;
        Serial.flush();
        Serial.begin(9600);
      }
    }
  }
}
void releaseTokens() {
  if (coins > 0) {
    // Se il motore è fermo, memorizzo il momento in cui parte.
    // In questo modo mi assicuro di impostare la variabile solo al fronte di salita di relePin
    if (digitalRead(relePin) == HIGH) {
      startTime = millis();
    }
    digitalWrite(relePin, LOW); //rilascio gettoni
    optoState = digitalRead(optoPin);
  }
  else {
    digitalWrite(relePin, HIGH);//STOP rilascio gettoni
    lcd.setCursor(0, 0);
    lcd.print("LaBucciata Slot $ $ Buona Fortuna $ $");
    delay(400);
    for (int positionCounter = 0; positionCounter < 1; positionCounter++) {
      lcd.scrollDisplayLeft();
    }


  }

  // Solo se il motore è ancora acceso, dopo 10 secondi dall'attivazione, resetto l'uscita
  if (digitalRead(relePin) == LOW) {
    if (millis() - startTime > TIMEOUT ) {
      digitalWrite(relePin, HIGH); //STOP rilascio gettoni
      digitalWrite(buzzerPin, HIGH);
      lcd.begin(16, 2);
      lcd.setCursor(5, 0);
      lcd.print("refill");
      digitalWrite(relePinRefill, HIGH);
      delay(3000);
      lcd.begin(16, 2);
      lcd.setCursor(0, 0);
      lcd.print("gettoni mancanti:");
      delay(1000);
      lcd.setCursor(13, 1);
      lcd.println(coins);
      coins = 0;
      digitalWrite(buzzerPin, LOW);
      lcd.begin(16, 2);
    }
  }

}

void tokensCountDown() {
  if (optoState != lastOptoState) { // se lo stato dell'opto è cambiato (cioè qualcosa sta passando, in entrata o uscita)
    lastOptoState = optoState;
    delay (100); //debounce "economico" (questo serve perchè nel passaggio di stato possono esserci fluttuazioni che portano a conteggi errati)
    if (lastOptoState == LOW && coins > 0) {
      coins = coins - 1 ; //decremento gettoni
      startTime = millis(); //reset timer

    }
  }
}

per la parte della password provvisoriamente posso anche farne a meno , la implemento dopo

>hashmaker: Quando si quota un post, NON è necessario riportarlo (inutilmente) tutto; bastano poche righe per far capire di cosa si parla ed a cosa ci si riferisce, inoltre, se si risponde al post immediatamente precedente, normalmente NON è necessario alcun "quote" dato che è sottinteso. :slight_smile:

Gli utenti da device "mobile" ringrazieranno per la cortesia :wink:

Guglielmo

P.S.: Ho troncato io il "quote" del tuo post qui sopra :wink:

hashmaker:
per la parte della password provvisoriamente posso anche farne a meno , la implemento dopo

Si ma in quella parte c'è anche la sezione che si occupa di leggere dalla seriale il numero di gettoni inseriti, quella la devi gestire comunque quindi in ogni caso devi metterti li, rimboccarti le maniche e studiarti il linguaggio e come realizzare ciò che a te serve. Il copia incolla ti ha portato fino al punto dove sei, per andare oltre occorre aggiungere del tuo, ripeto programma vuoto e gestione della singola parte che ti serve nel modo in cui ti serve, solo quando funziona la integri nel programma generale.

ciao ragazzi scusatemi ma io provando un po qua e la ho rivisitato il mio sketch e adesso con questo qui , ho la seguente situazione : tutto il vecchio controllo funziona e in piu ho attivato il menu che volevo , mettendo modalita refill on off e erogazione gettoni ( al momento senza richiesta password e con un numero di gettoni pari a 5 per ogni volta che si richiede questa parte ). Dunque in teoria mi mancherebbe aggiungere la richiesta password quando vado a chiedere l’erogazione dei gettoni e subito dopo chiedere il numero dei gettoni desiderati. Ripeto che ho gia fatto piu e piu prove e sembra funzionare tutto. Sopratutto ho eliminato il goto :smiley: :smiley:

#include <LiquidCrystal.h>
LiquidCrystal lcd ( 13, 3, 4, 5, 6, 7);
#include <Keypad.h>
const int optoPin = 12;
const int relePin = 2;
const int buzzerPin = 9;
const int relePinRefill = 8;
int pin;
int value;
int coins = 0;
char menu = 0x01;
char set1 = 0x00, set2 = 0x00;
boolean t_butDown, t_butP, t_butUp;
bool optoState;
bool lastOptoState;
unsigned long startTime;
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {11, 10, A5, A4};
byte colPins[COLS] = {A0, A1, A2, A3};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

#define TIMEOUT 10000
#define BUZTIME 100

void changeMenu();
void dispMenu();
void control1();
void control2();


void setup() {
  lcd.begin(16, 2);
  pinMode(buzzerPin, OUTPUT);
  pinMode(optoPin, INPUT_PULLUP);
  pinMode(relePin, OUTPUT);
  pinMode(relePinRefill, OUTPUT);
  digitalWrite(relePin, HIGH);
  digitalWrite(relePinRefill, LOW);
  Serial.begin(9600);
  optoState = digitalRead(optoPin);
  digitalWrite(buzzerPin, HIGH);
  delay(500);
  digitalWrite(buzzerPin, LOW);
  lcd.setCursor(0, 0);
  lcd.print("LaBucciata  Slot");
  delay(5000);
  lcd.setCursor(0, 1);
  lcd.print("Configurazione..");
  delay(500);
  lcd.begin(16, 2);
  lcd.setCursor(5, 0);
  lcd.print("Ready");
  delay(400);
  digitalWrite(buzzerPin, HIGH);
  delay(200);
  digitalWrite(buzzerPin, LOW);
  delay(200);
  digitalWrite(buzzerPin, HIGH);
  delay(200);
  digitalWrite(buzzerPin, LOW);
  lcd.begin(16, 2);
  keypad.addEventListener(keypadEvent);

  t_butDown = 0x00;
  t_butP  = 0x00;
  t_butUp = 0x00;
}

void loop() {
  checkForCredits1();
  releaseTokens();
  tokensCountDown();
  char keypressed = keypad.getKey();
  if (keypressed != NO_KEY)
  {
    Serial.print(keypressed);
  }
  changeMenu();
  dispMenu();
}

void keypadEvent(KeypadEvent eKey) {
  switch (keypad.getState()) {
    case PRESSED:
      digitalWrite(buzzerPin, HIGH);
      delay(50);
      digitalWrite(buzzerPin, LOW);
      lcd.begin(16, 2);
      lcd.setCursor(5, 0);
      Serial.print(eKey);
      switch (eKey) {
        case '*': t_butUp = 0x01; break;
        case '#': t_butDown = 0x01; break;
        case 'A': t_butP = 0x01; break;
        case 'B': digitalWrite(relePinRefill, HIGH); break;
        case 'D': digitalWrite(buzzerPin, HIGH); break;
        case 'C': digitalWrite(relePinRefill, LOW); break;
      }
  }
}
void changeMenu() {
  if (t_butUp && t_butUp) {
    t_butUp = 0x00;
    lcd.clear();
    menu++;
    if (menu > 0x02) {
      menu = 0x01;
    }
  }
  if (t_butDown && t_butDown) {
    t_butDown = 0x00;
    lcd.clear();
    menu--;
    if (menu < 0x01) {
      menu = 0x02;
    }
  }

}

void dispMenu() {
  switch (menu) {
    case 0x01:
      control1();
      break;
    case 0x02:
      control2();
      break;
  }
}

void control1() {
  lcd.setCursor(0, 1);
  lcd.print("refill");
  if (t_butP && t_butP) {
    t_butP = 0x00;
    set1++;
    if (set1 > 3) {
      set1 = 0x01;
    }
    switch (set1) {
      case 0x01:
        lcd.clear();
        lcd.setCursor(0, 1);
        digitalWrite(relePinRefill, HIGH);
        lcd.print("refill ON");
        break;
      case 0x02:
        lcd.clear();
        lcd.setCursor(0, 1);
        digitalWrite(relePinRefill, LOW);
        lcd.print("refill OFF");
        break;
    }
  }
}

void control2() {
  lcd.setCursor(0, 1);
  lcd.print("erogazione gettoni");
  if (t_butP && t_butP) {
    t_butP = 0x00;
    set2++;
    if (set2 > 1) {
      set2 = 0x01;
    }
    switch (set2) {
      case 0x01:
        lcd.clear();
        lcd.setCursor(0, 1);
        coins = (5);
        lcd.print("erogazione");
        break;
    }
  }
}

void checkForCredits1() {
  while (Serial.available() > 0) {
    value = Serial.parseInt();
    if (Serial.read() == 'x') {
      if (value > 0) {
        coins ++;
        Serial.flush();
        Serial.begin(9600);
      }
    }
  }
}

void releaseTokens() {
  if (coins > 0) {
    if (digitalRead(relePin) == HIGH) {
      startTime = millis();
    }
    digitalWrite(relePin, LOW);
    optoState = digitalRead(optoPin);
  }
  else {
    digitalWrite(relePin, HIGH);
    lcd.setCursor(0, 0);
    lcd.print("LaBucciata Slot $ $ Buona Fortuna $ $");
    delay(400);
    for (int positionCounter = 0; positionCounter < 1; positionCounter++) {
      lcd.scrollDisplayLeft();
    }
  }
  if (digitalRead(relePin) == LOW) {
    if (millis() - startTime > TIMEOUT ) {
      digitalWrite(relePin, HIGH);
      digitalWrite(buzzerPin, HIGH);
      lcd.begin(16, 2);
      lcd.setCursor(5, 0);
      lcd.print("refill");
      digitalWrite(relePinRefill, HIGH);
      delay(3000);
      lcd.begin(16, 2);
      lcd.setCursor(0, 0);
      lcd.print("gettoni mancanti:");
      delay(1000);
      lcd.setCursor(13, 1);
      lcd.println(coins);
      coins = 0;
      digitalWrite(buzzerPin, LOW);
      lcd.begin(16, 2);
    }
  }
}

void tokensCountDown() {
  if (optoState != lastOptoState) {
    delay (100);
    if (lastOptoState == LOW && coins > 0) {
      coins = coins - 1 ;
      startTime = millis();
    }
  }
}

bene, non mi è chiaro però come vuoi far inputare la password, sempre tramite il keypad?
In linea generale posso dirti che nel caso 'B' invece di pilotare direttamente il pin potresti settare una variabile che metta il programma in attesa della password, perché se la vuoi inserire tramite il keypad ad ogni pressione di un tasto verrà richiamata sempre la keypadEvent, se sei in inserimento password devi fare qualcosa di differente rispetto a quello che fa ora, mi spiego meglio il case attuale è perfetto ma manca la gestione dei numeri e devi scegliere un pulsante che permetta di dire ok, ho finito di inserire la password e comportarti di conseguenza, oppure se vuoi battezzare una password di lunghezza fissa devi comunque memorizzare il numero di pressioni avvenute dopo l'attivazione "inserimento password" e quando tutti i tasti sono statti premuti verificarla e decidere se attivare l'erogatore.

si sempre con il keypad e confermando con uno dei tasti lettera a b c d , magari avendo anche la possibilita di cancellare se per errore inserisci un numero sbagliato . questo per quello che e la password , mentre sto trovando difficoltà nel attribuire a una variabile un numero ottenuto sul keypad con piu pressioni , esempio 35 . Ho visto un pò in giro e ho letto di array , e di questo non ci ho capito un granchè , mentre sto cercando l'approccio matematico dove mi dice di moltiplicare la variabile x 10 + il numero corrispondente premuto , ma anche con questo sto avendo problemi ... Qualche suggerimento ?? magari poi la gestione della password lo vedro alla fine , anche perche a rigor di logica se capisco come attribuire alla variabile la sequenza dei numeri scritti , dovrei capire anche come gestire la password. Grazie per gli aiuti :slight_smile: .P.s il caso B dove piloto il pin del rele-refill in realta verra cancellato , perche quello gia lo gestisco dal menu refill , risparmiando cosi 2 lettere la b e la c e anche la D che attualmente mi attiva solo il buzzer

Allora io ho provato a fare la variabile con piu di una cifra … non so se ho fatto bene perche ho solo verificato senza compilare , in quanto al momento non sono davanti ad arduino , comunque posto lo sketch . Poi faccio sapere .

#include <LiquidCrystal.h>
LiquidCrystal lcd ( 13, 3, 4, 5, 6, 7);
#include <Keypad.h>
const int optoPin = 12;
const int relePin = 2;
const int buzzerPin = 9;
const int relePinRefill = 8;
int pin;
int value;
int coins = 0;
char customKey;
char menu = 0x01;
char set1 = 0x00, set2 = 0x00;
boolean t_butDown, t_butP, t_butUp;
bool optoState;
bool lastOptoState;
unsigned long startTime;
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {11, 10, A5, A4};
byte colPins[COLS] = {A0, A1, A2, A3};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

#define TIMEOUT 10000
#define BUZTIME 100



void changeMenu();
void dispMenu();
void control1();
void control2();

char tastobattuto;         // variabile di memorizzazione del carattere battuto
byte semaforoval   = 0;    // semaforo di gestione della routine di ricezione valori da tastiera
int valore         = 0;    //  numero battuto sulla tastiera
int moltiplicatore = 0;    // utilizzato nel calcolo del codice battuto su tastiera
int indice         = 0;    // utilizzato nel calcolo del valore battuto su tastiera
int tabnum [5];            // tabella di memorizzazione dei dati battuti (max 4 cifre, 9999)
byte indtab        = 0;    // indice di scorimento della tabella tabnum
byte semloop       = 0;    // semaforo che indica che la routine di acquisizione dei dati da tastiera e' stata lanciata dl loop

void tastieraecountdown ()
{
  if (semloop == 1)                         // se arrivo dal loop
    semaforoval = 1;                         // inibisce il loop di ricezione, a meno che non sia stato premuto un tasto
  tastobattuto = keypad.getKey();          // acquisisce il valore del tasto battuto, gia' trascodificato tramite la tabella chiave
  for (indtab = 0; indtab < 5; indtab ++)  // pulisce i dati eventualmente gia' memorizzati
    tabnum [indtab] = 0;
  indtab  = 0;
  valore  = 0;
  if (tastobattuto)                        // se e' stato premuto un tasto
  {
    lcd.clear ();
    delay (200);                           // 200 millisecondi di delay (per evitare una doppia battuta)
    semaforoval = 0;
  }
  while (semaforoval == 0)                 // loop di ricezione dati - data receiving loop
    if (semloop == 1)                      // se si e' in fase di loop
      if (tastobattuto)                     // se  e' stato battuto un tasto
      {
        lcd.setCursor (indtab, 0);
        lcd.print (tastobattuto);
      }
  if ((tastobattuto >= '0') && (tastobattuto <= '9'))
  {
    tabnum [indtab] = tastobattuto - 48;// memorizza il valore battuto, trasformato in un numero da 0 a 9
    indtab++;
    if (indtab == 4)
      semaforoval = 1;                  // esce dal loop di ricezione dati
  }
  if (tastobattuto == 'C')              // se e' stata annullata la digitazione
  {
    for (indtab = 0; indtab < 5; indtab ++)  // pulisce i dati eventualmente gia' memorizzati
      tabnum [indtab] = 0;
    indtab  = 0;
    valore  = 0;
    lcd.clear ();
    semaforoval = 1;                    // esce dal loop di ricezione dati
  }
  if (tastobattuto == 'D')
  {
    semaforoval = 1;                    // esce dal loop di ricezione dati
  }
  if (semaforoval == 0)
  {
    tastobattuto = keypad.getKey();       // acquisisce il valore del successivo tasto battuto (gia' trascodificato
    //                                       tramite la tabella chiave)
    delay (200);                          // attende 2/10 di secondo per evitare una doppia battuta
  }
  valore = 0;
  moltiplicatore = 1;
  for  (indice = indtab; indice > 1; indice--)
    moltiplicatore = moltiplicatore * 10;
  for (indice = 0; indice <= indtab; indice++)
  {
    valore = valore + tabnum[indice] * moltiplicatore;
    moltiplicatore = moltiplicatore / 10;
  }
}

void setup() {
  lcd.begin(16, 2);
  pinMode(buzzerPin, OUTPUT);
  pinMode(optoPin, INPUT_PULLUP);
  pinMode(relePin, OUTPUT);
  pinMode(relePinRefill, OUTPUT);
  digitalWrite(relePin, HIGH);
  digitalWrite(relePinRefill, LOW);
  Serial.begin(9600);
  optoState = digitalRead(optoPin);
  digitalWrite(buzzerPin, HIGH);
  delay(500);
  digitalWrite(buzzerPin, LOW);
  lcd.setCursor(0, 0);
  lcd.print("LaBucciata  Slot");
  delay(5000);
  lcd.setCursor(0, 1);
  lcd.print("Configurazione..");
  delay(500);
  lcd.begin(16, 2);
  lcd.setCursor(5, 0);
  lcd.print("Ready");
  delay(400);
  digitalWrite(buzzerPin, HIGH);
  delay(200);
  digitalWrite(buzzerPin, LOW);
  delay(200);
  digitalWrite(buzzerPin, HIGH);
  delay(200);
  digitalWrite(buzzerPin, LOW);
  lcd.begin(16, 2);
  keypad.addEventListener(keypadEvent);

  t_butDown = 0x00;
  t_butP  = 0x00;
  t_butUp = 0x00;
}

void loop() {
  checkForCredits1();
  releaseTokens();
  tokensCountDown();
  char keypressed = keypad.getKey();
  if (keypressed != NO_KEY)
  {
    Serial.print(keypressed);
  }
  changeMenu();
  dispMenu();
}

void keypadEvent(KeypadEvent eKey) {
  switch (keypad.getState()) {
    case PRESSED:
      digitalWrite(buzzerPin, HIGH);
      delay(50);
      digitalWrite(buzzerPin, LOW);
      lcd.begin(16, 2);
      lcd.setCursor(5, 0);
      Serial.print(eKey);
      switch (eKey) {
        case '*': t_butUp = 0x01; break;
        case '#': t_butDown = 0x01; break;
        case 'A': t_butP = 0x01; break;
      }
  }
}
void changeMenu() {
  if (t_butUp && t_butUp) {
    t_butUp = 0x00;
    lcd.clear();
    menu++;
    if (menu > 0x02) {
      menu = 0x01;
    }
  }
  if (t_butDown && t_butDown) {
    t_butDown = 0x00;
    lcd.clear();
    menu--;
    if (menu < 0x01) {
      menu = 0x02;
    }
  }

}

void dispMenu() {
  switch (menu) {
    case 0x01:
      control1();
      break;
    case 0x02:
      control2();
      break;
  }
}

void control1() {
  lcd.setCursor(0, 1);
  lcd.print("refill");
  if (t_butP && t_butP) {
    t_butP = 0x00;
    set1++;
    if (set1 > 3) {
      set1 = 0x01;
    }
    switch (set1) {
      case 0x01:
        lcd.clear();
        lcd.setCursor(0, 1);
        digitalWrite(relePinRefill, HIGH);
        lcd.print("refill ON");
        break;
      case 0x02:
        lcd.clear();
        lcd.setCursor(0, 1);
        digitalWrite(relePinRefill, LOW);
        lcd.print("refill OFF");
        break;
    }
  }
}

void control2() {
  lcd.setCursor(0, 1);
  lcd.print("erogazione gettoni");
  if (t_butP && t_butP) {
    t_butP = 0x00;
    set2++;
    if (set2 > 1) {
      set2 = 0x01;
    }
    switch (set2) {
      case 0x01:
        semloop = 1;                // setta il semaforo di "loop attivo"
        valore = 0;
        while (valore == 0)
          tastieraecountdown ();    // acquisisce dati da tastiera
        lcd.setCursor (0, 0);
        coins = (valore);
        break;
    }
  }
}

void checkForCredits1() {
  while (Serial.available() > 0) {
    value = Serial.parseInt();
    if (Serial.read() == 'x') {
      if (value > 0) {
        coins ++;
        Serial.flush();
        Serial.begin(9600);
      }
    }
  }
}

void releaseTokens() {
  if (coins > 0) {
    if (digitalRead(relePin) == HIGH) {
      startTime = millis();
    }
    digitalWrite(relePin, LOW);
    optoState = digitalRead(optoPin);
  }
  else {
    digitalWrite(relePin, HIGH);
    lcd.setCursor(0, 0);
    lcd.print("LaBucciata Slot $ $ Buona Fortuna $ $");
    delay(400);
    for (int positionCounter = 0; positionCounter < 1; positionCounter++) {
      lcd.scrollDisplayLeft();
    }
  }
  if (digitalRead(relePin) == LOW) {
    if (millis() - startTime > TIMEOUT ) {
      digitalWrite(relePin, HIGH);
      digitalWrite(buzzerPin, HIGH);
      lcd.begin(16, 2);
      lcd.setCursor(5, 0);
      lcd.print("refill");
      digitalWrite(relePinRefill, HIGH);
      delay(3000);
      lcd.begin(16, 2);
      lcd.setCursor(0, 0);
      lcd.print("gettoni mancanti:");
      delay(1000);
      lcd.setCursor(13, 1);
      lcd.println(coins);
      coins = 0;
      digitalWrite(buzzerPin, LOW);
      lcd.begin(16, 2);
    }
  }
}

void tokensCountDown() {
  if (optoState != lastOptoState) {
    delay (100);
    if (lastOptoState == LOW && coins > 0) {
      coins = coins - 1 ;
      startTime = millis();
    }
  }
}

buongiorno allora nulla da fare , anche se penso di essere sulla strada giusta , ho provato a caricare uno sketch trovato in rete che funge da sorta di gioco indovina numeri , e funziona , o meglio fa la cosa che io vorrei fare sul mio , adesso si tratta di riuscire a inserirlo nel mio , cosi come ho provato ieri , ma con lo sketch che ho postato ieri mi fa inserire i numeri pero poi al momento del rilascio gettoni si impalla , spiego meglio se chiedo 9 gettoni parte l’erogazione ma non tira via 9 gettoni , se chiedo 27 me ne da 2 se chiedo 15 me ne da 5 , un po a random . Qualche aiuto ?

Il solito consiglio già fornito in precedenza, fai un programma minimale che fa solo quello che ti da problemi, testalo a fondo quando funziona integralo nel tuo.
Se avessi seguito questa strada e avessi infarcito il tuo programma con quanti più possibili messaggi di debug sulla seriale ti saresti accorto quasi certamente del problema.
Detto questo ti consiglio di seguire questa strada, memorizza nell'array di char il valore del char senza la sottrazione che ti serve per memorizzare il valore numerico corrispondente, quando hai fimito di leggere usa l'apposita funzione che converte un array di char in un valore intero atoi e hai risolto.
Almeno per la parte dei gettoni.
Visto che sembra tu aver compreso la logica di come si popola un array di char (o se hai copiato la parte e non l'hai compresa devi dedicarci un pochino di studio) dovrai fare la medesima cosa con la password, solo che alla fine invece di convertirla in un valore intero (anche se in questo caso specifico potresti essendo il tastierino prettamente numerico e non alfanumerico) dovrai usare la funzione di confronto delle stringhe tra quella inserita e quella corretta strncmp.
Prova ad usare queste due funzioni e se hai problemi chiedi pure

e quello che sto cercando di fare e studiarmi bene il principio , anche se ripeto al momento sto avendo il problema che l'erogazione non è giusta . Mi voglio spiegare meglio.

La variabile coins e quella che se viene "popolata" comincia a fare l'erogazione e il countdown , giusto ?
Dunque io da seriale (Mame) per popolare la variabile coins inserisco dei valori ( anche a monitor seriale ) chiamati 1x 1x 1x 1x 1x dove per ogni 1x corrisponde un gettone ( devo scrivere per forza cosi a monitor seriale se no non eroga gettoni).
Adesso la parte del loop della tastiera , sembra funzionare , cioe io vado a scrivere 55 e lui sembra memorizzarlo , mentre quello che non succede è ugualiare la variabile coins con quella di valore e trovarmi quindi nel caso di 55 premuto su tastiera :

valore = 55 .... coins = valore ...... erogazione del numero dei coins-valore

Al momento sembra che eroga a random .... chiedendo 55 gettoni una volta eroga 13 una volta 15 una volta 10 ...

Allora facciamo distinzione tra lettura della password e dei gettoni.
La password devi inserirla con la logica che hai usato, ovvero il primo carattere nell'array posizione zero, il secondo nell'array posizione uno ecc.
Per la parte dei gettoni invece devi leggere la seriale finché ci sono caratteri validi, se non settato differentemente il monitor seriale quando premi invio manda quello che hai scritto + CRLF (invio e ritorno carrello).
Quindi devi verificare se ci sono dati nella seriale

while(Serial.available()>0)

leggerli e posizionarli in un array con la medesima logica, quando esci dal while devi confrontare la stringa in arrivo (1xCRLF se non ignori il CR e LF nel ciclo) con quello che ti aspetti (1x) con la funzione che ti fo indicato prima.
Se è uguale, ovvero quello che ti arriva è un gettone semplicemente sommi a coins uno.
Se invece gli 1x arrivano tutti assieme allora dentro il ciclo indicato sopra (serial.available) devi inserire i caratteri nel solito array di char, controllare se il carattere in arrivo è lo spazio, nel qual caso non lo inserisci nell'array ma controlly se il contenuto dell'array è 1x (con la solita funzione indicata prima), se si sommi e resetti l'indice dell'array in modo da scrivere sempre nelle prime due posizioni (zero e uno). In questo modo:
Ti arriva tutto lo stringone di gettoni

  • Leggi i primi due caratteri e li metti nell'array
  • Leggi il terzo, è lo spazio
  • Controlli il tuo array, è 1x? si sommo i gettoni, no nonfaccio la somma ma riporto l'indice dell'array a zero comunque
  • Leggi i caratteri successivi e ripeti i punti 2 e 3 fincheé non ci sono più caratteri

Quando esci dal ciclo il numero di gettoni dovrebbe essere corretto (probabilmente non lo sarà, te ne mancherà uno ma questo lo lascio a te da scoprire se è così o meno)

allora si confermo sono riuscito a gestire la variabile valore con quella di coins e adesso sia da seriale sia da mame sia da tastierino riesco a erogare il numero giusto dei gettoni . Adesso , in teoria mi manca solo aggiungere la password :D. Grazie mille perchè tramite il tuo approccio sto capendo cosa sto facendo e cosa fare sopratutto.

Lieto di essere d'aiuto.
Solo una doanda, ma la password esattamente a cosa ti serve farla introdurre? Se non ho capito male è una rivistazione dei vecchio coinop, quindi ci sarà una gettoniera o non ho capito il progetto?