LoRaWAN
No existe un conector “LoRaWAN” en el CMS, y no hace falta. Un sensor LoRaWAN no habla IP: transmite por radio, el gateway reenvía el paquete, y quien descifra, deduplica y entrega el dato es el Network Server — ChirpStack, The Things Stack (TTN), Actility, Loriot, Kerlink. El CMS conversa con el Network Server, y todos publican el dato por MQTT o por webhook HTTP, que el CMS ya recibe.
Sensor ──radio──▶ Gateway ──▶ Network Server ──MQTT──▶ CMS (Recolección MQTT_TOPIC_SUBSCRIBER)
▲
└──MQTT── CMS (Entrega MQTT_PUBLISH, downlink)Uplink — Recolección MQTT
- Registre el broker del Network Server en Conexiones Externas,
tipo
MQTT_BROKER, y úselo en el Keep Alive MQTT de la Aplicación. - Cree una Recolección
MQTT_TOPIC_SUBSCRIBERen el tópico de uplink:
| Network Server | Tópico de uplink | Broker |
|---|---|---|
| ChirpStack v4 | application/{applicationId}/device/+/event/up | El Mosquitto del propio ChirpStack |
| The Things Stack v3 | v3/{app-id}@{tenant}/devices/+/up | {región}.cloud.thethings.network:8883 (TLS), usuario app-id@tenant, contraseña = API key |
MQTT es el camino preferido en planta: es el CMS quien abre la conexión, así que funciona detrás de firewall. Un webhook HTTP del Network Server hacia la API dinámica de Recolección también sirve, cuando el Network Server está en la nube y el CMS es alcanzable desde afuera.
El payload
El sensor envía pocos bytes. El Network Server los entrega en base64 — data en ChirpStack,
frm_payload en TTS — y, si hay un codec configurado para el modelo del dispositivo, también ya
decodificados — object en ChirpStack, decoded_payload en TTS.
- Con codec en el Network Server, el Transformador solo mapea campos. Es el camino más simple: el codec del fabricante suele venir listo.
- Sin codec, el Transformador decodifica los bytes. El sandbox del Transformador no tiene
atobniBuffer, así que el base64 se decodifica en el propio script — el escenario de ejemplo del repositorio (scripts/cenario-lorawan/transformador-normaliza-uplink.js) trae el decodificador.
El Transformador va en el reenvío de la Recolección (o en la Entrega de destino), no en la
Recolección: la Recolección en sí entrega el JSON crudo. Y el {{alias}} del tópico de una Entrega
MQTT_PUBLISH lee el payload antes del Transformador — para armar un tópico por dispositivo,
use los campos del JSON original (deviceInfo.devEui en ChirpStack, end_device_ids.device_id en
TTS).
Guarde también los metadatos de radio: rssi, snr, el gateway que lo escuchó y el fCnt. Un
fCnt que salta es un paquete perdido; uno que vuelve a cero es un rejoin — ambos son diagnóstico de
campo que no aparece en ningún otro lugar.
Downlink — Entrega MQTT_PUBLISH
Para enviar un comando al sensor, la Entrega publica en el tópico de downlink del Network Server:
| Network Server | Tópico | Cuerpo |
|---|---|---|
| ChirpStack v4 | application/{app}/device/{devEui}/command/down | { "devEui": "…", "fPort": 10, "confirmed": true, "data": "<base64>" } |
| The Things Stack v3 | v3/{app}@{tenant}/devices/{device-id}/down/push | { "downlinks": [{ "f_port": 10, "frm_payload": "<base64>", "confirmed": true }] } |
La Entrega sale bien cuando el Network Server acepta el comando, no cuando llega al sensor. Un
dispositivo clase A solo abre la ventana de recepción después de transmitir: el downlink espera el
próximo uplink, que puede tardar minutos u horas. Para saber si llegó, suscríbase también a
…/event/txack y …/event/ack (ChirpStack) o …/down/sent y …/down/ack (TTS) — el ack solo
existe para downlink con confirmed: true.
Probar sin red LoRaWAN
El LoRaWAN Simulator levanta un broker dentro del CMS y
publica en él lo que ChirpStack v4 o TTS v3 publicaría, con sensores que envían uplink solos y
aceptan downlink clase A. El script scripts/seed-cenario-lorawan.mjs arma el escenario completo
sobre él: la Aplicación LORAWANSIM, Recolecciones de los dos formatos, el Transformador de
normalización y las Entregas de downlink LW_DOWN_CS y LW_DOWN_TTS, accionadas por
curl -k -X POST https://localhost/api/LW_ENTREGA/LW_DOWN_CS \
-H 'Content-Type: application/json' -H 'x-api-token: <token de la Aplicación LORAWANSIM>' \
-d '{"devEui":"70b3d5e75e000101","aplicacaoNs":"5f2b7c1e-8d4a-4b6f-9c3e-1a2b3c4d5e6f","intervaloSegundos":20}'