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
ValueError: Unsupported callback API versionou umTypeErrorsobrecallback_api_version: você está usando a construção no estilo 1.x com o paho-mqtt 2.x. Passemqtt.CallbackAPIVersion.VERSION2como primeiro argumento.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.ConnectionRefusedErrorou timeouts: a porta 1883 está bloqueada na sua rede. Tente TLS na 8883.- Conecta e desconecta em loop: outro cliente está usando o mesmo client ID. Gere um ID único, como fazem os exemplos.
- Nada é recebido: verifique se os topics batem exatamente, incluindo maiúsculas e minúsculas e o prefixo, e se há um loop rodando.
- 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:
- Estruture bem os payloads com o tutorial de JSON com MQTT em Python.
- Rode muitas tarefas concorrentes com aiomqtt e asyncio.
- Persista mensagens com o tutorial de MQTT para SQLite em Python.
- Compare o Python com outras linguagens na visão geral de bibliotecas de cliente MQTT.
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.



