Menú con submenus para Shield LCD en Arduino

Es el mismo menú que ya enseñe en la entrada del proyecto de control de emisoras de radioaficionado pero esta vez con el código mínimo para ponerlo a funcionar y para que trabaje sobre una Shield LCD.

En aquella ocasión lo probé en una pantalla de 4 lineas y 20 caracteres por linea, pero ya estaba preparado para trabar con menos lineas y menos caracteres como es el caso en las pantallas LCD de 16×2, en ese sentido no he tenido que tocar nada, pero si en el apartado de control pues el código anterior estaba pensado para un encoder digital y esta vez funciona con un pad analógico.

El código de ejemplo nos permite mostrar el tiempo que lleva ejecutándose el Arduino y la temperatura a la que esta el microcontrolador (más info aquí). Desde el menú podemos elegir si queremos ver o no cada uno de esos dos datos, podemos elegir la unidad de medida en la que se mostraran y también las coordenadas X e Y para que aparezca en la zona de pantalla donde queramos (con riesgo de que se solapen los datos o se corten por la derecha)

El menú sigue permitiendo guardar la configuración en la memoria EEPROM por lo que cuando volvamos a encender el Arduino la configuración sera la misma que la ultima aplicada.

Actualización: 2017/07/29
He optimizado y simplificado este menú, aquí hay publicado un ejemplo haciendo uso de un encoder.
También he aplicado el nuevo código para la Shield LCD, el nuevo es el siguiente.

26 comentarios en “Menú con submenus para Shield LCD en Arduino”

  1. Buenos días Gil.
    quisiera preguntarte, para que se usa el simbolo % en el codigo, esa parte no la he logrado entender.
    Otra enquietud que tengo es que, tome tu codigo lo compile y perfecto.
    luego de un rato volvi a compilarlo para ponerlo en un simulador y aparecio un error diciendo que la variable readConfiguration(); no esta creada en el ambito. Tienes idea de porque sucede esto?

    en ningun moomento borre algo. para descartar que hubiera borrado algo por error copie nuevamente el codigo desde tu web y apaercio el mismo error. Es extraño.

    1. Hola Yaudy,

      En la página 145 apartado 5 tienes la explicación de ese operador aritmético: INFORMÁTICA APLICADA PROGRAMACIÓN EN LENGUAJE C

      En cuanto a la otra duda, debería de irte siempre pero ese error parece como si las funciones setup y loop no encontraran las otras. En C las funciones de usuario deben declararse antes de las funciones principales para que puedan encontrarlas… en Arduino no suele hacer falta por eso las funciones del usuario las pongo debajo de las principales… puedes probar a cortarlas y ponerlas encima, por probar simplemente.

      Saludos.

    1. Hola,

      Lo que aparezca en pantalla depende de la configuración guardada en la EEPROM, esa configuración se cambia desde el menú accesible con los botones de la shield.

      En la linea 351 puedes configurar los valores de la EEPROM cuando se ejecuta por primera vez y no puede recuperar nada de la memoria. He añadido un vídeo a la entrada para que veas como debería de quedar.

      Saludos.

  2. hola, no existe una manera mas sencilla de poder usar el boton de la derecha sin que sea necesario el uso de la memoria EEPROM exactamente este parrafo.
    else if( btnRight )
    {
    switch( menuPosition )
    {
    case 0: openSubMenu( 0, TYPE_BOOL, &memory.d.time_show, 0, 1 ); break;
    case 1: openSubMenu( 1, TYPE_SMENU1, &memory.d.time_unit, 0, 2 ); break;
    case 2: openSubMenu( 2, TYPE_INT, &memory.d.time_x, 0, 15 ); break;
    case 3: openSubMenu( 3, TYPE_INT, &memory.d.time_y, 0, 1 ); break;
    case 4: openSubMenu( 4, TYPE_BOOL, &memory.d.temp_show, 0, 1 ); break;
    case 5: openSubMenu( 5, TYPE_SMENU2, &memory.d.temp_unit, 0, 1 ); break;
    case 6: openSubMenu( 6, TYPE_INT, &memory.d.temp_x, 0, 15 ); break;
    case 7: openSubMenu( 7, TYPE_INT, &memory.d.temp_y, 0, 1 ); break;
    case 8: writeConfiguration(); exitMenu = true; break; //Salir y guardar
    case 9: readConfiguration(); exitMenu = true; break; //Salir y cancelar cambios
    }
    }

    1. Hola,

      Si no usas la memoria EEPROM seria necesario configurar el Arduino accediendo al menú cada vez que lo conectaras a la corriente, no tiene ningún sentido no guardar la configuración en memoria.

      En cuanto a ese párrafo lo que hace es pintar el submenú que se haya seleccionado en el menú principal.

      Si puedes cambiar el código para que los botones funcionen de otra forma, si en vez de acceder al menú con derecha lo prefieres con OK o con izquierda solo tienes que tocar el código de ejemplo y ponerlo a tu gusto.

      Saludos.

    2. creo que no fui muy claro, me refiero a que si el boton de la derecha solo sirva para que sea un «ok» y logre mostrar algo y el de la izquierda un «atras o salir», agradeceria su ayuda 🙂

    3. Hola,

      Los botones hacen la funcionalidad que les programes, en este caso el de la derecha hace de OK, y el de la izquierda cancelar/salir.

      Pero puedes cambiar el código para hacer cada acción con el botón que quieras. Y si estas fuera del menú los botones pueden hacer otras cosas, las que tu programes.

      Saludos.

  3. hola gil! … un favor tengo problemas al utilizar el boton atras. Al llamar a una funcion dentro de los sub menus el boton atras ya no funciona se desactiva lo cual hace que no pueda volver al menu, pero si quito esa funcion y solo muestro opciones de si y no como en tu sub menu de temperatura me funciona perfecto. Sabes que error podria estar cometiendo? gracias por la ayuda

  4. Yo no puedo compilarlo me da error la librería lcd en la línea 94 en donde los caracteres.


    Menu_Giltesa_Nuevo.ino: In function 'void setup()':
    Menu_Giltesa_Nuevo.ino:94:33: error: invalid conversion from 'const byte* {aka const unsigned char*}' to 'uint8_t* {aka unsigned char*}' [-fpermissive]
    In file included from Menu_Giltesa_Nuevo.ino:4:0:
    C:\Program Files (x86)\Arduino\libraries\LiquidCrystal_I2C/LiquidCrystal_I2C.h:81:8: error: initializing argument 2 of 'void LiquidCrystal_I2C::createChar(uint8_t, uint8_t*)' [-fpermissive]
    void createChar(uint8_t, uint8_t[]);
    ^
    Error de compilación

    No se que hacer.
    Un saludo

    1. Hola Pablo,

      Si usas la misma shield si, es copiar y pegar, si es distinta puede que tengas que cambiar los pines en el código para que coincidan.

      Saludos.

  5. Hola Giltesa,
    Tu codigo anduvo de maravillas (luego de obviamente adaptarlo a mi proyecto) solo queria comentarte que tuve un inconveniente que tal vez no te sucedio porque usaste un LCD de 16×2 pero, segun entendi del codigo la pantalla del LCD se refresca solo cuando la posicion del menu esta en los extremos, osea en el caso de 16×2 es cuando esta en la fila 0 o 1, pero en el caso de un LCD de 4×20 solo refrescara la pantalla cuando este en 0 o en 3, por lo que si ingresas en un subMenu desde la posicion 1 o 2 cuando salgas la pantalla queda en blanco… nose si te sucedio, lo resolvi con la siguiente funcion luego del SwitchCase:

    //Esta seccion es para volver a graficar cuando salimos del submenu
    int menu_div=(iMENU+rowsLCD-1)/rowsLCD;
    int graphMenu = 0;
    for( int i=1 ; i<=menu_div ; i++)
    {
    if(menuPosition < rowsLCD*i){
    graphMenu = (i-1)*rowsLCD;
    break;
    }
    }

    for( int i=graphMenu ; i<(graphMenu+rowsLCD) ; i++ )
    {
    lcd.setCursor(1, i % rowsLCD);
    lcd.print( (i<iMENU) ? txMENU[i] : " " );
    Serial.print("if1\n");
    Serial.print(txMENU[i]);
    Serial.print("\n");
    }

    Desde ya muchas gracias por tus Aportes!

    1. Hola Mauro,

      Puede que falle como comentas, no lo probé mucho después de comentar la linea 342 que hacia que el menú principal cargara siempre en la misma posición.

      Saludos.

    1. Hola Emmanuel, pues con mucho código extra que te ayude a controlar la entrada de texto por teclado/botones, mientras haces eso puedes guardar la información en un string o array de caracteres y cuando el usuario termine la introducción de información puedes volcar eso a la EEPROM, SD, u otra memoria no volátil.
      Saludos.

  6. Hola qué tal? Un gusto saludarte. Te escribo porque estoy intentando hacer funcionar tu código pero a la hora de compilarlo (sin cambiar absolutamente nada) me da el siguiente error:

    invalid conversion from ‘const byte* {aka const unsigned char*}’ to ‘uint8_t* {aka unsigned char*}’

    El mismo es generado según me informa el idea por la siguiente línea:

    lcd.createChar(iARROW, bARROW);

    He estado buscando pero hasta ahora no encuentro a que se puede deber. Agradecería mucho tu ayuda. Saludos!

    1. Ok, me retracto de lo dicho porque no había leído que esta entrada es sobre la Shield LCD, esta pantalla no usa I2C así que no es po la librería.

      Me he descargado el IDE y también he ha dado el mismo problema, lo curioso ha sido cuando he tratado de compilar el código de nuevo… a la segunda a funcionado correctamente y no ha dado error, madre mía que cosa mas rara!

      PD: En realidad he eliminado una cosa en el código, ha compilado bien, la he vuelto a añadir para que el código quedase como es originalmente y ha compilado igualmente:

      En esta linea:
      const byte bARROW[]

      He eliminado:
      const

      Pero ya te digo que luego lo he vuelto a añadir y ha seguido compilando bien… sera cosa del ide que tiene algún fallo.

  7. Buenas noches Giltesa.com
    Excelente apoyo, gracias he estado con este tema varios meses y por fin gracias a ti, he logrado un avance.
    Una pregunta, que deberia agregar y en donde al codigo para que memorice el conteo del tiempo y no se ponga a 0 cuando se desconecta la placa? deseo contar horas Ej: 2-60-500-1000, etc. Muy agradecido de antemano. Sldos Henry

    1. Hola Henry, yo creo que deberías, cada cierto intervalo de tiempo, que guardes en una variable el tiempo trascurrido en la EEprom, y cada vez que reinicies el arduino, llames a la variable que tienes almacenada. Con eso, podrías tener el contador de tiempo. Saludos Tomás

Escriba aquí su comentario