Menú con submenus para LCD en Arduino + Encoder

Y con este van cuatro menús ya, mejorando de nuevo el código del anterior y esta vez haciendo uso de un encoder rotatorio para la interacción con el Arduino.

El código ha sido optimizado y simplificado, lo cual ha permitido una gran velocidad a la hora de desplazarse por las diferentes opciones del menú.

Recordar que el código es apto para funcionar con pantallas de diferentes lineas y columnas, solo hay que indicarlo en el código.
Al igual que los botones a usar, solo hay que cambiar el bloque de lectura y el enumerador Button.

40 comentarios en “Menú con submenus para LCD en Arduino + Encoder”

  1. Saludos, he visto tus versiones anteriores. He resucitado mi LCD de un cajon, que antes no sabia ni usarlas… Quería preguntarte que librerias usas para este ejemplo de menus. Gracias.

    1. Hola Sergio,

      No uso ninguna librería salvo la del LCD por I2C, el menú está programado en el propio sketch.

      Si es esa la librería que necesitabas la encontrarás con el buscador con «LCD I2C».

      Saludos.

  2. Hola buenos días, estoy teniendo problemas con el encoder, solamente me puedo desplazar en una dirección y en la otra no, ya probé de cambiar los pines y probar distintos encoders, existe la posibilidad de que halla un error en el código o yo estaré cometiendo algún error, desde ya muchas gracias

    1. Hola Franco, hay varios tipos de encoder y formas de conectarlo por lo que primero tienes que asegurarte que tu encoder funciona ya sea con tu propio código o con el mio añadiéndole además impresiones por SERIAL para depurar la función que recoge las lecturas. Cuando soluciones eso te funcionará el menú.
      Saludos

  3. Hola GILTESA,
    magnífica creación,
    Intenté cargar el código pero me envía el siguiente error:
    call of overloaded ‘createChar(const byte&, const byte [8])’ is ambiguous

    hasta pronto!

    1. Hola Cédric,

      Lo acabo de probar y me compila correctamente, asegúrate de usar la misma librería para controlar el LCD (esta publicada en el blog) o de adaptar el código a la tuya.

      Saludos.

    1. Hola Oskar,

      Es el carácter utilizado para acceder a los enumeradores o enum. Un enumerador es un conjunto de constantes enteras con un nombre definido, sus valores comienzan por el 0 de modo que para el enum Button las constantes Unknown, Ok, Left, Right se corresponden con los valores 0, 1, 2 y 3 respectivamente.

      Los valores se pueden personalizar pero en realidad nos da igual porque lo único que se hace es comparar una variable del tipo Button a la que se le asigno una de las constantes Button por las distintas constantes de Button…

      Es decir, la función readButtons() devuelve el tipo de dato Button y después en el main se compara la variable btnPressed (donde se volcó el valor de readButtons() ) con un if else:

      Si el valor que tiene btnPressed se corresponde con alguna de sus constantes posibles, entonces se realiza la acción deseada… quizás es un poco lioso al principio pero es la forma de tener el código ordenado y fácil de leer.

      Saludos.

  4. Lo estoy por probar … se te agradece compartir el Menu … estoy realizando un proyecto para la tesis de la Facu … una extrusora de filamento para impresion 3D … Voy a modificar el codigo para que se adapte a mi necesidades, despues cuando termine te dejo otro comentario para compartirtelo.

  5. Hola hno .. te hago una consulta .. como hago para ver el Submenu de forma vertical?? … y en caso de querer agregarle un submenu2 dentro del Submenu … como seria? … espero me puedas ayudar

    1. Hola Denis, el endfor1 y el endfor2 calculan cuántas filas de la pantalla es necesario repintar. Ese código fue una ampliación de otro usuario.
      El forceprint es un flag que permite ejecutar el bloque de código que repinta la pantalla cuando es necesario.
      Saludos.

    1. Lo acabo de probar y me funciona bien, ¿cuál es el error exacto que te da? Yo uso el IDE 1.8.5 (es bastante reciente aunque no el ultimo porque ya no programo mucho Arduino) intenta tener esa versión o superior.

  6. Hola, soy nuevo en el mundo Arduino. Estoy buscando consejos para un proyecto de riego automático.
    En este proyecto, quería poder programar un cronograma para encender el riego y regar «manualmente». Todo esto a través de menús y submenús.
    Me puedes ayudar

  7. Alberto me da error de compilacion para el mega 2560, probe cargarlo para el uno y me da el mismo error, voy a intentar renombrar los pines del i2c con los que a mi me funcionan a ver que sucede. el error que detallan mas arriba con el Positivo yo tambien lo tenia y lo solucione con tu libreria que es mucho mas completa que la que tenia yo he incluye la libreria LCD.h

    1. Hola Diego, debería de compilar y grabarse en la placa, independientemente de si tienes el circuito montado o no. Revisa que tengas todas las librerías necesarias y de que al copiar el código (en caso de que lo hayas copiado a mano) no incluya algún carácter extraño que este estropeandolo.

      Saludos

  8. Hola Alberto muchas gracias por este ejemplo funciona una maravilla, te cuento que ahorita lo estoy implementando para un diseño de ventilador mecánico grado medico pero tengo la siguiente pregunta:

    1* Como hacer para cuadrar resoluciones, en mi caso lo requiero de 0.1 tanto el incremento y decremento en datos numéricos por ejemplo que al cuadrar el valor de tiempo que es la que tengo que controlar, se muestre como en la variable de temperatura que la se observa en el display con dos décimas adicionales.

    2* En el item *value del void openSubMenu, en especifico lo puedo modificar para cada parámetro que requiera controlar en la apertura de submenu o por cada variable que cree lo tengo que manejar independiente? y si se pueden ajustar rangos numéricos? o afectaría toda la configuración del submenu?

    3* Del Item ejemplo

    case 0: openSubMenu( idxMenu, Screen::Flag, &memory.d.time_show, 0, 1 ); break;

    despues de &memory.d.time_show, que significa el parametro 0, 1 , especificamente el numero 0

    Muchas gracias!!!

  9. Muchísimas gracias !!! De hecho confirmo funciona a la perfección el código inclusive dio para mas ideas de configuración del Encoder y mas parámetros para manipulación y acceso de submenus

    Gratos Saludos !

  10. Hola Estimado Alberto, quería Felicitarte por tus Excelentes Códigos y por Tu Gran Generosidad en Compartilo con nosotros.

    Al Final de este tu Código figura como obtener la Temperatura del Micro.

    La Pregunta es la siguiente…

    Hay forma de obtener el Número de Serie de la Placa de Arduino en Tiempo de Ejecución (como se hace desde el menú Herramientas –> Obtén Información de la Placa en el IDE del Arduino) y si tenes idea de si se lo puede ocupar para personalizar el código según dicho Número de Serie?

    A esta parte de tu código me refiero.
    **************************************************************************************************
    /**
    * DEVUELVE LA TEMPERATURA DEL SENSOR INTERNO DEL MICROCONTROLADOR (NO TODOS LOS MODELOS DE ARDUINO LO TIENEN)
    */
    double getTemp()
    {
    ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
    ADCSRA |= _BV(ADEN);
    delay(20);
    ADCSRA |= _BV(ADSC);
    while( bit_is_set(ADCSRA,ADSC) );
    return (ADCW – 324.31 ) / 1.22;
    }
    **************************************************************************************************

    Desde ya muchísimas gracias por tus aportes.
    Saludos desde Argentina.

    Charles

  11. Hola Estimado Alberto.

    Muchísimas gracias por responder, voy a investigar con tu información proporcionada.
    Luego le comento mis resultados.

    Un Gran Saludo y Gran Admiración para Usted.

    Saludos.

    Charles

  12. Hola Alberto. He instalado la LiquidCrystal_I2C que descargué de tu blog, y la pero me da el siguiente error al compilar:
    Arduino:1.8.13 (Windows 10), Tarjeta:»Arduino Mega or Mega 2560, ATmega2560 (Mega 2560)»
    C:\Users\Manuel\Documents\Arduino\libraries\LiquidCrystal_I2C\I2CIO.cpp:35:10: fatal error: ../Wire/Wire.h: No such file or directory
    #include
    ^~~~~~~~~~~~~~~~
    compilation terminated.
    Utilizando biblioteca LiquidCrystal_I2C en carpeta: C:\Users\Manuel\Documents\Arduino\libraries\LiquidCrystal_I2C (legacy)

    Usando librería EEPROM con versión 2.0 en la carpeta: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\EEPROM

    exit status 1

    Error compilando para la tarjeta Arduino Mega or Mega 2560.
    Muchas gracias

    PD: Estaría bien quen colocaras el código en cada entrada del blog. Me ha costado un buen rato encontrarlo
    aquí: https://giltesa.com/wp-content/uploads/2017/07/codigoLcdParaDecimales.txt

    1. Hola Manuel,

      El código de esta entrada esta en esta entrada. El problema es cuando se actualiza algún plugin de wordpress y eso causa que se rompa algo, como en esta ocasión el plugin que permite insertar código fuente en las entradas. Ya lo he revisado y parece que vuelve a funcionar.

      Ese código que has usado no se corresponde al de esta entrada, prueba de nuevo con el código de esta entrada a ver si te funciona.

      Saludos!

    2. Al final he conseguido que compile, aunque no he probado que funcione. Ha sido un lío bueno.
      He hecho una instalación nueva del ide porque no veía la carpeta de la libreria wire en:
      C:\Users\Manuel\Documents\Arduino\libraries.
      Tras la instalación nueva y volver a meter tu librería (https://giltesa.com/2014/02/18/pantalla-lcd-por-comunicacion-i2c-para-arduino ) el error sucedia igual.
      Lo que he hecho es:
      1. Copiar manualmente la carpeta «Wire» desde:
      «C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries » a
      «C:\Users\Manuel\Documents\Arduino\libraries».
      2.Después he copiado desde :
      «C:\Users\Manuel\Documents\Arduino\libraries\Wire\src» el archivo «Wire.h» a
      «C:\Users\Manuel\Documents\Arduino\libraries\Wire».
      3. A continuación he tenido que editar el archivo «I2CIO.cpp» que se encuentra en:
      C:\Users\Manuel\Documents\Arduino\libraries\LiquidCrystal_I2C
      y he cambiado la linea
      #include
      Por la linea:
      #include
      Lo dicho, de momento me ha compilado. Espero que funcione.
      Un saludo

    3. Jajaja, mientras andaba cacharreando me has contestado. Vaya celeridad!!
      Bueno ya lo dejo para mañana. Probaré el código que me has dicho.
      Gracias!!!!

  13. hola buenos dias, lo primero agradecerte por compartir tus proyectos, queria preguntarte si seria posible añadir un rtf SS3231 y añadir una opción en el menu que sea configurar hora para poder configurarlo desde el lcd, muchas gracias

    1. Hola Fernando,
      Se puede hacer sin ningún problema, adapta el código para soportar el reloj y después el menú para poder configurar el reloj con los datos introducidos por pantalla.
      Saludos.

    2. Bueno, deberías de actualizar la pantalla cuando actualices la información. Qué sentido tiene refrescarla cada segundo o cada iteración del loop si la info es la misma.
      Si vas a hacer un reloj, pues tiene sentido cada segundo, si tu reloj no tiene segundero entonces tiene sentido cada minuto.
      Si tu reloj sin segundero tiene ademas un termómetro entonces interesa actualizar cada minuto o cada vez que cambie la temperatura… eso lo puedes controlar todo por código 🙂

  14. Hola como estas?
    muchas gracias por este menu, hice varios intentos con otros codigos pero este es el mejor…
    Modifique un poco tu codigo y como necesito mostrar varios submenu llegue a un punto que era muy extenso y repetia la funcion openMenu y SubMenu…Lo que hice fue pasarle valores a esa funcion para lograr que sea universal, pero me surgio una duda, dentro de la funcion openMenu vuelvo a llamarla las veces que sea necesario para abrir nuevos menu, no se si eso es lo correcto pero me funciona, lo que te queria preguntar es que es lo que realmente pasa con la variable local «idxMenu». Se que se destruye cuando termina la funcion, pero este caso al volver a llamar a la misma funcion pienso q se crea nuevamente….es posible esto o me estoy complicando?
    muchas gracias saludos

    dejo un ejemplo….

    #define MENU_PRINCIPAL 0

    const String text_Menu_Principal []= {
    «Opcion_1»,
    «Salir»
    };
    const byte item_Menu_Principal = COUNT(text_Menu_Principal); // Numero de items/opciones del menu principal

    #define OPCION_1 1

    const String text_Opcion_1 []= {
    «Opcion_1_1»,
    «Salir»
    };
    const byte item_Opcion_1 = COUNT(text_Opcion_1); // Numero de items/opciones del menu opcion_1

    #define OPCION_1_1 2

    const String text_Opcion_1_1 []= {
    «Valor_1»,
    «Valor_2»,
    «Salir»
    };
    const byte item_Opcion_1_1 = COUNT(text_Opcion_1_1); // Numero de items/opciones del menu opcion_1_1

    void setup()
    {
    }

    void loop()
    {
    openMenu( text_Menu_Principal, item_Menu_Principal, MENU_PRINCIPAL);
    }

    //———————————FUNCION———————————
    void openMenu( String *txMenu, byte iMenu, byte Menu )
    {
    byte idxMenu = 0;
    boolean exitMenu = false;

    lcd.clear();

    while( !exitMenu )
    {
    btnPressed = readButtons();. //lee estado de los botones

    if( btnPressed == Button::Up && idxMenu-1 >= 0 )
    {
    idxMenu–; //decrementa posición de cursor
    }
    else if( btnPressed == Button::Down && idxMenu+1 < iMenu )
    {
    idxMenu++;. //incrementa posición de cursor
    }
    else if( btnPressed == Button::Ok )
    {

    if( Menu == MENU_PRINCIPAL )
    {
    switch( idxMenu )
    {
    case 0: openMenu( text_Opcion_1, item_Opcion_1, OPCION_1); break;
    case 1: exitMenu = true; break; //Salir
    }
    }

    else if( Menu == OPCION_1 )
    {
    switch( idxMenu )
    {
    case 0: openMenu( text_Opcion_1_1, item_Opcion_1_1, OPCION_1_1); break;
    case 1: exitMenu = true; break; //Salir
    }
    }
    }

    }
    lcd.clear();
    }

    1. Hola Leandro,
      Pues en principio cada vez que llames recursivamente a la función los valores han de ser distintos, no sé comparten con los de la llamada previa.

      Saludos

  15. Disculpame el codigo no se como ponerlo para q se entienda, las ultimas lineas son las mas importantes y no se puede leer bien

  16. Muchas gracias por responder, entonces voy a seguir con este metodo de recursividad…
    Es excelente este codigo de menu que compartiste
    Gracias
    Saludos

  17. Buenas, la verdad me gustó mucho tu proyecto, habia estado buscando muchos ejemplos que me ayudaran a realizar mi proyecto pero el tuyo veo que es mas parecido a como quiero que se maneje mi proyecto. Quiero saber algo, por ejemplo, yo no usare las mismas variables que tu, usare unidades de medida de peso, y lo que quiero es que cuando ya haya elejido que tanto y que cosa quiero al momento que le de guardar que en mi caso le pondria otra leyenda, al momento que le de en guardar quiero que realice una funcion de pesado, yo ya tengo el codigo para pesar, es de una galga con sensor HX711, y quiero que uno pueda elejir que contenesor va a usar, que medida quiero y cuanto de esa medida quiere y ya elijiendo todo que se realice todo el proceso cuando se le de guardar, igual quisiera saber si esa libreria con su condiguracion serviria para un arduino mega 2560. Muchas gracias por compartir tu codigo. Espero que no sea de mucha molestia mi comentario.

Escriba aquí su comentario