Este proyecto, publicado por Electronics For You el 15 de octubre, implementa una pasarela IoT inteligente que conecta redes inalámbricas de sensores de largo alcance con un panel web moderno y análisis con IA. Con un transceptor SX1278 manejado por la biblioteca RadioLib, el receptor capta los paquetes LoRa que llegan en la banda de 433 MHz, calcula métricas de calidad de señal (RSSI y SNR) y ordena los datos válidos en un JSON.
¿Cómo funciona la pasarela?
El sistema usa el framework Arduino RouterBridge para exponer un endpoint RPC (getLatestPayload), con el que un router acompañante o un sistema anfitrión obtiene al instante los datos de los sensores. Esos datos van directo a un panel web adaptable que muestra la telemetría en tiempo real, las tendencias ambientales y el estado de cada nodo. Un componente de IA integrado analiza los flujos de datos para detectar anomalías, anticipar fallas de sensores o entregar diagnósticos automáticos. El proyecto combina comunicación de radio de largo alcance, llamadas a procedimientos remotos, visualización web en tiempo real y análisis con IA en una solución de monitoreo completa.
Código del receptor LoRa
Primero hay que instalar la biblioteca necesaria en el Arduino IDE. En el menú Sketch se elige "Manage Library", se busca el nombre de la biblioteca y se pulsa "Install". Este proyecto requiere la biblioteca RadioLib de Jan Gromes.
El código del receptor y del acuse de recibo con RadioLib se organiza en ocho pasos.
Paso 1. Inclusión de la biblioteca y pines
- Línea 1.
#include <RadioLib.h>importa la biblioteca universal de comunicación inalámbrica que controla el módulo de radio LoRa. - Líneas 3 a 6. Definen la interfaz SPI y los pines de control conectados al SX1278.
LORA_CS (10)es el pin de selección de chip para SPI.LORA_DIO0 (2)es el pin digital que señala las interrupciones de hardware, por ejemplo un paquete recibido.LORA_RESET (9)reinicia por hardware el transceptor.LORA_BUSY (RADIOLIB_NC)queda "no conectado", porque el SX1278, a diferencia de la serie SX1262, no tiene un pin BUSY dedicado.
Paso 2. Objetos globales
- Línea 8. Crea el objeto de radio SX1278 con el mapeo de pines del paso 1.
- Línea 11. Declara la cadena global
latestJsonPayload, que parte en{}. Guarda el último paquete válido para que lo lean componentes de nivel superior, como el lado Linux de la Arduino UNO Q a través de RPC.
Paso 3. Configuración inicial con setup()
- Líneas 14 y 15. Inician
Monitor.begin(9600), la interfaz serial de depuración del ecosistema Arduino App Lab y UNO Q, con un tiempo de espera de 3 segundos para que la placa arranque aunque no haya un terminal serial conectado. - Línea 17. Llama a
radio.begin(...)para fijar los parámetros de radio. Frecuencia portadora de 433,0 MHz, ancho de banda de 125,0 kHz, factor de dispersión SF7, tasa de codificación 4/5, palabra de sincronización 0x12, potencia de transmisión de 10 dBm y preámbulo de 8 símbolos. - Líneas 18 a 24. Si la inicialización devuelve
RADIOLIB_ERR_NONE(éxito), activa encabezados explícitos y la verificación CRC por hardware para descartar tramas corruptas, y llama aradio.startReceive()para dejar el módulo escuchando de forma continua y asíncrona. - Líneas 25 a 28. Si la inicialización falla, imprime el código de error y detiene la ejecución en un bucle
while(1).
Paso 4. Detección de paquetes en loop()
Línea 32. Revisa if (digitalRead(LORA_DIO0) == HIGH). El SX1278 pone DIO0 en alto apenas detecta en el aire un paquete válido y lo guarda en su búfer FIFO interno.
Paso 5. Extracción del paquete con dos estrategias
- Líneas 33 y 34. Intenta una lectura estándar de alto nivel con
radio.readData(payload). - Líneas 36 a 45, lógica de rescate. Si la extracción estándar falla, por ejemplo por un error al interpretar la cadena, crea un búfer de bytes
uint8_t buffer[16], lee a mano los primeros 16 bytes del FIFO, recorre los bytes y se queda solo con los caracteres ASCII imprimibles (rango 32 a 126). Así reconstruye el paquete y no se pierden datos.
Paso 6. Métricas de calidad de señal
- Líneas 47 y 48. Consultan los registros del SX1278 para diagnosticar el enlace.
getRSSI()mide la intensidad de la señal recibida en dBm ygetSNR()calcula la relación señal a ruido en dB.
Paso 7. Filtrado, JSON y acuse de recibo
- Línea 50. Evalúa si el paquete es válido. Solo se procesa si no está vacío y supera un umbral de claridad de señal (
snr > -12.0dB). - Líneas 51 a 57. Da formato JSON al paquete, el RSSI y el SNR (por ejemplo
{"payload":"hello","rssi":-45.00,"snr":8.50}) y actualiza la variable globallatestJsonPayload. - Línea 60. Envía el JSON a Monitor para depuración.
- Líneas 63 a 66, acuse de recibo. Espera 50 ms para que el nodo emisor pase a modo escucha, ejecuta
radio.transmit("ok")para devolverle un ACK y restablece los encabezados explícitos y el CRC del transceptor.
Paso 8. Vuelta al modo escucha
- Línea 69. Ejecuta otra vez
radio.startReceive()para que el módulo vuelva a la recepción pasiva después del ACK. - Línea 71.
delay(20)agrega una pausa corta entre lecturas para estabilizar la ejecución y bajar la carga de CPU cuando no hay actividad.
El panel web y el motor de anomalías
Estructura de directorios
Arquitectura del proyecto
El proyecto implementa un panel de telemetría LoRa con inteligencia en el borde, organizado en cuatro capas.
- Capa de ingesta por socket TCP. Un hilo en segundo plano (
tcp_bridge_reader_thread) escucha de forma continua en el puerto TCP 7500 (127.0.0.1) los JSON que envía el reenviador de paquetes del hardware LoRa. - Motor de tendencias y aprendizaje automático (
DynamicTrendMLModel). Clasifica la telemetría con expresiones regulares en categorías (temperatura y humedad, sonda de nivel de agua, contador o latido). Sigue la evolución de la línea base con un promedio móvil exponencial (EWMA) con factor alfa de 0,15. Calcula picos de tasa de cambio por minuto frente a umbrales de desviación estándar móvil de 3 sigma. Y usa un modelo Isolation Forest de Scikit-Learn para detectar anomalías no lineales en el espacio de temperatura, humedad y RSSI. - Persistencia de datos. Crea y alimenta de forma automática el archivo
data/sensor_history.csvcon registros estructurados. - Presentación web en Flask y reportes PDF. Sirve interfaces dinámicas con Chart.js para la visualización en tiempo real y usa ReportLab para generar reportes de auditoría en PDF.
Backend (app.py)
- Importaciones y configuración. Importa módulos estándar (socket, threading, re, csv) junto con herramientas numéricas (pandas, numpy, sklearn). Fija Matplotlib en el backend no interactivo "Agg" para que funcione en un servidor.
- Función
init_csv(). Valida el directorio de destino y creasensor_history.csvcon encabezados estándar (timestamp, raw_payload, data_type, parsed_json, rssi, snr, is_anomaly, anomaly_reason). - Clase
DynamicTrendMLModel.fit_model()ajusta un modelo IsolationForest cada vez que hay 15 muestras o más.classify_and_parse()extrae los parámetros físicos con expresiones regulares.detect_anomaly()valida las lecturas con la desviación de velocidad, límites absolutos (sobre 55 °C o bajo -10 °C), condiciones de estanque vacío y el puntaje de Isolation Forest. tcp_bridge_reader_thread(). Corre en un hilo daemon, interpreta de forma continua los objetos JSON separados por salto de línea que llegan al puerto 7500 y guarda los metadatos extraídos.- Ruta
download_report(). Reúne la telemetría reciente, arma en memoria cuatro gráficos de líneas con BytesIO, estructura tablas de detalle con estilo y devuelve un documento PDF generado de forma automática.
Interfaz web (templates/)
- login.html. Exige autenticación básica (admin / admin123) con sesiones de Flask y estilos de Bootstrap 5.
- index.html. Consulta
/api/datacada 2.000 milisegundos y muestra tarjetas con métricas en tiempo real y un gráfico de líneas dinámico de 20 muestras. - history.html. Pide
/api/history_allpara llenar una tabla de auditoría y marca en rojo las tramas anómalas con la clase CSSanomaly-row. - analytics.html. Integra Chart.js, hammerjs y chartjs-plugin-zoom para desplazar y ampliar los históricos de temperatura, humedad, RSSI y SNR.
¿Cómo se conecta el módulo Ra-02?
Estas son las conexiones entre el módulo LoRa Ai-Thinker Ra-02 y la Arduino UNO Q, según el diagrama del circuito.
Alimentación y tierra
- Tierra común (GND). El pin GND del conector de alimentación de la Arduino se une con el cable negro a los pines de tierra del Ra-02 para fijar una referencia eléctrica común.
- Voltaje de operación. El Ra-02 funciona estrictamente con lógica y alimentación de 3,3 V. Hay que asegurar que el montaje entregue 3,3 V al pin de 3,3 V del módulo.
Bus SPI
- SCK (pin 13). Cable rosado o rojo. Lleva la señal de reloj que genera la Arduino para sincronizar la transmisión.
- MISO (pin 12). Cable verde. Lleva los datos que salen del Ra-02 hacia la Arduino.
- MOSI (pin 11). Cable azul. Lleva los datos que salen de la Arduino hacia el Ra-02.
- NSS / CS (pin 10). Cable café. La Arduino lo usa para habilitar y seleccionar el Ra-02 en el bus SPI.
Control y reinicio
- RST (pin 9). Cable morado. Permite que la Arduino reinicie por hardware el chip SX1278 durante la inicialización.
Prueba del sistema
Para probar el sistema, primero se revisan todas las conexiones entre la Arduino UNO Q y el módulo Ra-02 para evitar cortocircuitos. Luego se encienden la pasarela y el nodo remoto de campo, que transmite en vivo temperatura y humedad desde un sensor DHT11. Después se inicia el puente de socket TCP en el puerto 7500 para comprobar que los paquetes de radio se interpretan, se guardan en data/sensor_history.csv y pasan por los motores de anomalías EWMA e Isolation Forest. Al final se entra al panel web en http://localhost:5050 para confirmar que las métricas del DHT11 se actualizan cada 2 segundos, verificar el desplazamiento de los gráficos en la página de análisis y generar un reporte de auditoría en PDF descargable.










