Este tutorial MQTT com Raspberry Pi leva você de uma instalação nova do Raspberry Pi OS a um programa Python que publica leituras de sensores, escuta comandos e aciona um pino GPIO de qualquer lugar. Você vai testar tudo no servidor MQTT público gratuito em broker.connectmq.com e depois levar o mesmo código para um broker privado protegido por TLS com algumas variáveis de ambiente. No caminho, vai aprender os padrões que mantêm um dispositivo IoT baseado em Pi confiável por meses, e não por minutos.
O Que Você Vai Construir
Ao final deste guia, seu Raspberry Pi vai rodar um único serviço Python que faz quatro coisas:
- Conecta-se a um broker MQTT e se reconecta automaticamente após quedas de Wi-Fi ou reinicializações.
- Publica a cada dez segundos uma mensagem de telemetria JSON com a temperatura da CPU, a carga média e, opcionalmente, um sensor de ambiente.
- Assina um topic de comando e aciona um LED (ou relé) no GPIO17.
- Publica um status online/offline retido, usando um last will do MQTT para que outros clientes saibam quando o Pi desaparece.
O layout de topics mantém tudo sob um único prefixo exclusivo, o que importa em um broker público compartilhado:
| Topic | Direção | Exemplo de payload | Retido |
|---|---|---|---|
connectmq/demo/<deviceId>/telemetry |
O Pi publica | {"cpu_temp":48.3,"load":0.21} |
Não |
connectmq/demo/<deviceId>/led/set |
O Pi assina | ON, OFF, TOGGLE |
Não |
connectmq/demo/<deviceId>/led/state |
O Pi publica | ON ou OFF |
Sim |
connectmq/demo/<deviceId>/status |
O Pi publica + last will | online ou offline |
Sim |
Se termos como topic, retained message ou last will forem novidade, dê uma olhada em o que um broker MQTT faz antes de começar. A versão curta: o broker é uma agência dos correios, seu Pi e seu notebook são clientes, e os topics são os endereços.
Pré-requisitos
Você precisa de muito pouco hardware para este tutorial MQTT com Raspberry Pi:
- Qualquer Raspberry Pi com rede: Pi 3, Pi 4, Pi 5 ou Zero 2 W. O código é idêntico em todos.
- Raspberry Pi OS Bookworm (a versão Lite serve), com SSH habilitado e acesso à rede.
- Um LED com um resistor de 330 ohms, ou um módulo de relé, no GPIO17 (pino físico 11) e no GND (pino 9). Sem ele, o código roda do mesmo jeito e apenas registra os comandos no log.
- Opcional: um sensor DHT22 ou DS18B20. Começamos com a temperatura interna da CPU, então nada extra é necessário.
- Uma segunda máquina com
mosquitto_subemosquitto_pubpara testes (instalemosquitto-clientsno Linux ou use o Homebrew no macOS).
Comparando placas antes de comprar? Nossa análise Raspberry Pi vs ESP32 explica quando um computador Linux completo supera um microcontrolador e quando ele é exagero.
Passo 1: Prepare o Python no Raspberry Pi
O Bookworm protege o Python do sistema, então pip install fora de um ambiente virtual falha com o erro "externally managed environment". Isso é bom: um ambiente virtual mantém as dependências do projeto isoladas e reproduzíveis. Crie um em uma pasta de projeto:
sudo apt update
sudo apt install -y python3-venv python3-lgpio mosquitto-clients
mkdir -p ~/pi-mqtt && cd ~/pi-mqtt
python3 -m venv --system-site-packages .venv
source .venv/bin/activate
pip install "paho-mqtt>=2.0" gpiozero
A flag --system-site-packages permite que o ambiente virtual enxergue o pacote lgpio instalado pelo apt, que o gpiozero usa como backend de pinos no Raspberry Pi OS atual (e que é obrigatório no Pi 5, onde a antiga biblioteca RPi.GPIO não funciona). Consulte a documentação oficial de GPIO do Raspberry Pi para ver a pinagem do seu modelo.
Verifique a versão do cliente instalada. Este guia usa a API de callbacks do paho-mqtt 2.x, que é diferente da de tutoriais mais antigos que você pode encontrar online:
python -c "import paho.mqtt; print(paho.mqtt.__version__)"
Passo 2: Seu Primeiro Publish e Subscribe
Antes de escrever o programa completo, comprove que o caminho de rede funciona. Escolha um device ID aleatório para que seus topics não colidam com os de mais ninguém no broker público. Em um terminal (no seu notebook ou no Pi), assine:
mosquitto_sub -h broker.connectmq.com -p 1883 -i sub-laptop-7f3a -v -t "connectmq/demo/pi-7f3a/#"
Em um segundo terminal, publique uma mensagem de teste:
mosquitto_pub -h broker.connectmq.com -p 1883 -i pub-pi-7f3a -t "connectmq/demo/pi-7f3a/hello" -m "hello from the pi"
O subscriber imprime o topic e a mensagem imediatamente. Todo cliente precisa de um client ID único e não vazio; se dois clientes compartilharem um ID, o broker desconecta o mais antigo e você verá um confuso loop de conexão e desconexão. O servidor MQTT público (gratuito) serve para testes e protótipos e pode ser lido por qualquer pessoa que adivinhe o seu topic, então nunca envie senhas, dados pessoais ou qualquer coisa que você não publicaria abertamente.
Passo 3: O Cliente MQTT Completo em Python
Salve o código a seguir como ~/pi-mqtt/pi_mqtt.py. Ele lê todas as configurações de variáveis de ambiente, então o mesmo arquivo funciona com o broker público hoje e com um broker privado depois.
#!/usr/bin/env python3
"""Raspberry Pi MQTT client: telemetry, GPIO control and online status."""
import json
import os
import signal
import ssl
import threading
import time
import uuid
import paho.mqtt.client as mqtt
try:
from gpiozero import LED
led = LED(int(os.getenv("LED_PIN", "17")))
except Exception as exc: # sem GPIO (por exemplo, testando em um notebook)
print(f"GPIO unavailable, running without LED: {exc}")
led = None
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USE_TLS = os.getenv("MQTT_TLS", "0") == "1"
USERNAME = os.getenv("MQTT_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD")
DEVICE_ID = os.getenv("DEVICE_ID", f"pi-{uuid.uuid4().hex[:6]}")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", DEVICE_ID)
PREFIX = os.getenv("TOPIC_PREFIX", f"connectmq/demo/{DEVICE_ID}")
INTERVAL = float(os.getenv("PUBLISH_INTERVAL", "10"))
T_TELEMETRY = f"{PREFIX}/telemetry"
T_LED_SET = f"{PREFIX}/led/set"
T_LED_STATE = f"{PREFIX}/led/state"
T_STATUS = f"{PREFIX}/status"
stop_event = threading.Event()
def read_cpu_temp() -> float:
with open("/sys/class/thermal/thermal_zone0/temp") as f:
return round(int(f.read().strip()) / 1000.0, 1)
def publish_led_state(client: mqtt.Client) -> None:
state = "ON" if (led and led.is_lit) else "OFF"
client.publish(T_LED_STATE, state, qos=1, retain=True)
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connect failed: {reason_code}")
return
print(f"Connected to {HOST}:{PORT} as {CLIENT_ID}")
client.publish(T_STATUS, "online", qos=1, retain=True)
client.subscribe(T_LED_SET, qos=1)
publish_led_state(client)
def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
print(f"Disconnected ({reason_code}), paho will retry automatically")
def on_message(client, userdata, msg):
command = msg.payload.decode("utf-8", errors="ignore").strip().upper()
print(f"Command on {msg.topic}: {command}")
if led is None:
return
if command == "ON":
led.on()
elif command == "OFF":
led.off()
elif command == "TOGGLE":
led.toggle()
else:
print("Unknown command, ignoring")
return
publish_led_state(client)
def build_client() -> mqtt.Client:
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
if USE_TLS:
client.tls_set(cert_reqs=ssl.CERT_REQUIRED)
client.will_set(T_STATUS, "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_message = on_message
return client
def main() -> None:
client = build_client()
client.connect_async(HOST, PORT, keepalive=30)
client.loop_start()
signal.signal(signal.SIGTERM, lambda *_: stop_event.set())
signal.signal(signal.SIGINT, lambda *_: stop_event.set())
while not stop_event.is_set():
if client.is_connected():
payload = {
"cpu_temp": read_cpu_temp(),
"load": round(os.getloadavg()[0], 2),
"ts": int(time.time()),
}
client.publish(T_TELEMETRY, json.dumps(payload), qos=0)
stop_event.wait(INTERVAL)
client.publish(T_STATUS, "offline", qos=1, retain=True).wait_for_publish(2)
client.loop_stop()
client.disconnect()
if __name__ == "__main__":
main()
Execute com um device ID fixo para saber quais topics acompanhar:
cd ~/pi-mqtt && source .venv/bin/activate
DEVICE_ID=pi-7f3a python pi_mqtt.py
Como o código funciona
Vale a pena entender algumas decisões de projeto, porque é nelas que a maioria dos projetos com Pi dá errado:
connect_asyncmaisloop_startroda o loop de rede em uma thread em segundo plano e deixa o paho repetir também a primeira conexão. Umconnect()simples lança uma exceção e mata o script se a rede ainda não estiver no ar, o que é comum logo após a inicialização.- O last will (
will_set) é armazenado pelo broker no momento da conexão. Se o Pi perder energia, o broker publicaofflinepor você depois que o keepalive expirar. O caminho de desligamento limpo publicaofflineexplicitamente. - Topics de estado retidos fazem com que um dashboard que se conecte depois ainda veja imediatamente o estado atual do LED e o status online, sem esperar pela próxima mudança.
- Assinar dentro de
on_connectrestaura a assinatura após cada reconexão. Assinar uma única vez depois deconnect()é um bug clássico que faz os comandos pararem silenciosamente após a primeira oscilação de rede. - QoS 1 para comandos e estado, QoS 0 para telemetria é um padrão sensato. Perder uma leitura periódica é inofensivo; perder um comando
OFFnão é. Nosso guia sobre níveis de QoS MQTT explica os trade-offs em profundidade.
Passo 4: Controle o GPIO via MQTT
Com o script rodando, envie comandos a partir do seu notebook:
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-laptop-7f3a -q 1 -t "connectmq/demo/pi-7f3a/led/set" -m "ON"
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-laptop-7f3a -q 1 -t "connectmq/demo/pi-7f3a/led/set" -m "TOGGLE"
O LED muda de estado e a janela do subscriber mostra o novo estado retido em led/state. O mesmo padrão aciona um relé para uma bomba, um ventilador ou uma fechadura elétrica; basta trocar LED por gpiozero.OutputDevice e considerar inverter a lógica para placas de relé ativas em nível baixo. Para botões, dimerização por PWM e vários pinos, continue com o guia dedicado de GPIO do Raspberry Pi via MQTT e, para hardware que comuta a rede elétrica, leia controle de relés com Raspberry Pi e MQTT para conhecer os cuidados de segurança na ligação.
Uma regra prática para atuadores: quem é dono da verdade é o dispositivo, não o dashboard. Sempre publique o estado que o hardware realmente atingiu e faça as interfaces exibirem esse topic de estado, em vez de presumir que o comando funcionou.
Passo 5: Adicione um Sensor Real
A temperatura da CPU comprova o pipeline, mas a maioria dos projetos mede o ambiente. Troque read_cpu_temp() por um leitor de sensor e adicione os valores ao dicionário do payload. A tabela abaixo resume as escolhas comuns e onde continuar:
| Sensor | Interface | O que mede | Guia detalhado |
|---|---|---|---|
| DHT22 / AM2302 | Um único GPIO, sensível a temporização | Temperatura, umidade | DHT22 com MQTT |
| DS18B20 | 1-Wire | Temperatura, sondas à prova d'água | DS18B20 com MQTT |
| Sense HAT | HAT I2C | Temperatura, umidade, pressão, IMU | Sense HAT para MQTT |
| HC-SR04 | Dois GPIOs | Distância, nível de tanque | Sensor de distância para MQTT |
Mantenha as leituras de sensores fora dos callbacks MQTT. Os callbacks rodam na thread de rede, e uma leitura lenta ali atrasa o tratamento do keepalive e as confirmações. O loop principal do script acima é o lugar certo para qualquer coisa que bloqueie.
Use um formato JSON consistente entre os dispositivos: valores numéricos, unidades do SI no nome da chave ou em um campo unit separado, e um timestamp Unix. Cada consumidor que você adicionar depois, do Grafana ao Node-RED, vai agradecer.
Passo 6: Rode o Script MQTT na Inicialização com systemd
Um script iniciado em uma sessão SSH morre quando você sai. Transforme-o em um serviço systemd para que ele inicie no boot e reinicie em caso de falha. Crie /etc/systemd/system/pi-mqtt.service:
[Unit]
Description=Raspberry Pi MQTT client
After=network-online.target
Wants=network-online.target
[Service]
User=pi
WorkingDirectory=/home/pi/pi-mqtt
EnvironmentFile=/home/pi/pi-mqtt/.env
ExecStart=/home/pi/pi-mqtt/.venv/bin/python /home/pi/pi-mqtt/pi_mqtt.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Substitua pi pelo seu nome de usuário, adicione o usuário ao grupo gpio se necessário (sudo usermod -aG gpio pi) e crie o arquivo .env:
cat > ~/pi-mqtt/.env <<'EOF'
DEVICE_ID=pi-7f3a
PUBLISH_INTERVAL=10
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now pi-mqtt
journalctl -u pi-mqtt -f
Como o script trata o SIGTERM, systemctl stop pi-mqtt publica um status offline limpo antes de sair. Para rotação de logs, watchdogs e alternativas como o @reboot do cron, veja rodando um script Python MQTT na inicialização.
Passo 7: Migre do Servidor MQTT Público para um Broker Privado
O servidor MQTT público é perfeito para aprender, mas é compartilhado, anônimo e tem limites de taxa (por exemplo, 10 mensagens por segundo por cliente e 64 KB por mensagem). Para qualquer coisa da qual você dependa, você vai querer suas próprias credenciais, criptografia e isolamento. Você tem duas opções:
| Opção | Indicada para | Você é responsável por |
|---|---|---|
| Mosquitto no próprio Pi | LANs offline, automação apenas local | Atualizações, certificados TLS, backups, acesso remoto |
| Um broker MQTT gerenciado | Dispositivos em vários lugares, dashboards remotos, equipes | Apenas o código do seu cliente |
Rodar o Mosquitto localmente é explicado em instalando o Mosquitto em um Raspberry Pi. Se você escolher um broker MQTT gerenciado, a ConnectMQ oferece um broker privado em poucos minutos: cadastre-se, escolha um plano, crie um broker, e o portal mostra um nome de usuário, uma senha e um client ID gerados. Depois, edite apenas o arquivo .env:
cat > ~/pi-mqtt/.env <<'EOF'
MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_TLS=1
MQTT_USERNAME=YOUR_USERNAME
MQTT_PASSWORD=YOUR_PASSWORD
MQTT_CLIENT_ID=YOUR_CLIENT_ID
DEVICE_ID=pi-7f3a
TOPIC_PREFIX=home/livingroom/pi-7f3a
EOF
sudo systemctl restart pi-mqtt
A porta 8883 usa TLS com um certificado Let's Encrypt publicamente confiável, então tls_set() o valida com o pacote de CAs que acompanha o Raspberry Pi OS; você não precisa copiar arquivos de certificado para o Pi. Na ConnectMQ, os client IDs são vinculados às credenciais emitidas, então use o client ID do portal em vez de um aleatório. Teste a partir do seu notebook com as mesmas credenciais:
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID-cli -v -t "home/#"
Se o portal emitiu um único client ID, crie um segundo conjunto de credenciais para o notebook em vez de reutilizar o do Pi; duas conexões com o mesmo ID derrubam uma à outra. O portal também inclui um cliente MQTT no navegador e um explorador de topics em tempo real, que muitas vezes é a forma mais rápida de ver as mensagens do seu Pi chegando sem instalar nada.
Checklist do Tutorial MQTT com Raspberry Pi para Dispositivos Confiáveis
A maioria dos problemas do tipo "ontem funcionava" vem da mesma lista curta. Antes de deixar um Pi dentro de um armário, percorra-a:
- Client ID único por conexão. Duplicatas causam loops infinitos de reconexão.
- Assinaturas dentro de
on_connect. Caso contrário, os comandos param após a primeira reconexão. - Last will e status retido. Os dashboards precisam conseguir distinguir "quieto" de "morto".
- TLS na porta 8883 com credenciais para qualquer coisa fora de um laboratório. Veja a lista completa de boas práticas de segurança MQTT.
- systemd com
Restart=on-failuree logs no journald. - Guarde leituras em buffer quando estiver offline se as lacunas importarem. O paho enfileira mensagens QoS 1 enquanto está desconectado, mas apenas em memória; buffer de mensagens MQTT offline em Python mostra uma abordagem com SQLite que sobrevive a reinicializações.
- Uma boa fonte de alimentação e um cartão SD de qualidade, ou boot por SSD. Subtensão causa mais desconexões aleatórias do que qualquer bug de software.
- Proteja o sistema operacional: apenas chaves SSH, atualizações de segurança automáticas, nada de senhas padrão. Protegendo um dispositivo IoT com Raspberry Pi mostra o passo a passo.
Solução de Problemas
Connect failed: Not authorized significa que o nome de usuário, a senha ou o client ID não corresponde ao que o broker emitiu. Copie-os de novo do portal e verifique se há espaços sobrando no .env.
ConnectionRefusedError ou timeouts na porta 8883 geralmente indicam que um firewall ou uma rede de convidados bloqueia portas de saída. Teste com nc -vz broker.connectmq.com 8883. Algumas redes corporativas só liberam a porta 443; nesse caso, o MQTT sobre WebSocket seguro na porta 8084 também pode estar bloqueado, então teste antes de se comprometer.
ssl.SSLCertVerificationError quase sempre significa que o relógio do Pi está errado. O TLS precisa de uma data correta; verifique com timedatectl e garanta que o NTP esteja ativado.
O LED não reage, mas as mensagens chegam aponta para permissões de GPIO ou para a ligação. Rode pinctrl get 17 em versões recentes do Raspberry Pi OS para ver o estado do pino e confirme que o usuário está no grupo gpio.
Loop de reconexão a cada poucos segundos é um client ID duplicado ou uma segunda cópia do script ainda rodando. Verifique com ps aux | grep pi_mqtt.
Próximos Passos
Este tutorial MQTT com Raspberry Pi oferece uma base sólida, e o restante da nossa série sobre Raspberry Pi parte exatamente deste script:
- Transforme o Pi em um tradutor de protocolos com o guia de gateway IoT com Raspberry Pi, incluindo fontes Modbus e BLE.
- Precisa de algo menor e mais barato para projetos com bateria? Experimente o MQTT no Raspberry Pi Pico W com MicroPython.
- Armazene suas leituras e gere gráficos com Grafana e InfluxDB em um Raspberry Pi.
- Publique dados de saúde de uma frota inteira com estatísticas do sistema do Raspberry Pi via MQTT.
Comece com a ConnectMQ
Quando o seu projeto com Pi superar o servidor MQTT público, crie um broker privado na ConnectMQ. O plano Serverless (Free) não exige cartão de crédito, suporta até 25 conexões e oferece TLS na porta 8883, MQTT 5 e acesso via WebSocket desde o início, o que é mais do que suficiente para um home lab ou um primeiro piloto, como o monitoramento de uma câmara fria em uma fazenda ou de uma bomba em uma planta. Frotas maiores podem migrar para os planos Serverless (Paid) ou Dedicated sem alterar uma linha do código dos clientes. Crie seu broker gratuito em app.connectmq.com e aponte seu arquivo .env para ele. Se quiser primeiro ter uma visão mais ampla das opções de hospedagem, leia nossa visão geral sobre como escolher um broker MQTT na nuvem.
Perguntas frequentes
Qual biblioteca MQTT devo usar em um Raspberry Pi?
Para Python, o paho-mqtt 2.x é a mais usada e é a que este guia utiliza. Quem usa Node.js pode usar o MQTT.js, e quem prefere low-code pode rodar o Node-RED no Pi com as mesmas configurações de broker.
Um Raspberry Pi pode ser ao mesmo tempo broker e cliente MQTT?
Sim. Você pode rodar o Mosquitto no Pi e conectar scripts locais a localhost, mas aí você mesmo cuida de segurança, atualizações e acesso remoto. Muitos projetos mantêm o Pi como cliente e usam um broker MQTT gerenciado, para que dispositivos e dashboards em locais diferentes consigam se comunicar.
Por que meu Pi fica se desconectando e reconectando?
As causas mais comuns são um client ID duplicado, uma segunda cópia do script rodando ou uma fonte de alimentação instável. Verifique se cada conexão usa um ID único e procure avisos de subtensão com dmesg.
O servidor MQTT público é seguro para o meu projeto com Raspberry Pi?
Ele é seguro para aprendizado e demonstrações, mas qualquer cliente pode ler os topics públicos, então nunca envie dados pessoais ou sensíveis. Use um broker privado com TLS e credenciais em implantações reais.
Este código funciona no Raspberry Pi 5?
Sim. O script usa o gpiozero com o backend lgpio, que suporta o Pi 5. Bibliotecas que escrevem nos antigos registradores de memória do GPIO, como a RPi.GPIO, não funcionam no Pi 5.



