Skip to Content

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)
  1. Registre el broker del Network Server en Conexiones Externas, tipo MQTT_BROKER, y úselo en el Keep Alive MQTT de la Aplicación.
  2. Cree una Recolección MQTT_TOPIC_SUBSCRIBER en el tópico de uplink:
Network ServerTópico de uplinkBroker
ChirpStack v4application/{applicationId}/device/+/event/upEl Mosquitto del propio ChirpStack
The Things Stack v3v3/{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 atob ni Buffer, 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.

Para enviar un comando al sensor, la Entrega publica en el tópico de downlink del Network Server:

Network ServerTópicoCuerpo
ChirpStack v4application/{app}/device/{devEui}/command/down{ "devEui": "…", "fPort": 10, "confirmed": true, "data": "<base64>" }
The Things Stack v3v3/{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}'