
El ESP32, el sucesor del ESP8266, ha sido una estrella en ascenso en proyectos relacionados con IoT o WiFi. Es un módulo WiFi de bajo costo que se puede programar para ejecutar un servidor web independiente con un poco de trabajo adicional. ¿Qué tan genial es eso?
Este tutorial te guiará a través del proceso de creación de un servidor web ESP32 simple en el IDE de Arduino.
¿Qué es exactamente un servidor web y cómo funciona?
Un servidor web es un lugar donde las páginas web se almacenan, procesan y sirven a los clientes web. Un cliente web no es más que un navegador web que utilizamos en nuestros computadores y teléfonos. Un cliente web y un servidor web se comunican mediante un protocolo especial conocido como Protocolo de transferencia de hipertexto (HTTP).

En este protocolo, un cliente inicia una conversación enviando una solicitud HTTP para una página web específica. Luego, el servidor devuelve el contenido de esa página web o un mensaje de error si no puede encontrarlo (el famoso error 404).
Modos de funcionamiento ESP32
Una de las características más útiles del ESP32 es su capacidad no solo para conectarse a una red WiFi existente y actuar como un servidor web, sino también para crear su propia red, permitiendo que otros dispositivos se conecten directamente a ella y accedan a páginas web. Esto es posible porque el ESP32 puede funcionar en tres modos: modo de estación (STA), modo de punto de acceso suave (AP) y ambos simultáneamente.
Station Mode (STA)
En el modo Station (STA), el ESP32 se conecta a una red WiFi existente (la creada por su router inalámbrico).

En el modo STA, el ESP32 obtiene una dirección IP del router inalámbrico al que está conectado. Con esta dirección IP, puede configurar un servidor web y servir páginas web a todos los dispositivos conectados en la red WiFi existente.
Soft Access Point Mode (AP)
En el modo de punto de acceso (AP), el ESP32 configura su propia red WiFi y actúa como un concentrador, al igual que un enrutador WiFi. Sin embargo, a diferencia de un router WiFi, no tiene una interfaz para una red cableada. Por lo tanto, este modo de operación se denomina Soft Access Point (soft-AP). Además, no más de cinco estaciones pueden conectarse a él al mismo tiempo.

En el modo AP, el ESP32 crea una nueva red WiFi y le asigna un SSID (el nombre de la red) y una dirección IP. Con esta dirección IP, puede servir páginas web a todos los dispositivos conectados.
Cableando LEDs a un ESP32
Ahora que entendemos los fundamentos de cómo funciona un servidor web y los modos en los que el ESP32 puede crear uno, es hora de conectar algunos LED al ESP32 que queremos controlar a través de WiFi.
Comience colocando el ESP32 en su placa de pruebas, asegurándose de que cada lado de la placa esté en un lado diferente de la placa de pruebas. A continuación, conecte dos LED a GPIO 4 y 5 digitales mediante una resistencia limitadora de corriente de 220 Ω.
Cuando hayas terminado, deberías tener algo que se parezca a la imagen de abajo.

La idea detrás del uso de un servidor web ESP32 para controlar las cosas
Te estarás preguntando: ¿Cómo controlo las cosas desde un servidor web que solo procesa y sirve páginas web?.
Es muy sencillo. Vamos a controlar las cosas visitando una URL específica.
Cuando ingresa una URL en un navegador web, envía una solicitud HTTP (también conocida como solicitud GET) a un servidor web. Es responsabilidad del servidor web manejar esta solicitud.
Supongamos que ha introducido una URL como http://192.168.1.1/ledon en un navegador. A continuación, el navegador envía una solicitud HTTP al ESP32. Cuando el ESP32 recibe esta solicitud, reconoce que el usuario desea encender el LED. Como resultado, enciende el LED y envía una página web dinámica a un navegador que muestra el estado del LED como “encendido”. Muy sencillo, ¿verdad?
Ejemplo 1: configuración del servidor web ESP32 en modo de punto de acceso (AP)
¡Vayamos a lo interesante ahora!
Este ejemplo, como sugiere el título, muestra cómo configurar el servidor web ESP32 en modo de punto de acceso (AP) y servir páginas web a cualquier cliente conectado. Para empezar, conecta tu ESP32 a tu ordenador y ejecuta el sketch. A continuación, lo veremos con más detalle.
#include <WiFi.h>
#include <WebServer.h>
/* Put your SSID & Password */
const char* ssid = "ESP32"; // Enter SSID here
const char* password = "12345678"; //Enter Password here
/* Put IP Address details */
IPAddress local_ip(192,168,1,1);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
WebServer server(80);
uint8_t LED1pin = 4;
bool LED1status = LOW;
uint8_t LED2pin = 5;
bool LED2status = LOW;
void setup() {
Serial.begin(115200);
pinMode(LED1pin, OUTPUT);
pinMode(LED2pin, OUTPUT);
WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);
server.on("/", handle_OnConnect);
server.on("/led1on", handle_led1on);
server.on("/led1off", handle_led1off);
server.on("/led2on", handle_led2on);
server.on("/led2off", handle_led2off);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
if(LED1status)
{digitalWrite(LED1pin, HIGH);}
else
{digitalWrite(LED1pin, LOW);}
if(LED2status)
{digitalWrite(LED2pin, HIGH);}
else
{digitalWrite(LED2pin, LOW);}
}
void handle_OnConnect() {
LED1status = LOW;
LED2status = LOW;
Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
server.send(200, "text/html", SendHTML(LED1status,LED2status));
}
void handle_led1on() {
LED1status = HIGH;
Serial.println("GPIO4 Status: ON");
server.send(200, "text/html", SendHTML(true,LED2status));
}
void handle_led1off() {
LED1status = LOW;
Serial.println("GPIO4 Status: OFF");
server.send(200, "text/html", SendHTML(false,LED2status));
}
void handle_led2on() {
LED2status = HIGH;
Serial.println("GPIO5 Status: ON");
server.send(200, "text/html", SendHTML(LED1status,true));
}
void handle_led2off() {
LED2status = LOW;
Serial.println("GPIO5 Status: OFF");
server.send(200, "text/html", SendHTML(LED1status,false));
}
void handle_NotFound(){
server.send(404, "text/plain", "Not found");
}
String SendHTML(uint8_t led1stat,uint8_t led2stat){
String ptr = "<!DOCTYPE html> <html>n";
ptr +="<head><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">n";
ptr +="<title>LED Control</title>n";
ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}n";
ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}n";
ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}n";
ptr +=".button-on {background-color: #3498db;}n";
ptr +=".button-on:active {background-color: #2980b9;}n";
ptr +=".button-off {background-color: #34495e;}n";
ptr +=".button-off:active {background-color: #2c3e50;}n";
ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}n";
ptr +="</style>n";
ptr +="</head>n";
ptr +="<body>n";
ptr +="<h1>ESP32 Web Server</h1>n";
ptr +="<h3>Using Access Point(AP) Mode</h3>n";
if(led1stat)
{ptr +="<p>LED1 Status: ON</p><a class="button button-off" href="/led1off">OFF</a>n";}
else
{ptr +="<p>LED1 Status: OFF</p><a class="button button-on" href="/led1on">ON</a>n";}
if(led2stat)
{ptr +="<p>LED2 Status: ON</p><a class="button button-off" href="/led2off">OFF</a>n";}
else
{ptr +="<p>LED2 Status: OFF</p><a class="button button-on" href="/led2on">ON</a>n";}
ptr +="</body>n";
ptr +="</html>n";
return ptr;
}
Acceso al servidor web en modo AP
Después de cargar el sketch, abre el monitor serie a 115200 baudios y presione el botón RESET en el ESP32. Si todo está bien, mostrará el mensaje “HTTP server started”.

Ahora, usa un teléfono, computadora portátil u otro dispositivo que pueda conectarse a la red WiFi y busca una red llamada “ESP32”. Conéctate a la red usando la contraseña 12345678.

Después de conectarte a la red AP ESP32, abre un navegador y navega hasta 192.168.1.1. El ESP32 debe mostrar una página web con el estado actual de los LED y los botones. Al mismo tiempo, puedes revisar el monitor serie para ver el estado de los pines GPIO del ESP32.


Ahora, mientras revisas la URL, haz clic en el botón para encender LED1. Una vez que hagaz clic en el botón, el ESP32 recibe una solicitud para la URL /led1on. A continuación, enciende el LED1 y muestra una página web con el estado del LED actualizado. También imprime el estado del pin GPIO en el monitor serie


Prueba el botón LED2 para ver si funciona de manera similar.
Ahora analicemos de cerca el código para ver cómo funciona y puedas modificarlo para adaptarlo a tus necesidades.
Explicación detallada del código
El ejemplo comienza incluyendo la biblioteca WiFi.h. Esta biblioteca contiene métodos específicos de ESP32 que utilizamos para conectarnos a la red. A continuación, incluimos la biblioteca WebServer.h, que contiene algunos métodos que nos ayudarán a configurar un servidor y manejar las solicitudes HTTP entrantes sin tener que preocuparnos por los detalles de implementación de bajo nivel.
#include <WiFi.h>
#include <WebServer.h>Debido a que estamos configurando el servidor web ESP32 en modo acess point (AP), creará su propia red WiFi. Por lo tanto, debemos configurar el SSID, la contraseña, la dirección IP, la máscara de subred IP y la puerta de enlace IP.
/* Put your SSID & Password */
const char* ssid = "ESP32"; // Enter SSID here
const char* password = "12345678"; //Enter Password here
/* Put IP Address details */
IPAddress local_ip(192,168,1,1);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);// declare an object of WebServer library
WebServer server(80);uint8_t LED1pin = 4;
bool LED1status = LOW;
uint8_t LED2pin = 5;
bool LED2status = LOW;Dentro de la función Setup()
En la función setup(), configuramos el servidor web ESP32 en modo AP . En primer lugar, establecemos una conexión serie con fines de debug y configuramos los pines GPIO para que se comporten como una salida (OUTPUT).Serial.begin(115200);
pinMode(LED1pin, OUTPUT);
pinMode(LED2pin, OUTPUT); WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100); server.on("/", handle_OnConnect);
server.on("/led1on", handle_led1on);
server.on("/led1off", handle_led1off);
server.on("/led2on", handle_led2on);
server.on("/led2off", handle_led2off); server.onNotFound(handle_NotFound); server.begin();
Serial.println("HTTP server started"); Dentro de la función Loop()
Las solicitudes HTTP entrantes reales se manejan en la función de bucle. Para ello, utilizamos el método handleClient() del objeto de servidor. También cambiamos el estado de los LED en función de la solicitud.void loop() {
server.handleClient();
if(LED1status)
{digitalWrite(LED1pin, HIGH);}
else
{digitalWrite(LED1pin, LOW);}
if(LED2status)
{digitalWrite(LED2pin, HIGH);}
else
{digitalWrite(LED2pin, LOW);}
} void handle_OnConnect() {
LED1status = LOW;
LED2status = LOW;
Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
server.send(200, "text/html", SendHTML(LED1status,LED2status));
} void handle_led1on() {
LED1status = HIGH;
Serial.println("GPIO4 Status: ON");
server.send(200, "text/html", SendHTML(true,LED2status));
}
void handle_led1off() {
LED1status = LOW; Serial.println("GPIO4 Status: OFF"); server.send(200, "text/html", SendHTML(false,LED2status)); } void handle_led2on() { LED2status = HIGH; Serial.println("GPIO5 Status: ON"); server.send(200, "text/html", SendHTML(LED1status,true)); } void handle_led2off() { LED2status = LOW; Serial.println("GPIO5 Status: OFF"); server.send(200, "text/html", SendHTML(LED1status,false)); } void handle_NotFound(){ server.send(404, "text/plain", "Not found"); } Visualización de la página web HTML
Cada vez que el servidor web ESP32 recibe una solicitud de un cliente web, la función sendHTML() genera una página web. Simplemente concatena el código HTML en una cadena larga y regresa a la función server.send() que discutimos anteriormente. La función utiliza el estado de los LED como parámetro para generar contenido HTML de forma dinámica. El primer texto que siempre debes enviar es la declaración <!DOCTYPE>, que indica que estamos enviando código HTML.String SendHTML(uint8_t led1stat,uint8_t led2stat){
String ptr = "<!DOCTYPE html> <html>n"; ptr +="<head><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">n";
ptr +="<title>LED Control</title>n"; Estilo de la página web
Después de eso, tenemos algo de CSS para diseñar los botones y la apariencia general de la página web. Seleccionamos la fuente Helvetica y definimos el contenido que se mostrará como un bloque en línea, alineado al centro.ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}n"; ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}n";
ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}n"; ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}n";
ptr +=".button-on {background-color: #3498db;}n";
ptr +=".button-on:active {background-color: #2980b9;}n";
ptr +=".button-off {background-color: #34495e;}n";
ptr +=".button-off:active {background-color: #2c3e50;}n"; Establecer el encabezado de la página web
A continuación, se establece el encabezado de la página web. Puede cambiar este texto a cualquier cosa que se ajuste a su aplicación.ptr +="<h1>ESP32 Web Server</h1>n";
ptr +="<h3>Using Access Point(AP) Mode</h3>n"; Visualización de los botones y el estado correspondiente
La instrucción if se utiliza para actualizar dinámicamente el estado de los botones y los LED.if(led1stat)
{ptr +="<p>LED1 Status: ON</p><a class="button button-off" href="/led1off">OFF</a>n";}
else
{ptr +="<p>LED1 Status: OFF</p><a class="button button-on" href="/led1on">ON</a>n";}
if(led2stat)
{ptr +="<p>LED2 Status: ON</p><a class="button button-off" href="/led2off">OFF</a>n";}
else
{ptr +="<p>LED2 Status: OFF</p><a class="button button-on" href="/led2on">ON</a>n";} Ejemplo 2: configuración del servidor web ESP32 en modo WiFi Station (STA)
Pasemos al siguiente ejemplo, que muestra cómo configurar el servidor web ESP32 en modo Station (STA) y servir páginas web a cualquier cliente conectado en la red existente.
Antes de continuar con la carga del sketch, debe realizar algunos cambios para asegurarse de que funcione para usted. Para conectar ESP32 a una red existente, debe modificar las dos variables siguientes con sus credenciales de red.
Una vez que hayas terminado, sigue adelante y prueba el sketch.
#include <WiFi.h>
#include <WebServer.h>
/*Put your SSID & Password*/
const char* ssid = " YourNetworkName"; // Enter SSID here
const char* password = " YourPassword"; //Enter Password here
WebServer server(80);
uint8_t LED1pin = 4;
bool LED1status = LOW;
uint8_t LED2pin = 5;
bool LED2status = LOW;
void setup() {
Serial.begin(115200);
delay(100);
pinMode(LED1pin, OUTPUT);
pinMode(LED2pin, OUTPUT);
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: "); Serial.println(WiFi.localIP());
server.on("/", handle_OnConnect);
server.on("/led1on", handle_led1on);
server.on("/led1off", handle_led1off);
server.on("/led2on", handle_led2on);
server.on("/led2off", handle_led2off);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
if(LED1status)
{digitalWrite(LED1pin, HIGH);}
else
{digitalWrite(LED1pin, LOW);}
if(LED2status)
{digitalWrite(LED2pin, HIGH);}
else
{digitalWrite(LED2pin, LOW);}
}
void handle_OnConnect() {
LED1status = LOW;
LED2status = LOW;
Serial.println("GPIO4 Status: OFF | GPIO5 Status: OFF");
server.send(200, "text/html", SendHTML(LED1status,LED2status));
}
void handle_led1on() {
LED1status = HIGH;
Serial.println("GPIO4 Status: ON");
server.send(200, "text/html", SendHTML(true,LED2status));
}
void handle_led1off() {
LED1status = LOW;
Serial.println("GPIO4 Status: OFF");
server.send(200, "text/html", SendHTML(false,LED2status));
}
void handle_led2on() {
LED2status = HIGH;
Serial.println("GPIO5 Status: ON");
server.send(200, "text/html", SendHTML(LED1status,true));
}
void handle_led2off() {
LED2status = LOW;
Serial.println("GPIO5 Status: OFF");
server.send(200, "text/html", SendHTML(LED1status,false));
}
void handle_NotFound(){
server.send(404, "text/plain", "Not found");
}
String SendHTML(uint8_t led1stat,uint8_t led2stat){
String ptr = "<!DOCTYPE html> <html>n";
ptr +="<head><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">n";
ptr +="<title>LED Control</title>n";
ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}n";
ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;} h3 {color: #444444;margin-bottom: 50px;}n";
ptr +=".button {display: block;width: 80px;background-color: #3498db;border: none;color: white;padding: 13px 30px;text-decoration: none;font-size: 25px;margin: 0px auto 35px;cursor: pointer;border-radius: 4px;}n";
ptr +=".button-on {background-color: #3498db;}n";
ptr +=".button-on:active {background-color: #2980b9;}n";
ptr +=".button-off {background-color: #34495e;}n";
ptr +=".button-off:active {background-color: #2c3e50;}n";
ptr +="p {font-size: 14px;color: #888;margin-bottom: 10px;}n";
ptr +="</style>n";
ptr +="</head>n";
ptr +="<body>n";
ptr +="<h1>ESP32 Web Server</h1>n";
ptr +="<h3>Using Station(STA) Mode</h3>n";
if(led1stat)
{ptr +="<p>LED1 Status: ON</p><a class="button button-off" href="/led1off">OFF</a>n";}
else
{ptr +="<p>LED1 Status: OFF</p><a class="button button-on" href="/led1on">ON</a>n";}
if(led2stat)
{ptr +="<p>LED2 Status: ON</p><a class="button button-off" href="/led2off">OFF</a>n";}
else
{ptr +="<p>LED2 Status: OFF</p><a class="button button-on" href="/led2on">ON</a>n";}
ptr +="</body>n";
ptr +="</html>n";
return ptr;
} Acceso al servidor web en modo AP
Después de cargar el sketch, abre el monitor serie a 115200 baudios y presiona el botón RESET en el ESP32. Si todo está bien, mostrará el mensaje “HTTP server started”.
A continuación, inicia un navegador y ve a la dirección IP que se muestra en el monitor serie. El ESP32 debe mostrar una página web con el estado actual de los LED y dos botones para controlarlos. Al mismo tiempo, puede comprobar el monitor serie para ver el estado de los pines GPIO del ESP32.

Ahora, mientras miras la URL, haz clic en el botón para encender LED1. Una vez que haces clic en el botón, el ESP32 recibe una solicitud para la URL /led1on. A continuación, enciende el LED1 y muestra una página web con el estado del LED actualizado. También imprime el estado del pin GPIO en el monitor serie.

Puedes probar el botón LED2 para ver si funciona de manera similar.
Revisemos más de cerca el código para ver cómo funciona y puedas adaptarlo a tus necesidades.
Explicación del código
La única diferencia entre este código y el código anterior es que no estamos creando nuestra propia red WiFi, sino uniéndonos a una red existente utilizando la función WiFi.begin().//connect to your local wi-fi network
WiFi.begin(ssid, password); //check wi-fi is connected to wi-fi network while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.print("."); } - WL_CONNECTED: cuando está conectado a una red Wi-Fi
- WL_NO_SHIELD: cuando no hay un escudo de Wi-Fi presente
- WL_IDLE_STATUS: un estado temporal asignado cuando se llama a WiFi.begin() y permanece activo hasta que expira el número de intentos (lo que resulta en WL_CONNECT_FAILED) o se establece una conexión (lo que resulta en WL_CONNECTED)
- WL_NO_SSID_AVAIL: cuando no hay SSID disponibles
- WL_SCAN_COMPLETED: cuando se completa el escaneo de redes
- WL_CONNECT_FAILED: cuando la conexión falla en todos los intentos
- WL_CONNECTION_LOST: cuando se pierde la conexión
- WL_DISCONNECTED: cuando se desconecta de una red
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: "); Serial.println(WiFi.localIP()); - Declarar los pines GPIO de ESP32 a los que están conectados los LED
- Definición de múltiples métodos server.on() para manejar las solicitudes HTTP entrantes
- Definiendo el método server.onNotFound() para manejar el error HTTP 404
- Creación de funciones personalizadas que se ejecutan cuando se alcanza una URL específica
- Creación de una página HTML
- Estilo de la página web
- Creación de botones y visualización de su estado
Aprende esto en un curso
Si prefieres verlo en vivo y con alguien que resuelva tus dudas, estos cursos presenciales de MCI Educación cubren el tema de este tutorial:
- IoT con ESP32 desde cero: Conecta y controla dispositivos inteligentes — nivel avanzado, 12 horas presenciales en Santiago
- Arduino en profundidad: Sensores y actuadores para proyectos reales — nivel intermedio, 12 horas presenciales en Santiago