Bienvenidos a esta práctica en la que vamos a crear un Teclado matricial básico con ARDUINO UNO Q y lo conectaremos en red a un ordenador con Kali Linux mediante un socket Stream AF_INET con Python. Esta práctica que vas a empezar se considera una práctica de IoT ya que, por un lado, conectaremos una placa electrónica ARDUINO a un PC con W10 y a la placa conectaremos un teclado matricial 4x4 que nos permitirá introducir los números del 0 al 9 y las letras de la A a la D, además de los símbolos * y #.

Por el otro lado, desde un servidor realizado en Python y que ejecutaremos desde un PC con KALI Linux, registraremos las pulsaciones del teclado matricial.

Todo esto es posible gracias a que la ARDUINO UNO Q tiene un módulo de comunicación Wi-Fi integrado en la placa y nos permitirá establecer dicha comunicación.

A continuación te indico los componentes necesarios para realizar dicho fin:

● Un PC con Windows.
● Un PC Kali Linux.
● Una placa Arduino UNO Q.
● Un teclado matricial 4x4.
● Arduino APP Lab.

Laboratorio montado para la práctica. A la izquierda Arduino conectado al teclado en un W10. A la derecha Kali Linux.

Una vez que tengas recopilado todo lo necesario, podemos empezar con la práctica. Lo primero que vamos a hacer es el montaje de la placa Arduino UNO Q y el teclado matricial 4x4.

Conexión de los puertos del teclado a los pines digitales del Arduino Q.

La conexión es directa a través de cableado de pines. Empezando por los zócalos de la derecha del teclado matricial hacia la izquierda del mismo, como hay 8 terminales, los conectaremos en dicho orden (de derecha a izquierda) a los pines del ARDUINO Q 2, 3, 4, 5, 6, 7, 8 y 9, como ves en la imagen anterior.

NUEVO SKETCH PARA EL TECLADO.

A continuación vamos a exponer el código para el teclado matricial con Arduino UNO Q. En mi canal de YouTube verás que existe un sketch de teclado matricial para ARDUINO UNO. Pero no nos vale ese código porque tiene integrada una librería antigua que en la versión de ARDUINO UNO R3 vale perfectamente, pero para la nueva Arduino UNO Q está obsoleta y es ineficaz, ya que el procesador de ARDUINO UNO Q es más potente que el procesador ATmega de la original. Pero en este código vamos a utilizar una librería Bridge que permite la comunicación entre nuestro Arduino Q y un sistema Linux (como Kali) conectado a través de USB o UART. En este caso, se usa para enviar las teclas pulsadas directamente a un script Python en Kali que haremos a continuación.

#include <Arduino_RouterBridge.h> const int numFilas = 4; const int numColumnas = 4; int pinFilas[numFilas] = {9, 8, 7, 6}; int pinColumnas[numColumnas] = {5, 4, 3, 2}; char teclas[numFilas][numColumnas] = { {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'} }; void setup() { Serial.begin(115200); Bridge.begin(); for (int i = 0; i < numFilas; i++) { pinMode(pinFilas[i], OUTPUT); digitalWrite(pinFilas[i], HIGH); } for (int j = 0; j < numColumnas; j++) { pinMode(pinColumnas[j], INPUT_PULLUP); } Serial.println("============================================="); Serial.println("** ARDUINO UNO Q - TECLADO Y BRIDGE KALI **"); Serial.println("============================================="); } void loop() { Bridge.update(); for (int f = 0; f < numFilas; f++) { digitalWrite(pinFilas[f], LOW); delayMicroseconds(50); for (int c = 0; c < numColumnas; c++) { if (digitalRead(pinColumnas[c]) == LOW) { char tecla = teclas[f][c]; // 1. Mostrar de inmediato en el Monitor Serie local Serial.print("[TECLA PRESIONADA]: "); Serial.println(tecla); // 2. Transmitir el evento a la parte Python String teclaStr = String(tecla); Bridge.call("enviar_tecla", teclaStr); // Antirrebote while (digitalRead(pinColumnas[c]) == LOW) { Bridge.update(); delay(10); } delay(150); } } digitalWrite(pinFilas[f], HIGH); } delay(10); } Arduino Q
¿Qué hace este sketch?

Este programa permite leer un teclado matricial 4x4 conectado a una placa Arduino UNO Q sin utilizar la librería Keypad.h habitualmente usada. Gestiona directamente los pines del microcontrolador para detectar qué tecla se ha pulsado y, además, utiliza el sistema Bridge para comunicarse con Kali Linux a través de la conexión USB.

El código escanea constantemente el teclado para detectar pulsaciones. Cuando el usuario presiona una tecla, el programa identifica cuál es, la muestra por el puerto serie (monitor serie de Arduino IDE) y, simultáneamente, la envía a Kali Linux mediante el sistema Bridge, donde un script Python puede procesarla para validar PINs, controlar accesos o cualquier otra funcionalidad.

Definición de variables.

#include <Arduino_RouterBridge.h> const int numFilas = 4; const int numColumnas = 4; int pinFilas[numFilas] = {9, 8, 7, 6}; int pinColumnas[numColumnas] = {5, 4, 3, 2}; char teclas[numFilas][numColumnas] = { {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'} };

La primera línea incluye la librería Arduino_RouterBridge.h, que permite la comunicación entre el Arduino y Kali Linux a través del puerto USB. Las variables numFilas y numColumnas definen las dimensiones del teclado (4x4). Los arrays pinFilas y pinColumnas asignan cada fila y columna a un pin físico de Arduino. Por ejemplo, la primera fila se conecta al pin digital 9, la segunda al 8, y así sucesivamente. Las columnas se conectan a los pines 5, 4, 3 y 2. La variable teclas crea un mapa que relaciona cada posición física con el carácter que debe mostrar: la tecla en la fila 0 y columna 0 es '1', la de la fila 0 columna 3 es 'A', etc.

Configuración del Setup.

void setup() { Serial.begin(115200); Bridge.begin(); for (int i = 0; i < numFilas; i++) { pinMode(pinFilas[i], OUTPUT); digitalWrite(pinFilas[i], HIGH); } for (int j = 0; j < numColumnas; j++) { pinMode(pinColumnas[j], INPUT_PULLUP); } Serial.println("============================================="); Serial.println("** ARDUINO UNO Q - TECLADO Y BRIDGE KALI **"); Serial.println("============================================="); }

En el setup(), primero se inicializa la comunicación serie a 115200 baudios y el sistema Bridge con Bridge.begin(), que establece el enlace con Kali Linux a través del USB. A continuación, se configuran los pines de las filas como salidas y se ponen en estado HIGH (desactivadas). Las columnas se configuran como entradas con resistencia de pull-up activada, lo que significa que en reposo leerán HIGH y solo cambiarán a LOW cuando se pulse una tecla. Finalmente, se muestra un mensaje de inicio por el puerto serie para confirmar que el sistema está listo.

Bucle principal del Loop.

void loop() { Bridge.update(); for (int f = 0; f < numFilas; f++) { digitalWrite(pinFilas[f], LOW); delayMicroseconds(50); for (int c = 0; c < numColumnas; c++) { if (digitalRead(pinColumnas[c]) == LOW) { char tecla = teclas[f][c]; // 1. Mostrar de inmediato en el Monitor Serie local Serial.print("[TECLA PRESIONADA]: "); Serial.println(tecla); // 2. Transmitir el evento a la parte Python String teclaStr = String(tecla); Bridge.call("enviar_tecla", teclaStr); // Antirrebote while (digitalRead(pinColumnas[c]) == LOW) { Bridge.update(); delay(10); } delay(150); } } digitalWrite(pinFilas[f], HIGH); } delay(10); }

El bucle loop() se ejecuta constantemente y su funcionamiento se divide en varias fases:

1. Actualización del Bridge: Bridge.update() mantiene activa la comunicación con Kali Linux, asegurando que los datos puedan transmitirse en cualquier momento.

2. Escaneo de filas: Se activa una fila poniéndola en LOW (las demás permanecen en HIGH) y se espera 50 microsegundos para estabilizar la señal.

3. Lectura de columnas: Se comprueba si alguna columna está en LOW. Si ocurre, significa que la tecla en esa intersección fila-columna ha sido pulsada.

char tecla = teclas[f][c]; Serial.print("[TECLA PRESIONADA]: "); Serial.println(tecla); String teclaStr = String(tecla); Bridge.call("enviar_tecla", teclaStr);

4. Procesamiento de la tecla: Cuando se detecta una pulsación, se identifica el carácter correspondiente, se muestra en el monitor serie y se envía a Kali Linux mediante Bridge.call("enviar_tecla", teclaStr), que ejecuta una función en el script Python del lado de Kali. El primer parámetro es el nombre de la función Python que procesará la tecla, y el segundo es el carácter pulsado.

5. Gestión de rebotes: Se utiliza un bucle while que espera hasta que el usuario suelte la tecla, evitando lecturas múltiples no deseadas. Durante esta espera, se sigue actualizando el Bridge con Bridge.update() para no perder la comunicación. Al soltar la tecla, se añade un retardo de 150 milisegundos para estabilizar la señal.

6. Desactivación de la fila: Una vez escaneada la fila, se desactiva poniéndola en HIGH y se pasa a la siguiente. Al finalizar el escaneo de todas las filas, se añade un pequeño retardo de 10 milisegundos para optimizar el uso del procesador y evitar saturación.

Ventajas de usar Bridge.

Nota importante: Kali Linux debe tener instalada la librería arduino-router-bridge y ejecutar un script Python que registre la función enviar_tecla para recibir las pulsaciones. Este script Python procesará las teclas según la lógica definida (validación de PINs, control de accesos, etc.).

Modificar el main.py del proyecto.

Como he mencionado al principio, este proyecto de Arduino UNO Q está diseñado a través del Arduino APP Lab 0.90, lo que implica que este entorno se conecta con PYTHON. Vete al menú del proyecto y ábrete el archivo Main.py.

Laboratorio montado en Arduino APP Lab. Los archivos se ven a la izquierda de la pantalla.

import socket import time import sys from arduino.app_utils import Bridge KALI_IP = "192.168.1.16" KALI_PORT = 5000 sock = None def conectar_kali(): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(2.0) s.connect((KALI_IP, KALI_PORT)) s.settimeout(None) print(f"[+] Conectado a Kali ({KALI_IP}:{KALI_PORT})", flush=True) return s except Exception as e: print(f"[-] Error conectando a Kali: {e}", flush=True) return None def enviar_a_kali(mensaje): global sock if sock is None: sock = conectar_kali() if sock: try: sock.sendall(f"{mensaje}\n".encode('utf-8')) print(f" --> [TCP -> KALI] Tecla enviada: '{mensaje}'", flush=True) except Exception as e: print(f"[!] Error de envío por socket ({e}). Reconectando...", flush=True) if sock: sock.close() sock = conectar_kali() def enviar_tecla(tecla): print(f"[EVENTO C++] Tecla pulsada: {tecla}", flush=True) enviar_a_kali(tecla) def main(): global sock print("[+] Servicio Bridge iniciando en Arduino UNO Q...", flush=True) sock = conectar_kali() bridge = Bridge() bridge.provide("enviar_tecla", enviar_tecla) print("[+] Método 'enviar_tecla' registrado en el Bridge", flush=True) print("[+] Escuchando eventos del teclado matricial...", flush=True) # Mantenemos el proceso vivo para procesar eventos RPC while True: time.sleep(1) if __name__ == "__main__": main() Python (Arduino APP Lab)
¿Qué hace este script de Python (main.py) en Arduino APP Lab?

Este script se ejecuta dentro del propio Arduino UNO Q en el entorno Arduino APP Lab, que permite ejecutar código Python junto con el sketch de C++. Su función principal es actuar como puente (Bridge) entre el código C++ del Arduino y Kali Linux a través de WiFi.

Mientras el sketch de C++ se encarga de leer el teclado matricial a bajo nivel, este script Python maneja la comunicación en red, recibiendo las teclas del sketch C++ y reenviándolas a Kali Linux mediante una conexión TCP/IP.

Flujo de trabajo del script:

  1. Inicialización: Al arrancar en el Arduino UNO Q, el script establece una conexión TCP con Kali Linux en la IP y puerto configurados (192.168.1.16:5000).
  2. Registro del Bridge: Utilizando la librería arduino.app_utils.Bridge, registra el método enviar_tecla para que pueda ser invocado desde el sketch de C++ mediante Bridge.call().
  3. Recepción de eventos desde C++: Cuando el sketch de C++ detecta una tecla pulsada, ejecuta Bridge.call("enviar_tecla", teclaStr), lo que activa la función enviar_tecla() en este script Python.
  4. Reenvío a Kali: La función enviar_tecla() recibe el carácter, lo muestra en la consola de Arduino APP Lab y llama a enviar_a_kali(), que envía la tecla por el socket TCP a Kali Linux para su procesamiento.
  5. Gestión de reconexiones: Si la conexión con Kali se pierde, el script intenta reconectarse automáticamente cada vez que se intenta enviar una tecla, asegurando la continuidad del servicio.

Componentes principales del código:

from arduino.app_utils import Bridge KALI_IP = "192.168.1.16" KALI_PORT = 5000 sock = None

Importa la clase Bridge que permite la comunicación entre C++ y Python en el Arduino. Define la IP y puerto de Kali Linux donde se enviarán las teclas. La variable sock almacenará el socket de conexión una vez establecido.

def conectar_kali(): # Crea un socket TCP, establece timeout de 2 segundos y se conecta a Kali # Retorna el socket si la conexión es exitosa, o None si falla

Esta función intenta establecer la conexión TCP con Kali Linux. Utiliza un timeout de 2 segundos para evitar bloqueos si Kali no responde. Si la conexión es exitosa, elimina el timeout para que la comunicación sea fluida y retorna el socket. Si falla, retorna None.

def enviar_a_kali(mensaje): global sock if sock is None: sock = conectar_kali() if sock: try: sock.sendall(f"{mensaje}\n".encode('utf-8')) except Exception as e: # Si hay error, cierra el socket y reconecta

Función responsable de la transmisión de datos a Kali. Primero verifica si el socket existe; si es None, intenta conectar. Convierte el mensaje a bytes (codificación UTF-8), añade un salto de línea al final y lo envía por el socket. Si ocurre un error, cierra el socket y lo reinicia para intentar reconectar en el próximo envío.

def enviar_tecla(tecla): print(f"[EVENTO C++] Tecla pulsada: {tecla}", flush=True) enviar_a_kali(tecla)

Este es el método registrado en el Bridge. Cuando el sketch de C++ ejecuta Bridge.call("enviar_tecla", teclaStr), Python recibe la tecla y llama a esta función. Muestra la tecla en la consola y la reenvía a Kali mediante enviar_a_kali(). Es el punto de entrada de los eventos desde el hardware.

def main(): bridge = Bridge() bridge.provide("enviar_tecla", enviar_tecla) while True: time.sleep(1)

La función principal crea una instancia de Bridge y registra el método enviar_tecla para que esté disponible para el sketch de C++. Luego, mantiene el script vivo con un bucle infinito que solo espera. El Bridge procesa los eventos RPC de forma asíncrona, por lo que el script solo necesita estar activo sin consumir recursos excesivos.

Comunicación completa entre C++ y Python en Arduino UNO Q:

Flujo de datos dentro del Arduino:

Usuario → Pulsa tecla → Sketch C++ (lee teclado) → Bridge.call("enviar_tecla", tecla) → Python (main.py) → Socket TCP → Kali Linux

Ejemplo de salida en Arduino APP Lab:
[EVENTO C++] Tecla pulsada: 5
--> [TCP -> KALI] Tecla enviada: '5'

Diferencias entre el sketch C++ y main.py:

Aspecto sketch.ino (C++) main.py (Python)
Función principal Leer el teclado matricial a bajo nivel Gestionar la comunicación en red con Kali
Acceso a hardware Controla directamente los pines GPIO No accede al hardware directamente
Comunicación Llama a Python mediante Bridge.call() Recibe llamadas desde C++ y envía a Kali vía TCP
Librerías Arduino_RPClite.h arduino.app_utils.Bridge y socket

Requisitos para su funcionamiento:

Conceptos clave aprendidos:

Nota importante: Este script debe ejecutarse dentro de Arduino APP Lab junto con el sketch de C++. Ambos se ejecutan simultáneamente en el Arduino UNO Q, comunicándose a través del Bridge.

Abordaremos la configuración del listener en Kali Linux en la siguiente entrega del tutorial.

Comparte este conocimiento en redes sociales