Tutoriais de IoT

Tutorial de Paho MQTT em Python: Publish e Subscribe em 10 Minutos

Instale o paho-mqtt 2.x, crie um publisher e um subscriber funcionando com o broker.connectmq.com, entenda o network loop, QoS, retained messages e Last Will, e depois migre para um broker privado com credenciais e TLS.

Terminal Python executando um publisher e um subscriber Paho MQTT trocando mensagens de sensores
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 Paho MQTT para Python é a forma padrão de falar MQTT a partir do Python, seja para um script de sensor no Raspberry Pi, um serviço de backend ou testes de dispositivos. Neste tutorial você vai instalar o paho-mqtt 2.x, escrever um subscriber e um publisher que conversam pelo broker.connectmq.com e entender o network loop bem o suficiente para evitar as armadilhas mais comuns. No final, você vai levar o mesmo código para um broker privado com credenciais e TLS.

O que você precisa

  • Python 3.8 ou mais recente (python --version)
  • Um terminal com acesso de saída à porta 1883, ou à 8883 para TLS
  • Dez minutos

Nenhuma conta é necessária na primeira parte. O servidor MQTT público gratuito em broker.connectmq.com aceita conexões anônimas com qualquer client ID único e não vazio. A área pública pode ser lida por qualquer pessoa, então use um prefixo de topic aleatório e nunca envie dados reais para lá.

Instale o paho-mqtt

Crie um ambiente virtual e instale a biblioteca:

python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install "paho-mqtt>=2.0"
python -c "import paho.mqtt; print(paho.mqtt.__version__)"

Deve aparecer uma versão 2.x. A versão 2.0 mudou a forma de construir o cliente e de chamar os callbacks. Muitos tutoriais na internet ainda mostram o estilo 1.x, que agora falha ou exibe avisos de depreciação. Tudo aqui usa o estilo atual; se você estiver migrando código existente, veja o guia de callback API version do paho-mqtt 2.0.

Passo 1: um subscriber

Salve isto como subscriber.py. Troque k7f3q2 por uma string aleatória sua:

import uuid

import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
PORT = 1883
TOPIC = "connectmq/demo/k7f3q2/#"


def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"Connection failed: {reason_code}")
        return
    print(f"Connected to {BROKER}")
    # Assine aqui para que a assinatura seja restaurada após cada reconexão.
    client.subscribe(TOPIC, qos=1)


def on_subscribe(client, userdata, mid, reason_code_list, properties):
    for rc in reason_code_list:
        if rc.is_failure:
            print(f"Subscription rejected: {rc}")
        else:
            print(f"Subscribed with QoS {rc.value}")


def on_message(client, userdata, msg):
    retained = " (retained)" if msg.retain else ""
    print(f"{msg.topic}{retained}: {msg.payload.decode('utf-8', errors='replace')}")


def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
    print(f"Disconnected: {reason_code}")


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"sub-{uuid.uuid4().hex[:8]}")
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_message = on_message
client.on_disconnect = on_disconnect

client.connect(BROKER, PORT, keepalive=60)
client.loop_forever()

Execute com python subscriber.py. Ele imprime "Connected" e "Subscribed with QoS 1" e fica esperando.

Passo 2: um publisher

Em um segundo terminal, salve isto como publisher.py:

import json
import random
import time
import uuid

import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
PORT = 1883
PREFIX = "connectmq/demo/k7f3q2"

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"pub-{uuid.uuid4().hex[:8]}")

# Last Will: publicado pelo broker se este cliente sumir sem desconectar.
client.will_set(f"{PREFIX}/status/publisher", payload="offline", qos=1, retain=True)

client.connect(BROKER, PORT, keepalive=60)
client.loop_start()  # network loop em uma thread em segundo plano

# Status retido: novos assinantes o recebem imediatamente.
client.publish(f"{PREFIX}/status/publisher", "online", qos=1, retain=True)

try:
    for i in range(10):
        payload = json.dumps({"seq": i, "temperature": round(random.uniform(20, 25), 2)})
        info = client.publish(f"{PREFIX}/sensors/room1", payload, qos=1)
        info.wait_for_publish(timeout=5)
        print(f"Sent #{i} (mid={info.mid}, published={info.is_published()})")
        time.sleep(1)
finally:
    client.publish(f"{PREFIX}/status/publisher", "offline", qos=1, retain=True).wait_for_publish(5)
    client.disconnect()
    client.loop_stop()

Execute python publisher.py. O subscriber imprime a mensagem de status e dez leituras do sensor, uma por segundo.

Entendendo o network loop do Paho MQTT em Python

O network loop é a parte do paho que lê e escreve no socket, envia os pings de keepalive e chama os seus callbacks. Nada acontece sem ele. Há três formas de executá-lo:

Método Bloqueia? Use quando
loop_forever() Sim, até disconnect() O script só reage a mensagens, como um subscriber ou um logger
loop_start() / loop_stop() Não, roda em uma thread em segundo plano A thread principal faz outro trabalho, como ler sensores
loop(timeout) Uma iteração por chamada Você integra com o seu próprio event loop

O bug mais comum de iniciantes é chamar publish() sem nenhum loop rodando e não entender por que nada chega, ou por que as mensagens QoS 1 nunca são confirmadas. Outro é esquecer que, com loop_start(), os callbacks rodam na thread do loop; se um callback mexe em dados que a thread principal também usa, proteja-os com um lock ou uma queue.Queue.

Tanto loop_forever() quanto loop_start() reconectam automaticamente após uma queda de conexão. É por isso que o subscriber assina dentro de on_connect: com uma sessão limpa, as assinaturas se perdem na reconexão e precisam ser refeitas. O guia de reconexão do paho-mqtt se aprofunda em backoff e buffer offline.

QoS, retained messages e Last Will na prática

O publisher usou três recursos do MQTT que vale a pena entender:

QoS 1 significa que o broker confirma cada mensagem, e o paho a reenvia se a confirmação não chegar. wait_for_publish() bloqueia até essa confirmação, o que é útil em scripts que terminam logo depois de publicar. QoS 0 é "dispare e esqueça" e um pouco mais barato; QoS 2 garante entrega exatamente uma vez, ao custo de uma ida e volta extra. Nosso artigo sobre níveis de QoS do MQTT os compara em detalhes.

Retained messages são armazenadas pelo broker, uma por topic, e enviadas imediatamente a quem assinar depois. São ideais para estado, como "online" ou a configuração atual de um dispositivo. Reinicie o subscriber e repare que ele recebe o status retido na hora, marcado com "(retained)".

Last Will é registrado no momento da conexão. Se o publisher travar ou perder a rede, o broker publica "offline" em nome dele depois que o keepalive expirar. Pressione Ctrl+C durante o loop e você verá o "offline" limpo do bloco finally; mate o processo com kill -9 e quem envia o will é o broker.

Testando com ferramentas de linha de comando

Você pode verificar o seu código Python pelo outro lado com os clientes do Mosquitto:

mosquitto_sub -h broker.connectmq.com -p 1883 -i cli-k7f3q2 -t 'connectmq/demo/k7f3q2/#' -v

O portal do ConnectMQ oferece a mesma visão sem instalar nada: o cliente MQTT no navegador e o explorador de topics ao vivo mostram cada topic sob o seu prefixo conforme as mensagens chegam, o que ajuda quando você está depurando em uma máquina onde não pode instalar o Mosquitto. Lembre-se de que o portal trabalha com o seu broker privado, enquanto o comando acima aponta para o público.

Se a CLI recebe as suas mensagens mas o subscriber em Python não, o problema está no código do subscriber; se nenhum dos dois recebe, olhe o publisher ou a rede.

Erros comuns e como corrigi-los

  1. ValueError: Unsupported callback API version ou um TypeError sobre callback_api_version: você está usando a construção no estilo 1.x com o paho-mqtt 2.x. Passe mqtt.CallbackAPIVersion.VERSION2 como primeiro argumento.
  2. TypeError: on_connect() takes 4 positional arguments but 5 were given: o seu callback tem a assinatura antiga. Use as assinaturas da versão 2 mostradas acima.
  3. ConnectionRefusedError ou timeouts: a porta 1883 está bloqueada na sua rede. Tente TLS na 8883.
  4. Conecta e desconecta em loop: outro cliente está usando o mesmo client ID. Gere um ID único, como fazem os exemplos.
  5. Nada é recebido: verifique se os topics batem exatamente, incluindo maiúsculas e minúsculas e o prefixo, e se há um loop rodando.
  6. Mensagens descartadas no broker público: ele permite 10 mensagens por segundo por cliente e 64 KB por mensagem. Diminua o ritmo ou agrupe as leituras.

Migrando para um broker privado com credenciais e TLS

Para dados reais, crie o seu próprio broker. Cadastre-se em app.connectmq.com, escolha o plano Serverless gratuito, crie um broker e copie o usuário, a senha e o client ID gerados. Depois, altere o código de conexão:

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()  # usa o repositório de CAs do sistema e verifica o hostname
client.connect("broker.connectmq.com", 8883, keepalive=60)

Use o nome de host exibido para o seu broker no portal. Os brokers do ConnectMQ usam um certificado Let's Encrypt, no qual o pacote de CAs padrão da maioria dos sistemas já confia. Se aparecerem erros de certificado, o guia de TLS com MQTT em Python explica cada um. Guarde as credenciais em variáveis de ambiente, e não em arquivos de código-fonte.

Próximos passos

Com publish e subscribe funcionando, estes guias se apoiam no mesmo cliente:

A documentação oficial do Eclipse Paho para Python é a referência de cada método e opção.

Comece a usar o ConnectMQ

O ConnectMQ é um broker MQTT gerenciado que suporta MQTT 3.1.1 e 5.0 sobre TCP, TLS e WebSocket, com um portal que inclui um cliente MQTT no navegador e um explorador de topics ao vivo para conferir o que o seu código Python publica. Experimente no servidor MQTT público e depois rode o tráfego de produção no seu próprio broker MQTT na nuvem. Crie uma conta gratuita no plano Serverless, sem cartão de crédito.

Perguntas frequentes

Qual versão do paho-mqtt devo usar?

Use a versão 2.x atual. Ela exige o argumento callback_api_version ao criar o cliente, e os callbacks da versão 2 são a escolha recomendada para código novo.

Qual é a diferença entre loop_start e loop_forever?

loop_forever() bloqueia a thread que o chama e executa o network loop até você desconectar. loop_start() executa o mesmo loop em uma thread em segundo plano, para que o seu código principal continue rodando.

O Paho MQTT para Python suporta MQTT 5?

Sim. Passe protocol=mqtt.MQTTv5 ao criar o cliente para usar recursos do MQTT 5, como properties, reason codes e session expiry.

Preciso de uma conta para testar o paho-mqtt com o ConnectMQ?

Não. O broker.connectmq.com aceita clientes anônimos na porta 1883 para testes. Crie uma conta gratuita quando precisar de um broker privado com credenciais.

Artigos relacionados

Melhores Ferramentas CLI MQTT para Testes, Scripts e Depuração

Uma comparação prática entre mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, a CLI do MQTT.js, curl e mqttui, com o mesmo teste rodando no broker.connectmq.com, para você escolher a ferramenta certa para scripts e depuração.

9 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.