Automação IoT

Integração MQTT do ChirpStack para LoRaWAN: Encaminhe Uplinks Decodificados a um Broker na Nuvem

Use a integração MQTT nativa do ChirpStack para publicar uplinks LoRaWAN decodificados e encaminhe-os a um broker MQTT na nuvem com uma bridge Mosquitto ou um pequeno forwarder em Node.js.

Integração ChirpStack MQTT encaminhando uplinks LoRaWAN decodificados de um network server local para um broker MQTT na nuvem
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

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:

  1. Backend de gateways. As bridges dos gateways publicam quadros de rádio brutos em um broker Mosquitto local, e o ChirpStack os assina.
  2. 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.

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

  1. Nada em event/up. Verifique se o dispositivo entrou na rede (procure por event/join), se a integração está ativada e se json = true está definido, caso você espere JSON.
  2. object ausente. O codec não existe, falhou ou retornou erros; as mensagens event/log mostram os erros do codec.
  3. A bridge conecta e depois cai. remote_clientid duplicado, credenciais erradas ou arquivo de CA ausente para a porta 8883. O log do Mosquitto mostra o motivo.
  4. 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 log para 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

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.

Artigos relacionados

Guia de Configuração do Zigbee2MQTT: do Coordenador ao Painel

Instale o Zigbee2MQTT com Docker, configure o coordenador e a conexão MQTT, pareie e renomeie dispositivos, controle-os via MQTT e veja seus estados em um painel, testando primeiro no servidor MQTT público (gratuito).

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.