A integração MQTT do ChirpStack publica cada evento LoRaWAN do seu próprio network server em um broker MQTT, o que a torna o ponto de partida natural para levar dados de sensores de campo, de uma fazenda a uma rede de saneamento, a painéis e automações. Neste tutorial você vai configurar a integração no ChirpStack v4, decodificar payloads com o codec do device profile e encaminhar os uplinks decodificados para um broker na nuvem de duas formas: uma bridge Mosquitto sem código e um forwarder em Node.js que remodela os dados na sua própria árvore de tópicos.
Como o ChirpStack usa o MQTT
O ChirpStack é um network server LoRaWAN de código aberto. Em uma instalação típica (por exemplo, o Docker Compose oficial), o MQTT aparece duas vezes:
- Backend de gateways. As bridges dos gateways publicam quadros de rádio brutos em um broker Mosquitto local, e o ChirpStack os assina.
- Integração MQTT. Após deduplicação, descriptografia e decodificação, o ChirpStack publica eventos de aplicação (uplinks, joins, confirmações, erros) em tópicos MQTT no mesmo broker local.
Os eventos da integração são o que interessa. Eles ficam em um broker dentro da rede do servidor LoRaWAN, que normalmente não é acessível a painéis, celulares ou outros locais. Encaminhá-los para um broker na nuvem resolve isso sem expor o próprio network server.
Sensors ~~LoRa~~> Gateway --> local Mosquitto <--> ChirpStack
|
application/.../event/up
|
Mosquitto bridge or Node.js forwarder
| (TLS)
v
Cloud MQTT broker --> Node-RED, dashboards, alerts
Passo 1: Ative a integração MQTT do ChirpStack
No chirpstack.toml, a seção de integração fica assim em uma instalação padrão:
[integration]
enabled = ["mqtt"]
[integration.mqtt]
server = "tcp://mosquitto:1883/"
json = true
Com json = true, os eventos saem em JSON em vez de Protobuf, o que facilita muito o trabalho. O tópico de evento padrão é:
application/<application_id>/device/<dev_eui>/event/<event>
onde <event> é up, join, ack, txack, log, status ou location. Reinicie o ChirpStack após alterar o arquivo e acompanhe o broker local no servidor:
mosquitto_sub -h localhost -p 1883 -t 'application/+/device/+/event/up' -v
Passo 2: Decodifique os payloads com um codec
Os payloads LoRaWAN brutos são bytes, entregues em base64 no campo data. Adicione um codec JavaScript ao device profile (Device profiles > your profile > Codec, tipo JavaScript) para que o ChirpStack inclua um campo object decodificado em cada uplink:
function decodeUplink(input) {
var b = input.bytes;
if (input.fPort !== 1 || b.length < 6) {
return { errors: ["unexpected fPort or length"] };
}
var temp = (b[2] << 8) | b[3];
if (temp & 0x8000) temp -= 0x10000;
return {
data: {
moisture_pct: ((b[0] << 8) | b[1]) / 10,
soil_temp_c: temp / 10,
battery_v: ((b[4] << 8) | b[5]) / 1000
}
};
}
Um evento de uplink passa então a conter, entre outros campos, deviceInfo.deviceName, deviceInfo.devEui, fCnt, fPort, time, object (seus valores decodificados) e rxInfo (uma entrada por gateway, com rssi e snr).
Opção A: Encaminhe com uma bridge Mosquitto
Se o ChirpStack já roda com o Mosquitto, uma bridge é o caminho mais rápido: algumas linhas de configuração e o Mosquitto encaminha os tópicos correspondentes ao broker na nuvem, reconectando automaticamente. Crie /etc/mosquitto/conf.d/cloud-bridge.conf (ou o equivalente no seu volume Docker):
connection connectmq
address broker.connectmq.com:1883
remote_clientid cs-bridge-c3h7k5
cleansession true
try_private false
bridge_protocol_version mqttv311
notifications true
notification_topic connectmq/demo/c3h7k5/bridge/state
# local: application/<app>/device/<eui>/event/up
# remoto: connectmq/demo/c3h7k5/<app>/device/<eui>/event/up
topic +/device/+/event/up out 1 application/ connectmq/demo/c3h7k5/
A linha topic recebe um padrão, uma direção, um QoS e dois prefixos. O Mosquitto remove o prefixo local application/ e adiciona o prefixo remoto, o que mantém o tópico remoto com oito níveis, o máximo permitido no servidor MQTT público. Substitua c3h7k5 por uma sequência aleatória sua; a área pública é compartilhada, pode ser lida por qualquer pessoa e serve para testes e protótipos, não para dados de produção. try_private false é recomendado quando o broker remoto não é um Mosquitto.
Reinicie o Mosquitto e assine de qualquer lugar:
mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-c3h7k5 \
-t 'connectmq/demo/c3h7k5/#' -v
Em produção, mude para TLS e use as credenciais emitidas para o seu broker privado:
connection connectmq
address broker.connectmq.com:8883
bridge_cafile /etc/ssl/certs/ca-certificates.crt
remote_clientid YOUR_CLIENT_ID
remote_username YOUR_USERNAME
remote_password YOUR_PASSWORD
cleansession true
try_private false
notifications true
notification_topic farm1/lorawan/bridge/state
topic +/device/+/event/up out 1 application/ farm1/lorawan/
A limitação de uma bridge é que os payloads são encaminhados sem alteração. Os eventos do ChirpStack são verbosos, com dados em base64, informações de transmissão e metadados completos dos gateways, e os tópicos usam UUIDs de aplicação e DevEUIs em vez de nomes legíveis.
Opção B: Um forwarder MQTT do ChirpStack em Node.js
Quando os consumidores precisam de dados compactos e com nomes, um pequeno forwarder é melhor. Este usa o MQTT.js v5, assina os uplinks no broker local e republica um registro limpo por dispositivo. Instale com npm install mqtt.
// forwarder.mjs
import mqtt from "mqtt";
const LOCAL_URL = process.env.LOCAL_URL ?? "mqtt://localhost:1883";
const OUT_URL = process.env.OUT_URL ?? "mqtt://broker.connectmq.com:1883";
const OUT_PREFIX = process.env.OUT_PREFIX ?? "connectmq/demo/c3h7k5/farm";
const out = mqtt.connect(OUT_URL, {
clientId: process.env.OUT_CLIENT_ID ?? "cs-forwarder-c3h7k5",
username: process.env.OUT_USER,
password: process.env.OUT_PASS,
protocolVersion: 5,
will: { topic: `${OUT_PREFIX}/forwarder/status`, payload: "offline", qos: 1, retain: true },
});
out.on("connect", () => {
console.log("cloud broker connected");
out.publish(`${OUT_PREFIX}/forwarder/status`, "online", { qos: 1, retain: true });
});
out.on("error", (err) => console.error("cloud:", err.message));
const local = mqtt.connect(LOCAL_URL, { clientId: "cs-forwarder-local-c3h7k5" });
local.on("connect", () => {
local.subscribe("application/+/device/+/event/up", { qos: 1 });
console.log("local broker connected");
});
local.on("message", (topic, payload) => {
let ev;
try {
ev = JSON.parse(payload.toString());
} catch {
return console.warn("non-JSON event on", topic);
}
if (!ev.object) {
return console.warn(`${ev.deviceInfo?.deviceName}: no decoded object, check the codec`);
}
const rx = ev.rxInfo ?? [];
const best = rx.reduce((a, g) => ((g.rssi ?? -999) > (a.rssi ?? -999) ? g : a), {});
const name = ev.deviceInfo.deviceName.replace(/[^A-Za-z0-9_-]/g, "_");
const record = {
ts: ev.time,
dev_eui: ev.deviceInfo.devEui,
...ev.object,
f_cnt: ev.fCnt,
rssi: best.rssi ?? null,
snr: best.snr ?? null,
gateways: rx.length,
};
out.publish(`${OUT_PREFIX}/${name}/telemetry`, JSON.stringify(record), { qos: 1, retain: true });
console.log(name, record);
});
Rode-o ao lado do ChirpStack:
node forwarder.mjs
# produção
OUT_URL=mqtts://broker.connectmq.com:8883 OUT_USER=YOUR_USERNAME \
OUT_PASS=YOUR_PASSWORD OUT_CLIENT_ID=YOUR_CLIENT_ID OUT_PREFIX=farm1/north \
node forwarder.mjs
Os nomes dos dispositivos são higienizados antes de entrar no tópico, já que caracteres como +, # e / quebrariam a hierarquia. Os registros são retidos para que os painéis vejam imediatamente a última leitura de cada sensor, mesmo que dispositivos LoRaWAN só enviem dados a cada 15 minutos.
Escolhendo entre as duas opções
| Critério | Bridge Mosquitto | Forwarder Node.js |
|---|---|---|
| Esforço de configuração | Um arquivo de configuração | Um pequeno script e um serviço |
| Payload | Evento completo do ChirpStack | Registro compacto definido por você |
| Nomes de tópico | ID da aplicação e DevEUI | Nomes de dispositivo legíveis, sua hierarquia |
| Partes móveis | Nenhuma além do Mosquitto | Um processo extra para monitorar |
| Downlinks | Adicione uma linha de tópico in |
Adicione uma assinatura e uma publicação |
Muitas instalações começam com a bridge para colocar os dados em circulação e migram para um forwarder quando os consumidores se multiplicam. O tutorial de The Things Network MQTT usa a mesma ideia de forwarder em Python, o que é útil se a sua fazenda mistura redes LoRaWAN comunitárias e privadas.
Enviando downlinks pelo ChirpStack
Para enfileirar um downlink, publique no tópico de comando do dispositivo no broker local:
mosquitto_pub -h localhost -p 1883 \
-t 'application/YOUR_APP_ID/device/0004a30b001c1234/command/down' \
-m '{"devEui":"0004a30b001c1234","confirmed":false,"fPort":2,"data":"Hg=="}'
data está em base64 (Hg== é o byte único 30, por exemplo um intervalo de envio em minutos). Dispositivos Classe A o recebem após o próximo uplink. Só encaminhe comandos de downlink vindos da nuvem se você confiar em todos os clientes que podem publicar neles, e valide-os no forwarder.
Solução de problemas
- Nada em
event/up. Verifique se o dispositivo entrou na rede (procure porevent/join), se a integração está ativada e sejson = trueestá definido, caso você espere JSON. objectausente. O codec não existe, falhou ou retornou erros; as mensagensevent/logmostram os erros do codec.- A bridge conecta e depois cai.
remote_clientidduplicado, credenciais erradas ou arquivo de CA ausente para a porta 8883. O log do Mosquitto mostra o motivo. - Mensagens rejeitadas no servidor MQTT público. O tópico passa de oito níveis ou o cliente ultrapassa dez mensagens por segundo; use prefixos mais curtos e não encaminhe eventos
logpara lá.
Monitorando o caminho de encaminhamento
Um pipeline LoRaWAN tem vários pontos em que pode falhar silenciosamente: um gateway perde energia, o ChirpStack para de decodificar depois de uma edição no codec ou o forwarder morre. Monitore cada camada separadamente. A interface web do ChirpStack mostra quando cada gateway foi visto pela última vez e os quadros por dispositivo. O tópico status retido do forwarder, sustentado pelo seu last will, mostra se o próprio forwarder está vivo, e o tópico de notificação da bridge faz o mesmo na Opção A. Por fim, a idade da mensagem telemetry retida de cada dispositivo diz se os dados ainda estão fluindo de ponta a ponta.
No lado da nuvem, o explorador de tópicos ao vivo da ConnectMQ é a forma mais rápida de confirmar que os registros estão chegando no formato esperado após uma mudança no codec, e uma regra de alerta por limite em um campo como battery_v avisa com antecedência antes que um sensor fique mudo.
Notas de produção
Mantenha o network server e o broker dos gateways privados, e faça do forwarder ou da bridge o único caminho de saída. Um broker MQTT gerenciado passa então a ser o hub compartilhado para dados LoRaWAN, dispositivos Wi-Fi e controladores, e o artigo sobre broker MQTT na nuvem compara as opções de hospedagem. Para saber o que fazer com os dados, o guia de agricultura inteligente com IoT e a montagem do sistema de irrigação automática mostram usos práticos, inclusive como base para um gêmeo digital da fazenda, e o guia de automação IoT cobre o panorama geral.
Encaminhe os dados do ChirpStack para a ConnectMQ
A ConnectMQ oferece aos seus dados LoRaWAN um broker MQTT privado e protegido por TLS, com explorador de tópicos ao vivo, painel e regras de alerta por limite para valores como bateria fraca ou geada. Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, e aponte para ele a sua bridge ou o seu forwarder.
Perguntas frequentes
Qual tópico MQTT o ChirpStack v4 usa para uplinks?
application/<application_id>/device/<dev_eui>/event/up por padrão, com payloads JSON quando json = true está definido na integração MQTT.
O ChirpStack pode publicar diretamente em um broker na nuvem?
Você pode apontar o server da integração para outro broker, mas mantê-lo local e encaminhar tópicos selecionados dá a você controle sobre o que sai da rede.
Por que os payloads do ChirpStack vêm em base64?
O campo data carrega os bytes LoRaWAN brutos codificados em base64. Adicione um codec ao device profile para obter os valores decodificados no campo object.
A bridge Mosquitto armazena mensagens quando a internet cai?
Com cleansession false e QoS 1, o Mosquitto pode enfileirar mensagens para a bridge enquanto o link está fora, dentro dos limites de fila configurados. Com cleansession true, as mensagens publicadas durante a queda não são encaminhadas.



