Lectura de datos de temperatura y humedad en el ESP32

Programación para leer DHT11, manejo de errores, validación de datos

Módulo 2 ⏱️ 2 horas 🛠️ ESP32 + DHT11 🌐 Flask + MQTT

Introducción

En esta clase aprenderemos a implementar un sistema completo de monitoreo de temperatura y humedad utilizando el ESP32 como dispositivo IoT. Desarrollaremos la capacidad de leer datos del sensor DHT11, procesar la información, implementar manejo de errores robusto y transmitir los datos a través de MQTT hacia una aplicación web Flask.

Este sistema constituye la base fundamental para aplicaciones IoT industriales y domóticas, donde la precisión en la lectura de datos ambientales y la confiabilidad del sistema son críticas para el éxito del proyecto.

Objetivos de la clase:
  • Configurar y programar el sensor DHT11 con ESP32
  • Implementar manejo de errores y validación de datos
  • Establecer comunicación MQTT con broker Mosquitto
  • Crear interfaz web con Flask para visualización

Conceptos Fundamentales

Para construir un sistema robusto de lectura de sensores, debemos comprender varios conceptos técnicos fundamentales:

Sensor DHT11 - Características Técnicas

  • Protocolo de comunicación: One-Wire digital
  • Rango de temperatura: 0-50°C (±2°C precisión)
  • Rango de humedad: 20-90% RH (±5% RH precisión)
  • Frecuencia de muestreo: 1Hz (1 lectura por segundo)
  • Voltaje de alimentación: 3.5V a 5.5V

Protocolo MQTT para IoT

  • Publisher/Subscriber: Patrón de mensajería asíncrona
  • QoS Levels: 0 (At most once), 1 (At least once), 2 (Exactly once)
  • Topics: Estructura jerárquica para organización de mensajes
  • Retain Flag: Mantiene último mensaje para nuevos suscriptores

Estrategias de Manejo de Errores

  • Timeout Handling: Manejo de timeouts en lecturas del sensor
  • Data Validation: Verificación de rangos válidos de datos
  • Retry Mechanisms: Reintentos automáticos con backoff exponencial
  • Graceful Degradation: Operación con funcionalidad reducida ante fallos

Conexiones Físicas del DHT11

Diagrama de conexión:

ESP32      DHT11
-----      -----
3.3V   →   VCC (Pin 1)
GPIO4  →   DATA (Pin 2)
N/C    →   N/C (Pin 3)
GND    →   GND (Pin 4)

Resistor pull-up 10kΩ entre DATA y VCC
            

Implementación Práctica

Implementaremos el sistema completo paso a paso, comenzando con la configuración del ESP32 y culminando con la aplicación web Flask.

Configuración Inicial del ESP32


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

// Configuración de pines y constantes
#define DHT_PIN 4
#define DHT_TYPE DHT11
#define LED_PIN 2

// Configuración WiFi
const char* ssid = "TU_WIFI_SSID";
const char* password = "TU_WIFI_PASSWORD";

// Configuración MQTT
const char* mqtt_server = "localhost";
const int mqtt_port = 1883;
const char* mqtt_user = "esp32_user";
const char* mqtt_password = "esp32_pass";
const char* topic_temperature = "sensors/temperature";
const char* topic_humidity = "sensors/humidity";
const char* topic_status = "sensors/status";

// Instancias de objetos
DHT dht(DHT_PIN, DHT_TYPE);
WiFiClient espClient;
PubSubClient client(espClient);

// Variables para control de tiempo y errores
unsigned long lastMeasurement = 0;
const unsigned long measurementInterval = 5000; // 5 segundos
int consecutiveErrors = 0;
const int maxConsecutiveErrors = 5;
            

Función de Configuración Inicial


void setup() {
    Serial.begin(115200);
    Serial.println("Iniciando sistema de monitoreo DHT11...");
    
    // Configuración de pines
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LOW);
    
    // Inicialización del sensor DHT11
    dht.begin();
    Serial.println("Sensor DHT11 inicializado");
    
    // Conexión WiFi
    setupWiFi();
    
    // Configuración MQTT
    client.setServer(mqtt_server, mqtt_port);
    client.setCallback(onMqttMessage);
    
    // Conexión inicial MQTT
    connectToMQTT();
    
    Serial.println("Sistema listo para operación");
    digitalWrite(LED_PIN, HIGH); // Indica sistema listo
}

void setupWiFi() {
    delay(10);
    Serial.println();
    Serial.print("Conectando a WiFi: ");
    Serial.println(ssid);
    
    WiFi.begin(ssid, password);
    
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 30) {
        delay(500);
        Serial.print(".");
        attempts++;
    }
    
    if (WiFi.status() == WL_CONNECTED) {
        Serial.println();
        Serial.println("WiFi conectado exitosamente!");
        Serial.print("Dirección IP: ");
        Serial.println(WiFi.localIP());
    } else {
        Serial.println();
        Serial.println("Error: No se pudo conectar a WiFi");
        // Reiniciar ESP32 después de fallo crítico
        ESP.restart();
    }
}
            

Lectura Robusta del Sensor DHT11


struct SensorData {
    float temperature;
    float humidity;
    bool isValid;
    String errorMessage;
};

SensorData readDHT11Sensor() {
    SensorData data = {0.0, 0.0, false, ""};
    
    Serial.println("Leyendo sensor DHT11...");
    
    // Lectura de los valores del sensor
    float humidity = dht.readHumidity();
    float temperature = dht.readTemperature();
    
    // Verificación básica de NaN
    if (isnan(humidity) || isnan(temperature)) {
        data.errorMessage = "Error: Lectura NaN del sensor";
        Serial.println(data.errorMessage);
        return data;
    }
    
    // Validación de rangos técnicos del DHT11
    if (temperature < -10.0 || temperature > 60.0) {
        data.errorMessage = "Error: Temperatura fuera del rango válido (" + 
                           String(temperature) + "°C)";
        Serial.println(data.errorMessage);
        return data;
    }
    
    if (humidity < 0.0 || humidity > 100.0) {
        data.errorMessage = "Error: Humedad fuera del rango válido (" + 
                           String(humidity) + "%)";
        Serial.println(data.errorMessage);
        return data;
    }
    
    // Datos válidos
    data.temperature = temperature;
    data.humidity = humidity;
    data.isValid = true;
    
    Serial.println("Lectura exitosa - Temp: " + String(temperature) + 
                   "°C, Humedad: " + String(humidity) + "%");
    
    return data;
}

void handleSensorReading() {
    SensorData sensorData = readDHT11Sensor();
    
    if (sensorData.isValid) {
        // Resetear contador de errores consecutivos
        consecutiveErrors = 0;
        
        // Publicar datos válidos via MQTT
        publishSensorData(sensorData.temperature, sensorData.humidity);
        
        // Indicador visual de operación exitosa
        blinkLED(1, 100); // 1 parpadeo rápido
        
    } else {
        // Incrementar contador de errores
        consecutiveErrors++;
        
        Serial.println("Error en lectura del sensor (#" + 
                       String(consecutiveErrors) + "): " + 
                       sensorData.errorMessage);
        
        // Publicar estado de error
        publishErrorStatus(sensorData.errorMessage);
        
        // Indicador visual de error
        blinkLED(3, 200); // 3 parpadeos lentos
        
        // Verificar si hay demasiados errores consecutivos
        if (consecutiveErrors >= maxConsecutiveErrors) {
            Serial.println("ALERTA: Demasiados errores consecutivos. Reiniciando...");
            publishErrorStatus("CRITICO: Reiniciando por errores consecutivos");
            delay(1000);
            ESP.restart();
        }
    }
}
            

Gestión de Comunicación MQTT


void connectToMQTT() {
    while (!client.connected()) {
        Serial.print("Intentando conexión MQTT...");
        
        String clientId = "ESP32Client-";
        clientId += String(random(0xffff), HEX);
        
        if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
            Serial.println(" conectado!");
            
            // Publicar mensaje de conexión
            publishErrorStatus("ESP32 conectado y operativo");
            
            // Suscribirse a topics de control (opcional)
            client.subscribe("sensors/control");
            
        } else {
            Serial.print(" falló, rc=");
            Serial.print(client.state());
            Serial.println(" reintentando en 5 segundos");
            delay(5000);
        }
    }
}

void publishSensorData(float temperature, float humidity) {
    // Crear JSON con los datos del sensor
    StaticJsonDocument<200> doc;
    doc["timestamp"] = millis();
    doc["device_id"] = "ESP32-DHT11-01";
    doc["temperature"] = round(temperature * 100.0) / 100.0; // 2 decimales
    doc["humidity"] = round(humidity * 100.0) / 100.0; // 2 decimales
    
    String jsonString;
    serializeJson(doc, jsonString);
    
    // Publicar temperatura
    String tempPayload = String(temperature, 2);
    bool tempResult = client.publish(topic_temperature, tempPayload.c_str(), true);
    
    // Publicar humedad
    String humPayload = String(humidity, 2);
    bool humResult = client.publish(topic_humidity, humPayload.c_str(), true);
    
    // Publicar datos completos en JSON
    bool jsonResult = client.publish("sensors/data", jsonString.c_str(), true);
    
    if (tempResult && humResult && jsonResult) {
        Serial.println("Datos publicados exitosamente via MQTT");
    } else {
        Serial.println("Error al publicar algunos datos MQTT");
    }
}

void publishErrorStatus(String errorMessage) {
    StaticJsonDocument<200> doc;
    doc["timestamp"] = millis();
    doc["device_id"] = "ESP32-DHT11-01";
    doc["status"] = "error";
    doc["message"] = errorMessage;
    doc["consecutive_errors"] = consecutiveErrors;
    
    String jsonString;
    serializeJson(doc, jsonString);
    
    client.publish(topic_status, jsonString.c_str(), true);
}

void onMqttMessage(char* topic, byte* payload, unsigned int length) {
    Serial.print("Mensaje recibido en topic: ");
    Serial.println(topic);
    
    String message;
    for (int i = 0; i < length; i++) {
        message += (char)payload[i];
    }
    Serial.println("Payload: " + message);
}

void blinkLED(int times, int delayMs) {
    for (int i = 0; i < times; i++) {
        digitalWrite(LED_PIN, LOW);
        delay(delayMs);
        digitalWrite(LED_PIN, HIGH);
        delay(delayMs);
    }
}
            

Loop Principal del ESP32


void loop() {
    // Verificar conexión MQTT
    if (!client.connected()) {
        connectToMQTT();
    }
    client.loop();
    
    // Verificar conexión WiFi
    if (WiFi.status() != WL_CONNECTED) {
        Serial.println("WiFi desconectado. Reintentando...");
        setupWiFi();
    }
    
    // Realizar lectura periódica del sensor
    unsigned long now = millis();
    if (now - lastMeasurement > measurementInterval) {
        lastMeasurement = now;
        handleSensorReading();
    }
    
    // Pequeña pausa para evitar saturar el procesador
    delay(100);
}