ESP32 y Arduino

ESP32 DHT22 MQTT: publica temperatura y humedad en JSON

Conecta un DHT22 a un ESP32, lee temperatura y humedad de forma fiable, publica JSON en broker.connectmq.com y consulta las lecturas en directo en el explorador de topics de ConnectMQ.

Sensor DHT22 conectado a un ESP32 en una protoboard enviando lecturas a la nube en un proyecto esp32 dht22 mqtt
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

Un sensor ESP32 DHT22 MQTT es el clásico primer proyecto IoT, y merece la pena construirlo bien. En este tutorial conectarás un DHT22, lo leerás sin los típicos errores NaN, publicarás temperatura, humedad e índice de calor en JSON en broker.connectmq.com y después verás las lecturas en directo en el explorador de topics de ConnectMQ.

Qué necesitas

  • Una placa de desarrollo ESP32 (cualquier ESP32 clásico, S3, C3 o C6).
  • Un DHT22 (también vendido como AM2302), ya sea el sensor desnudo de 4 pines o un módulo de 3 pines.
  • Una resistencia de 10 kOhm si usas el sensor desnudo. Los módulos suelen incluirla.
  • Cables puente y una protoboard.
  • Arduino IDE 2.x con el paquete de placas esp32.
  • Librerías del Library Manager: DHT sensor library de Adafruit, Adafruit Unified Sensor y PubSubClient de Nick O'Leary.

Conexión del DHT22 al ESP32

Pin del DHT22 Número de pin del sensor desnudo Pin del ESP32 Notas
VCC 1 3V3 3,3 V funciona; mantén los cables cortos
DATA 2 GPIO 4 Pull-up de 10 kOhm de DATA a 3V3
NC 3 sin conectar Solo sensor desnudo
GND 4 GND

GPIO 4 es una opción segura en un ESP32 clásico porque no interviene en el arranque. Evita GPIO 0, 2, 12 y 15 para sensores; su estado durante el reset afecta al arranque. En el C3 y el C6, elige cualquier GPIO libre que no sea un pin de strapping y cambia la constante DHT_PIN.

Mantén el sensor a unos centímetros del ESP32. El regulador y la radio de la placa se calientan durante el funcionamiento, y un DHT22 pegado directamente a la placa marcará uno o dos grados de más.

Cómo se comporta el DHT22

El DHT22 usa un protocolo de un solo hilo con una temporización estricta. Dos características condicionan el código:

  1. Solo se puede leer cada 2 segundos. Leer más rápido devuelve valores en caché o errores. Para telemetría MQTT, una vez cada 10 a 60 segundos es más que suficiente.
  2. Las lecturas fallan de vez en cuando. Una interrupción del Wi-Fi en mal momento corrompe la temporización de los bits y la librería devuelve NaN. El código de producción debe comprobarlo y reintentar en lugar de publicar basura.

La precisión es de aproximadamente medio grado Celsius y unos pocos puntos porcentuales de humedad relativa. Si necesitas más precisión en la humedad o una lectura de presión, el tutorial de ESP32 con BME280 usa un sensor I2C menos sensible a la temporización.

Diseño de topics y payload

Usa un prefijo único para que tus datos no choquen con los de otros usuarios del servidor público de pruebas:

Topic Retenido Payload
connectmq/demo/dht7w3p/telemetry No {"temperature":22.4,"humidity":48.1,"heatIndex":22.1,"rssi":-61}
connectmq/demo/dht7w3p/status Sí online u offline (last will)

Un objeto JSON por lectura mantiene juntos los valores relacionados, de modo que un consumidor nunca ve una temperatura emparejada con una humedad de otro momento.

El sketch completo ESP32 DHT22 MQTT

#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>

// ---------- ajustes ----------
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";   // cámbialo por tu propio id aleatorio
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);                        // el DHT22 necesita 2 s entre lecturas
  }
  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");
  }
}

Una nota sobre el topic de estado: tras un error del sensor, el sketch publica sensor-error como estado retenido, y la siguiente conexión correcta lo sobrescribe con online. Si prefieres un topic de salud independiente, añádelo; lo importante es que un sensor averiado sea visible en lugar de silencioso.

Elegir el intervalo y ahorrar energía

El intervalo de 15 segundos del sketch es adecuado para una demostración en el escritorio. Para espacios reales, piensa para qué sirven los datos. La supervisión del confort rara vez necesita más de una lectura por minuto, y la alarma de un invernadero o de un armario de servidores quizá quiera una cada 30 segundos. Publicar con menos frecuencia reduce el tráfico del broker y facilita representar los datos en gráficas.

Si el nodo funciona con baterías, mantener el Wi-Fi conectado todo el tiempo es el mayor consumo. Una estructura mejor es: despertar del deep sleep, alimentar el sensor, esperar dos segundos, leer, conectarse al Wi-Fi y al broker, publicar, desconectarse limpiamente y llamar a esp_deep_sleep_start() con un despertar por temporizador. En ese modo el last will es menos útil, porque cada periodo de sueño parece una desconexión, así que publica en su lugar un valor retenido lastSeen y deja que el dashboard marque los sensores que no han informado durante varios intervalos. Alimenta el DHT22 desde un pin GPIO si quieres que quede totalmente apagado durante el sueño, y recuerda esperar los dos segundos completos tras alimentarlo antes de la primera lectura.

Pruebas con la línea de comandos

Suscríbete con el cliente de Mosquitto, usando cualquier client ID único:

mosquitto_sub -h broker.connectmq.com -p 1883 -i dht-viewer-7w3p -t "connectmq/demo/dht7w3p/#" -v

Echa el aliento sobre el sensor y la humedad sube en una o dos lecturas. Recuerda que el área pública es compartida y cualquiera puede leerla, así que sirve para una demostración en el escritorio, pero no para datos que quieras mantener privados. La guía del servidor MQTT público gratuito enumera sus límites.

Ver los datos ESP32 DHT22 MQTT en el explorador de topics

La línea de comandos es rápida, pero una vista visual resulta más cómoda cuando tienes varios sensores. Crea una cuenta gratuita en app.connectmq.com, elige el plan gratuito Serverless, crea un broker y copia el usuario, la contraseña y el client ID generados. Después cambia el sketch para conectarse con credenciales sobre TLS:

#include <WiFiClientSecure.h>

WiFiClientSecure net;              // sustituye a WiFiClient
PubSubClient mqtt(net);

// ISRG Root X1 (Let's Encrypt) en formato PEM, copiado de letsencrypt.org
static const char ROOT_CA[] = R"PEM(
-----BEGIN CERTIFICATE-----
...paste ISRG Root X1 here...
-----END CERTIFICATE-----
)PEM";

// en setup(), antes de connectMqtt():
net.setCACert(ROOT_CA);
mqtt.setServer("broker.connectmq.com", 8883);

// en connectMqtt():
mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD",
             topicStatus.c_str(), 1, true, "offline");

Abre tu broker en el portal de ConnectMQ y ve al explorador de topics en vivo. Tu topic de telemetría aparece en el árbol y cada nueva lectura se actualiza en su sitio. Desde el mismo portal puedes añadir una regla de alerta por umbral, por ejemplo humedad por encima del 70 por ciento, y revisar las alertas disparadas en el registro de alertas. Para gráficas a largo plazo, ejecuta Node-RED, Telegraf o un pequeño suscriptor en Python que se conecte al broker como cliente MQTT y escriba en la base de datos que elijas.

Solución de problemas

  1. Todas las lecturas devuelven NaN. Comprueba la resistencia pull-up y que DATA va realmente al pin configurado en DHT_PIN. La falta de pull-up es la causa más común con sensores desnudos.
  2. NaN ocasionales. Es normal con carga de Wi-Fi. El bucle de reintentos lo gestiona; no reduzcas la pausa de 2 segundos.
  3. Temperatura atascada o que cambia despacio. El DHT22 responde lentamente y tarda decenas de segundos en estabilizarse. Es el sensor, no MQTT.
  4. Lecturas un par de grados altas. Autocalentamiento de la placa. Aleja el sensor con un cable corto.
  5. Los cables largos dan errores. El protocolo de un solo hilo es sensible. Mantén el cable corto o reduce ligeramente el valor del pull-up.
  6. Estado MQTT -2. Falló la conexión TCP; comprueba el Wi-Fi y que el puerto 1883 saliente esté permitido.

Para formatear payloads más grandes de forma segura, consulta ArduinoJson para payloads MQTT en ESP32. Si necesitas varios puntos de temperatura pero no humedad, el proyecto multisensor con DS18B20 encaja mejor.

Empieza con ConnectMQ

Un nodo DHT22 resulta útil cuando sus datos llegan a un sitio fiable. ConnectMQ es un broker MQTT gestionado: un broker MQTT en la nube con TLS en el 8883, MQTT 3.1.1 y 5.0, un explorador de topics en vivo y reglas de alerta, además de un plan gratuito Serverless sin tarjeta de crédito. Graba la versión TLS de este sketch con tus credenciales y continúa con el tutorial de IoT con ESP32 para ver más proyectos con sensores y dispositivos.

Preguntas frecuentes

¿Con qué frecuencia debe publicar un ESP32 las lecturas del DHT22 por MQTT?

Cada 10 a 60 segundos es lo habitual para supervisar una habitación. El DHT22 no puede leerse más rápido que cada 2 segundos, y su respuesta lenta hace inútil publicar con más frecuencia.

¿Puedo usar un DHT11 en lugar de un DHT22?

Sí. Cambia DHT22 por DHT11 en el constructor. El DHT11 tiene menos precisión y rango, así que el DHT22 o un BME280 son mejores para cualquier cosa que vaya más allá de una demostración.

¿Debo publicar la temperatura y la humedad en topics separados?

Ambas opciones funcionan. Un único objeto JSON mantiene juntos los valores de un mismo momento, mientras que los topics separados son prácticos para herramientas que esperan un número por topic.

¿Por qué usar un topic de estado retenido con un last will?

El broker publica el mensaje retenido offline si el ESP32 desaparece sin desconectarse, así que cualquier dashboard puede distinguir un sensor muerto de uno que simplemente está en silencio.

Artículos relacionados

Ejemplo de ESP-IDF MQTT: crea un cliente esp-mqtt desde cero

Construye un proyecto ESP-IDF desde una carpeta vacía con el componente esp-mqtt: configuración de Wi-Fi en modo estación, el manejador de eventos MQTT, publicación y suscripción, propiedades de MQTT 5 y TLS con el bundle de certificados.

9 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.