Introducción
¿Te imaginas controlar un relé, leer un sensor o encender un LED desde el navegador del celular, sin instalar ninguna app? Eso es exactamente lo que habilita un servidor web corriendo dentro del ESP32. En esta guía vas a armar un servidor web local con el ESP32 programado con ESP-IDF (el framework oficial de Espressif) desde VS Code, capaz de servir una página HTML accesible desde cualquier dispositivo conectado a la misma red WiFi.
Al terminar vas a tener una base sólida sobre la cual construir proyectos más serios: dashboards IoT, control de GPIO desde el navegador o automatización del hogar. Trabajar con ESP-IDF en lugar de Arduino IDE te da más control sobre el chip y mejor rendimiento, a cambio de una curva de aprendizaje algo más empinada.
¿Prefieres Arduino IDE? El concepto es el mismo, solo cambia el entorno. Esta versión se enfoca en el flujo nativo de Espressif con ESP-IDF.
¿Qué es un servidor web?
Un servidor web es, en términos simples, un computador que entrega páginas web. Guarda los archivos del sitio (documentos HTML, imágenes, hojas de estilo CSS, fuentes y otros recursos) y los envía al navegador del usuario cuando este los solicita.
Cuando abres una página en tu navegador, en realidad envías una petición al servidor usando el protocolo HTTP. Ese protocolo define cómo se solicita y se entrega la información en internet. El servidor responde devolviendo la página que pediste, también a través de HTTP.
Cliente y servidor: el ESP32 como servidor

El flujo es sencillo. Tu dispositivo (el cliente) envía una petición HTTP. El servidor recibe esa petición y responde con la página solicitada. En este tutorial el ESP32 va a actuar como servidor, y tú, desde tu navegador, vas a ser el cliente.
Cómo funciona el servidor web en el ESP32
Veamos el caso práctico: el ESP32 funcionando como servidor web local dentro de tu red WiFi.

Lo habitual es que el ESP32, corriendo como servidor, se conecte por WiFi a tu router. Tu PC, teléfono o tablet también están conectados al mismo router (por WiFi o Ethernet), así que el ESP32 y el navegador quedan en la misma red local.

Cuando escribes la dirección IP del ESP32 en el navegador, le mandas una petición HTTP. El ESP32 responde con datos: un valor, una lectura de sensor, una página HTML o lo que definas en el código.
En este proyecto, al escribir la IP del ESP32, este responde con HTML y muestra una página simple como esta:

La página es deliberadamente básica. En tutoriales siguientes la podrás ampliar con botones y campos para controlar los GPIO.
Materiales
- Placa ESP32 (cualquier modelo: DevKit V1, ESP32 WROOM, ESP32 S3, etc.)
- Cable USB (micro USB o tipo C según tu placa) para programar la placa desde VS Code
Crear un proyecto plantilla ESP-IDF para el ESP32
La extensión ESP-IDF te permite crear un proyecto desde cero con todos los archivos y configuraciones necesarias generados automáticamente. Para crear un nuevo proyecto ESP-IDF en VS Code, sigue estos pasos:
- Abre la extensión ESP-IDF Espressif.
- Expande el menú Advanced.
- Haz clic en la opción New Project Wizard.
- Elige Use ESP-IDF v5.4.1 para seleccionar la versión del framework.

Se abre una nueva ventana. Completa estos campos:
- Project Name: el nombre del proyecto.
- Enter Project Directory: la carpeta donde se guardará todo.
- Choose ESP-IDF Board: tu modelo de ESP32.
- Choose Serial Port: el puerto serial al que está conectada la placa.

A continuación, elige la plantilla sample_project en ESP-IDF: Template:

Cuando termines, se creará el proyecto y se abrirá una nueva ventana de VS Code:

Abrir el proyecto ESP-IDF en VS Code
Si el proyecto no se abre automáticamente, ve a File > Open Folder y selecciona la carpeta del proyecto recién creado:


Una vez abierto, navega a la carpeta main y abre el archivo principal main.c (o el nombre que le hayas dado al proyecto):

Crear el archivo de configuración WiFi (Kconfig.projbuild)
Para no hardcodear el SSID y la contraseña en el código, vamos a usar el sistema menuconfig de ESP-IDF. Crea un archivo llamado Kconfig.projbuild dentro de la carpeta main:

Pega el siguiente contenido en Kconfig.projbuild:
menu "Wi-Fi Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
help
SSID (network name) you want to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
help
Password of the Wi-Fi network you want to connect to.
endmenu

Ahora abre el SDK Configuration Editor (menuconfig) desde la barra inferior de VS Code:

Busca el menú WiFi Configuration y completa el WiFi SSID y el WiFi Password con las credenciales de tu red:

Guarda los cambios. Esto genera las macros CONFIG_ESP_WIFI_SSID y CONFIG_ESP_WIFI_PASSWORD que vamos a usar desde el código en C.
Código: servidor web simple en ESP-IDF
Reemplaza el contenido de main.c con el siguiente código:
/*
Rui Santos & Sara Santos - Random Nerd Tutorials
https://RandomNerdTutorials.com/esp-idf-esp32-web-server/
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_http_server.h"
#include "sdkconfig.h"
#define MY_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
#define MY_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
static const char *TAG = "web_server";
// HTML web page to serve
static const char *html_page =
"<!DOCTYPE html>"
"<html>"
"<head>"
"<title>ESP-IDF: ESP32 Web Server</title>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
"</head>"
"<body>"
"<h1>ESP-IDF: ESP32 Web Server</h1>"
"<p>Hello from ESP32!</p>"
"</body>"
"</html>";
// HTTP GET handler for root "/"
static esp_err_t root_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, html_page, HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
// Start the HTTP server
static httpd_handle_t start_web_server(void)
{
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_handle_t server = NULL;
if (httpd_start(&server, &config) == ESP_OK) {
ESP_LOGI(TAG, "HTTP server started on port %d", config.server_port);
// Register URI handler
httpd_uri_t uri_get = {
.uri = "/",
.method = HTTP_GET,
.handler = root_get_handler,
.user_ctx = NULL
};
httpd_register_uri_handler(server, &uri_get);
return server;
}
ESP_LOGE(TAG, "Failed to start HTTP server");
return NULL;
}
// Wi-Fi and IP event handler
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
ESP_LOGI(TAG, "Wi-Fi STA started. Connecting to %s...", MY_ESP_WIFI_SSID);
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGW(TAG, "Wi-Fi disconnected. Retrying connection...");
esp_wifi_connect();
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP Address: " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Web Server ready! Access at http://" IPSTR "/", IP2STR(&event->ip_info.ip));
}
}
void app_main(void)
{
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// Initialize TCP/IP stack and event loop
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// Create default Wi-Fi STA interface
esp_netif_create_default_wifi_sta();
// Initialize Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// Register event handlers
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&wifi_event_handler,
NULL, NULL));
// Configure Wi-Fi STA
wifi_config_t wifi_config = {
.sta = {
.ssid = MY_ESP_WIFI_SSID,
.password = MY_ESP_WIFI_PASS,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
// Start the web server (it will be ready once IP is assigned)
httpd_handle_t server = start_web_server();
if (server) {
ESP_LOGI(TAG, "Web Server initialized. Waiting for Wi-Fi connection...");
}
}
El programa hace cuatro cosas en orden. Primero inicializa la memoria no volátil (NVS), que la pila WiFi necesita para guardar parámetros de calibración. Luego levanta el stack TCP/IP y el loop de eventos, configura la interfaz en modo estación (STA) y arranca la conexión WiFi con las credenciales del menuconfig. El handler wifi_event_handler reacciona a los eventos: si la conexión se cae, reintenta automáticamente, y cuando el router asigna una IP, la imprime en el monitor serial. Finalmente, start_web_server levanta el servidor HTTP con httpd_start y registra un único handler para la ruta raíz /, que devuelve la página HTML guardada en html_page.
Compilar, flashear y probar
Con el código listo, usa los botones de la barra inferior de VS Code en este orden:
- Build del proyecto (ícono de cilindro).
- Flash a la placa (ícono de rayo).
- Monitor para abrir el monitor serial (ícono de enchufe).
En el monitor serial deberías ver al ESP32 conectándose a la red WiFi y, una vez conectado, su dirección IP. Copia esa IP y pégala en el navegador de tu PC o teléfono (que tienen que estar en la misma red). La página HTML que sirve el ESP32 aparecerá en pantalla.
Si la página no carga: revisa que el SSID y la contraseña del menuconfig estén bien escritos, y que el dispositivo desde el que abres el navegador esté en la misma red WiFi que el ESP32. Si el monitor serial muestra reintentos de conexión infinitos, casi siempre es una credencial mal puesta.
Variantes y mejoras
Esta página estática es solo el punto de partida. Algunas extensiones concretas que puedes agregar sobre esta misma base:
- Controlar un GPIO desde el navegador: agrega un segundo handler (por ejemplo en
/led/ony/led/off) y llama agpio_set_leveldentro de cada uno. Con eso enciendes y apagas un LED o un relé desde un botón en la página. - Mostrar lecturas de un sensor en vivo: conecta un DHT22 o un DS18B20 y arma el HTML dinámicamente con
snprintf, insertando la temperatura actual antes de enviarlo conhttpd_resp_send. - Servir archivos más grandes desde SPIFFS: en lugar de tener el HTML como cadena en C, guarda
index.html, CSS y JS en una partición SPIFFS o LittleFS y sírvelos desde ahí. Así separas el diseño del firmware y puedes editar la interfaz sin recompilar.
Personalización para Chile
Todo lo que necesitas para este proyecto lo consigues en MechatronicStore:
- Placa ESP32 DevKit (tipo C) (SKU X2-10V2): $7.990 CLP. Cualquier variante WROOM o S3 sirve igual; el código no cambia.
- Cable USB tipo C a USB tipo A 1 m (SKU B-101): $2.190 CLP, para programar y alimentar la placa desde el PC.
Si en el tutorial original ves una placa de otra marca o un módulo importado, la ESP32 DevKit del catálogo cumple exactamente la misma función para este proyecto, a precio local y con stock inmediato.
Recursos
- Tutorial original: ESP-IDF: ESP32 Simple Web Server (Serve HTML Page)
- Código completo (main.c): wifi_simple_web_server.c en GitHub
- Documentación oficial: esp_http_server (ESP-IDF API Reference)
Versión chilena con componentes en stock local en MechatronicStore. Contenido inspirado en el tutorial de Rui Santos y Sara Santos (Random Nerd Tutorials).









