TECLADO CON LCD.
Registro de entrada por número PIN.
PROLOGO.
En otro artículo ya hice un esbozo superficial sobre las pantallas de cristal líquido LCD, distinguiendo dos grandes grupos, las pantallas alfanuméricas y las pantallas gráficas. Por el momento, nos referiremos las pantallas LCD alfanuméricas, en concreto las de cuatro líneas y 40 columnas. En la actualidad la mayoría de los LCD están controlados con el conocido HD44780 de Hitachi. El cual puede ser configurado para manejar un display de matriz de puntos de cristal líquido bajo el control de un microprocesador de 4 u 8 bits.
En este artículo, pretendo hacer una aplicación que tenga utilidad para quien disponga de un LCD y quiera realizar una aplicación que le sirva de ayuda en su trabajo o para una aplicar en un proyecto. Hay muchas aplicaciones descritas en gran cantidad de artículos y webs de distintos autores y en todas ellas subyace un ‘toque’ personal que hace la diferencia, espero que en este, también encuentren la novedad o el punto que buscaban.
El propósito de este artículo es reunir una serie de unidades sencillas, como base de los ejemplos, con pantalla de cristal líquido LCD, que se encuentran disponibles para trabajar con Arduino. Se describen algunos ejercicios para comprender los principios de uso y sin perder el carácter didáctico de lo descrito.
DISPLAY LCD.
El uso de este dispositivo se ha extendido en los últimos años debido a la proliferación de los microcontroladores con sus cada ves mayores prestaciones y los sistemas de desarrollo que los sustentan. Hay esencialmente dos tipos de pantalla para elegir, serie y paralelo, en referencia a cómo se conectan y se comunican con el mismo PC.
En cuanto a los LCD, en el mercado pueden encontrarse distintos modelos que se adaptan a las necesidades de los proyectos que se planteen. Así, podemos encontrar LCD de una línea y 8 a 40 caracteres, 4 líneas por 40 caracteres y tantas líneas y caracteres que se necesiten. Los LCD gráficos están avanzando tanto que sus precios son cada día más asequibles a cualquier necesidad.
La mayoría de los LCD paralelos son muy similares, usan chips de interfaz estándar en la industria (como el HD44780 o HD44100) para la asignación de pines, a menudo son idénticos.
No voy a entrar sobre los pros y los contras de cada uno, ya que este no es el objetivo del presente artículo, sin embargo, lo que voy a decir es que que, fundamentalmente, la decisión se reduce a su costo en comparación con la facilidad de uso, es la cuestión. Si bien los display’s serie son más fáciles de poner en marcha y tienen un poco mejor ayuda con el software, además de tener algunas características adicionales. Por otro lado, los LCD paralelos, necesita cablearlas usted mismo, pero son mucho más baratos y en su mayor parte se puede mostrar la misma información.
En este artículo, sólo trataré de los LCD de cuatro líneas y 20 caracteres o columnas. Naturalmente por que es el que tengo a mi disposición. Para controlar pantallas Cristal Liquido (LCD) basada en el Hitachi HD44780 (o compatible) se necesita la «Nueva librería LiquidCrystal» que nos proporciona FM, como se le conoce en el foro. Además, también utilizaremos un teclado 3×4 similar al de la imagen de abajo, dos resistencias, dos LED’s, dos pulsadores y algunos hilos de colores para conectar todo.
Lo que pretendo realizar es un teclado, con el que introducir una clave de un número de entre 4, 6 o más dígitos y el programa compare los dígitos que se pulsan por el usuario, con la clave guardada y si hay coincidencia que, haga una cosa, en caso de no coincidir que, haga otra cosa. En principio, se ve sencillo. Para abordar el proyecto, necesitamos unos pocos materiales a parte de la placa Arduino y el LCD, como:
El más importante, el teclado, este tipo de teclados, a pesar de que es de los años 80 (foto de Futurlec), es bastante corriente y fácil de encontrar en el mercado. El resto de componentes, es de uso común. El circuito electrónico que voy a utilizar se muestra en la imagen que sigue.
Mediante la ayuda de un teclado de 12 pulsadores, podemos entrar una serie de dígitos que, formen el código «secreto» para activar un sistema, si existe coincidencia o por el contrario, pondrá en acción otro sistema que indique que, se está forzando el acceso no autorizado a la instalación, por un desconocido.
Para esto, en primer lugar debemos configurar la librería Keypad[V2].zip y declararemos los 7 pines de Arduino que conformaran el teclado, por un lados las 4 líneas (rows) y las 3 columnas (cols) y la conformación que tomarán los contactos, como se puede ver a continuación:
El primer paso es incluir la librería y las constantes que vamos a utilizar:
1 2 3 4 5 6 7 8 9 10 |
#include "Keypad.h" const byte ROWS = 4; //four rows const byte COLS = 3; //three columns char keys[ROWS][COLS] = { {'1','2','3'}, {'4','5','6'}, {'7','8','9'}, {'*','0','#'} }; |
El paso que sigue es, reservar memoria en bytes, para los valores de los pulsadores y la asignación del pin que se corresponde con los de Arduino. Sin embargo, hay que observar que, vamos a necesitar un número alto de pines en este y los siguientes ejemplos. Por este motivo, la asignación de pines, se hace con vistas a no tener que cambiar con frecuencia dicha disposición. Además, se asignaran otras variables, en función de las necesidades que surjan.
1 2 |
byte rowPins[ROWS] = { 5, 4, 3, 2}; //conectar a los pinouts row del keypad byte colPins[COLS] = { 8, 7, 6}; //conectar a los pinouts column del keypad |
Este es el momento de ‘crear’ el teclado propiamente dicho, es decir, con esta sentencia, el programa generará una matriz que verá como un teclado, al que se remitirá, cuando tenga que representar un dígito. Esto se consigue con la siguiente línea:
1 |
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); |
Tenemos que guardar una clave, luego, necesitamos hacer una reserva de memoria y además, el código que introduzca el usuario, también requiere un espacio de memoria. Posteriormente, con sólo comparar ambas matrices, obtendremos fácilmente si hay coincidencia entre ambas. Veamos como se hace:
1 2 |
char PIN[6]={'1','2','3','4','5','6'}; // o numero secreto (!) char attempt[6]={0,0,0,0,0,0}; // usado para comparar |
En la asignación de las dos matrices de 6 dígitos, utilizamos la función char para almacenar los bytes de cada matriz. Una, para los dígitos de entrada llamada attempt, se compararán con la otra matriz, contenida en PIN. Sólo, si hay coincidencia, la clave es correcta y actuará la salida correspondiente. Se han dispuesto dos salidas complementarias, las cuales se pueden conectar a los dispositivos adecuados que, deriven la potencia necesaria para sus actuadores.
En el setup, configuramos los distintos pines, según convenga a cada uno, se declara el puerto serie y su velocidad:
1 2 3 4 5 6 |
pinMode(sal1, OUTPUT); pinMode(sal2, OUTPUT); Serial.begin(9600); //Configura la velocidad del puerto serie keypad.setHoldTime(500); // Default is 1000mS keypad.setDebounceTime(250); // Default is 50mS |
Destacar las funciones que afectan al rebote de los pulsadores que, están integrados en la librería keypad representados por keypad.setHoldTime y keypad.setDebounceTime. Aquí. se ha reducido su tiempo de mantenimiento como se puede apreciar en el propio código.
Además, se ha incluido el mensaje que presentará al inicio del programa. En este mensaje, se puede hacer una descripción corta de lo que hay que hacer para entrar el código PIN. Esto se ha hecho con el siguiente código:
1 2 3 4 5 |
Serial.println(" Debe introducir la secuencia: "); Serial.print(" * X X X X X X # "); Serial.println(" >> X = del 0 al 9"); incorrectPIN();Serial.print(" "); Serial.println(" Intentelo: "); |
En este punto, para una mayor claridad, se deben declarar las rutinas que intervengan en el programa, acuérdese de hacer comentarios, para poder comprender qué hace cada rutina. Así pues, se declara la rutina void correctPIN() que, se ejecutará en el caso de haber coincidencia, es decir:
1 2 |
digitalWrite(10, LOW); // desactiva un contacto pin 1 digitalWrite(11, HIGH); // activa un contacto pin 2 |
Y le seguirá la rutina void incorrectPIN() que se ejecutará en el caso contrario. Que hace exactamente lo mismo pero invertidos
1 2 |
digitalWrite(11, LOW); // desactiva un contacto pin 2 digitalWrite(10, HIGH); // activa un contacto pin 1 |
Se ha incluido una rutina para activar un sistema (en este caso, visual) para activar un LED en su caso. Con void readKeypad(), se lee cualquier entrada de teclado, o sea, hace un chequeo del teclado, de modo que cuando se presione un pulsador o llave. Esto lo consigue mediante este código, asignando a char key, todo lo que llegue por el teclado con: char key = keypad.getKey(); si no se produce una entrada, permanecerá en espera. Gracias a esta sentencia de if (key != NO_KEY), en la cual permanecerá la ejecución del programa, esperando a que se produzca una entrada. Así que, este es el listado:
1 2 |
char key = keypad.getKey(); if (key != NO_KEY) |
Le sigue la función switch (key) que es capaz de detectar la tecla que se ha pulsado. Cuando se produce una entrada del teclado, se guarda en la variable attempt, lo reconoce y evalúa con PIN[] si es exactamente igual, sale de esta subrutina y salta a la subrutina correctPIN() y sigue hasta el final de la misma.
En caso de que la evaluación no sea exacta, salta a la subrutina incorrectPIN(); y cuando termina, vuelve hasta la línea:
for (int zz=0; zz<6; zz++) // borrar tentativa, para borrar la tentativa actual.
En último lugar está el lazo o loop() que, en este caso, contiene la llamada a readKeypad(); que ejecutará continuamente.
Veamos un primer ejemplo, este es el código para el programa de reconocimiento de clave:
Número PIN
Este es el código para el programa de reconocimiento de clave./p>
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 |
// // pin_clave.pde - keypad switch con seis-digitos para el PIN // basado en un programa de: http://tronixstuff.wordpress.com/ /* Usando el actual ejemplo de hardware se pueden activa algo o desactivar mediante el teclado - emulando lo que se puede encontrar en algunos sistemas de alarma y así sucesivamente. Nuestro objetivo con este ejemplo es muy simple. El sistema, espera obtener un PIN que se guarda previamente. Si el PIN es correcto, hace algo. Si el PIN es incorrecto, hace otra cosa. Este ejemplo es para darle un concepto y un marco para construir ideas propias. 19.10.2011 funciona bien. */ #include "Keypad.h" const byte ROWS = 4; //four rows const byte COLS = 3; //three columns char keys[ROWS][COLS] = { {'1','2','3'}, {'4','5','6'}, {'7','8','9'}, {'*','0','#'}}; byte rowPins[ROWS] = { 5, 4, 3, 2}; //connect to the row pinouts of the keypad byte colPins[COLS] = { 8, 7, 6}; //connect to the column pinouts of the keypad Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); char PIN[6]={'1','2','3','4','5','6'}; // su numero secreto (!) char attempt[6]={0,0,0,0,0,0}; // usado para comparar int z=0; int led13= 13; int sal1 = 10; // salida1, pin A4 int sal2 = 11; // salida2, pin A5 void setup(){ pinMode(sal1, OUTPUT); pinMode(sal2, OUTPUT); Serial.begin(9600); //Configura la velocidad del puerto serie keypad.setHoldTime(500); // Default is 1000mS keypad.setDebounceTime(250); // Default is 50mS Serial.println(" Debe introducir la secuencia: "); Serial.print(" * X X X X X X # "); Serial.println(" >> X = del 0 al 9"); incorrectPIN(); Serial.print(" "); Serial.println(" Intentelo: "); } void correctPIN(){ // hacer esto. Si el PIN escrito es correcto digitalWrite(10, LOW); // desactiva un contacto pin 1 digitalWrite(11, HIGH); // activa un contacto pin 2 Serial.print(" Correcto "); Serial.print(attempt); } void incorrectPIN(){ // hacer esto. Si el PIN escrito es incorrecto digitalWrite(11, LOW); // desactiva un contacto digitalWrite(10, HIGH); // activa un contacto Serial.print("Incorrecto "); //Serial.print(" "); } void led(){ if (led13 == LOW) led13= HIGH; else { led13 = LOW; } } void checkPIN(){ int correct=0; for (int q=0; q<=5; q++){ if (attempt[q]==PIN[q]){ correct++; } } if (correct==6){ correctPIN(); } else{ incorrectPIN(); } for (int zz=0; zz<6; zz++){ // borrar tentativa attempt[zz]=0; } } void readKeypad(){ char key = keypad.getKey(); if (key != NO_KEY) { switch(key) { case '*': z=0; Serial.println(" "); break; case '#': delay(50); Serial.println(" "); checkPIN(); break; default: attempt[z]=key; z++; } Serial.print("*"); led(); } } void loop(){ readKeypad(); } |
Este código, es susceptible de ser modificado y mejorado, sin duda. Por ese motivo, vamos a seguir en nuestro empeño en descubrir nuevos modos de lograr ese resultado e incluso mejorarlo. A continuación, veremos otra forma de enfocar el proyecto para lograr el mismo resultado.
EJEMPLO PIN CON LCD.
Esto esta listo, sin embargo, si le añadimos un LCD, tendremos un proyecto mucho más profesional que, podremos incluir en un proyecto que requiera de un sistema de seguridad que, incluya una clave de 6 o más dígitos. Veamos los pasos a seguir, para comprender cómo lograrlo.
EL CIRCUITO.
En primer lugar, presento el esquema electrónico es muy sencillo, a pesar de los cables que necesita utilizar para el conexionado de los distintos componentes. Se adjunta el archivo Fritzing, aunque lleva pequeñas modificaciones (el teclado y el PCF8574 son de mi creación).
Como previsiblemente se van a necesitar muchos puertos de Arduino, he pensado en aplicar un expansor de puertos, para actualizar y refrescar conocimientos, se puede leer el expansor de puerto. Aquí, lo utilizamos para interconectar el teclado, porque necesita 7 entradas/salidas, de igual modo, se podría haber utilizado para el LCD. En este ejemplo, se ha dotado de un display LCD de 20×4 como se aprecia en el circuito anterior.
EL CÓDIGO.
El siguiente es el código del segundo ejemplo, en este listado, he introducido unos cambios que permitirán una mayor flexibilidad, en el aspecto de margen de clave a utilizar, gracias a usar la librería Password que, nos facilita esta labor a la hora de crear y comparar una matriz de n dígitos.
La idea es la misma descrita en el ejemplo anterior. La inclusión de un LCD, para su control, hace necesario el uso de la librería LiquidCrystal, por supuesto, también hace falta usar las librerías incluidas en el ejemplo anterior que, no es necesario repetir.
Un punto a resaltar es que debido a que necesitamos más puertos del Arduino, en este caso, vamos a utilizar las entradas analógicas como entradas digitales, esto es posible si los declaramos con el numeral que le corresponde, es decir, en Arduino, los pines digitales normalmente, van del pin0 al pin13. Según el playground de Arduino:
1 2 3 4 5 6 7 8 9 |
Mapeo de Pins. Los pines analógicos de Arduino A0-A5 corresponden a los pines, del p14 al p19. Observe que esto son pines de Arduino y no se corresponden con los números de los pines físicos del chip Atmega. Los pines analógicos, pueden usarse de manera idéntica a los digitales, así que, por ejemplo, podría ver un código como este para configurar un pin analógico y establecerlo a HIGH: pinMode(14, OUTPUT); digitalWrite(14, HIGH); |
Aquí, usaremos los siguientes, el pinA0 por pin14, …, el pinA3 por pin17, procurando no utilizar los pines analógicos pinA4 y pinA5, por si se emplean en algún momento como E/S I2C.
1 2 3 4 5 6 |
byte rowPins[ROWS] = {5, 4, 3, 2}; // pines a conectar los row del teclado. byte colPins[COLS] = {16, 15, 14}; // AN2, AN1 y AN0. Por falta de pines const int buttonPin = 6; // para los relés mediante una R de 10k y un transistor NPN int sal1 = 17; // pin A4 salida a rele int sal2 = 18; // pin A5 salida a rele |
La subrutina: «void keypadEvent(KeypadEvent eKey)» tiene dos tareas; atiende al teclado y compara con la clave guardada. La subrutina: «void guessPassword()» es la que, mediante la ayuda de un transistor universal NPN, permite activar o desactivar un relé con el que, podemos manejar la potencia necesaria para abrir o cerrar el sistema de acceso, si es el caso, en el ejemplo activará un LED.
Número PIN largo.
Código completo con un PIN largo, usar el PCF8574.
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 |
/* password_lcd.pde Basado en artículos de la red. Modificado y adaptado el 07.11.2011 por V. Garcia. Para hispavila.com Utilizamos las librerías: [Password.h LiquidCrystal.h Keypad.h] que puede encontrar en: https://hispavila.com/3ds/atmega/clavenum.html Usando el actual ejemplo de hardware se pueden activa algo o desactivar mediante el teclado - emulando lo que se puede encontrar en algunos sistemas de alarma y así sucesivamente. Nuestro objetivo con este ejemplo es muy simple. Un sistema de espera para obtener un PIN que se especifique previamente. Si el PIN es correcto, hacer algo. Si el PIN es incorrecto, hacer otra cosa. Lo que las acciones pueden llegar a hacer. Con el proyecto vamos a activar o desactivar una salida digital. Este ejemplo es para darle un concepto y un marco para adaptar o construir ideas propias. Usa 4654 bytes con el IDE v. 00013 */ #include #include #include LiquidCrystal lcd(7, 8, 9, 10, 11, 12); Password password = Password( "1234456" ); // aquí puede poner su pasword const byte ROWS = 4; // Cuatro rows const byte COLS = 3; // Tres columns // Define el Keymap del teclado char keys[ROWS][COLS] = { {'1','2','3',}, {'4','5','6',}, {'7','8','9',}, {'*','0',' ',} }; // Conectar keypad ROW0, ROW1, ROW2 y ROW3 a los pines de Arduino. byte rowPins[ROWS] = {5, 4, 3, 2}; // pines a conectar los row del teclado. byte colPins[COLS] = {16, 15, 14}; // AN0, AN1 y AN2. Por falta de pines const int buttonPin = 6; // int buttonState = 0; int i; int sal1 = 17; // pin A4 salida a rele int sal2 = 18; // pin A5 salida a rele // Crear el Keypad Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); #define ledPin 13 void setup(){ pinMode(buttonPin, INPUT); pinMode(sal1, OUTPUT); pinMode(sal2, OUTPUT); pinMode(ledPin, OUTPUT); lcd.begin(20, 4); digitalWrite(ledPin, LOW); // pone el LED en off Serial.begin(9600); keypad.addEventListener(keypadEvent); //añad. evento listener al keypad keypad.setDebounceTime(250); lcd.clear(); //Borra el LCD lcd.setCursor(0,0); lcd.print("Entre secuencia PIN:"); lcd.setCursor(2,2); lcd.print("* Para terminar"); lcd.setCursor(0,3); lcd.print(" Intentelo: "); } void loop(){ keypad.getKey(); buttonState = digitalRead(buttonPin); if (buttonState == HIGH) { lcd.clear(); } } // atender algunos eventos especiales void keypadEvent(KeypadEvent eKey){ switch (keypad.getState()){ case PRESSED: lcd.print(eKey); switch (eKey){ case ' ': guessPassword(); break; default: password.append(eKey); } } } void guessPassword(){ if (password.evaluate()){ digitalWrite(ledPin,HIGH); // activa el LED de la puerta .5 seg. delay(500); digitalWrite(ledPin,LOW); // desactiva LED de la puerta digitalWrite(sal1, HIGH); // activa relé1 de la puerta digitalWrite(sal2, LOW); // desactiva relé2 de la puerta lcd.clear(); lcd.setCursor(0,0); lcd.print("PASSWORD VALIDO "); // password.reset(); // resetea password despues de entrada correcta delay(1000); lcd.setCursor(0,0); lcd.print("Bienvenido, Sr."); delay(1000); for (int positionCounter = 0; positionCounter < 22; positionCounter++) { // scroll one position right: lcd.scrollDisplayRight(); // espera un poco: delay(150); } delay(35000); // espera 35 segundos antes de volver al inicio setup(); // al inicio } else{ i = i++; digitalWrite(ledPin,LOW); digitalWrite(sal1, LOW); digitalWrite(sal2, HIGH); lcd.clear(); lcd.print("PASSWORD INVALIDO "); password.reset(); //resets password after INCORRECT entry delay(2000); // tiempo de retardo lcd.clear(); lcd.setCursor(0,2); lcd.print(" Intentelo: "); } if (i==3) { lcd.setCursor(0,1); lcd.print("Lo siento... "); lcd.setCursor(0,2); lcd.print("Intentelo mas tarde"); delay(30000); lcd.clear(); lcd.setCursor(0,2); lcd.print(" Intentelo: "); i=0; } } |
Descripción.
A estas subrutinas, se les ha incluido unos mensajes que, están acordes en cada caso, para hacer comprender al usuario, la situación que impera en cada momento. De modo que al iniciar el proceso, rige un mensaje que invita a introducir un número PIN y la forma de hacer su entrada mediante la pulsación de la tecla «*» (asterisco), como confirmación del PIN introducido.
Mediante la librería Pasword, se evalúa el PIN introducido y se produce un destello de un LED por un corto período de tiempo 1/2 segundo cuando hay coincidencia con la clave correcta. En ese momento, el mensaje que muestra la pantalla LCD, cambia a un nuevo mensaje que, le indica al usuario que ha obtenido el acceso y termina el proceso.
Un tiempo después (este intervalo de tiempo se puede ajustar a su gusto, antes de compilar), se vuelve a iniciar todo el proceso, a la espera de un nuevo intento por un nuevo usuario.
Este ejemplo es un proyecto «completo», ya que como elementos externos, sólo necesita de dos resistencias de 10k, dos transistores universales NPN y dos diodos 1N4007 y dos relés de 12V con doble contacto o similar, para poder activar cargas de mayor consumo. Para terminar el presente tutorial, se muestra un archivo AVI que ilustra el proyecto.
Fig. 5
Por supuesto que pude utilizar un dispositivo expansor de puertos como el conocido PCF8574. No lo he incluido en este diseño, por que los pines del Arduino Diecimila, están «clavados», es decir, el proyecto no necesita más de los que ya tiene. Sin embargo se muestra una distribución del mismo proyecto utilizando el PCF8574, por si alguien lo quisiera incluir. Anímese y pruebe usted mismo.
En este diseño, para mayor claridad, no se han incluido los relés y los componentes asociados para su control. Del mismo modo, se puede utilizar un segundo PCF8574, para el LDC y así liberar más puertos de Arduino, si fuera necesario.
Nota.- En estos ejemplos, se ha utilizado un LCD de 20×4, sin duda que el usuario, puede adaptar otro tipo de LCD, modificando los pines que se dedican al control de cada tipo de LCD.
Del mismo modo, se puede añadir otro PCF8574 (con A0 y A1 del PCF8574A a masa, en su direccionado I2C), para liberar los puertos extra que utiliza la pantalla LCD.
Las librerías utilizadas en este artículo o tutorial se pueden descargar desde estos enlaces:
LiquidCrystal.
Keypad.zip.
Password.
Esquema_teclado.
Esto es todo, por este simple tutorial, si necesita una aclaración indíquemelo.