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.
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 |
/** * Name: Arduino - Menu for Shield LCD * Autor: Alberto Gil Tesa * Web: http://giltesa.com * License: CC BY-NC-SA 3.0 * Date: 2016/12/10 * * Arduino Uno, Pinout: * _______________ * | USB | * |13 12| * |3V3 11| * |AREF 10| * APAD |A0 9| LCD * |A1 8| LCD * |A2 7| LCD * |A3 6| LCD * |A4 5| LCD * |A5 4| LCD * | 3/SCL| * | 2/SDA| * |5V GND| * |RST RST| * |GND 1/INT2/RX| * |VIN 0/INT3/TX| * |MISO SS| * |SCK MOSI| * |_______________| * */ /* LIBRERIAS Y OBJETOS */ #include <LiquidCrystal.h> #include <EEPROM.h> LiquidCrystal lcd(8, 9, 4, 5, 6, 7); /* CONSTANTES Y VARIABLES */ #define TYPE_SMENU1 1 #define TYPE_SMENU2 2 #define TYPE_BOOL 3 #define TYPE_INT 4 unsigned long tNow = 0; unsigned long tPrevious = 0; boolean btnLeft = false; // Atras / Salir boolean btnRight = false; // Adelante / Entrar menu/submenu boolean btnUp = false; // Opcion anterior boolean btnDown = false; // Opcion siguiente boolean exitMenu = false; byte menuPosition = 0; const byte rowsLCD = 2; // Numero de filas del LCD const byte columnsLCD = 16; // Numero de columnas del LCD const byte iArrow[8] = { B00000, B00100, B00110, B11111, B00110, B00100, B00000, B00000 }; const byte ARROW = 0; /* TEXTOS MENUS */ const byte iMENU = 10; const char *txMENU[] = { "Ver tiempo ", // Maximo columnsLCD - 1 caracteres "Unid. tiempo ", "Eje X tiempo ", "Eje Y tiempo ", "Ver temperatura", "Unid. temp. ", "Eje X temp. ", "Eje Y temp. ", "Guardar y salir", "Salir " }; /* TEXTOS SUB MENU 1 */ const byte iMENU1 = 3; const char *txSMENU1[] = { " Milisegundos ", // Maximo columnsLCD - 2 caracteres " Segundos ", " Minutos " }; /* TEXTOS SUB MENU 2 */ const byte iMENU2 = 2; const char *txSMENU2[] = { " Grados C ", // Maximo columnsLCD - 2 caracteres " Grados F " }; /* ESTRUCTURAS CONFIGURACION */ struct MYDATA{ 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{ MYDATA d; byte b[sizeof(MYDATA)]; } memory; /** * */ void setup() { // Carga la configuracion de la EEPROM, y la configura la primera vez: readConfiguration(); // Inicia el LCD: lcd.begin(columnsLCD, rowsLCD); lcd.createChar(ARROW, iArrow); // Imprime la informacion del proyecto: lcd.setCursor(0,0); lcd.print("Menu Shield LCD "); lcd.setCursor(0,1); lcd.print(" giltesa.com "); delay(2000); lcd.clear(); } /** * */ void loop() { tNow = millis(); readButtons(); if( btnRight ) 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 0: lcd.print(tNow); lcd.print(" Mil"); break; case 1: lcd.print(tNow/1000); lcd.print(" Seg"); break; case 2: lcd.print(tNow/1000/60); lcd.print(" Min"); break; } } if( memory.d.temp_show == 1 ) { lcd.setCursor(memory.d.temp_x, memory.d.temp_y); switch( memory.d.temp_unit ) { case 0: lcd.print(getTemp()); lcd.print(" C"); break; case 1: lcd.print(1.8 * getTemp() + 32); lcd.print(" F"); break; } } } } /** * Muestra el Menu principal en el LCD. */ void openMenu() { lcd.clear(); while( !exitMenu ) { readButtons(); if( btnLeft ) { exitMenu = true; } else if( btnUp && menuPosition-1 >= 0 ) { menuPosition--; } else if( btnDown && menuPosition+1 < iMENU ) { menuPosition++; } 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 } } if( !exitMenu && millis()%500 == 0 ) { if( menuPosition % rowsLCD == 0 ) { for( int i=menuPosition ; i<(menuPosition+rowsLCD) ; i++ ) { lcd.setCursor(1, i % rowsLCD); lcd.print( i<iMENU ? txMENU[i] : " " ); } } if( (menuPosition-(rowsLCD-1)) % rowsLCD == 0 ) { for( int i=(menuPosition-(rowsLCD-1)) ; i<((menuPosition-(rowsLCD-1))+rowsLCD) ; i++ ) { lcd.setCursor(1, i % rowsLCD); lcd.print( i<iMENU ? txMENU[i] : " " ); } } for( int i=0 ; i<rowsLCD ; i++ ) { lcd.setCursor(0,i); lcd.print(" "); } lcd.setCursor(0, menuPosition % rowsLCD ); lcd.write(ARROW); } } exitMenu = false; menuPosition = 0; lcd.clear(); } /** * Muestra el SubMenu en el LCD. * * @param nameID Indice del array que contiene el titulo del submenu. * @param typeMenu 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 nameID, byte typeMenu, int *value, int minValue, int maxValue ) { lcd.clear(); while( !exitMenu ) { readButtons(); if( btnRight || btnLeft ) { exitMenu = true; } else if( btnUp && (*value)-1 >= minValue ) { (*value)--; } else if( btnDown && (*value)+1 <= maxValue ) { (*value)++; } if( !exitMenu && millis()%250 == 0 ) { lcd.setCursor(0,0); lcd.print(txMENU[nameID]); lcd.setCursor(0,1); lcd.print("<"); lcd.setCursor(columnsLCD-1,1); lcd.print(">"); if( typeMenu == TYPE_SMENU1 ) { lcd.setCursor(1,1); lcd.print(txSMENU1[*value]); } else if( typeMenu == TYPE_SMENU2 ) { lcd.setCursor(1,1); lcd.print(txSMENU2[*value]); } else if( typeMenu == TYPE_BOOL ) { lcd.setCursor(columnsLCD/2-1, 1); lcd.print(*value == 0 ? "NO" : "SI"); } else if( typeMenu == TYPE_INT ) { lcd.setCursor(columnsLCD/2-1, 1); lcd.print(*value); lcd.print(" "); } } } exitMenu = false; //menuPosition = 0; lcd.clear(); } /** * */ void readConfiguration() { for( int i=0 ; i < sizeof(memory.d) ; i++ ) memory.b[i] = EEPROM.read(i); if( !memory.d.initialized ) { memory.d.initialized = true; 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; } } /** * */ void writeConfiguration() { for( int i=0 ; i<sizeof(memory.d) ; i++ ) EEPROM.write( i, memory.b[i] ); } /** * */ void readButtons() { int val = analogRead(A0); btnLeft = false; btnRight = false; btnUp = false; btnDown = false; if( val < 50 ) btnRight = true; else if( val < 250 ) btnUp = true; else if( val < 450 ) btnDown = true; else if( val < 650 ) btnLeft = true; while( (btnLeft || btnRight || btnUp || btnDown) && analogRead(A0) < 1000 ); } /** * */ 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; } |
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.[/notification]
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 |
/** * Name: Arduino - Menu for Shield LCD * Autor: Alberto Gil Tesa * Web: http://giltesa.com * License: CC BY-NC-SA 3.0 * Date: 2017/08/01 * * Arduino Uno, Pinout: * _______________ * | USB | * |13 12| * |3V3 11| * |AREF 10| * APAD |A0 9| LCD * |A1 8| LCD * |A2 7| LCD * |A3 6| LCD * |A4 5| LCD * |A5 4| LCD * | 3/SCL| * | 2/SDA| * |5V GND| * |RST RST| * |GND 1/INT2/RX| * |VIN 0/INT3/TX| * |MISO SS| * |SCK MOSI| * |_______________| * */ /** * LIBRERIAS NECESARIAS PARA EL FUNCIONAMIENTO DEL CODIGO */ #include <LiquidCrystal.h> #include <EEPROM.h> /** * OBJETOS DE LAS LIBRERIAS */ LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //Configuracion 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 pPAD = A0; // Pin analogico de la Shield para los botones const byte rowsLCD = 2; // Filas del LCD const byte columnsLCD = 16; // 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, Up, Down, 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() { // 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 Shield LCD "); lcd.setCursor(0,1); lcd.print(" giltesa.com "); delay(2000); lcd.clear(); lcd.setCursor(0,0); lcd.print(" Ver. 2017/07 "); lcd.setCursor(0,1); 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::Up && idxMenu-1 >= 0 ) { idxMenu--; } else if( btnPressed == Button::Down && 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::Up && (*value)-1 >= minValue ) { (*value)--; } else if( btnPressed == Button::Down && (*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. */ Button readButtons() { int val = analogRead(pPAD); btnPressed = Button::Unknown; if( val < 50 ) btnPressed = Button::Right; else if( val < 250 ) btnPressed = Button::Up; else if( val < 450 ) btnPressed = Button::Down; else if( val < 650 ) btnPressed = Button::Left; else if( val < 850 ) btnPressed = Button::Ok; while( btnPressed != Button::Unknown && analogRead(pPAD) < 1000 ); 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; } |
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.
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.
Hola en este menú me compila pero solo me sale la M.
Uso una lcd bus i2c de 20,4.
Muchas gracias por tu ayuda.
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.
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
}
}
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.
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 🙂
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.
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
Hola,
Debugea el código imprimiendo los valores de las variables por el monitor serial pata averiguar qué sucede.
Saludos
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
Hola,
El código de este ejemplo es para pantallas conectadas directamente, no por I2C.
Aquí tienes un ejemplo para usar pantallas I2C:
https://giltesa.com/search/lcd+i2c
Saludos.
Muy buenas, seria copiar y pegar? O abria que modificar algo?perdon por la duda tan basica. Gracias
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.
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!
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.
Hola, como estas? te hago una consulta, como puedo escribir en la pantalla lcd y guardar este en una variable, gracias
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.
Hola, gracias por tu respuesta, no tenes ningun codigo con el que me pueda orientar?, muchas gracias, saludos!
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!
Hola Mauro,
Creo que el error que tienes puede ser debido a que no tengas la librería LCD que yo use, esta te permite conexiones I2C y añadir iconos. Prueba a ver si con eso se soluciona.
https://giltesa.com/2014/02/18/pantalla-lcd-por-comunicacion-i2c-para-arduino?highlight=LiquidCrystal
¡Saludos!
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.
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
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
Para arduino Mega 2560 R3 sirve el mismo codigo o tendre que cambiar la numeración de los pines??
Hola Mpr,
Si, debería de irte también con un Mega.
Saludos.