Fundamentos de MQTT

Status Online/Offline de Dispositivos MQTT com LWT e Retained Messages

Um padrão de presença passo a passo para frotas MQTT: birth messages retidas, um Last Will retido, mensagens de desligamento controlado e um monitor que acompanha o estado de cada dispositivo em tempo real.

Dashboard de frota com indicadores verdes e cinzas mostrando o status online de cada dispositivo MQTT
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

Saber quais dispositivos estão conectados agora é a primeira coisa que todo operador pede, da sala de controle de uma empresa de saneamento à central de uma fazenda solar, e o status online de dispositivos MQTT é surpreendentemente fácil de implementar errado. O padrão confiável combina três blocos: uma birth message retida, um Last Will retido e uma mensagem offline explícita no desligamento. Neste guia você vai construir esse padrão passo a passo, adicionar um monitor de frota em Python e aprender a evitar os casos extremos que fazem os dashboards mentirem.

Os blocos de construção

Cada peça resolve uma parte do problema:

Bloco Quem publica Quando Cobre
Birth message (online, retida) Dispositivo Logo após cada conexão bem-sucedida Dispositivo ativo, inclusive após reconexões
Last Will (offline, retido) Broker, em nome do dispositivo Conexão perdida sem DISCONNECT Travamentos, falta de energia, falha de rede
Mensagem de desligamento (offline, retida) Dispositivo Antes de uma desconexão limpa Reinícios e desligamentos planejados
Flag retain nos três Todos Sempre Dashboards abertos depois ainda veem o estado atual

Deixe qualquer um deles de fora e você terá um ponto cego. Sem a birth message, um dispositivo que se recupera continua "offline". Sem o will, travamentos passam despercebidos. Sem a mensagem de desligamento, um desligamento planejado deixa o dispositivo "online" para sempre, porque um DISCONNECT limpo diz ao broker para descartar o will. Se o mecanismo de will é novidade para você, leia antes MQTT Last Will and Testament.

Desenhando o topic de status

Dê a cada dispositivo exatamente um topic de status, em um lugar previsível:

connectmq/demo/<prefix>/<site>/<device-id>/status

Um wildcard como connectmq/demo/<prefix>/+/+/status retorna então o status de toda a frota em uma única assinatura. Como todas as mensagens de status são retidas, um monitor que acaba de iniciar recebe imediatamente o último estado conhecido de cada dispositivo. O funcionamento da retenção é explicado em retained messages no MQTT.

Para o payload, uma string simples online ou offline basta para muitas ferramentas. O JSON acrescenta contexto que ajuda na operação:

{"state":"online","ts":1760600000,"fw":"2.4.1","ip":"10.0.4.17","reason":"boot"}

Para o will, o dispositivo não tem como saber o timestamp de uma falha futura, então mantenha-o simples: {"state":"offline","reason":"lwt"}. O monitor registra quando o recebeu.

Passo 1: o dispositivo

Este script com paho-mqtt 2.x se comporta como um dispositivo bem projetado. Ele define um will retido antes de conectar, publica uma birth message retida dentro de on_connect para que ela se repita a cada reconexão e publica uma mensagem offline retida no desligamento controlado. Ele se conecta ao servidor MQTT público em broker.connectmq.com, que serve para testes e não para dados de produção:

import json
import signal
import sys
import time
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
DEVICE_ID = sys.argv[1] if len(sys.argv) > 1 else "pump-07"
STATUS = f"connectmq/demo/z8b2/plant1/{DEVICE_ID}/status"

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id=f"z8b2-{DEVICE_ID}")
client.will_set(STATUS, json.dumps({"state": "offline", "reason": "lwt"}),
                qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=30)

def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print("connect failed:", reason_code)
        return
    birth = {"state": "online", "ts": int(time.time()), "reason": "connect"}
    client.publish(STATUS, json.dumps(birth), qos=1, retain=True)
    print("online")

def shutdown(signum, frame):
    bye = {"state": "offline", "ts": int(time.time()), "reason": "shutdown"}
    client.publish(STATUS, json.dumps(bye), qos=1, retain=True).wait_for_publish(timeout=5)
    client.disconnect()
    client.loop_stop()
    sys.exit(0)

client.on_connect = on_connect
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)

client.connect(BROKER, 1883, keepalive=20)
client.loop_start()
while True:
    time.sleep(1)  # a telemetria real seria publicada aqui

Execute-o como python device.py pump-07 e, em outro terminal, como python device.py fan-02.

Passo 2: o monitor da frota

O monitor assina todos os topics de status e mantém uma tabela de dispositivos. Como os status são retidos, ele conhece o estado completo da frota segundos depois de iniciar:

import json
import time
import uuid
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
FILTER = "connectmq/demo/z8b2/+/+/status"
fleet = {}

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(FILTER, qos=1)

def on_message(client, userdata, msg):
    device = msg.topic.split("/")[-2]
    if not msg.payload:  # payload retido vazio = dispositivo removido
        fleet.pop(device, None)
        return
    try:
        state = json.loads(msg.payload).get("state", "unknown")
    except ValueError:
        state = msg.payload.decode(errors="replace")
    previous = fleet.get(device, {}).get("state")
    fleet[device] = {"state": state, "since": time.strftime("%H:%M:%S")}
    tag = "retained" if msg.retain else "live"
    if previous != state:
        print(f"[{tag}] {device}: {previous} -> {state}")

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id=f"monitor-{uuid.uuid4().hex[:8]}")
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, 1883, keepalive=30)
client.loop_forever()

Passo 3: teste todos os modos de falha

Rode o monitor e os dois dispositivos, depois percorra este checklist e confirme que o monitor reage corretamente em cada caso:

  1. Reinício do monitor. Pare e inicie o monitor. Os dois dispositivos aparecem como online imediatamente, marcados como retained.
  2. Desligamento controlado. Pressione Ctrl+C no fan-02. O monitor mostra offline com o motivo shutdown na hora.
  3. Travamento. Mate o pump-07 com kill -9. O sistema operacional fecha o socket, o broker publica o will e o monitor mostra offline.
  4. Perda silenciosa de rede. Desconecte a rede do dispositivo sem matar o processo. Depois de cerca de 1,5 vez o keepalive de 20 segundos, o will é disparado.
  5. Recuperação. Restaure a rede. O paho se reconecta, o on_connect é executado e a birth message volta o dispositivo para online.
  6. Descomissionamento. Publique uma mensagem retida vazia no topic de status. O monitor remove o dispositivo.

Você também pode acompanhar os mesmos topics ao vivo no navegador; o client integrado ao navegador e o explorador de topics do portal ConnectMQ mostram as mudanças de status em tempo real quando você aponta os dispositivos para o seu próprio broker.

Casos extremos que fazem os dashboards mentirem

Client IDs duplicados. Dois dispositivos com o mesmo ID derrubam um ao outro. Cada tomada de conexão pode disparar um will e uma birth, então o status oscila o tempo todo. IDs únicos são inegociáveis; veja o guia de client ID MQTT.

Birth publicada só uma vez no boot. Se você publica online fora do callback de conexão, um dispositivo que se reconecta após uma instabilidade de rede fica offline para sempre. Publique-a sempre, a cada conexão.

Will não retido. Um dashboard aberto depois da falha nunca fica sabendo dela.

Reconexões rápidas causando oscilação. Um dispositivo que se reconecta em segundos ainda gera um par offline/online. No MQTT 5, um will delay interval suprime o will se a sessão for retomada a tempo.

Keepalive longo. Com um keepalive de 300 segundos, um dispositivo morto parece online por mais de sete minutos. Escolha um keepalive compatível com a rapidez com que você precisa saber.

"Conectado" não é "saudável". Um dispositivo pode manter sua conexão MQTT enquanto o sensor falhou. Publique um campo separado de saúde ou heartbeat se você precisa de vivacidade no nível da aplicação.

Presença vs heartbeat: qual você precisa?

Presença baseada em conexão e heartbeats de aplicação respondem a perguntas diferentes, e muitas frotas precisam dos dois.

Sinal Responde Tempo de detecção Custo
Birth + LWT no topic de status O dispositivo está conectado ao broker? Imediato até 1,5 x keepalive Quase nada; apenas pings de keepalive
Mensagem periódica de heartbeat O loop da aplicação ainda está rodando? Um ou dois intervalos de heartbeat Uma pequena publicação por intervalo
Verificação de atualidade da telemetria O sensor ainda está produzindo dados? Depende da taxa de publicação esperada Nenhum no dispositivo; lógica no consumidor

Um dispositivo cujo loop principal travou ainda pode responder aos pings de keepalive se o client MQTT roda em sua própria thread, então o status de conexão continua online. Um heartbeat publicado a partir do loop principal, ou um consumidor que sinaliza dispositivos cuja telemetria ficou desatualizada, pega esse caso. Mantenha os heartbeats sem retain e com frequência moderada; uma vez por minuto é suficiente para a maior parte do monitoramento.

Usando os topics de status em outras ferramentas

Como o padrão é feito apenas de mensagens MQTT retidas, qualquer ferramenta compatível com MQTT pode consumi-lo sem integração especial. No Node-RED, um nó mqtt in assinando o wildcard de status alimenta um dashboard ou um fluxo de notificação. A integração MQTT do Home Assistant pode mapear um topic de status para uma configuração de disponibilidade com os payloads online e offline. O input de consumidor MQTT do Telegraf pode gravar as mudanças de estado em um banco de séries temporais para relatórios de disponibilidade. Cada um deles se conecta ao broker como um client MQTT comum, então o único requisito é combinar os nomes dos topics e os valores dos payloads. Strings simples online e offline são a opção mais portável se você planeja usar ferramentas prontas; o JSON dá um contexto mais rico para o seu próprio código.

Status do lado do broker no ConnectMQ

O padrão acima é portável para qualquer broker. Além disso, o ConnectMQ publica o estado da conexão em $SYS/broker/connection/{clientId}/state, que um monitor pode assinar como uma fonte independente, que não depende de o firmware do dispositivo configurar seu will corretamente. A visualização de dispositivos do portal organiza seus dispositivos pelos níveis ISA-95, e regras de alerta por limite podem notificar você sobre valores que ultrapassam limites. Em produção, use TLS na porta 8883 com YOUR_USERNAME, YOUR_PASSWORD e YOUR_CLIENT_ID obtidos no portal. Um broker MQTT gerenciado mantém a infraestrutura de presença funcionando para que você não precise fazê-lo; para os fundamentos por trás disso, veja o tutorial de MQTT.

Comece a usar o ConnectMQ

Crie um broker gratuito em app.connectmq.com no plano Serverless, sem cartão de crédito. Implante os scripts do dispositivo e do monitor com suas credenciais e você terá presença confiável da frota em um broker MQTT na nuvem em minutos.

Perguntas frequentes

Por que meu dispositivo aparece online depois de ter sido desligado?

Um DISCONNECT limpo descarta o Last Will. Publique você mesmo uma mensagem offline retida antes de desconectar.

Por que meu dispositivo continua offline depois de se reconectar?

Provavelmente a birth message é publicada só uma vez na inicialização. Publique-a no callback de conexão para que cada reconexão atualize o status.

Com que rapidez um dispositivo offline é detectado?

Imediatamente, se a conexão TCP for fechada. Se o dispositivo desaparecer silenciosamente, em cerca de 1,5 vez o intervalo de keepalive.

As mensagens de status devem ser retidas?

Sim. Reter as mensagens de birth, will e desligamento garante que qualquer client que assine depois receba o estado atual na hora.

Artigos relacionados

Por que Seu Cliente MQTT Fica Desconectando (e Como Corrigir)

Loops de reconexão estão entre os problemas mais comuns do MQTT. Conheça as cinco causas habituais, de client IDs duplicados a timeouts de keep alive e pacotes grandes demais, e siga um fluxo de diagnóstico que identifica a sua.

9 min de leitura

Quem Inventou o MQTT? A História dos Oleodutos à IoT

O MQTT foi inventado em 1999 por Andy Stanford-Clark, da IBM, e Arlen Nipper, da Arcom, para monitorar oleodutos via satélite. Acompanhe sua trajetória até o open source, a OASIS, a ISO e os brokers MQTT gerenciados na nuvem de hoje.

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.