← PortalIr al tutorial ↓

DPOIT · IoT · ESP32

ESP32 con WiFiManager, OLED, DHT22 y PHP

Sistema de monitoreo ambiental: un ESP32 mide temperatura y humedad con un DHT22, las muestra en una pantalla OLED y las envía a un servidor PHP que las guarda en el archivo json.json. La red WiFi se configura con WiFiManager, sin tocar el código.

  • ESP32 DevKit V1
  • OLED SSD1306 I2C
  • DHT22
  • WiFiManager
  • HTTP + JSON
  • PHP

Resumen del proyecto

Qué se necesita y cómo se conecta todo.

Componentes

  • ESP32 DevKit V1
  • Pantalla OLED SSD1306 (I2C)
  • Sensor DHT22
  • Cables de conexión
  • Cable USB para programación

Pantalla OLED (I2C)

  • SDA → GPIO21
  • SCL → GPIO22
  • VCC → 3.3V
  • GND → GND

Sensor DHT22

  • DATA → GPIO33 (según el código)
  • VCC → 3.3V
  • GND → GND

Librerías requeridas

  • WiFiManager (by tzapu)
  • DHTesp
  • U8g2lib
  • HTTPClient
  • ArduinoJson
Pin del DHT22: en dht22.h el pin definido es pinDHT = 33. El texto original del tutorial menciona GPIO15; conecta el sensor al pin que realmente uses en el código.

Tutorial

Guía paso a paso del funcionamiento y configuración del proyecto.

Índice

01Estructura del programa

El programa está dividido en varios archivos para mejor organización.

esp32_wifimanager_oled_dht22_php.ino

Archivo principal que coordina todo el sistema.

oled.h

Manejo de la pantalla OLED.

wm.h

Gestión de la conexión WiFi.

dht22.h

Lectura del sensor DHT22.

php.h

Envío de datos al servidor PHP.

php.php

Programa del servidor que almacena los datos. Crea json.json.

vistaprevia.html

Visualización de los datos en tiempo real.

02Configuración inicial

En setup() se realizan estas operaciones, y después loop() mantiene todo en marcha.

Serial 115200→OLED→Mensaje inicial→WiFiManager→DHT22

Inicialización (setup)

  • Inicia la comunicación serial a 115200 baudios
  • Configura la pantalla OLED
  • Muestra un mensaje inicial en la OLED
  • Inicia WiFiManager
  • Configura el sensor DHT22

Bucle principal (loop)

  • loopWM(): mantiene la conexión WiFi
  • loopDHT22(): lee el sensor
  • loopPHP(): envía los datos al servidor

03Configuración WiFi

El sistema usa WiFiManager para facilitar la conexión sin modificar el código.

  1. Al iniciar por primera vez, el ESP32 crea un punto de acceso WiFi.
  2. Conéctate a la red del ESP32 desde un dispositivo (en el tutorial original, "ESP32_AP").
  3. Abre el navegador y accede a 192.168.4.1.
  4. Selecciona la red WiFi e introduce la contraseña.
  5. El ESP32 se conectará automáticamente a esa red.
Resetear el WiFi: si mantienes pulsado el botón BOOT (GPIO0) durante 3 segundos, se borran los datos de red guardados y el ESP32 se reinicia, mostrando de nuevo el portal cautivo.

04Lectura de sensores

El programa lee los datos del DHT22 cada 10 segundos.

Temperatura

En grados Celsius.

Humedad

Humedad relativa en porcentaje.

Los datos se muestran en la pantalla OLED y se almacenan en variables globales para su envío.

05Envío de datos PHP (php.h)

El ESP32 envía un JSON por POST cada 10 segundos y el servidor responde con otro JSON.

DHT22→ESP32 (JSON)→ POST →php.php→json.json

URL a modificar en php.h:

const char* serverUrl = "https://unicarlos.es/arduino/esp32_wifimanager_oled_dht22_php/php.php";
Importante: el servidor guarda solo los últimos 10 registros (temperatura, humedad y fecha y hora) en json.json.

06Visualización

Los datos pueden verse de tres formas.

Pantalla OLED

Muestra los valores actuales de temperatura y humedad.

Monitor Serial

Información de depuración y estado del sistema.

Interfaz web

Gráficas y medidores en tiempo real.

Vista previa

Interfaz web del Sistema de Monitoreo Ambiental con ESP32: temperatura y humedad actuales y gráfica de temperatura a lo largo del tiempo.

Pulsa la imagen para ampliarla y vuelve a pulsar para cerrarla.

Código del proyecto

Elige un archivo para ver su contenido.

#include "oled.h"
#include "wm.h"
#include "dht22.h"
#include "php.h"

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println("Iniciando...");
  setupOled();
  mostrarOled("Iniciando...", "WiFiManager", "OLED", "ESP32");
  setupWM();
  setupDHT22();
}

void loop() {
  loopWM();
  loopDHT22();
  loopPHP();
}
#include <DHTesp.h>

DHTesp dht;
const int pinDHT = 33;

TempAndHumidity datosTH;
String temperatura;
String humedad;
float temperaturaFloat;
float humedadFloat;

unsigned long tiempoAnterior = 0;
const long intervalo = 10000;  // intervalo de 10seg

void setupDHT22() {
  dht.setup(pinDHT, DHTesp::DHT22);
}

void loopDHT22() {
  unsigned long tiempoActual = millis();
  if (tiempoActual - tiempoAnterior >= intervalo) {
    tiempoAnterior = tiempoActual;

    datosTH = dht.getTempAndHumidity();
    temperatura = String(datosTH.temperature, 2) + " C";
    humedad = String(datosTH.humidity, 1) + " %";
    temperaturaFloat = datosTH.temperature;
    humedadFloat = datosTH.humidity;
    Serial.print("Temperatura: ");
    Serial.print(temperatura);
    Serial.print("  Humedad: ");
    Serial.print(humedad);
    Serial.println();

    mostrarOled("ip: "+ipActual, "Temperatura: "+temperatura,"Humedad: "+humedad,"cvf");
  }
}
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);

void setupOled() {
  u8g2.begin();
}

void mostrarOled(String cad1="", String cad2="", String cad3="", String cad4="") {
  u8g2.clearBuffer();  // clear the internal memory
  u8g2.setFont(u8g2_font_6x10_tf);
  u8g2.setCursor(5, 14);
  u8g2.print(cad1);
  u8g2.setCursor(5, 30);
  u8g2.print(cad2);
  u8g2.setCursor(5, 46);
  u8g2.print(cad3);
  u8g2.setCursor(5, 62);
  u8g2.print(cad4);
  u8g2.sendBuffer();  // transfer internal memory to the display
}
#include <HTTPClient.h>
#include <ArduinoJson.h>

HTTPClient http;
//const char* serverUrl = "http://192.168.1.105/arduino/esp32_wifimanager_oled_dht22_php/php.php";
const char* serverUrl = "https://unicarlos.es/arduino/esp32_wifimanager_oled_dht22_php/php.php";

unsigned long lastTime = 0;
const long interval = 10000; // Intervalo de 10 segundos

void enviarDatosJSON(float temperatura, float humedad) {
  if (WiFi.status() == WL_CONNECTED) {
    // Crear documento JSON
    StaticJsonDocument<200> doc;
    doc["accion"] = "guardarTemHum";
    doc["temp"] = temperatura;
    doc["hum"] = humedad;

    // Serializar JSON
    String jsonString;
    serializeJson(doc, jsonString);

    // Configurar petición HTTP
    http.begin(serverUrl);
    http.addHeader("Content-Type", "application/json");

    // Enviar POST
    int httpCode = http.POST(jsonString);

    Serial.print("Enviando: ");
    Serial.println(jsonString);

    if (httpCode > 0) {
      String response = http.getString();
      Serial.print("Respuesta del servidor: ");
      Serial.println(response);
      
      // Procesar respuesta JSON
      StaticJsonDocument<200> respDoc;
      DeserializationError error = deserializeJson(respDoc, response);
      
      if (!error) {
        bool exito = respDoc["exito"];
        const char* mensaje = respDoc["mensaje"];
        Serial.print("Éxito: ");
        Serial.println(exito);
        Serial.print("Mensaje: ");
        Serial.println(mensaje);
      }
    } else {
      Serial.print("Error en HTTP request: ");
      Serial.println(httpCode);
    }

    http.end();
  }
}

void loopPHP() {
  unsigned long currentTime = millis();
  
  if (currentTime - lastTime >= interval) {
    lastTime = currentTime;
    enviarDatosJSON(temperaturaFloat, humedadFloat);
  }
}
#include "WiFi.h"
#include <WiFiManager.h>

const int pinCero = 0;
const int pinDos = 2;
WiFiManager wm;
String ipActual = "ninguna";

unsigned long tiempoPulsado = 0;
bool botonCeroPulsado = false;
bool estadoLedDos = LOW;
unsigned long tiempoRetardo = 0;

void setupWM() {
  pinMode(pinCero, INPUT);
  pinMode(pinDos, OUTPUT);
  digitalWrite(pinDos, LOW);
  // conectando
  Serial.println();
  Serial.println(WiFi.getHostname());
  digitalWrite(pinDos, HIGH);
  mostrarOled("SSID", WiFi.getHostname(), "Portal Cautivo", "192.168.4.1");
  if (wm.autoConnect(WiFi.getHostname(), "")) {
    IPAddress ip = WiFi.localIP();
    ipActual = ip.toString();
    mostrarOled("IP: " + ipActual);
    digitalWrite(pinDos, LOW);
  } else {
    Serial.println("Ha fallado la conexión");
  }
}

void loopWM() {
  if (digitalRead(pinCero) == LOW) {
    if (!botonCeroPulsado) {
      tiempoPulsado = millis();
      botonCeroPulsado = true;
      Serial.println("Boton boot pulsado");
      mostrarOled("Boton 0 pulsado", "", "", "");
    }

    int segundos = (millis() - tiempoPulsado) / 1000;
    mostrarOled("Boton 0 pulsado", "Tiempo: " + String(segundos) + " s", "Restante: " + String(3 - segundos) + " s");

    // Si el botón ha sido presionado por menos de 3 segundos, parpadea el LED
    if (millis() - tiempoPulsado < 3000) {
      if (millis() - tiempoRetardo >= 200) {  // Parpadea cada 500 ms
        estadoLedDos = !estadoLedDos;
        digitalWrite(pinDos, estadoLedDos);
        tiempoRetardo = millis();
      }
    } else {
      // Si el botón ha sido presionado por más de 3 segundos, mantén el LED encendido
      digitalWrite(pinDos, HIGH);
      mostrarOled("", "Reseteando WiFi...", "", "");
      wm.resetSettings();  // borra los datos de la memoria interna (SSID y contraseña)
      ESP.restart();
    }
  } else {
    if (botonCeroPulsado) {
      digitalWrite(pinDos, LOW);
      botonCeroPulsado = false;
      mostrarOled("IP: " + ipActual);
    }
  }
}
<?php
header('Content-Type: application/json');

// Función para enviar respuestas JSON
function enviarRespuesta($exito, $mensaje, $datos = null) {
    $respuesta = [
        'exito' => $exito,
        'mensaje' => $mensaje
    ];
    
    if ($datos !== null) {
        $respuesta['datos'] = $datos;
    }
    
    echo json_encode($respuesta);
    exit;
}

// Verificar que sea método POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    enviarRespuesta(false, 'Método no permitido');
}

// Obtener y decodificar JSON recibido
$jsonData = file_get_contents('php://input');
$data = json_decode($jsonData, true);

// Verificar JSON válido
if ($data === null) {
    enviarRespuesta(false, 'JSON inválido');
}

// Verificar acción y datos requeridos
if (!isset($data['accion']) || $data['accion'] !== 'guardarTemHum' ||
    !isset($data['temp']) || !isset($data['hum'])) {
    enviarRespuesta(false, 'Datos incompletos o acción inválida');
}

// Preparar nuevo registro
$registro = [
    'temperatura' => $data['temp'],
    'humedad' => $data['hum'],
    'fecha_hora' => date('Y-m-d H:i:s')
];

// Nombre del archivo JSON
$archivo = 'json.json';

// Leer datos existentes o crear array vacío
$datosExistentes = [];
if (file_exists($archivo)) {
    $contenido = file_get_contents($archivo);
    if ($contenido) {
        $datosExistentes = json_decode($contenido, true) ?? [];
    }
}

// Añadir nuevo registro
$datosExistentes[] = $registro;

// Mantener solo los últimos 10 registros
if (count($datosExistentes) > 10) {
    array_shift($datosExistentes);
}

// Guardar en archivo
if (file_put_contents($archivo, json_encode($datosExistentes, JSON_PRETTY_PRINT))) {
    enviarRespuesta(true, 'Datos guardados correctamente', $registro);
} else {
    enviarRespuesta(false, 'Error al guardar los datos');
}
?>

Notas importantes

  1. Modifica las URL en php.h según tu configuración.
  2. La pantalla OLED debe estar conectada a los pines I2C (21 y 22).
  3. Comprueba el pin del DHT22: el código usa GPIO33 (el tutorial original cita GPIO15).
  4. El intervalo de envío de datos es de 10 segundos.