Qué vas a conseguir
En este tutorial aprenderás a crear un servidor web ESP32 para controlar salidas (por ejemplo un relé), servir interfaces estáticas y exponer una API REST local. Verás opciones sencillas con WebServer, una alternativa asíncrona con ESPAsyncWebServer, cómo usar un sistema de archivos (SPIFFS / LittleFS) para servir HTML/CSS/JS y buenas prácticas de seguridad y despliegue.
Requisitos de hardware y materiales
- Placa ESP32 (p. ej. ESP32 DevKit v1 o variantes con CH340/CP210x). Comprueba que tienes suficiente GPIO libres para tu relé.
- Módulo relé compatible con 3.3 V (o un transistor/MOSFET y optoacoplador si trabajas con cargas AC).
- Fuente de alimentación estable: el ESP32 y el relé pueden requerir corrientes pico; una fuente de 5 V con al menos 1 A (o fuente separada para el relé) es recomendable.
- Cables, protoboard y, en producción, aisladores y fusibles para seguridad eléctrica.
Herramientas software recomendadas
- Arduino IDE (con soporte para el core de ESP32) — rápido para prototipos.
- PlatformIO (VS Code) — recomendable para proyectos reproducibles y manejo de filesystem uploads.
- ESP-IDF — para proyectos avanzados en C/C++ con control fino del sistema.
Qué librerías usar y por qué
Dos rutas habituales:
- WebServer (incluida en el core Arduino-ESP32): sencillo, fácil de entender y suficiente para pocos clientes concurrentes. Ideal para ejemplos y proyectos personales.
- ESPAsyncWebServer + AsyncTCP: solución asíncrona que maneja mejor múltiples conexiones, streaming y WebSockets. Requiere instalar la dependencia AsyncTCP; es la opción recomendada si esperas muchos clientes simultáneos o websockets.
servidor web ESP32 — preparación del entorno
Antes de compilar configura tu entorno:
- Instala soporte ESP32 en Arduino IDE: en Preferencias añade la URL del paquete del core ESP32 (búscala en el repositorio oficial de espressif/arduino-esp32) y luego usa el Gestor de placas para instalar el core y seleccionar la placa correcta.
- En PlatformIO crea un proyecto con platform = espressif32, board = esp32dev y framework = arduino. Para filesystem LittleFS añade en platformio.ini:
board_build.filesystem = littlefs. - Instala librerías: para la vía asíncrona instala ESPAsyncWebServer y AsyncTCP. En Arduino IDE añadelas desde Gestor de librerías o compáralas en PlatformIO.
Nota
No inventes rutas ni uses versiones no verificadas: si tienes dudas sobre la URL del core o versiones compatibles, consúltalas en el repositorio oficial del core antes de instalar.
Partición de archivos: SPIFFS vs LittleFS (qué elegir hoy)
SPIFFS fue la opción histórica; LittleFS ofrece ventajas: reparaciones más robustas tras apagones, mejor gestión de metadatos y estabilidad en cores recientes. Si tu versión del core soporta LittleFS, úsala; si trabajas con proyectos antiguos que usan SPIFFS, documenta la migración (cambio de includes, llamadas begin y herramientas de subida). En PlatformIO la elección se configura en platformio.ini y el comando para subir el FS es pio run -t uploadfs (o equivalentemente platformio run --target uploadfs).
Ejemplo básico (Arduino): servidor simple con WebServer.h
Este sketch crea una red WiFi en modo estación, expone / para servir una página y /relay?state=1 para encender/apagar un relé.
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "TU_SSID";
const char* password = "TU_PASS";
const int RELAY_PIN = 15;
WebServer server(80);
void handleRoot() {
server.send(200, "text/html", "<h1>ESP32 OK</h1>");
}
void handleRelay() {
if(!server.hasArg("state")) {
server.send(400, "text/plain", "missing state");
return;
}
String s = server.arg("state");
if (s=="1") digitalWrite(RELAY_PIN, HIGH);
else digitalWrite(RELAY_PIN, LOW);
server.send(200, "application/json", "{\"relay\":\""+s+"\"}");
}
void setup(){
pinMode(RELAY_PIN, OUTPUT); digitalWrite(RELAY_PIN, LOW);
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print('.'); }
Serial.println(WiFi.localIP());
server.on("/", HTTP_GET, handleRoot);
server.on("/relay", HTTP_GET, handleRelay);
server.begin();
}
void loop(){
server.handleClient();
}
Verificación: al compilar y subir, conecta al WiFi y abre la IP mostrada en el Monitor Serial. Accede a /relay?state=1 y comprueba que el relé actúa.
Ejemplo avanzado (Arduino/PlatformIO): interfaz estática desde LittleFS y API REST
Pasos resumidos:
- Prepara los archivos web (index.html, app.js, style.css) en la carpeta data/ del proyecto (PlatformIO) o usa la herramienta Upload Filesystem en Arduino IDE.
- En el sketch monta LittleFS y sirve index.html al acceder a /; añade endpoints REST para leer/establecer estado.
#include <WiFi.h>
#include <WebServer.h>
#include <LittleFS.h>
WebServer server(80);
const int RELAY_PIN = 15;
void handleRoot(){
File f = LittleFS.open("/index.html","r");
if(!f){ server.send(500, "text/plain", "missing file"); return; }
server.streamFile(f, "text/html");
f.close();
}
void handleApiRelay(){
if(server.method()==HTTP_GET){
int state = digitalRead(RELAY_PIN);
server.send(200, "application/json", String("{\"state\":") + state + "}");
} else if(server.method()==HTTP_POST){
if(!server.hasArg("plain")){ server.send(400); return; }
String body = server.arg("plain");
// parse JSON simple o usar ArduinoJson (recomendado)
if(body.indexOf('1')>=0) digitalWrite(RELAY_PIN, HIGH); else digitalWrite(RELAY_PIN, LOW);
server.send(200, "application/json", "{\"ok\":true}");
}
}
void setup(){
Serial.begin(115200);
LittleFS.begin();
pinMode(RELAY_PIN, OUTPUT); digitalWrite(RELAY_PIN, LOW);
WiFi.begin("SSID","PASS"); while(WiFi.status()!=WL_CONNECTED) delay(200);
server.on("/", HTTP_GET, handleRoot);
server.on("/api/relay", HTTP_GET, handleApiRelay);
server.on("/api/relay", HTTP_POST, handleApiRelay);
server.begin();
}
void loop(){ server.handleClient(); }
Ejemplo con ESPAsyncWebServer: múltiples conexiones y websockets/AJAX
Con ESPAsyncWebServer no necesitas llamar a handleClient(); funciona de forma asíncrona y es más escalable. Para autenticación simple compueba cabeceras o tokens en cada petición.
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <LittleFS.h>
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
void setup(){
Serial.begin(115200); LittleFS.begin();
WiFi.begin("SSID","PASS"); while(WiFi.status()!=WL_CONNECTED) delay(200);
server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html");
server.on("/api/relay", HTTP_POST, [](AsyncWebServerRequest *request){
if(!request->hasHeader("X-Auth")) { request->send(401); return; }
String token = request->header("X-Auth");
if(token != "mi-token-secreto"){ request->send(403); return; }
// manejar body (Async) o parámetros
request->send(200, "application/json", "{\"ok\":true}");
});
server.addHandler(&ws);
server.begin();
}
void loop(){}
Consideraciones de seguridad
- No publiques el ESP32 directamente en Internet sin un gateway/VPN. Si necesitas acceso remoto, usa soluciones intermedias (túnel seguro, servidor central, MQTT con TLS o servicios de mensajería) o, para usos sencillos, integra control a través de Telegram (ver alternativas) en lugar de exponer puertos.
- Autenticación: añade al menos autenticación básica o tokens. En WebServer puedes usar
server.authenticate(user,pass). En rutas asíncronas valida cabeceras o tokens. - Protege el filesystem: no pongas archivos con credenciales en texto plano.
- CORS: si tu interfaz se sirve desde otro origen, configura cabeceras CORS con cuidado y limita orígenes permitidos.
Mejoras recomendadas
- OTA: permite actualizaciones sin cable (ArduinoOTA u otros mecanismos) — útil pero configúralo con contraseña y, preferiblemente, en red local.
- mDNS para resolver
esp32.localen entornos compatibles. - DHCP estático o reserva por MAC en el router para IPs fijas; evita depender de cambio de IP.
- Implementa reintentos de reconexión WiFi y watchdog para reinicios si el dispositivo queda bloqueado.
Pruebas y resolución de problemas comunes
- Error de compilación por falta de AsyncTCP: instala la dependencia AsyncTCP antes de ESPAsyncWebServer.
- Problemas al subir imágenes del filesystem: en Arduino IDE usa la herramienta de «Upload Filesystem image» correspondiente al core; en PlatformIO usa
pio run -t uploadfsy confirma que la partición coincide con la configuración de la placa. - Conexiones lentas o bloqueos: en sketches sin yield() evita operaciones largas en loop; con WebServer síncrono el número de clientes simultáneos es limitado—para escalar usa la variante asíncrona.
- Relé que no actúa: verifica lógica inversa (LOW vs HIGH), alimentación del relé y nivel de señal 3.3 V.
Checklist final antes de desplegar
- Probar en red local: acceso a la IP, endpoints GET/POST, interfaz web correcta.
- Configurar autenticación y cambiar credenciales por defecto.
- Asignar IP estática o reserva DHCP, y configurar firewall/puertos en router si es necesario.
- Habilitar OTA si se quiere actualizaciones futuras seguras.
- Monitorizar logs y preparar plan de recuperación (por ejemplo, pulsador de reset o modo firmware seguro).
Errores comunes y soluciones rápidas
- Compilación falla por versiones del core: selecciona en el Gestor de placas una versión del core probada o usa PlatformIO con versiones pinned en
platformio.ini. - Archivos no aparecen al servir: asegúrate de que la imagen del filesystem se ha subido a la partición correcta (SPIFFS vs LittleFS).
- Watchdog por operaciones largas: usa tareas FreeRTOS, yield() o separa operaciones intensivas fuera del loop de red.
Recursos internos relacionados
Si buscas control remoto alternativo, puede interesarte Controlar ESP32 desde Telegram: Guía Completa. Para gestionar archivos en el ESP32 como alternativa al servidor web, consulta también Servidor FTP ESP32: Crea tu propio servidor FTP fácilmente.
Conclusión y siguientes pasos
Un servidor web ESP32 te permite controlar dispositivos localmente con bajo coste y flexibilidad. Empieza con WebServer para comprender la lógica; si necesitas concurrencia, pasa a ESPAsyncWebServer. Usa LittleFS si tu core lo soporta para más fiabilidad, y nunca publiques el dispositivo sin protección. Como siguientes pasos: añade OTA seguro, registra el dispositivo en tu infraestructura y, si necesitas acceso externo, estudia VPN/TLS o colas seguras como MQTT.
Preguntas frecuentes
¿Puedo exponer el ESP32 directamente a Internet?
No es recomendable. Si necesitas acceso remoto usa un servidor intermedio, VPN o servicios que ofrezcan túneles seguros; publica sólo APIs seguras con autenticación y cifrado.
¿Cómo elijo entre SPIFFS y LittleFS?
Si tu core soporta LittleFS, es la opción preferida por robustez y recuperación tras fallos. Mantén compatibilidad leyendo archivos con la API del FS y migrando las rutas/llamadas de begin() y open().
¿Qué limita el número de conexiones?
Con WebServer (síncrono) el número de conexiones simultáneas está limitado por el stack y la implementación; para muchos clientes usa ESPAsyncWebServer y maneja conexiones con WebSockets si necesitas push.
Si quieres, preparo ejemplos adicionales listos para copiar (index.html + app.js) o un proyecto PlatformIO con LittleFS y endpoints completos.
