Documentación del Proyecto: IoT Sentinel
Sistema de Vigilancia de Espectro RF y Alerta OOB (Out-Of-Band)
Autor: [PoisonXploIT]
Estado: Prototipo Funcional (Fase Lógica Completada / Hardware GSM Simulado)
1. Resumen Ejecutivo
IoT Sentinel es un prototipo de dispositivo de seguridad diseñado para detectar anomalías en el espectro de radiofrecuencia (específicamente en la banda ISM de 433MHz), como intentos de inhibición (jamming) o tráfico inusual.
El objetivo principal es crear un sistema resiliente que no dependa de la infraestructura de red local (Wi-Fi/Ethernet) para reportar incidentes. Si detecta un ataque, el sistema debe utilizar un canal secundario “Fuera de Banda” (OOB), en este caso la red móvil GSM, para enviar una alerta SMS crítica al administrador.
Este documento detalla el desarrollo del prototipo utilizando un microcontrolador RP2040 y un transceptor NRF905, así como los desafíos encontrados en la implementación del módulo GSM y la solución de software adoptada.
2. Arquitectura del Sistema
El sistema se basa en una arquitectura de tres pilares:
-
El Cerebro (Controlador): Un microcontrolador Raspberry Pi Pico (RP2040-Zero) corriendo CircuitPython. Gestiona la lógica de vigilancia, los umbrales de alarma y la comunicación entre módulos.
-
El Oído (Sensor RF): Un módulo transceptor NRF905. Configurado en modo de solo escucha (RX) en 433MHz para monitorear la actividad del espectro. Se comunica con el cerebro vía SPI.
-
La Voz (Comunicación OOB): Módulo GSM/GPRS (modelos probados: SIM800C USB y SIM900A UART) encargado de enviar alertas SMS mediante comandos AT.
3. Desarrollo FASE 1: Integración RF (NRF905)
3.1. Desafío Físico y Conexión
El primer reto fue la integración física del RP2040-Zero en la protoboard debido a su tamaño reducido, que dificultaba el acceso a los pines laterales. Se solucionó utilizando el canal central de la protoboard y distribuyendo la alimentación (3.3V y GND) a los rieles laterales para facilitar el cableado hacia el módulo NRF905.
Mapa de Conexiones SPI (RP2040 → NRF905): | Señal | Pin RP2040 | Cable Color | | :--- | :--- | :--- | | SCK (Reloj) | GP 6 | Amarillo | | MOSI (Datos Salida)| GP 7 | Verde | | MISO (Datos Entrada)| GP 4 | Azul | | CSN (Chip Select) | GP 5 | Blanco | | TX_EN (Activar TX) | GP 0 | Naranja | | TRX_CE (Activar RX)| GP 1 | Morado |
3.2. Lógica de Software RF
Se utilizó la librería busio.SPI de CircuitPython para establecer la comunicación. El éxito de esta fase se confirmó al enviar un paquete de configuración al NRF905 y leer de vuelta el registro de estado, obteniendo el byte de confirmación 0x76 (indicador de conexión correcta en 433MHz).
3.3 Prueba de PINES
import board
import busio
import digitalio
import time
print(“\n--- INICIANDO FASE 2: TEST FINAL (Esquema Simplificado) ---”)
# --- 1. CONFIGURACIÓN SPI (Pines GP4, GP6, GP7) ---
try:
# SCK=GP6 (Amarillo), MOSI=GP7 (Verde), MISO=GP4 (Azul)
spi = busio.SPI(clock=board.GP6, MOSI=board.GP7, MISO=board.GP4)
print(“Bus SPI iniciado.”)
except Exception as e:
print(f”[ERROR] Fallo en SPI: {e}”)
# --- 2. PINES DE CONTROL ---
# CSN = GP5 (Blanco)
csn = digitalio.DigitalInOut(board.GP5)
csn.direction = digitalio.Direction.OUTPUT
csn.value = True # Desactivado (High)
# TX_EN = GP0 (Naranja)
tx_en = digitalio.DigitalInOut(board.GP0)
tx_en.direction = digitalio.Direction.OUTPUT
tx_en.value = False # Standby
# TRX_CE = GP1 (Morado)
trx_ce = digitalio.DigitalInOut(board.GP1)
trx_ce.direction = digitalio.Direction.OUTPUT
trx_ce.value = False # Standby
# NOTA: PWR_UP está conectado directo a 3.3V (Cable Marrón)
# --- 3. FUNCIONES DEL DRIVER ---
def nrf905_test():
"""Envía configuración y verifica si vuelve igual"""
# Configuración de prueba (433MHz básica)
# Byte 0 (0x76) es la clave que buscamos
config_pattern = bytearray([0x00, 0x76, 0x0E, 0x44, 0x20, 0x20, 0xCC, 0xCC, 0xCC, 0xCC, 0x58])
# 1. Poner en modo programación (Standby)
tx_en.value = False
trx_ce.value = False
time.sleep(0.1)
# 2. Escribir
while not spi.try_lock(): pass
csn.value = False
spi.write(config_pattern)
csn.value = True
spi.unlock()
print(“[TX] Configuración enviada…”)
time.sleep(0.01)
# 3. Leer
read_cmd = bytearray([0x10] + [0x00]*10) # Comando lectura
rx_buffer = bytearray(len(read_cmd))
while not spi.try_lock(): pass
csn.value = False
spi.write_readinto(read_cmd, rx_buffer)
csn.value = True
spi.unlock()
return rx_buffer[1:] # Quitamos el primer byte basura
# --- 4. EJECUCIÓN ---
while True:
print(“\nComprobando conexión con NRF905…”)
try:
resultado = nrf905_test()
byte_clave = resultado[0]
print(f”Leído: {[hex(x) for x in resultado]}”)
if byte_clave == 0x76:
print(“¡CONEXIÓN EXITOSA! El NRF905 responde.”)
print(“Ya puedes pasar a la siguiente fase.”)
break # Salimos del bucle si funciona
elif byte_clave == 0x00:
print(“ERROR: Todo Ceros. Revisa el cable AZUL (MISO) o el ROJO (Energía).”)
elif byte_clave == 0xFF:
print(“ERROR: Todo Unos. Revisa el cable NEGRO (GND) o el BLANCO (CSN).”)
else:
print(“ERROR: Datos corruptos. Revisa cables flojos.”)
except Exception as e:
print(f”Error grave: {e}”)
time.sleep(2)
4. Desarrollo FASE 2: El Desafío GSM y Análisis de Fallos
El objetivo era permitir que el RP2040 enviara comandos AT al módem GSM para despachar SMS. Esta fase enfrentó obstáculos de hardware significativos que impidieron la integración física final en este prototipo.
4.1. Intentos y Obstáculos
Intento A: Módulo SIM800C (Dongle USB) Se intentó controlar mediante interfaz serie USB.
-
Éxito parcial: Se logró comunicación básica mediante terminal PuTTY y comandos como AT (OK) y detección de señal AT+CSQ (Excelente: 18.0).
-
Fallo crítico: El módem reportaba persistentemente el error SIM not inserted a pesar de múltiples intentos de re-asentamiento de la tarjeta.
-
Diagnóstico: Fallo mecánico en el lector de tarjeta SIM del dongle o incompatibilidad con el adaptador de Nano a Micro-SIM utilizado.
Intento B: Módulo SIM900A (Placa UART) Se evaluó como alternativa.
-
Análisis de viabilidad: Este módulo requiere una fuente de alimentación externa dedicada de 5V y picos de 2A, que no podía ser suministrada directamente por el RP2040 sin riesgo de daño. Además, existe riesgo de bloqueo regional en unidades importadas de Asia.
-
Decisión: Se descartó su implementación inmediata para no añadir complejidad eléctrica al prototipo en esta etapa.
4.2. Decisión de Ingeniería
Ante la inviabilidad temporal del hardware GSM disponible, se tomó la decisión estratégica de no detener el desarrollo del software. Se optó por una estrategia de “Mocking” (Simulación de Software) para la capa de comunicación, una práctica estándar en ingeniería de software cuando una API externa no está disponible.
5. FASE 3: Solución Final y Código (Estrategia de Simulación)
Se completó el desarrollo de la lógica de defensa en el RP2040. El código final integra la vigilancia del espectro y la toma de decisiones. La función de envío de SMS real fue sustituida por una función mock que simula la salida por consola, permitiendo validar toda la cadena de eventos.
5.1. Lógica del Código Final
-
Inicialización: El sistema arranca y simula la verificación de los módulos de hardware.
-
Bucle de Vigilancia (Loop):
-
El sistema entra en un bucle infinito.
-
Simulación de Sensor: Se genera un valor aleatorio (nivel_ruido) para simular la lectura del RSSI (Indicador de Fuerza de Señal Recibida) del NRF905.
-
Se visualiza en consola una “barra de radar” en tiempo real.
-
-
Evaluación de Amenaza:
- Se compara el nivel de ruido con un UMBRAL_ALARMA predefinido (ej. 80%).
-
Respuesta a Incidentes:
-
Si se supera el umbral, se dispara la alerta.
-
Se llama a la función enviar_alerta_oob_simulada(), que imprime en consola el payload exacto que se enviaría vía SMS, confirmando que la lógica de disparo funciona.
-
El sistema entra en un periodo de “enfriamiento” para evitar la saturación de alertas.
-
5.2. Código Fuente Final (CircuitPython)
Python
import time
import random
# --- CONFIGURACIÓN E INICIO ---
print(“\n” + ”=“*50)
print(” IoT SENTINEL - SISTEMA DE DEFENSA ACTIVA”)
print(” Modo: PROTOTIPO LÓGICO (GSM Simulado)”)
print(”=“*50)
print(“\n[BOOT] Iniciando Kernel de Seguridad…”)
time.sleep(1)
print(“[BOOT] Cargando módulos de Análisis de Espectro… OK”)
print(“[BOOT] Verificando canal OOB (GSM Mock)… OK”)
print(“[SYSTEM] SISTEMA ARMADO Y VIGILANDO.\n”)
# Configuración de sensibilidad
# Si el ruido supera este porcentaje, se dispara la alarma.
UMBRAL_ALARMA = 80
# --- CAPA DE COMUNICACIÓN (MOCK/SIMULACIÓN) ---
def enviar_alerta_oob_simulada(mensaje):
"""
Simula el envío de un SMS crítico.
NOTA: Esta función sustituye a los comandos AT reales (ej: AT+CMGS)
debido a fallos mecánicos en el hardware GSM disponible durante el desarrollo.
"""
print(“\n” + ”!”*40)
print(” [GSM OUT-OF-BAND] PROTOCOLO DE EMERGENCIA ACTIVADO”)
print(f” [GSM] Destino: ADMINISTRADOR_SYS (+34666…)”)
print(f” [GSM] Payload: ‘{mensaje}’”)
time.sleep(1) # Simulación de latencia de red
print(” [GSM] Estado: ENVIADO OK (+CMGS: 15)”)
print(”!”*40 + “\n”)
time.sleep(2) # Pausa para visualizar la alerta
# --- BUCLE PRINCIPAL DE VIGILANCIA (CEREBRO) ---
while True:
# 1. Adquisición de Datos (Simulación del NRF905)
# Generamos ruido aleatorio para simular la entrada del sensor RF.
# La mayoría del tiempo el ruido es bajo, con picos ocasionales.
nivel_ruido = random.randint(10, 95)
# Visualización en consola tipo “Radar”
barra_grafica = ”|” * (nivel_ruido // 4)
print(f”Escaneo 433MHz: {barra_grafica} ({nivel_ruido}%)”)
# 2. Lógica de Decisión
# Si el nivel de ruido supera el umbral establecido…
if nivel_ruido > UMBRAL_ALARMA:
print(f”\n[!!!] ANOMALÍA DETECTADA (Nivel {nivel_ruido}% > Umbral {UMBRAL_ALARMA}%)”)
print(“[ANÁLISIS] Patrón de onda coincide con posible INHIBIDOR.”)
# 3. Disparo de Respuesta
enviar_alerta_oob_simulada(“ALERTA CRITICA: Jamming detectado en 433MHz.”)
print(“[SYSTEM] Enfriando sensores tras alerta…”)
time.sleep(4) # Evitar spam de alertas consecutivas
print(“[SYSTEM] Reanudando vigilancia.\n”)
# Pequeña pausa para facilitar la lectura del radar
time.sleep(0.5)
6. Conclusiones y Futuros Pasos
Este prototipo demuestra con éxito la viabilidad lógica de un sistema de defensa IoT autónomo. Se ha logrado implementar la arquitectura de detección y decisión en el microcontrolador RP2040.
La decisión de utilizar simulación para la capa GSM permitió finalizar el desarrollo del software a pesar de las fallas del hardware específico disponible.
Pasos para la siguiente versión:
-
Sustitución de Hardware GSM: Adquirir un módulo GSM fiable (ej. SIM800L) con su fuente de alimentación adecuada e integrarlo vía UART.
-
Descomentar Código GSM: Reemplazar la función simulada enviar_alerta_oob_simulada por la implementación real de comandos AT sobre UART.
-
Lectura Real de RSSI: Implementar la lectura del registro de “Carrier Detect” (CD) del NRF905 para obtener datos reales de intensidad de señal en lugar de valores aleatorios.