Raspberry Pi

Tutorial MQTT com Raspberry Pi: Publique, Assine e Automatize com Python

Um tutorial MQTT completo para Raspberry Pi: configure um ambiente virtual Python, publique leituras de CPU e de sensores, acione um LED via MQTT, rode o script como serviço e migre para TLS com suas próprias credenciais.

Placa Raspberry Pi ligada a um LED e a um sensor publicando dados em um broker na nuvem, em um tutorial MQTT com Raspberry Pi
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

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:

  1. Conecta-se a um broker MQTT e se reconecta automaticamente após quedas de Wi-Fi ou reinicializações.
  2. 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.
  3. Assina um topic de comando e aciona um LED (ou relé) no GPIO17.
  4. 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_sub e mosquitto_pub para testes (instale mosquitto-clients no 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_async mais loop_start roda o loop de rede em uma thread em segundo plano e deixa o paho repetir também a primeira conexão. Um connect() 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 publica offline por você depois que o keepalive expirar. O caminho de desligamento limpo publica offline explicitamente.
  • 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_connect restaura a assinatura após cada reconexão. Assinar uma única vez depois de connect() é 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 OFF nã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:

  1. Client ID único por conexão. Duplicatas causam loops infinitos de reconexão.
  2. Assinaturas dentro de on_connect. Caso contrário, os comandos param após a primeira reconexão.
  3. Last will e status retido. Os dashboards precisam conseguir distinguir "quieto" de "morto".
  4. 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.
  5. systemd com Restart=on-failure e logs no journald.
  6. 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.
  7. 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.
  8. 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:

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.

Artigos relacionados

Registro de Acessos Raspberry Pi RFID MQTT com o Leitor RC522

Ligue um leitor RFID RC522 a um Raspberry Pi, leia tags em Python, confira-as em uma lista de permissões local e publique cada tentativa de acesso como um evento MQTT que loggers, dashboards e ferramentas de alerta podem consumir.

8 min de leitura

Confiabilidade do Raspberry Pi: Rodando um Pi 24/7 como Gateway IoT

Um guia prático para manter um gateway MQTT com Raspberry Pi rodando por meses: desgaste do armazenamento, sistemas de arquivos somente leitura, alimentação, refrigeração, watchdogs de hardware e software e monitoramento remoto de saúde.

9 min de leitura

Pico W vs Pico 2 W para MQTT: Desempenho, Memória e TLS do RP2040 vs RP2350

O Pico 2 W mantém o formato e o chip Wi-Fi do Pico W, mas troca o RP2040 pelo RP2350, mais rápido. Esta comparação mostra o que isso significa para dispositivos MQTT: CPU, memória, TLS, recursos de segurança, suporte de software e qual placa comprar para cada projeto.

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.