Documentación del Proyecto: IoT Sentinel

Sistema de Vigilancia de Espectro RF y Alerta OOB (Out-Of-Band)

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:

  1. 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.

  2. 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.

  3. 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ñalPin RP2040Cable Color
SCK (Reloj)GP 6Amarillo
MOSI (Datos Salida)GP 7Verde
MISO (Datos Entrada)GP 4Azul
CSN (Chip Select)GP 5Blanco
TX_EN (Activar TX)GP 0Naranja
TRX_CE (Activar RX)GP 1Morado

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
        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

  1. Inicialización: El sistema arranca y simula la verificación de los módulos de hardware.
  2. 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 del NRF905.
  • Se visualiza en consola una “barra de radar” en tiempo real.
  1. Evaluación de Amenaza: Se compara el nivel de ruido con un UMBRAL_ALARMA predefinido (ej. 80%).
  2. 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.
  • El sistema entra en un período de “enfriamiento” para evitar la saturación de alertas.

5.2. Código Fuente Final (CircuitPython)

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")
 
# 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)
    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
    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 Crítica: Jamming detectado en 433MHz.")
        print("[SYSTEM] Enfriando sensores tras alerta...")
        time.sleep(4)  # Evitar spam de alertas consecutivas
        print("[SYSTEM] Reanudando vigilancia.\n")
 
    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:

  1. 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.
  2. Descomentar Código GSM: Reemplazar la función simulada enviar_alerta_oob_simulada por la implementación real de comandos AT sobre UART.
  3. 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.