Con el año nuevo he comenzado un proyecto nuevo para arduino, más por curiosidad propia que por necesidad. El proyecto requiere introducir texto de alguna forma, y puede que haya sido porque soy pésima buscando por internet, pero no he encontrado ningún teclado completo adaptado para conectarse a un arduino, solamente tecladitos numéricos (que a partir de ahora llamaré keypads).
Como el solo tener un keypad para escribir texto nunca fue un problema para quienes construyeron los primeros teléfonos móviles, he decidido imitar a esos visionarios, y programar el comportamiento para arduino.
Como este código es parte de un proyecto más grande, y en cualquier caso yo trabajo siempre pasito a pasito, el programa realmente “no sirve para nada”: solo escribe por el monitor serie el texto que se haya ido introduciendo, cuando se pulsa la tecla ‘#’ (si no se ha introducido texto, muestra la cadena vacía). Pero como algoritmo base para escribir texto desde un keypad, para añadirlo a otros proyectos, le veo mucha utilidad.
El esquema es la cosa más sencilla que os vayais a encontrar por aquí:
Sobre el circuito:
Ni siquiera me he molestado en pintar la fuente de alimentación porque esto es solo un prototipo y el arduino va enganchado al portátil por el puerto usb. Cómo va la conexión keypad-arduino y cómo funciona por dentro lo podéis ver muy bien explicado en este enlace (que siento decir que está en inglés).
El código, cuya parte interesante es el comportamiento cuando se pulsa una tecla, ha quedado así:
#include <Keypad.h>
const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {9, 8, 7, 6}; //connect to the column pinouts of the keypad
// matrix corresponding the letters with each of the buttons of the keypad
char letters[14] [4] = {
{'0', '0', '0', '0'},
{'a', 'b', 'c', '1'},
{'d', 'e', 'f', '2'},
{'g', 'h', 'i', '3'},
{'j', 'k', 'l', '4'},
{'m', 'n', 'o', '5'},
{'p', 'q', 'r', '6'},
{'s', 't', 'u', '7'},
{'v', 'w', 'x', '8'},
{'y', 'z', '9', '9'},
{',', '.', '-', ':'},
{'<', '?', '>', '!'},
{'@', '+', '=', '#'},
{'(', ')', '*', '/'}
};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
long latter_time;
long difference;
bool endtext = false;
char last_key_pressed = ' ';
byte num_times_pressed = 0;
char last_character = ' ';
String text = "";
void setup() {
Serial.begin(9600);
keypad.addEventListener(keypadEvent); // Add an event listener for this keypad
}
void loop() {
char key = keypad.getKey();
// if the key '#' has been pressed, it prints the text and sets it to ""
if (endtext) {
Serial.println("texto final");
Serial.println(text);
text = "";
last_character = ' ';
endtext = false;
}
}
// When a key is pressed, it has to be translated to the correct letter.
void keypadEvent(KeypadEvent key) {
switch (keypad.getState()) {
case PRESSED:
if (key == '#') {
// key '#' sets the end of the text.
if (last_character != ' ') {
text += last_character;
}
endtext = true;
} else {
if (key == last_key_pressed) {
/* If the same key is pressed several times, with an interval of less than 2 seconds,
* it is considered "looking for the next character".
* If the interval is of more than 2 seconds, it is considered a new character
*/
difference = millis() - latter_time;
latter_time = millis();
if (difference > 2000) {
text += last_character;
byte indice;
/* For keys A, B, C and D, corresponding positions 10 to 14 of the keys array,
* the way of calculating the index is, when converting to integer,
* substracting the ASCII corresponding to '7', that leaves A as 10, B as 11, and so on.
*/
if (key > '9') {
indice = key - '7';
} else {
indice = key - '0';
}
last_character = letters[indice][0];
Serial.print("caracter: ");
Serial.println(last_character);
num_times_pressed = 1;
} else {
byte indice;
if (key > '9') {
indice = key - '7';
} else {
indice = key - '0';
}
last_character = letters[indice][num_times_pressed % 4];
Serial.print("caracter: ");
Serial.println(last_character);
num_times_pressed++;
}
} else {
last_key_pressed = key;
if (last_character != ' ') {
text += last_character;
}
byte indice;
if (key > '9') {
indice = key - '7';
} else {
indice = key - '0';
}
last_character = letters[indice][0];
Serial.print("caracter: ");
Serial.println(last_character);
num_times_pressed = 1;
latter_time = millis();
}
}
break;
}
}
El repositorio con el código lo podéis encontrar aquí.
Sobre el código:
La configuración de qué letras es cada tecla del keypad, como soy más chula que un ocho, la he hecho configurable (en la matriz ‘letters‘).
Para saber el índice de la primera dimensión de la matriz tengo que hacer un poco de encaje de bolillos: en el caso de las teclas del 0 al 9 solo he tenido que pasarlas a entero cogiendo su código ASCII, restándoles el código ASCII de 0, y pasando el resultado a entero. Esto permite acceder a las posiciones 0 a 9 de letters. Para las teclas A, B, C y D he hecho lo mismo, pero como las posiciones en la matriz son la 10, 11, 12 y 13, he calculado qué carácter, al restarle su código ASCII a esos cuatro caracteres, daría esos cuatro índices. La respuesta es el ‘7’.
El índice de la segunda dimensión depende de si se está pulsando una tecla diferente a la última pulsada o la misma, y en este último caso, si han pasado más de dos segundos desde la última pulsación. Se accede haciendo módulo de 4 para que si se pulsa la misma tecla más de 4 veces se vuelva a seleccionar la fila desde el primer elemento, en vez de dar error.
Como he comentado, el programa solo escribe por el monitor serie, pero escribe la “letra” seleccionada cada vez que se pulsa una tecla. O sea que si se pulsa una vez el 1, escribirá “a”, si se pulsa dos veces seguidas el 1 escribirá “a” “b”, y así. Y bueno, cuando se pulse ‘#’ escribirá toda la cadena que se lleve escrita, y reseteará el texto.
He dejado el botón ‘*’ (asterisco) libre porque lo voy a necesitar cuando use este código en la siguiente fase del proyecto, pero añadirle también caracteres es bastante inmediato, sabiendo cómo está hecho el resto.
************************************************************
Espero que esta entrada pueda ser de utilidad, y si no, como siempre, aquí tenéis un gato para compensar.
One thought on “Proyecto con Arduino: escribir texto con un teclado numérico (como en un móvil antiguo)”