Problema con menú al que se accede desde un interruptor

Buenas noches! tengo el siguiente problema... tengo un interruptor de esos de palanca que sube y baja, cuando la palanca está abajo, está en el LCD la pantalla principal en la que todo funciona perfectamente, y cuando subo la palanca, entra al menú... en el menú hay otro submenú y cuando pulso la tecla 1 o pulso la tecla 2 en un teclado de esos de 4x4 debería de acceder a la opción marcada del submenú, pero se queda en la pantalla principal del menú, sin acceder a los submenús. "que creo que sí que accede al submenú, pero como es llevado desde el loop si la palanca está arriba, y sigue arriba, pues vuelve a la pantalla principal del menú" pensé que quizás poniendo un pulsador pull up en vez de un interruptor de palanca lo arreglaría, pero me pasa lo mismo... mientras está pulsado el pulsador, accedo al menú,una vez en el menú pulso la tecla 1 o 2 para navegar por el submenú y creo que por una milésima de segundo se visualiza el submenú, pero automáticamente vuelve a cargar la pantalla del menú llamada desde el loop y cuando suelto el pulsador, vuelve a la pantalla principal...

El código es este:

#include <Time.h>
#include <TimeAlarms.h>
#include <DS1307RTC.h>
#include <LiquidCrystal.h>
#include <Keypad.h>

int botonestado = 0;
int viejoestado = 0;     

// Creamos un objeto lcd con los pines a los que está conectado
LiquidCrystal lcd(7, 8, 9, 10, 11 , 12);

// Creamos un array bidimensional para el control del teclado 4x4

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};

byte rowPins[ROWS] = {26, 24, 22, 28}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {27, 25, 23, 29}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

// Definimos el pin del interruptor del menú
const int botonmenu = 3;     // Pin del interruptor del menú

void setup() {
  
  // Preparar la interfaz serial
  Serial.begin(9600);

  // pin del pulsador de entrada
  pinMode(botonmenu, INPUT_PULLUP);   // Pin del pulsador negro  encendido

void loop(){

 // Creamos el objeto key, para el keypad
 
     char key = keypad.getKey();

 //  String caracterpresionado(key);

 if (digitalRead(botonmenu) == LOW) {
    menu(); //AQUÍ ES DONDE AL TENER LA PALANCA HACIA ARRIBA ESTÁ EN LOW Y ENTRA AL MENÚ
  }else{ resto del código de la pantalla principal.... "la cual funciona muy bien" }

}

void menu(){

// Texto a mostrar en pantalla LCD
  lcd.setCursor(0,0);
  lcd.write("  Elija una opcion  ");
  lcd.setCursor(0,2);
  lcd.write("1 - Horarios riegos ");
  lcd.setCursor(0,3);
  lcd.write("2 - inicio y fin    ");
  lcd.setCursor(0,1);
  lcd.write("                    ");

// Método de selección de opciones
   char key1 = keypad.getKey();
  
      if (key1 == '1') {
       Serial.print("Menú de horarios y riegos");
       modificaralarmas();
       }
      if (key1 == '2') {
       Serial.print("Definir variables de inicio y fin de riegos");
       definirvariablesinicioyfin();
       }
}


// Función principal para modificar las alarmas de riego
void modificaralarmas(){

  Serial.println(" Dentro de modificaralarmas() ");
  lcd.setCursor(0,0);
  lcd.write("1 - ver horarios    ");
  lcd.setCursor(0,1);
  lcd.write("2 - modificar horas ");
  lcd.setCursor(0,2);
  lcd.write("2 - borrar horas    ");
  lcd.setCursor(0,3);
  lcd.write("4 - agregar riegos  ");

  char key2 = keypad.getKey();

        if (key2 == '1') {
       Serial.print(" Ver horarios");
       modificaralarmas();
       }
      if (key2 == '2') {
       Serial.print(" modificar horas");
       definirvariablesinicioyfin();
       }
        if (key2 == '3') {
       Serial.print(" borrar horas");
       modificaralarmas();
       }
      if (key2 == '4') {
       Serial.print(" agregar riegos");
       definirvariablesinicioyfin();
       }
      if (key2 == '*') {
       Serial.print(" Volver al menú principal");
       menu();
       }
       
}

void definirvariablesinicioyfin(){

"aun por definir la función..."  
}

Alguna idea de cómo hacer para que una vez que desde el if que hay en el loop, me lleve a entrar en la función menu() que está fuera del loop, deje de dar vueltas y entras una y otra vez y no dejarme navegar por los submenús??????

GRACIAS!!!!

sigo sin conseguir nada... en lugar de probar con un if() he probado con un switch, y me sigue pasando lo mismo... entro al menú, selecciono el 1 o el 2 y por una micra de segundo veo en el LCD que carga el submenú, pero automáticamente en otra micra de segundo vuelvo a la pantalla principal del menú... sé que es porque viene desde el loop y la palanca del interruptor sigue hacia arriba "en LOW" pero no sé cómo solucionarlo....

¿¿¿ Alguna ayuda ???

GRACIAS!

Hola ArduMyth, yo lo que tengo es un interruptor de esos que tienen una palanca que la puedes subir o bajar... y cuando la palanca está abajo, se muestra la pantalla principal con los datos de los sensores, pero cuando subo la palanca quiero que entre en un menú, el cual tiene submenús... y como explicaba antes, el if(palancaestaarriba = HIGH) lo tengo en el loop, por lo que cuando subo la palanca me va al menú, pero al seleccionar una opción para entrar en los submenús, me vuelve al menú principal en todo momento....

Eso que me dices de debounce usando millis() y guardar el estado en un booleano no sé cómo hacerlo... podrías echarme una mano??

dejo el código completo "aunque el problema lo tengo en el loop que me lleva al menu() y de ahí a los submenus"

#include <Time.h>
#include <TimeAlarms.h>
#include <DS1307RTC.h>
#include <LiquidCrystal.h>
#include <Keypad.h>

int botonestado = 0;
int viejoestado = 0;     

LiquidCrystal lcd(7, 8, 9, 10, 11 , 12);

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};

byte rowPins[ROWS] = {26, 24, 22, 28}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {27, 25, 23, 29}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


const int sensorPin = A0;

const int relePin = 4; 

const int buttonPin = 5;
const int buttonPin2 = 6;

const int botonmenu = 3;

int comienzoriego = 600;

int finriego = 500;

void setup() {

  Serial.begin(9600);

  pinMode(buttonPin, INPUT_PULLUP);   
  pinMode(buttonPin2, INPUT_PULLUP);  
  pinMode(botonmenu, INPUT_PULLUP);  


  pinMode(sensorPin, INPUT);

  pinMode(relePin, OUTPUT);  
  

  lcd.begin(20, 4);

  digitalWrite(relePin, HIGH);

  lcd.setCursor(9,1);
  lcd.write("NO");

  setSyncProvider(RTC.get);
  if (timeStatus() != timeSet)
    Serial.println("Fallo de RTC");
  else
    Serial.println("Sincronizado con RTC");
    

  // Crear las alarmas y configurar las funciones correspondientes a cada una
  Alarm.alarmRepeat(8, 30, 0, aperturaelectrovalvula);
  Alarm.alarmRepeat(20, 30, 0, aperturaelectrovalvula);

  ///// Fin del sistema de riego /////
}

void loop(){

 // Creamos el objeto key, para el keypad
 
     char key = keypad.getKey();
   //  String caracterpresionado(key);

 if (digitalRead(botonmenu) == LOW) {
    menu();
  }else{

  // Si el valor del pin del pulsador es HIGH es que esta pulsado
  if (digitalRead(buttonPin) == LOW) {     
    // Se enciende el riego:   
    Serial.println("Palanca Arriba encender electroválvula de manera manual");


   // llamamos a la función humedadporcentaje, pasándole el valorHumedad en porcentaje, para que valore si está por debajo del 10, que mueva los caracteres en el LCD para una correcta visualización    
  int valorHumedad = map(analogRead(sensorPin), 0, 1023, 100, 0);

  // Cogemos la información del sensor de humedad por segunda vez.
  int humedad2 = analogRead(sensorPin);

  // Mostrar el reloj en el monitor serial
   Serial.print("Hora: ");
  digitalClockDisplay();
  
  // Imprimimos por pantalla los valores de humedad y su porcentaje tanto en LCD como en Serial Monitor.
  Serial.print("Humedad en porcentaje: ");
  Serial.print(valorHumedad);
  Serial.println("%");

  Serial.print("Humedad en valor: ");
  Serial.println(humedad2);
  
  lcd.setCursor(9,2);
  lcd.print(humedad2);

  // Ponemos el relé en LOW por lo que se abrirá la electroválvula.
  digitalWrite(relePin, LOW);

  // Imprimimos en el LCD la palabra SI donde pone regando: y antes ponía NO.
  lcd.setCursor(9,1);
  lcd.write("SI");
  lcd.setCursor(0,3); 
  lcd.write("Encendido manual  ");

  Serial.println("Regando: SI"); 
  Serial.println("");

  LCD1602ClockDisplay();

  humedadValor(humedad2);

  humedadPorcentaje(valorHumedad);

    
  }
  else if (digitalRead(buttonPin2) == LOW){
    // Se apaga el riego:
    Serial.println("Palanca Abajo apagar electroválvula de manera manual");
 
  int valorHumedad = map(analogRead(sensorPin), 0, 1023, 100, 0);

  int humedad2 = analogRead(sensorPin);

   Serial.print("Hora: ");
  digitalClockDisplay();
  
  Serial.print("Humedad en porcentaje: ");
  Serial.print(valorHumedad);
  Serial.println("%");

  Serial.print("Humedad en valor: ");
  Serial.println(humedad2);
  
  lcd.setCursor(9,2);
  lcd.print(humedad2);

  digitalWrite(relePin, HIGH);

  lcd.setCursor(9,1);
  lcd.write("NO");
  lcd.setCursor(0,3); 
  lcd.write("Parada manual     ");


  Serial.println("Regando: NO"); 
  Serial.println("");

  LCD1602ClockDisplay();

  humedadValor(humedad2);

  
  humedadPorcentaje(valorHumedad);


  }
  else {
    
     Serial.println("");
     Serial.println("Palanca en medio, sistema de riego automático");

  Serial.print("Hora: ");
  digitalClockDisplay();

  LCD1602ClockDisplay();

  int valorHumedad = map(analogRead(sensorPin), 0, 1023, 100, 0);
  int humedad = analogRead(sensorPin);


  Serial.print("Humedad en porcentaje: ");
  Serial.print(valorHumedad);
  Serial.println("%");

  Serial.print("Humedad en valor: ");
  Serial.println(humedad);

  Serial.println("Regando: NO"); 
  Serial.println("");
  
  humedadPorcentaje(valorHumedad);

  humedadValor(humedad);

   Alarm.delay(1000);
  
  lcd.setCursor(0,0);
  lcd.write("Hora: ");
  lcd.setCursor(0,1);
  lcd.write("Regando: ");
  lcd.setCursor(0,2);
  lcd.write("Humedad: ");
  lcd.setCursor(9,2);
  lcd.print(humedad);

  lcd.setCursor(13,2);
  lcd.write(' ');
  lcd.setCursor(14,2);
  lcd.write(' ');

  digitalWrite(relePin, HIGH);
  lcd.setCursor(9,1);
  lcd.write("NO");
  lcd.setCursor(0,3); 
  lcd.write("Sistema Automatico");
  }
}
}


void menu(){

  lcd.setCursor(0,0);
  lcd.write("  Elija una opcion  ");
  lcd.setCursor(0,2);
  lcd.write("1 - Horarios riegos ");
  lcd.setCursor(0,3);
  lcd.write("2 - inicio y fin    ");
  lcd.setCursor(0,1);
  lcd.write("                    ");

   char key1 = keypad.getKey();
   
   int pasadoaint = (key1-48);

switch (pasadoaint) {
    case 1:
          Serial.print("Menú de horarios y riegos");
          modificaralarmas();
          break;
    case 2:
          Serial.print("Definir variables de inicio y fin de riegos");
          definirvariablesinicioyfin();
          break;
 }
}


void modificaralarmas(){

  Serial.println(" Dentro de modificaralarmas() ");
  lcd.setCursor(0,0);
  lcd.write("1 - ver horarios    ");
  lcd.setCursor(0,1);
  lcd.write("2 - modificar horas ");
  lcd.setCursor(0,2);
  lcd.write("2 - borrar horas    ");
  lcd.setCursor(0,3);
  lcd.write("4 - agregar riegos  ");

  char key2 = keypad.getKey();

        if (key2 == '1') {
       Serial.print(" Ver horarios");
       modificaralarmas();
       }
      if (key2 == '2') {
       Serial.print(" modificar horas");
       definirvariablesinicioyfin();
       }
        if (key2 == '3') {
       Serial.print(" borrar horas");
       modificaralarmas();
       }
      if (key2 == '4') {
       Serial.print(" agregar riegos");
       definirvariablesinicioyfin();
       }
      if (key2 == '*') {
       Serial.print(" Volver al menú principal");
       menu();
       }
       
}

void definirvariablesinicioyfin(){
  
}

void aperturaelectrovalvula(){

 if (int humedad = analogRead(sensorPin)  > comienzoriego){ // está en 600
  while(int humedad = analogRead(sensorPin)  > finriego && digitalRead(buttonPin2) == HIGH && digitalRead(buttonPin) == HIGH) // fin de riego está en 500
   { 
 
  int valorHumedad = map(analogRead(sensorPin), 0, 1023, 100, 0);

  int humedad2 = analogRead(sensorPin);

   Serial.print("Hora: ");
  digitalClockDisplay();
  
  Serial.print("Humedad en porcentaje: ");
  Serial.print(valorHumedad);
  Serial.println("%");

  Serial.print("Humedad en valor: ");
  Serial.println(humedad2);
  
  lcd.setCursor(9,2);
  lcd.print(humedad2);


  digitalWrite(relePin, LOW);
.
  lcd.setCursor(9,1);
  lcd.write("SI");

  Serial.println("Regando: SI"); 
  Serial.println("");

  LCD1602ClockDisplay();

  humedadValor(humedad2);

  humedadPorcentaje(valorHumedad);

  //delay(1000);
   }
}
}


void digitalClockDisplay() {
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println();
}

void LCD1602ClockDisplay(){

lcd.setCursor(6,0);
printDigitsLCDHora(hour() );

lcd.setCursor(14,0);
lcd.write("      ");

lcd.setCursor(8,0);
lcd.print(':');

lcd.setCursor(9,0);
printDigitsLCD(minute());;

lcd.setCursor(11,0);
lcd.print(':');

lcd.setCursor(12,0);
printDigitsLCD(second());
}

Muchas gracias

Exacto ArduMyth quizás no sea la mejor opción, también tengo un pulsador y lo he probado, pero me pasa lo mismo... cuando está pulsado entro al menú y si pulso por ejemplo el 1 para acceder al submenú Nº1 no accede porque la tecla sigue pulsada, si dejo de pulsar la tecla vuelvo a la pantalla principal saliendo del menú.

Por lo que puedo hacerlo con pulsador o con interruptor de palanca, eso me da igual, el caso es que no sé cómo hacerlo, porque siempre el loop me vuelve a cargar el menú principal y no sé cómo poder navegar por el submenú...

He cambiado un poco el código para tenerlo más estructurado pero sigo sin encontrar la manera de solucionarlo:

void loop(){
 
    if (digitalRead(botonmenu) == HIGH) {
    pantallaprincipal();}
    if(digitalRead(botonmenu) == LOW){ //en cuanto muevo la palanca para LOW entra al menú
    menu();}
}

void menu(){
  
// Texto a mostrar en pantalla LCD
  lcd.setCursor(0,0);
  lcd.write("  Elija una opcion  ");
  lcd.setCursor(0,2);
  lcd.write("1 - Horario riegos  "); //si pulso 1 para entrar a la función modificaralarmas(); se queda en la pantalla del menú principal porque el pulsador sigue en LOW
  lcd.setCursor(0,3);
  lcd.write("2 - inicio y fin    ");
  lcd.setCursor(0,1);
  lcd.write("                    ");

// Método de selección de opciones
   char key1 = keypad.getKey();
   
   int pasadoaint = (key1-48);

switch (pasadoaint) {
    case 1:
          Serial.print("Menú de horarios y riegos");
          modificaralarmas(); //si pulso 1 para entrar a la función modificaralarmas(); se queda en la pantalla del menú principal porque el pulsador sigue en LOW
          break;
    case 2:
          Serial.print("Definir variables de inicio y fin de riegos");
          definirvariablesinicioyfin();
          break;
 }
}

// Función principal para modificar las alarmas de riego
void modificaralarmas(){

  Serial.println(" Dentro de modificaralarmas() ");
  lcd.setCursor(0,0);
  lcd.write("1 - ver horarios    ");
  lcd.setCursor(0,1);
  lcd.write("2 - modificar horas ");
  lcd.setCursor(0,2);
  lcd.write("2 - borrar horas    ");
  lcd.setCursor(0,3);
  lcd.write("4 - agregar riegos  ");

  char key2 = keypad.getKey();

        if (key2 == '1') {
       Serial.print(" Ver horarios");
       modificaralarmas();
       }
      if (key2 == '2') {
       Serial.print(" modificar horas");
       definirvariablesinicioyfin();
       }
        if (key2 == '3') {
       Serial.print(" borrar horas");
       modificaralarmas();
       }
      if (key2 == '4') {
       Serial.print(" agregar riegos");
       definirvariablesinicioyfin();
       }
      if (key2 == '*') {
       Serial.print(" Volver al menú principal");
       menu();
       }
       
}

void pantallaprincipal(){

Pantalla principal con datos de los sensores... funciona correctamente

}

¿¿¿¿ Alguna ayuda de cómo hacerlo de la manera que me comenta ArduMyth?

if(palancaestaarriba && estaEnMenu){
  estaEnMenu = false;
}

GRACIAS!!

Y porque no usas las librerías de menú que existen?

Hay muchas librerías buscar Arduino Menu. Algunas son fáciles de usar y otras no tanto. Todas en inglés asi que tomalo con calma.
Un ejemplo es MenuBackEnd y hasta hay un tutorial

El tutorial esta hecho para varios pulsadores pero debe estar contemplado el uso para un teclado como el tuyo.
Revisa !!

Buenas!!! Se que existen librerías de menús, ayer estuve probando con varias y mirando los ejemplos que aparecen y no me aclaraba... Ayer estuve unas cinco horas intentando solucionar el tema del menú sin conseguirlo... Y no se pero creo que con una variable que me marque si esta dentro del menú o no me valdría como me decía ArdyMyth pero tampoco consigo hacerlo.... Creo que estoy cerca y pero no doy con ello y me estoy viendo loco

Gracias por la respuesta ArduMyth, pero por más que intento modificar tu código para adaptarlo al mio no lo consigo... o no entra al submenú, o al menú, o si entra luego no me capta el digito que aprieto en el teclado....estoy desesperado ya con el menú... creo que es algo que se escapa a mis conocimientos de carpintero, por lo que voy a ir a una tienda de electrónica de mi ciudad a ver si por un módico precio alguien puede hacerlo...

Bueno si alguien por aquí puede arreglarme el código por un precio módico también podemos hacerlo... mi código es el siguiente:

#include <Time.h>
#include <TimeAlarms.h>
#include <DS1307RTC.h>
#include <LiquidCrystal.h>
#include <Keypad.h>

// Creamos un objeto lcd con los pines a los que está conectado
LiquidCrystal lcd(7, 8, 9, 10, 11 , 12);

// Creamos un array bidimensional para el control del teclado 4x4

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};

byte rowPins[ROWS] = {26, 24, 22, 28}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {27, 25, 23, 29}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

// Definimos el pin del interruptor del menú
const int botonmenu = 3;     // Pin del interruptor del menú

void setup() {
  
  // Preparar la interfaz serial
  Serial.begin(9600);

  // pin del pulsador de entrada
  pinMode(botonmenu, INPUT_PULLUP);   // Pin del pulsador negro  encendido


  //cargamos un objeto lcd e iniciamos la pantalla con 20 caracteres por línea y 4 líneas
  lcd.begin(20, 4);


  // Crear las alarmas y configurar las funciones correspondientes a cada una
  Alarm.alarmRepeat(8, 30, 0, aperturaelectrovalvula);
  Alarm.alarmRepeat(20, 30, 0, aperturaelectrovalvula);
}

void loop(){
    if (digitalRead(botonmenu) == HIGH) {
      menu();    
    }
    else{
     pantallaprincipal();
    }
}

void menu(){

// Texto a mostrar en pantalla LCD
  lcd.setCursor(0,0);
  lcd.write("  Elija una opcion  ");
  lcd.setCursor(0,2);
  lcd.write("1 - Horario riegos  ");
  lcd.setCursor(0,3);
  lcd.write("2 - inicio y fin    ");
  lcd.setCursor(0,1);
  lcd.write("                    ");

  char key1 = keypad.getKey();

  int pasadoaint = (key1-48);

switch (pasadoaint) {
    case 1:
          Serial.print("Menú de horarios y riegos");
          modificaralarmas();
          break;
    case 2:
          Serial.print("Definir variables de inicio y fin de riegos");
          definirvariablesinicioyfin();
          break;
  }
 }

// Submenú 2
void modificaralarmas(){
  
  Serial.println(" Dentro de modificaralarmas() ");
  lcd.setCursor(0,0);
  lcd.write("1 - ver horarios    ");
  lcd.setCursor(0,1);
  lcd.write("2 - modificar horas ");
  lcd.setCursor(0,2);
  lcd.write("2 - borrar horas    ");
  lcd.setCursor(0,3);
  lcd.write("4 - agregar riegos  ");

  char key2 = keypad.getKey();

        if (key2 == '1') {
       Serial.print(" Ver horarios");
       modificaralarmas();
       }
      if (key2 == '2') {
       Serial.print(" modificar horas");
       definirvariablesinicioyfin();
       }
        if (key2 == '3') {
       Serial.print(" borrar horas");
       modificaralarmas();
       }
      if (key2 == '4') {
       Serial.print(" agregar riegos");
       definirvariablesinicioyfin();
       }
      if (key2 == '*') {
       Serial.print(" Volver al menú principal");
       menu();
       }
       
}

// Submenú 1

void definirvariablesinicioyfin(){
  
}

void pantallaprincipal(){

 // código de pantalla principal la cual funciona perfectamente

}

Gracias!!!