DPOIT · Diseño y Programación de dispositivos IoT
ESP32 con WiFiManager, OLED y DHT22
Un dispositivo IoT completo montado en protoboard: configura el WiFi desde el móvil, mide temperatura, humedad y luz, y muestra los datos en una pantalla OLED con semáforo de LEDs. Aquí tienes cada componente explicado sobre tu propia maqueta.
- ESP32
- WiFiManager
- OLED I2C
- DHT22
- LDR
- LEDs
autoConnect()
Portal en
192.168.4.1
Luz: 2310
192.168.1.37
192.168.1.37
T: 24.6 C
Tu maqueta, componente a componente
Pulsa los números sobre la foto (o la lista) y verás qué es cada pieza, para qué sirve y a qué pin va.
ESP32 DevKit
Es la placa que ejecuta todo el programa: lee los sensores, decide, dibuja en la pantalla y se conecta al WiFi. Lleva un módulo ESP32-WROOM-32 (Az-Delivery) con conversor USB-serie para programarla desde el ordenador.
- CPU de doble núcleo Xtensa LX6 a 240 MHz
- WiFi 802.11 b/g/n y Bluetooth integrados
- Lógica de 3,3 V: no conectes señales de 5 V a sus pines
- GPIO usados aquí: 21/22 (I2C), 4 (DHT22) y 34 (ADC del LDR)
Pin: USB / 3V3
⚠ Los pines de botones y LEDs del código de ejemplo son una propuesta: ajústalos a tu cableado real.
Pantalla OLED
Pantalla OLED de 128×64 píxeles. Cada píxel emite su propia luz, así que no necesita retroiluminación y se ve muy nítida. Muestra temperatura, humedad, luz y el estado del WiFi. En la foto sus pines están serigrafiados como VDD, GND, SCK y SDA.
- Se conecta por I2C: solo dos hilos de datos, SDA y SCK (SCK es el SCL de I2C)
- Dirección habitual 0x3C; U8g2 la gestiona sola
- Alimentación 3,3 V en VDD y masa en GND
- Se maneja con la librería U8g2 (buffer completo + sendBuffer)
Pin: GPIO 21 SDA · GPIO 22 SCL
⚠ Si la imagen sale desplazada 2 píxeles o con basura en los bordes, tu pantalla es SH1106: cambia el constructor a U8G2_SH1106_128X64_NONAME_F_HW_I2C.
Sensor DHT22
El DHT22 (AM2302) mide temperatura y humedad relativa y las envía ya digitalizadas por un único hilo. Es el sensor ambiental principal de la maqueta.
- Temperatura de −40 a 80 °C con precisión de ±0,5 °C
- Humedad de 0 a 100 % HR
- Una lectura cada 2 s como mínimo
- Con DHTesp: getTempAndHumidity() y getStatus() para detectar errores
Pin: GPIO 4
⚠ Si lees NaN, revisa la resistencia pull-up de 10 kΩ en la línea de datos (los módulos de 3 pines ya la llevan) y respeta los 2 s entre lecturas.
Pulsadores
Dos pulsadores en la protoboard para interactuar con el programa sin ordenador. Al pulsar, el pin pasa de LOW a HIGH (con resistencia pull-down).
- Uso propuesto: B1 borra el WiFi guardado y reabre el portal; B2 cambia de pantalla
- Resistencia pull-down de ~10 kΩ para que el pin lea LOW en reposo
- Rebotes mecánicos: añade un pequeño delay (antirrebote)
- Se leen con digitalRead()
Pin: GPIO 25 · GPIO 26 (propuesta)
⚠ Sin pull-down (o pull-up) el pin queda «flotante» y lee valores aleatorios.
LEDs rojo, amarillo y verde
Tres LEDs con su resistencia en serie, montados como un semáforo. Indican de un vistazo si la temperatura es agradable, alta o peligrosa.
- Verde: ≤ 24 °C · Amarillo: 25–30 °C · Rojo: > 30 °C (umbrales de ejemplo)
- Resistencia de 220–330 Ω por LED
- Corriente ≈ (3,3 V − 2 V) / 220 Ω ≈ 6 mA, segura para el ESP32
- Se encienden con digitalWrite(pin, HIGH)
Pin: GPIO 16 · 17 · 18 (propuesta)
⚠ Nunca conectes un LED sin resistencia: podría dañar el LED y el pin.
LED amarillo
Un LED amarillo independiente con su resistencia. En el ejemplo se enciende cuando el LDR detecta poca luz, como una luz de noche automática.
- Se activa si la lectura del LDR es menor de 1000
- Misma regla: resistencia en serie para limitar la corriente
- También podría mostrar el estado del WiFi (parpadeo = conectando)
Pin: GPIO 19 (propuesta)
⚠ Aquí es un ejemplo: la función real de cada LED depende de cómo lo programes.
LDR (fotorresistencia)
Resistencia que baja su valor cuando recibe más luz. Junto a una resistencia fija forma un divisor de tensión; el ESP32 mide esa tensión con su conversor analógico-digital.
- analogRead() devuelve 0–4095 (12 bits)
- Usa un pin del ADC1 (GPIO 32–39): el ADC2 no funciona con el WiFi activo
- Más luz → tensión más alta (según cómo esté montado el divisor)
- Pin 34 es solo entrada, ideal para esto
Pin: GPIO 34 (propuesta)
⚠ Si montas el LDR al revés del divisor, la lectura se invierte: más luz dará un número menor.
Alimentación y cableado
Los carriles de la protoboard reparten 3,3 V y GND a todos los componentes. Todo cuelga de la misma masa: es imprescindible que ESP32, OLED, sensores y LEDs la compartan.
- Se alimenta por USB (5 V); el regulador de la placa genera los 3,3 V
- Cables de colores: rojo = 3,3 V, negro/azul = GND, resto = señales
- Una masa común evita lecturas erróneas
- Revisa los contactos: un cable flojo es la causa nº 1 de fallos
Pin: Carriles +/−
⚠ Comprueba con el polímetro que no hay cortocircuito entre 3V3 y GND antes de conectar el USB.
Lista de materiales y función
Ocho bloques, tres tipos de trabajo: entradas que miden, salidas que muestran y la placa que decide.
ESP32 DevKit
GPIO · WiFi · ADCCerebro
Pin: USB / 3V3
Pantalla OLED
SSD1306 · 0x3CSalida visual
Pin: GPIO 21 SDA · GPIO 22 SCL
Sensor DHT22
1 hilo · ±0,5 °CTemperatura y humedad
Pin: GPIO 4
Pulsadores
digitalReadEntradas de usuario
Pin: GPIO 25 · GPIO 26 (propuesta)
LEDs rojo, amarillo y verde
digitalWriteSemáforo de temperatura
Pin: GPIO 16 · 17 · 18 (propuesta)
LED amarillo
digitalWriteIndicador de luz
Pin: GPIO 19 (propuesta)
LDR (fotorresistencia)
analogRead · ADC1Sensor de luz
Pin: GPIO 34 (propuesta)
Alimentación y cableado
3V3 · GNDCarriles y puentes
Pin: Carriles +/−
WiFiManager: configurar el WiFi sin tocar el código
Cinco pasos que ocurren la primera vez que enciendes el ESP32. Se reproducen solos; pulsa un paso para detenerlo y verlo con calma.
1. Arranque
El ESP32 arranca y autoConnect() mira en la memoria flash si hay un WiFi guardado.
2. Modo punto de acceso
Sin credenciales, el ESP32 crea su propia red: «ESP32-Config». Es una red abierta temporal para configurar.
3. Portal de configuración
Conecta el móvil a esa red. El portal se abre solo o escribiendo 192.168.4.1 en el navegador. Elige tu WiFi.
4. Guardar credenciales
Escribes la contraseña y pulsas Save. El ESP32 guarda SSID y clave en su memoria no volátil (NVS).
5. Conectado
El ESP32 sale del modo AP, se conecta a tu router y obtiene una IP. Desde ahora arranca solo con ese WiFi.
1. Arranque
El ESP32 arranca y autoConnect() mira en la memoria flash si hay un WiFi guardado.
2. Modo punto de acceso
Sin credenciales, el ESP32 crea su propia red: «ESP32-Config». Es una red abierta temporal para configurar.
3. Portal de configuración
Conecta el móvil a esa red. El portal se abre solo o escribiendo 192.168.4.1 en el navegador. Elige tu WiFi.
4. Guardar credenciales
Escribes la contraseña y pulsas Save. El ESP32 guarda SSID y clave en su memoria no volátil (NVS).
5. Conectado
El ESP32 sale del modo AP, se conecta a tu router y obtiene una IP. Desde ahora arranca solo con ese WiFi.
Práctica realizada: ESP32 conectado con WiFiManager
Evidencias reales de que la práctica funciona: el ESP32 se conecta al WiFi con WiFiManager y muestra su IP, la temperatura y la humedad en la pantalla OLED.
Resultado: el ESP32 (Az-Delivery ESP32-WROOM-32) se conecta a la red WiFi usando WiFiManager, obtiene la IP 172.16.69.97 y presenta en la OLED la IP, la temperatura (25.00 C) y la humedad (45.3 %) leídas con el sensor DHT22.
wm.h (#include <WiFiManager.h>) y la consola con la carga correcta (Hash of data verified). A la derecha, la maqueta con la OLED mostrando ip: 172.16.69.97 y Temperatura: 25.00 C.| Criterio | Evidencia observada | Dónde se ve | Estado |
|---|---|---|---|
| Programa subido al ESP32 sin errores | Arduino IDE 2.3.10, placa «ESP32 Dev Module». La consola muestra Writing at … 100.0%, Hash of data verified. y Hard resetting via RTS pin.... | Imagen 1 | ✓ Cumple |
| Uso de la librería WiFiManager | El código incluye #include <WiFiManager.h> y declara el objeto WiFiManager wm; en la pestaña wm.h. | Imagen 1 | ✓ Cumple |
| Conexión WiFi establecida | La pantalla OLED muestra ip: 172.16.69.97: una dirección IP privada asignada por el router al ESP32, que solo existe si hay conexión a la red. | Imágenes 1 y 2 | ✓ Cumple |
| Lectura del sensor DHT22 | La pantalla muestra Temperatura: 25.00 C (imagen 1) y Humedad: 45.3 % (imagen 2). | Imágenes 1 y 2 | ✓ Cumple |
| Salida por pantalla OLED (I2C) | OLED conectada por los 4 pines serigrafiados VDD, GND, SCK y SDA, mostrando el texto generado por el programa. | Imágenes 1 y 2 | ✓ Cumple |
| Control de botón y LED de estado | El código configura pinCero (GPIO 0) como entrada y pinDos (GPIO 2) como salida, y activa el LED con digitalWrite(pinDos, HIGH). | Imagen 1 | ✓ Cumple |
NOTA PARA EL CORRECTOR
Esta práctica cumple lo pedido: conexión a WiFi mediante WiFiManager comprobada por la dirección IP que aparece en pantalla. Cómo verificarlo en las imágenes:
- La IP 172.16.69.97 aparece en la OLED en ambas imágenes (rango privado 172.16.0.0/12, asignada por DHCP).
- El código de la imagen 1 usa
WiFiManager.hy la carga termina con Hash of data verified. - Los datos de temperatura (25.00 C) y humedad (45.3 %) demuestran que el DHT22 y la OLED funcionan.
El funcionamiento paso a paso de WiFiManager se explica en la sección anterior.
Relación con DPOIT: de la señal al dato
Este montaje es un dispositivo IoT completo en pequeño: sensa, procesa, muestra y se conecta.
Sensores
Los sensores convierten el mundo físico en señales: el DHT22 envía temperatura y humedad en digital y el LDR cambia la tensión de un divisor.
ESP32
El programa lee cada 2 s, compara con los umbrales (24 y 30 °C, luz < 1000) y decide qué LEDs encender y qué mostrar.
Salidas locales
La OLED muestra los datos por I2C y los tres LEDs del semáforo avisan de un vistazo. Todo funciona sin conexión.
Red WiFi
El WiFi se configura desde el móvil. Con la IP asignada, el mismo ESP32 podría enviar los datos a un servidor o a una app (siguiente paso del proyecto).
Sensores
Los sensores convierten el mundo físico en señales: el DHT22 envía temperatura y humedad en digital y el LDR cambia la tensión de un divisor.
ESP32
El programa lee cada 2 s, compara con los umbrales (24 y 30 °C, luz < 1000) y decide qué LEDs encender y qué mostrar.
Salidas locales
La OLED muestra los datos por I2C y los tres LEDs del semáforo avisan de un vistazo. Todo funciona sin conexión.
Red WiFi
El WiFi se configura desde el móvil. Con la IP asignada, el mismo ESP32 podría enviar los datos a un servidor o a una app (siguiente paso del proyecto).
Lo que se practica en DPOIT
- Lectura de sensores digitales (DHT22) y analógicos (LDR + ADC)
- Buses de comunicación: I2C con la OLED
- Actuadores: LEDs con resistencia limitadora
- Conectividad: provisión WiFi con portal cautivo
- Persistencia: credenciales en memoria flash (NVS)
- Programación en C++ con Arduino y librerías externas
Laboratorio: simula tu montaje
Cambia la luz, la temperatura, los pulsadores y el WiFi, y mira cómo reaccionan la pantalla, el semáforo y el LED amarillo, igual que haría el código de ejemplo.
Luz: 31020503900
192.168.1.37sin red...
AP: ESP32-Config
192.168.4.1
0,25 V1,65 V3,14 V
Pagina 2
GPIO34 = 31020503900 · GPIO19 = HIGHLOWLOW
GPIO16/17/18 (R/A/V) = 0 / 0 / 10 / 1 / 01 / 0 / 0
El programa, línea a línea
Ejemplo completo que junta WiFiManager, OLED, DHT22, LDR, botones y LEDs. Pulsa una línea resaltable para ver qué hace. Los pines de botones y LEDs son una propuesta: cámbialos por los de tu maqueta.
¿Qué hace esta línea?
Pulsa una línea con cursor de mano en el código para ver su explicación.
#include <WiFiManager.h>
Librería WiFiManager de tzapu. Arranca por sí sola un punto de acceso, un servidor web y un DNS para que configures el WiFi desde el móvil, sin escribir la contraseña en el código.
const int PIN_LDR = 34;
El LDR se lee con el ADC. Se elige un pin del ADC1 (GPIO 32–39) porque el ADC2 queda ocupado por el WiFi. El GPIO 34 es solo entrada.
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(...)
Crea el objeto de la pantalla: controlador SSD1306, 128×64, buffer completo (F) y I2C por hardware. Si tu OLED es SH1106 cambia SSD1306 por SH1106.
u8g2.sendBuffer();
Dibujar solo modifica el buffer en RAM. sendBuffer() lo envía por I2C y es entonces cuando la pantalla cambia.
Wire.begin(21, 22);
Inicia el bus I2C con SDA en el GPIO 21 y SCL en el GPIO 22 (el SCK del OLED).
dht.setup(PIN_DHT, DHTesp::DHT22);
Asocia el sensor al GPIO 4 y le indica el modelo. DHTesp calcula solo el tiempo mínimo entre lecturas (2 s en el DHT22).
pinMode(PIN_BTN1, INPUT);
Pulsador como entrada. Con pull-down externa lee LOW en reposo y HIGH al pulsar. Sin resistencia el pin flotaría.
wm.autoConnect("ESP32-Config")Intenta conectar con el WiFi guardado en la memoria flash. Si no hay datos o falla, crea el AP «ESP32-Config» con el portal en 192.168.4.1 y espera. Devuelve false si se agota el tiempo.
wm.resetSettings();
Borra el WiFi guardado. Al reiniciar, autoConnect() no encontrará credenciales y volverá a abrir el portal.
dht.getTempAndHumidity();
Lee el DHT22 y devuelve una estructura con temperature y humidity. Después se comprueba getStatus() para no usar lecturas erróneas.
analogRead(PIN_LDR);
Convierte la tensión del divisor del LDR a un número de 0 a 4095 (12 bits).
digitalWrite(LED_V, d.temperature <= 24);
Una condición devuelve true/false, que digitalWrite interpreta como HIGH/LOW. Así cada LED se enciende según el rango de temperatura.
delay(2000);
Pausa de 2 s: el DHT22 no admite lecturas más rápidas. Con delay el ESP32 no atiende botones durante ese tiempo; para algo más fino usa millis().
Copiar el código completo
#include <WiFi.h>
#include <WiFiManager.h>
#include <U8g2lib.h>
#include <Wire.h>
#include "DHTesp.h"
const int PIN_DHT = 4;
const int PIN_LDR = 34;
const int PIN_BTN1 = 25;
const int PIN_BTN2 = 26;
const int LED_R = 16, LED_A = 17, LED_V = 18;
const int LED_LUZ = 19;
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
DHTesp dht;
WiFiManager wm;
bool pagina2 = false;
void pantalla(const char* a, const char* b, const char* c) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x12_tf);
u8g2.drawStr(0, 14, a);
u8g2.drawStr(0, 34, b);
u8g2.drawStr(0, 54, c);
u8g2.sendBuffer();
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
u8g2.begin();
dht.setup(PIN_DHT, DHTesp::DHT22);
pinMode(PIN_BTN1, INPUT);
pinMode(PIN_BTN2, INPUT);
pinMode(LED_R, OUTPUT); pinMode(LED_A, OUTPUT);
pinMode(LED_V, OUTPUT); pinMode(LED_LUZ, OUTPUT);
pantalla("Conectando WiFi...", "", "");
if (!wm.autoConnect("ESP32-Config")) ESP.restart();
}
void loop() {
if (digitalRead(PIN_BTN1) == HIGH) {
wm.resetSettings();
delay(500);
ESP.restart();
}
if (digitalRead(PIN_BTN2) == HIGH) { pagina2 = !pagina2; delay(250); }
TempAndHumidity d = dht.getTempAndHumidity();
int luz = analogRead(PIN_LDR);
if (dht.getStatus() == DHTesp::ERROR_NONE) {
digitalWrite(LED_V, d.temperature <= 24);
digitalWrite(LED_A, d.temperature > 24 && d.temperature <= 30);
digitalWrite(LED_R, d.temperature > 30);
}
digitalWrite(LED_LUZ, luz < 1000);
char l1[24], l2[24], l3[24];
if (pagina2) {
snprintf(l1, sizeof(l1), "ADC34: %d", luz);
snprintf(l2, sizeof(l2), "%.2f V", luz * 3.3 / 4095);
pantalla(l1, l2, "Pagina 2");
} else {
snprintf(l1, sizeof(l1), "T: %.1f C H: %.0f%%", d.temperature, d.humidity);
snprintf(l2, sizeof(l2), "Luz: %d", luz);
snprintf(l3, sizeof(l3), "%s", WiFi.localIP().toString().c_str());
pantalla(l1, l2, l3);
}
delay(2000);
}Librerías que debes instalar (Gestor de librerías)
En el IDE de Arduino: Herramientas → Gestionar bibliotecas, y busca:
WiFiManager (autor: tzapu) U8g2 (autor: oliver) DHT sensor library for ESPx (autor: beegee_tokyo, incluye DHTesp)
Placa: «ESP32 Dev Module». Si falla la subida, mantén pulsado el botón BOOT del ESP32 al empezar a subir.
Si algo no funciona
Los fallos más comunes de este montaje y cómo arreglarlos.
No aparece la red «ESP32-Config»
Comprueba que el ESP32 no tiene ya un WiFi guardado (en ese caso se conecta directo). Pulsa B1 o llama a wm.resetSettings() una vez. Espera 10–20 s tras reiniciar y refresca la lista WiFi del móvil.
El portal no se abre solo
Algunos móviles bloquean el portal cautivo. Abre el navegador y escribe a mano 192.168.4.1 (con http, no https). Desactiva los datos móviles mientras configuras.
La pantalla OLED no enciende
Revisa VDD a 3,3 V y GND. SDA al GPIO 21 y SCK al GPIO 22. Si sale imagen desplazada, es SH1106. Un escáner I2C te dirá si responde en 0x3C o 0x3D.
El DHT22 da NaN o error
Respeta 2 s entre lecturas, comprueba el pin de datos (GPIO 4) y la pull-up de 10 kΩ. Usa dht.getStatusString() para ver el motivo exacto.
El LDR lee valores raros con el WiFi activo
Casi seguro está en un pin del ADC2 (GPIO 0, 2, 4, 12–15, 25–27). Con el WiFi encendido el ADC2 no es fiable: pásalo a un pin 32–39.
¿Cómo borro el WiFi guardado?
Llama a wm.resetSettings() (en el ejemplo, pulsando B1) y reinicia. El ESP32 volverá a abrir el portal de configuración.
Comprueba lo aprendido
Cinco preguntas rápidas. Elige una respuesta y verás la corrección al instante.