Automação IoT

Integração MQTT do The Things Network: Leve Uplinks LoRaWAN ao Seu Broker

Assine o servidor MQTT do The Things Network, decodifique uplinks LoRaWAN com um payload formatter e republique JSON limpo dos sensores na sua própria árvore de tópicos com uma bridge Python curta.

Bridge The Things Network MQTT levando uplinks de sensores LoRaWAN de um gateway para a árvore de tópicos de 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

O servidor MQTT do The Things Network oferece a cada aplicação um fluxo ao vivo dos seus uplinks LoRaWAN, o que facilita trazer dados de sensores de longo alcance para os seus próprios sistemas. Neste tutorial você vai escrever um payload formatter, conectar-se ao servidor MQTT do TTN com uma chave de API e rodar uma pequena bridge em Python que republica as leituras decodificadas como JSON limpo na árvore de tópicos do seu próprio broker, onde painéis, Node-RED e automações podem usá-las junto com seus outros dispositivos.

Por que fazer uma bridge do TTN para o seu broker

O The Things Network (TTN) cuida da parte de rádio do LoRaWAN: gateways, ativação de dispositivos, deduplicação e descriptografia. Seu servidor MQTT publica cada uplink como um grande documento JSON com metadados de todos os gateways que ouviram o pacote. Isso é excelente para depuração, mas ruim como modelo de dados de longo prazo:

  • os tópicos são do TTN (v3/<app>@ttn/devices/<device>/up), e não da hierarquia do seu site;
  • os payloads são grandes e aninhados, enquanto a maioria dos consumidores quer três ou quatro valores;
  • seus dispositivos Wi-Fi, CLPs e controladores já vivem em outro broker.

Uma bridge resolve os três problemas. Ela assina o TTN, extrai o que importa e publica mensagens compactas na mesma árvore de tópicos de todo o resto. Os consumidores nunca precisam de credenciais do TTN e, se no futuro você migrar para um network server privado, só a bridge muda. O guia de agricultura inteligente com IoT explica onde isso se encaixa em um projeto para a fazenda inteira, seja um cafezal em Minas ou uma lavoura de soja no Mato Grosso. Com os dados normalizados, a mesma árvore de tópicos pode alimentar até um gêmeo digital da propriedade.

Passo 1: Decodifique o payload no TTN

Dispositivos LoRaWAN enviam bytes compactos. Adicione um payload formatter no TTN Console em Applications > your app > Payload formatters > Uplink, tipo Custom Javascript formatter. Este exemplo decodifica um nó de solo que envia umidade (uint16, em décimos de porcento), temperatura do solo (int16, em décimos de grau) e bateria (uint16, em milivolts):

function decodeUplink(input) {
  var b = input.bytes;
  if (input.fPort !== 1 || b.length < 6) {
    return { errors: ["unexpected port or length"] };
  }
  var moisture = ((b[0] << 8) | b[1]) / 10;
  var tRaw = (b[2] << 8) | b[3];
  if (tRaw & 0x8000) tRaw -= 0x10000;
  var battery = ((b[4] << 8) | b[5]) / 1000;
  return {
    data: {
      moisture_pct: moisture,
      soil_temp_c: tRaw / 10,
      battery_v: battery
    },
    warnings: [],
    errors: []
  };
}

Use o painel Test com um payload hexadecimal como 0136 008C 0E74 para confirmar a saída antes de instalar qualquer dispositivo. O objeto decodificado aparece em cada uplink como uplink_message.decoded_payload.

Passo 2: Obtenha as credenciais MQTT do TTN

No Console, abra Integrations > MQTT. Você verá:

Configuração Valor
Servidor Seu cluster, por exemplo eu1.cloud.thethings.network
Portas 1883 (sem criptografia) e 8883 (TLS)
Usuário <application-id>@ttn
Senha Uma chave de API; clique em Generate new API key

Copie a chave de API imediatamente; o TTN a exibe apenas uma vez. Use sempre a porta 8883 para o TTN, já que a chave dá acesso aos dados da sua aplicação. A seção sobre o servidor MQTT da documentação oficial do The Things Stack lista o conjunto completo de tópicos, incluindo eventos de join, downlink e serviço.

O tópico de uplink de uma aplicação é:

v3/<application-id>@ttn/devices/<device-id>/up

Você pode verificar o fluxo antes de escrever qualquer código:

mosquitto_sub -h eu1.cloud.thethings.network -p 8883 --capath /etc/ssl/certs \
  -u 'my-farm-app@ttn' -P 'NNSXS.YOUR_TTN_API_KEY' \
  -t 'v3/my-farm-app@ttn/devices/+/up' -v

Passo 3: A bridge MQTT do The Things Network em Python

A bridge usa dois clientes paho-mqtt 2.x: um que assina o TTN e outro que publica no seu broker. Rode-a primeiro com o servidor MQTT público, que serve para testes e não para dados de produção, substituindo t6n1b4 por uma sequência aleatória sua, porque a área pública pode ser lida por qualquer pessoa.

import json
import os
import ssl

import paho.mqtt.client as mqtt

TTN_HOST = "eu1.cloud.thethings.network"
TTN_APP = os.environ.get("TTN_APP", "my-farm-app")
TTN_KEY = os.environ["TTN_API_KEY"]

OUT_HOST = os.environ.get("OUT_HOST", "broker.connectmq.com")
OUT_PORT = int(os.environ.get("OUT_PORT", "1883"))
OUT_USER = os.environ.get("OUT_USER")          # defina em produção
OUT_PASS = os.environ.get("OUT_PASS")
OUT_CLIENT_ID = os.environ.get("OUT_CLIENT_ID", "ttn-bridge-out-t6n1b4")
OUT_PREFIX = os.environ.get("OUT_PREFIX", "connectmq/demo/t6n1b4/farm")

# --- broker de saída ---------------------------------------------------------
out = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=OUT_CLIENT_ID)
if OUT_USER:
    out.username_pw_set(OUT_USER, OUT_PASS)
if OUT_PORT == 8883:
    out.tls_set(cert_reqs=ssl.CERT_REQUIRED)
out.will_set(f"{OUT_PREFIX}/bridge/status", "offline", qos=1, retain=True)

def out_on_connect(client, userdata, flags, reason_code, properties):
    print("out broker:", reason_code)
    client.publish(f"{OUT_PREFIX}/bridge/status", "online", qos=1, retain=True)

out.on_connect = out_on_connect
out.connect(OUT_HOST, OUT_PORT, keepalive=60)
out.loop_start()

# --- TTN ---------------------------------------------------------------------
def ttn_on_connect(client, userdata, flags, reason_code, properties):
    print("ttn:", reason_code)
    client.subscribe(f"v3/{TTN_APP}@ttn/devices/+/up", qos=1)

def ttn_on_message(client, userdata, msg):
    try:
        up = json.loads(msg.payload)
        device = up["end_device_ids"]["device_id"]
        um = up["uplink_message"]
        decoded = um.get("decoded_payload")
    except (ValueError, KeyError) as exc:
        print("skipping malformed uplink:", exc)
        return
    if not decoded:
        print(f"{device}: no decoded payload (check the formatter)")
        return

    rx = um.get("rx_metadata", [])
    best = max(rx, key=lambda g: g.get("rssi", -999)) if rx else {}
    record = {
        "ts": up.get("received_at"),
        **decoded,
        "rssi": best.get("rssi"),
        "snr": best.get("snr"),
        "gateways": len(rx),
        "f_cnt": um.get("f_cnt"),
    }
    topic = f"{OUT_PREFIX}/{device}/telemetry"
    out.publish(topic, json.dumps(record), qos=1, retain=True)
    print(topic, record)

ttn = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                  client_id=f"ttn-bridge-in-{TTN_APP}")
ttn.username_pw_set(f"{TTN_APP}@ttn", TTN_KEY)
ttn.tls_set(cert_reqs=ssl.CERT_REQUIRED)
ttn.on_connect = ttn_on_connect
ttn.on_message = ttn_on_message
ttn.connect(TTN_HOST, 8883, keepalive=60)
ttn.loop_forever()

Execute:

pip install paho-mqtt
export TTN_API_KEY='NNSXS.YOUR_TTN_API_KEY'
python ttn_bridge.py

E acompanhe a saída limpa em outro terminal:

mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-t6n1b4 \
  -t 'connectmq/demo/t6n1b4/farm/#' -v

Cada uplink vira uma mensagem retida, como {"ts":"2026-10-16T06:15:02Z","moisture_pct":31.0,"soil_temp_c":14.0,"battery_v":3.7,"rssi":-108,"snr":4.5,"gateways":2,"f_cnt":412}. Como ela é retida, um painel iniciado depois ainda vê a última leitura de cada sensor imediatamente, o que é importante para dispositivos que só enviam dados a cada 15 minutos.

Notas de projeto

Por que incluir RSSI, SNR e número de gateways? Eles são o primeiro sinal de uma antena com defeito, de um gateway que mudou de lugar ou de um sensor afundando na lama. Armazená-los quase não custa nada.

Por que mensagens retidas? Dispositivos LoRaWAN ficam em silêncio na maior parte do tempo. Sem retain, qualquer consumidor que se conecte entre dois uplinks não vê nada até o próximo.

Por que QoS 1 dos dois lados? A bridge pode reiniciar enquanto o TTN entrega mensagens; QoS 1 com um cliente que se reconecta reduz as lacunas. Para entender as contrapartidas, veja níveis de QoS do MQTT explicados.

Profundidade dos tópicos. connectmq/demo/t6n1b4/farm/<device>/telemetry tem seis níveis, bem dentro do limite de oito níveis do servidor MQTT público. Em produção, use sua própria hierarquia, como farm1/north/<device>/telemetry.

Tamanho das mensagens. Um uplink bruto do TTN com metadados de vários gateways pode chegar a alguns kilobytes. O registro republicado tem algumas centenas de bytes, muito abaixo do limite de 64 KB da área pública de testes e do limite de 256 KB do plano gratuito da ConnectMQ, e é muito mais barato de processar para painéis em dispositivos a bateria e clientes móveis.

Lidando com lacunas e duplicatas

O LoRaWAN é, por definição, um enlace de rádio com perdas: os dispositivos transmitem sem confirmação, e um pacote que nenhum gateway ouve simplesmente se perde. O contador de quadros (f_cnt) incluído em cada registro permite aos consumidores detectar lacunas. Se o contador saltar de 410 para 413, dois uplinks se perderam no rádio, e não no MQTT. Essa distinção economiza tempo quando alguém reclama de dados faltando.

A bridge também pode ver o mesmo uplink duas vezes se reconectar ao TTN enquanto uma mensagem QoS 1 está em trânsito. Consumidores que armazenam dados devem tratar a combinação de ID do dispositivo e contador de quadros como chave única e ignorar repetições. Observe que o contador é zerado quando um dispositivo entra novamente na rede, então use-o para detectar lacunas em janelas curtas, e não como identificador permanente.

Para enviar um comando a um dispositivo (por exemplo, um novo intervalo de envio), a bridge pode assinar um tópico de comando no seu broker e encaminhá-lo para o tópico de downlink do TTN:

import base64

def forward_downlink(device_id, port, raw_bytes):
    body = {"downlinks": [{
        "f_port": port,
        "frm_payload": base64.b64encode(raw_bytes).decode(),
        "priority": "NORMAL",
    }]}
    ttn.publish(f"v3/{TTN_APP}@ttn/devices/{device_id}/down/push",
                json.dumps(body), qos=1)

# exemplo: definir o intervalo em 30 minutos (um byte) na porta 2
# forward_downlink("soil-node-07", 2, bytes([30]))

Dispositivos Classe A só recebem downlinks após o próximo uplink, então os comandos chegam com um atraso igual ao intervalo de envio. Use downlinks com moderação; as regras de tempo de ar e duty cycle do LoRaWAN os tornam caros.

Rodando com confiabilidade

  1. Rode a bridge como serviço systemd ou contêiner com reinício automático.
  2. Mantenha a chave de API do TTN em uma variável de ambiente ou em um cofre de segredos, nunca no script.
  3. Dê à chave de API apenas os direitos de que ela precisa (ler o tráfego da aplicação e, se usar, escrever downlinks).
  4. Monitore bridge/status: o last will o muda para offline se o processo morrer.
  5. Lembre-se da política de uso justo do TTN para a rede comunitária; ela foi pensada para sensores de baixa frequência, não para dispositivos que falam demais.

Produção em um broker privado

Defina OUT_HOST=broker.connectmq.com, OUT_PORT=8883, OUT_USER, OUT_PASS e OUT_CLIENT_ID com as credenciais emitidas para o seu broker privado e troque OUT_PREFIX pela sua própria hierarquia. O restante do script continua igual. Um broker MQTT gerenciado oferece aos seus dados LoRaWAN, sensores Wi-Fi e controladores um lar compartilhado e criptografado que você não precisa manter. Se você roda seu próprio network server em vez do TTN, o tutorial de ChirpStack MQTT mostra a bridge equivalente, e a montagem do sensor de umidade do solo com MQTT é uma alternativa Wi-Fi para campos próximos a edificações. Mais padrões estão reunidos no guia de automação IoT, e a explicação sobre o que faz um broker MQTT ajuda se o MQTT for novidade para você.

Traga seus dados LoRaWAN para a ConnectMQ

A ConnectMQ oferece aos dados do TTN trazidos pela bridge um broker MQTT privado com TLS, um explorador de tópicos ao vivo, um painel e regras de alerta por limite para valores como bateria fraca ou solo seco. Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, e aponte para ele a saída da bridge.

Perguntas frequentes

Qual é o usuário e a senha MQTT do TTN?

O usuário é o ID da sua aplicação seguido de @ttn (na edição comunitária do The Things Network), e a senha é uma chave de API gerada em Integrations > MQTT.

Nenhum payload formatter de uplink está configurado, ou ele gerou um erro. Teste-o no painel de formatters do Console com um payload hexadecimal real.

Posso conectar o TTN diretamente a outro broker sem código?

O TTN oferece webhooks e outras integrações, mas, para publicar no seu próprio broker MQTT com a sua própria estrutura de tópicos, uma pequena bridge como esta é a opção mais simples e transparente.

A bridge precisa rodar perto do gateway?

Não. As duas conexões passam pela internet, então ela pode rodar em qualquer servidor, em um Raspberry Pi ou em um contêiner.

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.