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ú.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
/** * Name: Arduino - Menu for LCD I2C + Encoder * Autor: Alberto Gil Tesa * Web: http://giltesa.com * License: CC BY-NC-SA 3.0 * Date: 2017/07/29 * * Arduino pinout: * _______________ * | USB | * |13 12| * |3V3 11| * |AREF 10| * |A0 9| * |A1 8| * |A2 7| * |A3 6| * LCD |A4 5| * LCD |A5 4| ENCODER CLK * | 3| ENCODER DT * | 2| ENCODER SW * |5V GND| * |RST RST| * |GND 1/INT2/RX| * |VIN 0/INT3/TX| * |MISO SS| * |SCK MOSI| * |_______________| * */ /** * LIBRERIAS NECESARIAS PARA EL FUNCIONAMIENTO DEL CODIGO */ #include <Wire.h> #include <LCD.h> #include <LiquidCrystal_I2C.h> #include <EEPROM.h> /** * OBJETOS DE LAS LIBRERIAS */ LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //Configuracion del LCD I2C (puede ser necesario cambiar el primer valor con la direccion del LCD) /** * MACROS, CONSTANTES, ENUMERADORES, ESTRUCTURAS Y VARIABLES GLOBALES */ #define COUNT(x) sizeof(x)/sizeof(*x) // Macro para contar el numero de elementos de un array const byte pENCO_SW = 2; // Pin encoder SW const byte pENCO_DT = 3; // Pin encoder DT const byte pENCO_CLK = 4; // Pin encoder CLK const byte rowsLCD = 4; // Filas del LCD const byte columnsLCD = 20; // Columnas del LCD const byte iARROW = 0; // ID icono flecha const byte bARROW[] = { // Bits icono flecha B00000, B00100, B00110, B11111, B00110, B00100, B00000, B00000 }; enum Button{ Unknown, Ok, Left, Right } btnPressed; // Enumerador con los diferentes botones disponibles enum Screen{ Menu1, Menu2, Flag, Number }; // Enumerador con los distintos tipos de submenus disponibles const char *txMENU[] = { // Los textos del menu principal, la longitud maxima = columnsLCD-1, rellenar caracteres sobrantes con espacios. "Ver tiempo ", "Unid. tiempo ", "Eje X tiempo ", "Eje Y tiempo ", "Ver temperatura ", "Unid. temp. ", "Eje X temp. ", "Eje Y temp. ", "Guardar y salir ", "Salir " }; const byte iMENU = COUNT(txMENU); // Numero de items/opciones del menu principal enum eSMENU1{ Milliseconds, Seconds, Minutes, Hours }; // Enumerador de las opciones disponibles del submenu 1 (tienen que seguir el mismo orden que los textos) const char *txSMENU1[] = { // Textos del submenu 1, longitud maxima = columnsLCD-2, rellenar caracteres sobrantes con espacios " Milisegundos ", " Segundos ", " Minutos ", " Horas " }; enum eSMENU2{ GradeC, GradeF }; // Enumerador de las opciones disponibles del submenu 2 (tienen que seguir el mismo orden que los textos) const char *txSMENU2[] = { // Textos del submenu 1, longitud maxima = columnsLCD-2, rellenar caracteres sobrantes con espacios " Grados C ", " Grados F " }; /* ESTRUCTURAS CONFIGURACION */ struct MYDATA{ // Estructura STRUCT con las variables que almacenaran los datos que se guardaran en la memoria EEPROM int initialized; int time_show; int time_unit; int time_x; int time_y; int temp_show; int temp_unit; int temp_x; int temp_y; }; union MEMORY{ // Estructura UNION para facilitar la lectura y escritura en la EEPROM de la estructura STRUCT MYDATA d; byte b[sizeof(MYDATA)]; } memory; /** * INICIO Y CONFIGURACION DEL PROGRAMA */ void setup() { pinMode(pENCO_SW, INPUT_PULLUP); pinMode(pENCO_DT, INPUT_PULLUP); pinMode(pENCO_CLK, INPUT_PULLUP); // Carga la configuracion de la EEPROM, y la configura la primera vez: readConfiguration(); // Inicia el LCD: lcd.begin(columnsLCD, rowsLCD); lcd.createChar(iARROW, bARROW); // Imprime la informacion del proyecto: lcd.setCursor(0,0); lcd.print(" Menu LCD + Encoder "); lcd.setCursor(0,1); lcd.print(" giltesa.com "); lcd.setCursor(0,2); lcd.print(" Ver. 2017/07 "); lcd.setCursor(0,4); for( int i=0 ; i<columnsLCD ; i++ ) { lcd.print("."); delay(150); } lcd.clear(); } /** * PROGRAMA PRINCIPAL */ void loop() { static unsigned long tNow = 0; static unsigned long tPrevious = 0; tNow = millis(); btnPressed = readButtons(); if( btnPressed == Button::Ok ) openMenu(); // Pinta la pantalla principal cada 1 segundo: if( tNow - tPrevious >= 1000 ) { tPrevious = tNow; if( memory.d.time_show == 1 || memory.d.temp_show == 1 ) lcd.clear(); if( memory.d.time_show == 1 ) { lcd.setCursor(memory.d.time_x, memory.d.time_y); switch( memory.d.time_unit ) { case eSMENU1::Milliseconds: lcd.print(tNow); lcd.print(" Mil"); break; case eSMENU1::Seconds: lcd.print(tNow/1000); lcd.print(" Seg"); break; case eSMENU1::Minutes: lcd.print(tNow/1000/60); lcd.print(" Min"); break; case eSMENU1::Hours: lcd.print(tNow/1000/60/60); lcd.print(" Hor"); break; } } if( memory.d.temp_show == 1 ) { lcd.setCursor(memory.d.temp_x, memory.d.temp_y); switch( memory.d.temp_unit ) { case eSMENU2::GradeC: lcd.print(getTemp()); lcd.print(" C"); break; case eSMENU2::GradeF: lcd.print(1.8 * getTemp() + 32); lcd.print(" F"); break; } } } } /** * MUESTRA EL MENU PRINCIPAL EN EL LCD. */ void openMenu() { byte idxMenu = 0; boolean exitMenu = false; boolean forcePrint = true; lcd.clear(); while( !exitMenu ) { btnPressed = readButtons(); if( btnPressed == Button::Left && idxMenu-1 >= 0 ) { idxMenu--; } else if( btnPressed == Button::Right && idxMenu+1 < iMENU ) { idxMenu++; } else if( btnPressed == Button::Ok ) { switch( idxMenu ) { case 0: openSubMenu( idxMenu, Screen::Flag, &memory.d.time_show, 0, 1 ); break; case 1: openSubMenu( idxMenu, Screen::Menu1, &memory.d.time_unit, 0, COUNT(txSMENU1)-1 ); break; case 2: openSubMenu( idxMenu, Screen::Number, &memory.d.time_x, 0, columnsLCD-1 ); break; case 3: openSubMenu( idxMenu, Screen::Number, &memory.d.time_y, 0, rowsLCD-1 ); break; case 4: openSubMenu( idxMenu, Screen::Flag, &memory.d.temp_show, 0, 1 ); break; case 5: openSubMenu( idxMenu, Screen::Menu2, &memory.d.temp_unit, 0, COUNT(txSMENU2)-1 ); break; case 6: openSubMenu( idxMenu, Screen::Number, &memory.d.temp_x, 0, columnsLCD-1 ); break; case 7: openSubMenu( idxMenu, Screen::Number, &memory.d.temp_y, 0, rowsLCD-1 ); break; case 8: writeConfiguration(); exitMenu = true; break; //Salir y guardar case 9: readConfiguration(); exitMenu = true; break; //Salir y cancelar cambios } forcePrint = true; } if( !exitMenu && (forcePrint || btnPressed != Button::Unknown) ) { forcePrint = false; static const byte endFor1 = (iMENU+rowsLCD-1)/rowsLCD; int graphMenu = 0; for( int i=1 ; i<=endFor1 ; i++ ) { if( idxMenu < i*rowsLCD ) { graphMenu = (i-1) * rowsLCD; break; } } byte endFor2 = graphMenu+rowsLCD; for( int i=graphMenu, j=0; i< endFor2 ; i++, j++ ) { lcd.setCursor(1, j); lcd.print( (i<iMENU) ? txMENU[i] : " " ); } for( int i=0 ; i<rowsLCD ; i++ ) { lcd.setCursor(0, i); lcd.print(" "); } lcd.setCursor(0, idxMenu % rowsLCD ); lcd.write(iARROW); } } lcd.clear(); } /** * MUESTRA EL SUBMENU EN EL LCD. * * @param menuID ID del menu principal para usarlo como titulo del submenu * @param screen Segun el tipo, se representara el submenu de una forma u otra. * @param value Puntero a la variable que almacena el dato, y que se modificara. * @param minValue Valor minimo que puede tener la variable. * @param maxValue Valor maximo que puede tener la variable. */ void openSubMenu( byte menuID, Screen screen, int *value, int minValue, int maxValue ) { boolean exitSubMenu = false; boolean forcePrint = true; lcd.clear(); while( !exitSubMenu ) { btnPressed = readButtons(); if( btnPressed == Button::Ok ) { exitSubMenu = true; } else if( btnPressed == Button::Left && (*value)-1 >= minValue ) { (*value)--; } else if( btnPressed == Button::Right && (*value)+1 <= maxValue ) { (*value)++; } if( !exitSubMenu && (forcePrint || btnPressed != Button::Unknown) ) { forcePrint = false; lcd.setCursor(0,0); lcd.print(txMENU[menuID]); lcd.setCursor(0,1); lcd.print("<"); lcd.setCursor(columnsLCD-1,1); lcd.print(">"); if( screen == Screen::Menu1 ) { lcd.setCursor(1,1); lcd.print(txSMENU1[*value]); } else if( screen == Screen::Menu2 ) { lcd.setCursor(1,1); lcd.print(txSMENU2[*value]); } else if( screen == Screen::Flag ) { lcd.setCursor(columnsLCD/2-1, 1); lcd.print(*value == 0 ? "NO" : "SI"); } else if( screen == Screen::Number ) { lcd.setCursor(columnsLCD/2-1, 1); lcd.print(*value); lcd.print(" "); } } } lcd.clear(); } /** * LEE (Y CONFIGURA LA PRIMERA VEZ) LA MEMORIA EEPROM CON LA CONFIGURACION DE USUARIO */ void readConfiguration() { for( int i=0 ; i < sizeof(memory.d) ; i++ ) memory.b[i] = EEPROM.read(i); if( memory.d.initialized != 'Y' ) { memory.d.initialized = 'Y'; memory.d.time_show = 1; memory.d.time_unit = 1; memory.d.time_x = 0; memory.d.time_y = 0; memory.d.temp_show = 1; memory.d.temp_unit = 0; memory.d.temp_x = 0; memory.d.temp_y = 1; writeConfiguration(); } } /** * ESCRIBE LA MEMORIA EEPROM CON AL CONFIGURACION DE USUARIO */ void writeConfiguration() { for( int i=0 ; i<sizeof(memory.d) ; i++ ) EEPROM.write( i, memory.b[i] ); } /** * LEE LOS DISTINTOS BOTONES DISPONIBLES Y DEVUELVE EL QUE HAYA SIDO PULSADO * Este bloque de codigo varia dependiendo del tipo de teclado conectado, en mi blog estan * disponibles los ejemplos para teclados digitales, analogicos, y este para encoder rotatorio. * Al cambiar de tipo de teclado hay que adaptar tambien el resto de codigo para que haga uso de cada boton segun queramos. */ Button readButtons() { static boolean oldA = HIGH; static boolean newA = LOW; static boolean newB = LOW; btnPressed = Button::Unknown; newA = digitalRead(pENCO_DT); newB = digitalRead(pENCO_CLK); if( !oldA && newA ) { btnPressed = !newB ? Button::Left : Button::Right; delay(50); } else if( !digitalRead(pENCO_SW) ) { while(!digitalRead(pENCO_SW)); btnPressed = Button::Ok; delay(50); } oldA = newA; return btnPressed; } /** * 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; } |
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.
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.
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.
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
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
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!
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.
Hola GILTESA,
no me queda claro para que usas el ::
Saludos
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.
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.
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
Buenos dias, Para qie sirven el endfor1 y el forceprint, lo probe y funciona bien sino que no me queda claro esos dos. Gracias.
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.
Hola buenos días, me da errores de compilación el código.
Me sale el fallo en esta línea lcd.createChar(iARROW, bARROW); yo no en cuentro el error
Hola Sergio,
Asegúrate de estar usando esta librería, para conexión I2C y no con conexión en «paralelo». Esta es la que uso yo:
https://giltesa.com/2014/02/18/pantalla-lcd-por-comunicacion-i2c-para-arduino?highlight=lcd
Saludos.
En un principio la librería que tengo es la misma , porque la he usado en otros proyectos.
Por eso me estraña
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.
De todas formas la he descargado y la añadido y sigue igual.
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
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
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
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!!!
Hola Geraldin,
Te he grabado un video tratando de explicarte como funciona el código y como modificarlo, échale un vistazo y me dices.
Te adjunto el código también, pero no se si funciona porque no puedo probarlo.
https://youtu.be/TjGEFDkKues
https://giltesa.com/wp-content/uploads/2017/07/codigoLcdParaDecimales.txt
Saludos!
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 !
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
Hola Charles,
Prueba esto, acabo de encontrarlo buscando por internet, no sé si funciona pero tiene buena pinta:
https://github.com/ricaun/ArduinoUniqueID
https://www.thethingsnetwork.org/forum/t/arduino-has-a-unique-id/21415/11
Saludos.
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
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
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!
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
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!!!!
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
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.
Muchas gracias, una cosilla es necesario actualizar la pantalla cada segundo? para evitar el parpadeo gracias
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 🙂
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();
}
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
Disculpame el codigo no se como ponerlo para q se entienda, las ultimas lineas son las mas importantes y no se puede leer bien
Muchas gracias por responder, entonces voy a seguir con este metodo de recursividad…
Es excelente este codigo de menu que compartiste
Gracias
Saludos
Otro que se une a felicitarte por tu código, está excelente y lo usaré para multitud de proyectos.
Un saludo
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.