Um sensor Raspberry Pi DHT22 MQTT é um projeto IoT clássico para começar, e também uma fonte clássica de frustração: o DHT22 é barato e preciso o bastante para ambientes internos e estufas, mas seu protocolo sensível a temporização faz algumas leituras falharem em uma placa Linux. Neste tutorial você vai ligar o sensor, lê-lo a partir do Python e publicar JSON limpo de temperatura e umidade em um broker MQTT, com tratamento adequado de leituras com falha, valores implausíveis e quedas de rede.
Por que o DHT22 às vezes falha
O DHT22 (também vendido como AM2302) não usa I2C nem 1-Wire. O Pi precisa alternar uma linha GPIO e depois cronometrar uma rajada de pulsos que duram dezenas de microssegundos. O Raspberry Pi OS não é um sistema operacional de tempo real, então quando o kernel agenda outra coisa no momento errado, um bit se perde e o checksum falha. Esse é o comportamento normal, não um sensor com defeito. A solução está no software:
- Espere pelo menos dois segundos entre leituras; o sensor não consegue amostrar mais rápido.
- Capture o
RuntimeErrorque a biblioteca lança e tente de novo. - Rejeite valores fora da faixa física do sensor.
- Faça várias leituras boas e publique a mediana.
- Conte as falhas e publique-as, para perceber quando um sensor realmente morre.
Ligações
| Pino do DHT22 | Raspberry Pi |
|---|---|
| VCC (pino 1) | 3,3 V (pino físico 1) |
| DATA (pino 2) | GPIO4 (pino físico 7) |
| NC (pino 3) | Não conectado |
| GND (pino 4) | GND (pino físico 9) |
Um DHT22 avulso precisa de um resistor de pull-up de 4,7 k a 10 k entre DATA e 3,3 V. Módulos breakout de três pinos geralmente já o incluem. Mantenha o cabo curto no início; cabos longos funcionam, mas são mais sensíveis a ruído, e reduzir o valor do pull-up pode ajudar em cabos maiores. Alimente com 3,3 V, nunca 5 V, porque a linha de dados vai direto a um pino GPIO que não tolera 5 V.
Instalando as bibliotecas
sudo apt update
sudo apt install -y python3-venv libgpiod2
mkdir -p ~/dht22-mqtt && cd ~/dht22-mqtt
python3 -m venv .venv
source .venv/bin/activate
pip install "paho-mqtt>=2.0" adafruit-circuitpython-dht
O adafruit-circuitpython-dht traz o Adafruit Blinka, que fornece o módulo board. Teste o sensor sozinho antes de adicionar o MQTT:
python -c "import time, board, adafruit_dht; d = adafruit_dht.DHT22(board.D4); time.sleep(2); print(d.temperature, d.humidity); d.exit()"
Se aparecer um erro como "A full buffer was not returned", rode o comando de novo; uma falha isolada é esperada. Se todas as tentativas falharem, verifique as ligações e o pull-up. Em alguns modelos de Raspberry Pi e versões do sistema operacional, o helper de leitura de pulsos não funciona; nesse caso, crie o sensor com adafruit_dht.DHT22(board.D4, use_pulseio=False) para usar o fallback por bit-banging.
Payload e topics
O script publica um documento JSON por intervalo, mais um status retido:
{
"temperature": 22.4,
"humidity": 48.1,
"samples": 3,
"read_errors": 2,
"ts": 1792152000
}
| Topic | Conteúdo | Retido |
|---|---|---|
connectmq/demo/c3f19a/greenhouse/dht22 |
Leitura JSON a cada 30 s | Não |
connectmq/demo/c3f19a/greenhouse/status |
online / offline (last will) |
Sim |
Troque c3f19a por uma string aleatória sua. Incluir read_errors em cada mensagem custa alguns bytes e transforma um problema de hardware silencioso em algo que você pode colocar em gráfico e monitorar com alertas.
O script Raspberry Pi DHT22 MQTT
Salve como dht22_mqtt.py:
#!/usr/bin/env python3
"""Read a DHT22 and publish validated readings to MQTT."""
import json
import os
import statistics
import time
import uuid
import adafruit_dht
import board
import paho.mqtt.client as mqtt
BROKER = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", f"dht22-{uuid.uuid4().hex[:8]}")
PREFIX = os.getenv("MQTT_PREFIX", "connectmq/demo/c3f19a/greenhouse")
INTERVAL = int(os.getenv("PUBLISH_INTERVAL", "30"))
SAMPLES = 3
MAX_ATTEMPTS = 10
sensor = adafruit_dht.DHT22(board.D4)
total_errors = 0
def plausible(temp, hum):
return (
temp is not None and hum is not None
and -40.0 <= temp <= 80.0
and 0.0 <= hum <= 100.0
)
def read_median():
"""Collect SAMPLES good readings and return their medians, or None."""
global total_errors
temps, hums = [], []
attempts = 0
while len(temps) < SAMPLES and attempts < MAX_ATTEMPTS:
attempts += 1
try:
t, h = sensor.temperature, sensor.humidity
if plausible(t, h):
temps.append(t)
hums.append(h)
else:
total_errors += 1
except RuntimeError as err:
# Erros de checksum ou de temporização são esperados de vez em quando.
total_errors += 1
print(f"Read failed ({err}); retrying")
time.sleep(2.2)
if not temps:
return None
return statistics.median(temps), statistics.median(hums), len(temps)
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connection refused: {reason_code}")
return
print(f"Connected as {CLIENT_ID}")
client.publish(f"{PREFIX}/status", "online", qos=1, retain=True)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.on_connect = on_connect
client.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
client.tls_set()
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()
try:
while True:
started = time.monotonic()
result = read_median()
if result is None:
payload = {"error": "no valid reading", "read_errors": total_errors,
"ts": int(time.time())}
else:
temp, hum, n = result
payload = {
"temperature": round(temp, 1),
"humidity": round(hum, 1),
"samples": n,
"read_errors": total_errors,
"ts": int(time.time()),
}
info = client.publish(f"{PREFIX}/dht22", json.dumps(payload), qos=1)
if info.rc != mqtt.MQTT_ERR_SUCCESS:
print(f"Publish queued while offline (rc={info.rc})")
print(payload)
elapsed = time.monotonic() - started
time.sleep(max(0, INTERVAL - elapsed))
except KeyboardInterrupt:
pass
finally:
client.publish(f"{PREFIX}/status", "offline", qos=1, retain=True).wait_for_publish(2)
client.loop_stop()
client.disconnect()
sensor.exit()
Alguns detalhes merecem destaque. O intervalo de pouco mais de dois segundos entre leituras respeita o limite de amostragem do sensor. A mediana de três leituras remove o pico isolado ocasional que passa no checksum, mas continua errado. Quando todas as tentativas falham, o script ainda publica uma mensagem com um campo error, para que os subscribers distingam "sensor quebrado" de "Pi offline", situação coberta pelo topic de status retido. Por fim, sensor.exit() libera a linha GPIO; pular essa etapa pode deixar o pino ocupado na próxima execução.
Acompanhando os dados
Assine a partir do seu notebook para confirmar que as mensagens chegam:
mosquitto_sub -h broker.connectmq.com -p 1883 -i laptop-c3f19a \
-t 'connectmq/demo/c3f19a/greenhouse/#' -v
Você deve ver status online imediatamente (ele é retido) e uma leitura JSON aproximadamente a cada 30 segundos. Desconecte a rede do Pi por um minuto e reconecte: o paho reconecta automaticamente. Mensagens QoS 1 publicadas enquanto a conexão está caída ficam em fila na memória e são enviadas após a reconexão, desde que o processo continue rodando. Se você precisa que as leituras sobrevivam a um reboot durante uma queda, veja como guardar mensagens MQTT offline em Python com SQLite.
Escolhendo um intervalo de publicação
Temperatura e umidade em um ambiente ou estufa mudam devagar. Publicar a cada 30 a 60 segundos dá gráficos suaves sem inundar o broker ou o seu banco de dados. Mais rápido que a cada 10 segundos raramente acrescenta informação vinda de um DHT22, e cada ciclo de leitura com novas tentativas já leva vários segundos. Se você só se importa com mudanças, publique quando o valor variar mais de 0,2 °C ou 1 % de umidade, mais um heartbeat a cada poucos minutos.
O posicionamento do sensor importa tanto quanto o intervalo. Monte o DHT22 longe do próprio Pi, fora da luz solar direta e longe de aquecedores ou saídas de ar, com fluxo de ar livre em volta das aberturas da sua caixa. Um sensor colado com fita na caixa do Pi vai medir principalmente o processador. Em uma estufa, um pequeno abrigo contra radiação feito de pratos brancos empilhados evita que a luz do sol infle a leitura de temperatura.
DHT22 vs. outros sensores
O DHT22 serve bem para monitoramento interno, mas não é a única opção. Uma sonda à prova d'água DS18B20 é muito mais robusta só para temperatura e suporta várias sondas em um único fio; veja o guia Raspberry Pi DS18B20 MQTT. Se preferir rodar o mesmo sensor em um microcontrolador a bateria, o tutorial ESP32 DHT22 MQTT usa um payload idêntico, para que os dois dispositivos alimentem o mesmo dashboard. Para o panorama geral de publicar, assinar e rodar scripts no boot, comece pelo nosso tutorial de MQTT com Raspberry Pi.
Migrando para um broker privado
O servidor MQTT público gratuito em broker.connectmq.com é perfeito para testes, mas qualquer topic ali pode ser lido por qualquer pessoa. Para uma estufa ou sala de servidores real, crie um broker privado no ConnectMQ, um broker MQTT gerenciado, depois defina as variáveis de ambiente e reinicie:
export MQTT_PORT=8883
export MQTT_USERNAME=YOUR_USERNAME
export MQTT_PASSWORD=YOUR_PASSWORD
export MQTT_CLIENT_ID=YOUR_CLIENT_ID
export MQTT_PREFIX=site1/greenhouse/zone1
python dht22_mqtt.py
No portal ConnectMQ você pode acompanhar as leituras no explorador de topics ao vivo e criar uma regra de alerta por limite, por exemplo umidade acima de 85 %, sem escrever um subscriber extra.
Checklist de solução de problemas
- Todas as leituras falham: verifique se DATA está no GPIO4, se o pull-up está presente e se o VCC é 3,3 V.
- "Unable to set line to input" ou GPIO ocupado: outro processo está segurando o pino; pare scripts antigos e garanta que
sensor.exit()seja executado. - Os valores parecem certos, mas pulam ocasionalmente: mantenha o filtro de mediana ativo e aumente
SAMPLESpara 5. - Umidade travada em 99,9: condensação no sensor; o DHT22 não gosta de saturação constante.
- As leituras param, mas o status continua
online: o script travou na leitura; adicione um watchdog ou reinicie pelo systemd comRestart=always. - Conexão recusada, não autorizado: credenciais ou client ID não correspondem ao broker.
Comece com o ConnectMQ
Leve seu sensor da bancada para uma implantação real em um broker MQTT na nuvem. Crie um broker Serverless gratuito, sem cartão de crédito, copie as credenciais geradas para o seu ambiente e conecte via TLS na porta 8883. Suas leituras do DHT22 estarão disponíveis para dashboards, fluxos Node-RED e regras de alerta em poucos minutos.
Perguntas frequentes
Por que meu DHT22 retorna "Checksum did not validate" no Raspberry Pi?
O Linux ocasionalmente interrompe a temporização precisa de que o DHT22 precisa, então algumas leituras falham. Capture o erro, espere dois segundos e tente de novo; algumas falhas por hora são normais.
Com que frequência posso ler um DHT22?
No máximo uma vez a cada dois segundos. Para publicação MQTT, um intervalo de 30 a 60 segundos é mais que suficiente para monitorar ambientes e estufas.
Devo usar QoS 0 ou QoS 1 para leituras de sensores?
QoS 1 é um bom padrão porque garante a entrega com pouca sobrecarga. QoS 0 é aceitável para leituras muito frequentes em que perder uma não faz diferença.
Posso conectar vários sensores DHT22 a um Raspberry Pi?
Sim, dê a cada um seu próprio pino GPIO e crie um objeto DHT22 por pino. Leia-os um após o outro, e não ao mesmo tempo, e publique cada um em seu próprio topic.



