Um sensor ESP32 DHT22 MQTT é o clássico primeiro projeto de IoT, e vale a pena montá-lo direito. Neste tutorial, você vai ligar um DHT22, lê-lo sem os famosos erros de NaN, publicar temperatura, umidade e índice de calor em JSON no broker.connectmq.com e depois acompanhar as leituras ao vivo no explorador de topics do ConnectMQ.
O que você precisa
- Uma placa de desenvolvimento ESP32 (qualquer ESP32 clássico, S3, C3 ou C6).
- Um DHT22 (também vendido como AM2302), seja o sensor de 4 pinos ou um módulo breakout de 3 pinos.
- Um resistor de 10 kOhm se você usar o sensor sem módulo. Os módulos breakout normalmente já o incluem.
- Jumpers e uma protoboard.
- Arduino IDE 2.x com o pacote de placas esp32.
- Bibliotecas do Library Manager: DHT sensor library da Adafruit, Adafruit Unified Sensor e PubSubClient de Nick O'Leary.
Ligando o DHT22 ao ESP32
| Pino do DHT22 | Número do pino no sensor | Pino do ESP32 | Observações |
|---|---|---|---|
| VCC | 1 | 3V3 | 3,3 V funciona; mantenha os fios curtos |
| DATA | 2 | GPIO 4 | Pull-up de 10 kOhm do DATA para 3V3 |
| NC | 3 | não conectado | Apenas no sensor sem módulo |
| GND | 4 | GND |
O GPIO 4 é uma escolha segura em um ESP32 clássico porque não tem função de strapping no boot. Evite GPIO 0, 2, 12 e 15 para sensores; o estado deles no reset afeta o boot. No C3 e no C6, escolha qualquer GPIO livre que não seja pino de strapping e altere a constante DHT_PIN.
Mantenha o sensor a alguns centímetros do ESP32. O regulador e o rádio da placa esquentam durante o funcionamento, e um DHT22 colado diretamente na placa vai ler um ou dois graus a mais.
Como o DHT22 se comporta
O DHT22 usa um protocolo de fio único com temporização rigorosa. Duas características moldam o código:
- Ele só pode ser lido a cada 2 segundos. Ler mais rápido retorna valores em cache ou erros. Para telemetria MQTT, uma vez a cada 10 a 60 segundos é mais que suficiente.
- Leituras falham de vez em quando. Uma interrupção do Wi-Fi no momento errado corrompe a temporização dos bits, e a biblioteca retorna
NaN. Código de produção precisa verificar isso e tentar de novo, em vez de publicar lixo.
A precisão é de cerca de meio grau Celsius e alguns pontos percentuais de umidade relativa. Se você precisa de mais precisão na umidade ou de uma leitura de pressão, o tutorial ESP32 BME280 usa um sensor I2C menos sensível à temporização.
Design de topics e payload
Use um prefixo único para que seus dados não colidam com os de outros usuários do servidor MQTT público (gratuito):
| Topic | Retido | Payload |
|---|---|---|
connectmq/demo/dht7w3p/telemetry |
Não | {"temperature":22.4,"humidity":48.1,"heatIndex":22.1,"rssi":-61} |
connectmq/demo/dht7w3p/status |
Sim | online ou offline (last will) |
Um objeto JSON por leitura mantém os valores relacionados juntos, então um consumidor nunca vê uma temperatura combinada com uma umidade de outro momento.
O sketch ESP32 DHT22 MQTT completo
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
// ---------- configurações ----------
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* PREFIX = "connectmq/demo/dht7w3p"; // troque pelo seu próprio id aleatório
const uint8_t DHT_PIN = 4;
const uint32_t INTERVAL_MS = 15000;
DHT dht(DHT_PIN, DHT22);
WiFiClient net;
PubSubClient mqtt(net);
String clientId, topicTelemetry, topicStatus;
unsigned long lastRead = 0;
void connectWifi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(400);
Serial.print(".");
}
Serial.printf(" connected, IP %s\n", WiFi.localIP().toString().c_str());
}
void connectMqtt() {
while (!mqtt.connected()) {
Serial.print("MQTT... ");
if (mqtt.connect(clientId.c_str(), topicStatus.c_str(), 1, true, "offline")) {
Serial.println("connected");
mqtt.publish(topicStatus.c_str(), "online", true);
} else {
Serial.printf("failed (state %d), retry in 5 s\n", mqtt.state());
delay(5000);
}
}
}
bool readDht(float& t, float& h) {
for (int attempt = 0; attempt < 3; attempt++) {
h = dht.readHumidity();
t = dht.readTemperature(); // Celsius
if (!isnan(h) && !isnan(t)) return true;
delay(2100); // o DHT22 precisa de 2 s entre leituras
}
return false;
}
void setup() {
Serial.begin(115200);
dht.begin();
clientId = "esp32-dht22-" + String((uint32_t)ESP.getEfuseMac(), HEX);
topicTelemetry = String(PREFIX) + "/telemetry";
topicStatus = String(PREFIX) + "/status";
connectWifi();
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(512);
connectMqtt();
}
void loop() {
if (WiFi.status() != WL_CONNECTED) connectWifi();
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
if (millis() - lastRead < INTERVAL_MS) return;
lastRead = millis();
float t, h;
if (!readDht(t, h)) {
Serial.println("DHT22 read failed three times, skipping this cycle");
mqtt.publish(topicStatus.c_str(), "sensor-error", true);
return;
}
float heatIndex = dht.computeHeatIndex(t, h, false); // false = Celsius
char payload[160];
snprintf(payload, sizeof(payload),
"{\"temperature\":%.1f,\"humidity\":%.1f,\"heatIndex\":%.1f,\"rssi\":%d}",
t, h, heatIndex, WiFi.RSSI());
if (mqtt.publish(topicTelemetry.c_str(), payload)) {
Serial.println(payload);
} else {
Serial.println("publish failed");
}
}
Uma observação sobre o topic de status: após um erro do sensor, o sketch publica sensor-error como status retido, e a próxima conexão bem-sucedida o substitui por online. Se preferir um topic de saúde separado, adicione um; o importante é que um sensor com defeito fique visível em vez de silencioso.
Escolhendo o intervalo e economizando energia
O intervalo de 15 segundos do sketch serve para uma demonstração na bancada. Para ambientes reais, pense na finalidade dos dados. O monitoramento de conforto raramente precisa de mais de uma leitura por minuto, enquanto um alarme de estufa ou de rack de servidores pode querer uma a cada 30 segundos. Publicar com menos frequência reduz o tráfego no broker e facilita a geração de gráficos.
Se o nó funciona com bateria, manter o Wi-Fi conectado o tempo todo é o maior consumo. Uma estrutura melhor é: acordar do deep sleep, alimentar o sensor, esperar dois segundos, ler, conectar ao Wi-Fi e ao broker, publicar, desconectar de forma limpa e então chamar esp_deep_sleep_start() com despertar por timer. Nesse modo, o last will é menos útil, porque todo sono parece uma desconexão; publique um valor retido lastSeen e deixe o dashboard sinalizar sensores que não reportam há alguns intervalos. Alimente o DHT22 por um pino GPIO se quiser desligá-lo totalmente durante o sono e lembre-se de esperar os dois segundos completos após ligá-lo antes da primeira leitura.
Testando pela linha de comando
Assine com o cliente Mosquitto, usando qualquer client ID único:
mosquitto_sub -h broker.connectmq.com -p 1883 -i dht-viewer-7w3p -t "connectmq/demo/dht7w3p/#" -v
Assopre no sensor e a umidade sobe em uma ou duas leituras. Lembre-se de que o servidor MQTT público é compartilhado e pode ser lido por qualquer pessoa, então serve para uma demonstração na bancada, mas não para dados de produção ou que você quer manter privados. O guia citado acima lista seus limites.
Vendo os dados ESP32 DHT22 MQTT no explorador de topics
A linha de comando é rápida, mas uma visualização gráfica é mais agradável quando você roda vários sensores. Crie uma conta gratuita em app.connectmq.com, escolha o plano gratuito Serverless, crie um broker e copie o usuário, a senha e o client ID gerados. Depois, altere o sketch para conectar com credenciais via TLS:
#include <WiFiClientSecure.h>
WiFiClientSecure net; // substitui o WiFiClient
PubSubClient mqtt(net);
// ISRG Root X1 (Let's Encrypt) em formato PEM, copiado de letsencrypt.org
static const char ROOT_CA[] = R"PEM(
-----BEGIN CERTIFICATE-----
...paste ISRG Root X1 here...
-----END CERTIFICATE-----
)PEM";
// em setup(), antes de connectMqtt():
net.setCACert(ROOT_CA);
mqtt.setServer("broker.connectmq.com", 8883);
// em connectMqtt():
mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD",
topicStatus.c_str(), 1, true, "offline");
Abra seu broker no portal do ConnectMQ e vá ao explorador de topics ao vivo. Seu topic de telemetria aparece na árvore, e cada nova leitura é atualizada no lugar. No mesmo portal, você pode adicionar uma regra de alerta por limite, por exemplo umidade acima de 70 por cento, e revisar os alertas disparados no log de alertas. Para gráficos de longo prazo, rode Node-RED, Telegraf ou um pequeno assinante em Python que se conecte ao broker como cliente MQTT e grave no banco de dados de sua escolha.
Solução de problemas
- Toda leitura retorna NaN. Verifique o resistor de pull-up e se o DATA realmente vai ao pino definido em
DHT_PIN. A falta do pull-up é a causa mais comum em sensores sem módulo. - NaN ocasional. Normal com carga de Wi-Fi. O loop de novas tentativas resolve; não reduza o intervalo de 2 segundos.
- Temperatura travada ou lenta para mudar. O DHT22 responde devagar e leva dezenas de segundos para estabilizar. É o sensor, não o MQTT.
- Leituras alguns graus acima. Autoaquecimento da placa. Afaste o sensor com um cabo curto.
- Cabos longos geram erros. O protocolo de fio único é sensível. Mantenha o cabo curto ou reduza um pouco o valor do pull-up.
- Estado MQTT -2. A conexão TCP falhou; verifique o Wi-Fi e se a saída na porta 1883 é permitida.
Para formatar payloads maiores com segurança, veja ArduinoJson para payloads MQTT no ESP32. Se você precisa de vários pontos de temperatura, mas não de umidade, o projeto com vários sensores DS18B20 é mais adequado.
Comece com o ConnectMQ
Um nó DHT22 se torna útil quando seus dados chegam a um lugar confiável. O ConnectMQ é um broker MQTT gerenciado e oferece um broker MQTT na nuvem com TLS na porta 8883, MQTT 3.1.1 e 5.0, explorador de topics ao vivo e regras de alerta, além de um plano gratuito Serverless sem cartão de crédito. Grave a versão TLS deste sketch com suas credenciais e continue com o tutorial de IoT com ESP32 para mais projetos de sensores e dispositivos.
Perguntas frequentes
Com que frequência um ESP32 deve publicar leituras do DHT22 via MQTT?
A cada 10 a 60 segundos é o típico para monitorar ambientes. O DHT22 não pode ser lido mais rápido que a cada 2 segundos, e sua resposta lenta torna inútil publicar com mais frequência.
Posso usar um DHT11 em vez de um DHT22?
Sim. Troque DHT22 por DHT11 no construtor. O DHT11 tem precisão e faixa menores, então o DHT22 ou um BME280 são melhores para qualquer coisa além de uma demonstração.
Devo publicar temperatura e umidade em topics separados?
As duas opções funcionam. Um único objeto JSON mantém juntos os valores de um mesmo momento, enquanto topics separados são práticos para ferramentas que esperam um número por topic.
Por que usar um topic de status retido com last will?
O broker publica a mensagem retida offline se o ESP32 desaparecer sem se desconectar, então qualquer dashboard consegue distinguir um sensor inativo de um sensor apenas silencioso.



