Blog

Cómo crear un servidor web con ESP32 para controlar dispositivos

Tutorial práctico: monta un servidor HTTP en ESP32, sirve páginas desde SPIFFS/LittleFS, controla pines por GET/POST y añade mejoras como OTA y autenticación.

8 min aprox. alext0918
TecnoSender Guía técnica
8 min aprox. Lectura

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:

  1. 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.
  2. 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.
  3. 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:

  1. 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.
  2. 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.local en 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 uploadfs y 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

  1. Probar en red local: acceso a la IP, endpoints GET/POST, interfaz web correcta.
  2. Configurar autenticación y cambiar credenciales por defecto.
  3. Asignar IP estática o reserva DHCP, y configurar firewall/puertos en router si es necesario.
  4. Habilitar OTA si se quiere actualizaciones futuras seguras.
  5. 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.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *