Levar leituras de sensores de um Raspberry Pi para o MQTT exige três peças: uma biblioteca para o sensor, o cliente paho-mqtt e um gerenciador de serviços que mantenha o script vivo. Este tutorial liga um BME280 (ou um DHT22) ao Pi, publica temperatura, umidade e pressão em JSON a cada 10 segundos e roda o publicador como um serviço systemd que reconecta sozinho e inicia no boot.
Tudo a seguir foi escrito para o Raspberry Pi OS Bookworm (ou mais novo) com Python 3.11+, paho-mqtt 2.x e os drivers de sensores CircuitPython da Adafruit. Funciona no Pi Zero 2 W, Pi 3, Pi 4 e Pi 5.
O que você precisa
| Item | Observações |
|---|---|
| Raspberry Pi com rede | Wi-Fi ou Ethernet, SSH habilitado |
| Placa BME280 (I2C) | Temperatura, umidade, pressão. Endereço 0x76 ou 0x77 |
| ou DHT22 / AM2302 | Temperatura e umidade em um único pino GPIO |
| 4 jumpers | 3,3 V, GND, SDA, SCL (BME280) |
| Um broker MQTT | Qualquer broker acessível; os exemplos usam broker.connectmq.com |
Ligue o BME280 em 3V3 (pino 1), GND (pino 6), SDA (pino 3, GPIO2) e SCL (pino 5, GPIO3). Um DHT22 vai em 3V3, GND e um pino de dados como o GPIO4 (pino 7), com um resistor de pull-up de 10 kΩ se o seu módulo ainda não tiver um.
Você não precisa de um broker no Pi. Se quiser um lá mesmo assim, veja rodar um broker no Raspberry Pi; este artigo cobre só o lado do cliente.
1. Habilite o I2C e verifique o sensor
sudo raspi-config nonint do_i2c 0
sudo apt update && sudo apt install -y i2c-tools python3-venv
i2cdetect -y 1O i2cdetect imprime uma grade. Você deve ver 76 ou 77: é o endereço do BME280. Uma grade vazia indica problema na fiação (SDA e SCL trocados é o clássico) ou I2C ainda não habilitado; reinicie uma vez depois do raspi-config se necessário.
2. Crie um ambiente virtual
O Bookworm recusa pip install no sistema com error: externally-managed-environment. Use um venv numa pasta de projeto:
mkdir -p ~/sensors && cd ~/sensors
python3 -m venv .venv
.venv/bin/pip install paho-mqtt adafruit-circuitpython-bme280
# para um DHT22:
# .venv/bin/pip install paho-mqtt adafruit-circuitpython-dhtOs drivers da Adafruit instalam o Blinka, a camada de compatibilidade que fornece board e busio no Linux.
Um teste rápido antes de qualquer código MQTT:
.venv/bin/python -c "import board; from adafruit_bme280 import basic as b; s=b.Adafruit_BME280_I2C(board.I2C(), address=0x76); print(s.temperature, s.relative_humidity, s.pressure)"Se aparecer ValueError: No I2C device at address: 0x76, tente address=0x77.
3. O script publicador
O script conecta com um Last Will para que os assinantes saibam quando o Pi cai, publica um status online retido e depois envia uma leitura JSON a cada 10 segundos. As credenciais vêm de variáveis de ambiente, então o mesmo arquivo funciona na sua mesa e em produção.
import json, os, socket, time
import board
import paho.mqtt.client as mqtt
from adafruit_bme280 import basic as adafruit_bme280
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "8883"))
DEVICE = socket.gethostname()
BASE = f"testmqtt/pi/{DEVICE}"
INTERVAL = 10
sensor = adafruit_bme280.Adafruit_BME280_I2C(board.I2C(), address=0x76)
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"falha na conexão: {reason_code}", flush=True)
return
print("conectado", flush=True)
client.publish(f"{BASE}/status", "online", qos=1, retain=True)
def on_disconnect(client, userdata, flags, reason_code, properties):
print(f"desconectado: {reason_code}", flush=True)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"pi-{DEVICE}")
client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
if PORT == 8883:
client.tls_set() # repositório de CAs do sistema
client.will_set(f"{BASE}/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.connect_async(HOST, PORT, keepalive=60)
client.loop_start() # thread de rede, tenta até conectar
while True:
reading = {
"temperature": round(sensor.temperature, 2),
"humidity": round(sensor.relative_humidity, 1),
"pressure": round(sensor.pressure, 1),
"ts": int(time.time()),
}
info = client.publish(f"{BASE}/bme280", json.dumps(reading), qos=1)
print(f"publish mid={info.mid} rc={info.rc} {reading}", flush=True)
time.sleep(INTERVAL)Algumas escolhas que valem explicação:
connect_async()+loop_start(): no boot, a rede pode ainda não estar pronta.connect()lançaria uma exceção na hora;connect_async()deixa a thread de fundo da paho continuar tentando, e a mesma thread reconecta depois de qualquer queda posterior com o backoff definido emreconnect_delay_set().- QoS 1 nas leituras, para que uma queda curta não perca dados em silêncio. A paho enfileira mensagens QoS 1 enquanto está desconectada e as envia quando a conexão volta. QoS 0 seria mais leve, mas descarta tudo o que for enviado durante a queda.
- Status retido + Last Will: quem assinar depois vê na hora
onlineouoffline. O padrão é explicado em MQTT Last Will. - Client ID a partir do hostname: estável e único por Pi. Dois dispositivos com o mesmo ID derrubam um ao outro do broker em loop.
Usando um DHT22
O protocolo do DHT22 é sensível a temporização no Linux, então leituras falham com frequência com RuntimeError (por exemplo "Checksum did not validate" ou "A full buffer was not returned"). Isso é normal: descarte a amostra e tente de novo:
import adafruit_dht
dht = adafruit_dht.DHT22(board.D4)
def read_dht():
for _ in range(5):
try:
return {"temperature": dht.temperature, "humidity": dht.humidity}
except RuntimeError:
time.sleep(2) # o DHT22 precisa de ~2 s entre leituras
return None # nada é publicado nesta rodadaChame read_dht() no loop principal e publique só quando ela retornar um valor.
4. Teste manualmente
Coloque as credenciais num arquivo que só o seu usuário possa ler e rode o script em primeiro plano:
MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_USER=username
MQTT_PASS=passwordchmod 600 ~/sensors/mqtt.env
set -a; . ~/sensors/mqtt.env; set +a
.venv/bin/python publish.pyVocê deve ver conectado seguido de uma linha publish mid=... a cada 10 segundos. Agora acompanhe de outra máquina:
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" -t 'testmqtt/pi/#' -vOu pule a instalação e abra o cliente MQTT online no navegador, conecte no mesmo broker e assine testmqtt/pi/#. Primeiro aparece a mensagem status retida, depois um novo payload JSON bme280 a cada intervalo.
Veja as leituras do seu Pi no navegador
Se nada chegar, confira o reason code impresso. Bad user name or password e Not authorized são recusas do broker; consulte-os na ferramenta de reason codes MQTT. Um timeout geralmente significa porta bloqueada: o guia de erros de conexão passa por cada caso.
5. Rode como serviço systemd
Um script iniciado numa sessão SSH morre quando você sai. O systemd o inicia no boot, reinicia se ele travar e guarda a saída no journal.
[Unit]
Description=BME280 to MQTT publisher
Wants=network-online.target
After=network-online.target
[Service]
User=pi
WorkingDirectory=/home/pi/sensors
EnvironmentFile=/home/pi/sensors/mqtt.env
ExecStart=/home/pi/sensors/.venv/bin/python -u /home/pi/sensors/publish.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetTroque pi pelo seu nome de usuário real: as imagens recentes do Raspberry Pi OS pedem que você escolha um na configuração. O usuário precisa estar no grupo i2c (ou gpio para o DHT22); o usuário padrão já está.
sudo systemctl daemon-reload
sudo systemctl enable --now bme280-mqtt
journalctl -u bme280-mqtt -fO -u na linha de comando do Python desativa o buffer de saída para que as linhas apareçam no journal imediatamente (o flush=True no script faz o mesmo).
Prove que ele sobrevive a falhas
sudo reboot: as leituras devem voltar em menos de um minuto, sem nenhuma sessão SSH aberta.- Desconecte a rede por um minuto. Os assinantes veem
offlinequando o broker percebe a falta de keep-alive (até 1,5 × 60 segundos), depoisonlinede novo após a reconexão, seguido das leituras enfileiradas durante a queda. sudo systemctl kill bme280-mqtt: o systemd reinicia o script após 5 segundos.
Próximos passos
- Formate o payload para dashboards e bancos de dados: payloads JSON em Python.
- Adicione mais sensores e um display: a estação meteorológica MQTT parte deste script.
- Alimente o Home Assistant publicando mensagens de discovery, como mostrado em Home Assistant MQTT.
Um Pi, um sensor e quarenta linhas de Python bastam para uma telemetria confiável, desde que as partes chatas estejam resolvidas: um venv para as dependências, um client ID estável, um Last Will para presença e o systemd para reinícios. Teste cada falha uma vez na sua mesa e o Pi no jardim vai se comportar do mesmo jeito.
Perguntas frequentes
Preciso rodar um broker MQTT no próprio Raspberry Pi?
Não. O Pi só precisa de uma biblioteca cliente como a paho-mqtt. Ele pode publicar em qualquer broker acessível: um instalado no mesmo Pi, outra máquina da sua rede local ou um broker gerenciado na nuvem.
Por que o pip falha com "externally-managed-environment" no Raspberry Pi OS?
O Raspberry Pi OS Bookworm e posteriores protegem o Python do sistema (PEP 668). Crie um ambiente virtual com python3 -m venv, instale nele a paho-mqtt e as bibliotecas do sensor e aponte o serviço systemd para o python do venv.
BME280 ou DHT22: qual sensor usar com MQTT?
O BME280 usa I2C, lê de forma confiável toda vez e ainda mede a pressão do ar. O DHT22 é barato, mas usa um protocolo de um fio sensível a temporização, então leituras falham com frequência e o seu código precisa tentar de novo. Para telemetria contínua, o BME280 dá menos dor de cabeça.

