Chisme 182 del Lince: servidor web ESP8266, un LED desde el móvil
Era de noche en el laboratorio de Lynx Enterprises, y Víctor Vílchez, el Lince de la Noche, tenía un problema muy serio: la luz de la cueva estaba al otro lado de la sala y levantarse a apagarla le daba pereza.
—Voy a hacer una app —anunció—. Con su icono, su pantalla de inicio y su tienda de aplicaciones.
La Gata Montesa, enroscada sobre el teclado, abrió un ojo.
—¿Y tu móvil no trae ya un navegador?
—Sí, pero…
—Pues que la placa le sirva una página web. Tú pides, ella responde. Sin instalar nada.
El Lince se quedó mirando la pequeña placa NodeMCU que tenía sobre la mesa. Era más sencillo de lo que él quería admitir. Así nació este servidor web ESP8266.

Introducción
Vamos a montar un servidor web ESP8266 en una placa NodeMCU. Desde el móvil, y sin ninguna app, encenderemos y apagaremos el LED integrado.
No hay cableado: solo un cable micro-USB. Lo importante está dentro: entender cómo hablan un cliente (el móvil) y un servidor (la placa).
Puedes probarlo antes en el simulador del Chisme 182, que no necesita placa y tiene un modo demostración.
Por qué este chisme
Casi todo lo que haces en internet es una conversación de este tipo: un cliente pide, un servidor responde. Aquí la tienes en miniatura y puedes ver cada palabra del diálogo.
También vas a ver algo muy real: las cosas fallan. El chisme cuenta el proceso tal como fue, con sus errores. Cada fallo se convierte en una mejora del ciclo prueba → mejora.
Aviso: es una ESP8266, no una ESP32
Esta placa se parece a las ESP32 de otros chismes, pero no es igual. Antes de copiar nada, apunta estas diferencias:
- Librerías:
ESP8266WiFi.hyESP8266WebServer.h(noWiFi.hniWebServer.h). - Todo funciona a 3,3 V.
- Pines D0 a D8 y una sola entrada analógica, A0.
- WiFi solo de 2,4 GHz.
- LED integrado con lógica invertida: LOW enciende y HIGH apaga.
- Pines de arranque: D3, D4 y D8.
Cómo funciona un servidor web ESP8266
La placa se conecta a tu WiFi y recibe una dirección IP local, por ejemplo 192.168.1.45. Después se queda escuchando en el puerto 80.
Cuando escribes esa IP en el navegador, el móvil envía una petición con el método GET y una ruta, por ejemplo /on. La placa responde con un código de estado:
- 200: «aquí tienes lo que pediste».
- 303: «lo que pides ya está hecho; ahora ve a esta otra ruta».
- 404: «esa ruta no existe».
Tendremos tres rutas: / (la página), /on (enciende) y /off (apaga). Puedes leer los detalles del código 303 en MDN.
Qué necesitamos
- Una NodeMCU ESP8266 (clon V1, 30 pines, CH340).
- Un cable micro-USB.
- Un ordenador con Arduino IDE y el paquete de placas
esp8266. - Un móvil y una red WiFi de 2,4 GHz.
En Arduino IDE elige la placa NodeMCU 1.0 (ESP-12E Module). No hay componentes externos: solo el LED integrado, y el USB da la corriente.
Paso 1: ¿ve la placa tu WiFi?
El primer fallo fue un «Conectando……» que no terminaba nunca. La clave del código no era la de la red actual, y la placa no decía por qué.
La solución: que la placa escanee las redes que ve y escriba el código de estado en cada intento. Es la misma idea que el radar de redes del Chisme 30.
Predice: si escribes la red en minúsculas, cuando en realidad lleva mayúsculas, ¿qué número crees que saldrá?
// Chisme 182 - Paso 1: diagnostico WiFi
#include <ESP8266WiFi.h>
// Pon aqui tus datos (mayusculas exactas)
const char* ssid =
"TU_WIFI";
const char* password =
"TU_CLAVE";
void setup() {
Serial.begin(115200);
delay(500);
WiFi.persistent(false);
WiFi.disconnect(true);
delay(200);
WiFi.mode(WIFI_STA);
Serial.println("Buscando redes...");
int n = WiFi.scanNetworks();
for (int i = 0; i < n; i++) {
Serial.print(WiFi.SSID(i));
Serial.print(" senal ");
Serial.print(WiFi.RSSI(i));
Serial.print(" dBm canal ");
Serial.println(WiFi.channel(i));
}
Serial.print("Conectando a: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
}
void loop() {
Serial.print("estado ");
Serial.println(WiFi.status());
if (WiFi.status() == WL_CONNECTED) {
Serial.print("IP: ");
Serial.println(WiFi.localIP());
delay(5000);
}
delay(1000);
}
Abre el monitor serie a 115200. Los estados más habituales son:
- 3: conectada.
- 1: la placa no ve esa red (nombre mal escrito o red de 5 GHz).
- 6: ve la red, pero la clave no es correcta.
Los números exactos pueden variar un poco entre versiones del paquete. Fíate del que sale en tu monitor y consulta la documentación de ESP8266WiFi.
Paso 2: la versión con error
Con el WiFi resuelto, la placa sirve su primera página. Y aquí llegó el segundo fallo.
Si dentro de un enlace <a> metes un <button>, la página funciona en el ordenador. Pero en el móvil el botón no hace nada y siempre pone APAGADO.
Predice: ¿llegará alguna petición a la placa cuando pulses el botón en el móvil? Mira el monitor serie.
Para provocar el fallo, en el sketch final (más abajo) sustituye las dos líneas de los enlaces por estas:
<a href="/on"><button class="btn on">
Encender</button></a>
<a href="/off"><button class="btn off">
Apagar</button></a>
En el móvil el monitor se queda mudo: la petición nunca sale. Eso te dice que el fallo no está en la placa, sino en la página. Un botón dentro de un enlace es HTML no válido, y cada navegador lo trata a su manera (consulta el elemento <a> en MDN).
La solución: enlaces <a> que parecen botones gracias al CSS. Es lo que lleva el código final.

El código final
Pon tu red y tu clave. El programa completo es este:
// Servidor web LED con diagnostico WiFi
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
// Pon aqui tus datos (mayusculas exactas)
const char* ssid =
"TU_WIFI";
const char* password =
"TU_CLAVE";
// Pin del LED (prueba 2 o 16 si no va)
const int PIN_LED = LED_BUILTIN;
ESP8266WebServer server(80);
bool ledEncendido = false;
// Pagina web (%ESTADO% se sustituye)
const char PAGINA[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width,
initial-scale=1">
<title>LED NodeMCU</title>
<style>
body{text-align:center;
font-family:sans-serif}
.btn{display:block;
margin:20px auto;
padding:25px;
width:80%;
max-width:300px;
font-size:2em;
color:#fff;
text-decoration:none;
border-radius:12px}
.on{background:#2e9e44}
.off{background:#c0392b}
</style>
</head>
<body>
<h1>LED NodeMCU</h1>
<p>Estado: <b>%ESTADO%</b></p>
<a class="btn on" href="/on">
Encender</a>
<a class="btn off" href="/off">
Apagar</a>
</body>
</html>
)rawliteral";
void paginaInicio() {
Serial.println("Peticion: /");
String html = FPSTR(PAGINA);
if (ledEncendido) {
html.replace("%ESTADO%",
"ENCENDIDO");
} else {
html.replace("%ESTADO%",
"APAGADO");
}
server.sendHeader("Cache-Control",
"no-store");
server.send(200, "text/html", html);
}
void encender() {
digitalWrite(PIN_LED, LOW);
ledEncendido = true;
Serial.println("Encender");
server.sendHeader("Cache-Control",
"no-store");
server.sendHeader("Location", "/");
server.send(303);
}
void apagar() {
digitalWrite(PIN_LED, HIGH);
ledEncendido = false;
Serial.println("Apagar");
server.sendHeader("Cache-Control",
"no-store");
server.sendHeader("Location", "/");
server.send(303);
}
// Lista las redes que ve la placa
void mostrarRedes() {
Serial.println("Buscando redes...");
int n = WiFi.scanNetworks();
Serial.print("Redes vistas: ");
Serial.println(n);
for (int i = 0; i < n; i++) {
Serial.print(i + 1);
Serial.print(") ");
Serial.print(WiFi.SSID(i));
Serial.print(" senal ");
Serial.print(WiFi.RSSI(i));
Serial.print(" dBm canal ");
Serial.print(WiFi.channel(i));
if (WiFi.SSID(i) == String(ssid)) {
Serial.print(" <-- TU RED");
}
Serial.println();
}
}
// Intenta conectar durante 20 s
bool conectarWiFi() {
Serial.print("Conectando a: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
for (int i = 0; i < 40; i++) {
if (WiFi.status() == WL_CONNECTED) {
return true;
}
Serial.print("estado ");
Serial.println(WiFi.status());
delay(500);
}
return false;
}
void setup() {
Serial.begin(115200);
delay(500);
Serial.println();
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, HIGH);
WiFi.persistent(false);
WiFi.disconnect(true);
delay(200);
WiFi.mode(WIFI_STA);
mostrarRedes();
if (!conectarWiFi()) {
Serial.println("NO CONECTA.");
Serial.println("Reinicio en 10 s");
delay(10000);
ESP.restart();
}
Serial.print("Conectado. IP: ");
Serial.println(WiFi.localIP());
server.on("/", paginaInicio);
server.on("/on", encender);
server.on("/off", apagar);
server.begin();
Serial.println("Servidor listo");
}
void loop() {
server.handleClient();
}

Lee y predice
Antes de probar, responde con lo que has leído:
- ¿Por qué
digitalWrite(PIN_LED, LOW)enciende el LED? - ¿Qué devuelve
encender(): la página o una redirección? ¿Qué código? - ¿Para qué sirve
Cache-Control: no-store? (Pista: el tercer fallo.)
Cómo probar el servidor web ESP8266
- Sube el programa y abre el monitor serie a 115200.
- Espera a ver
Conectado. IP:y apunta la dirección. - Con el móvil en la misma red que la placa, escribe la IP en el navegador.
- Pulsa Encender y Apagar. Mira el LED y el monitor.
Si no pasa nada, repasa los fallos reales que tuvimos:
- «Conectando……» sin parar. Red o clave incorrectas. Escanea y mira el estado.
- El botón no responde en el móvil. Era
<button>dentro de<a>. - La página no se actualiza. El navegador guardaba una copia. Se evita con
Cache-Control: no-store. - El LED no se enciende. En el paquete
esp8266,LED_BUILTINpuede apuntar a GPIO16 (D0) o a GPIO2 (D4), según la placa. Por esoPIN_LEDes una constante: prueba con 2 o con 16. - El móvil no abre la página. Tiene que estar en la misma red que la placa.
Qué estamos midiendo realmente
El chisme mide tres cosas:
- El estado del LED, que guardamos en una variable.
- El estado del WiFi, con
WiFi.status(). - La señal, el RSSI, en dBm.
El RSSI es una escala logarítmica: -50 dBm es mucho más fuerte que -80 dBm. Pero cuidado, con la placa quieta el valor varía unos decibelios de una lectura a otra.
Además, el RSSI no es la calidad de internet ni la velocidad: solo dice con qué fuerza llega la señal. Una vez más: medir no significa medir bien. Eso mismo viste en el detector de presencia con RSSI del Chisme 43.
Seguridad: este servidor web ESP8266 obedece a cualquiera
Fíjate en lo que no hay: contraseña. Cualquier dispositivo de la red local que conozca la IP puede pedir /on o /off, y la placa obedece sin preguntar quién es.
En una red local pequeña no pasa nada grave. Pero no abras este servidor a internet tal cual. Cómo hacerlo con seguridad es un tema para otro chisme.
Para el aula
Estas actividades del servidor web ESP8266 siguen las pautas DUA: ofrecen varias formas de entender, de participar y de demostrar lo aprendido.
Para entender (representación)
- Usa a la vez el simulador, el monitor serie y un dibujo del cliente y el servidor.
- Recorre la conversación en voz alta: ¿quién pide?, ¿quién responde?, ¿qué código llega?
Para participar (implicación)
- Antes de cada versión, el alumnado predice qué pasará y lo anota. Después se compara con el resultado.
- Se explica por qué fallaron las versiones con errores, con sus palabras.
- Cada alumno elige cómo trabajar: simulador, placa real o ambos.
Para demostrar lo aprendido (acción y expresión)
- Explicar el ciclo petición-respuesta con un esquema, un texto o un audio.
- Las preguntas de comprobación del simulador, para autoevaluarse.
- Pregunta de investigación: ¿qué ocurre si el móvil está en otra red? ¿Y si te alejas del router?
Retos
- Haz que funcione. Enciende y apaga el LED desde el móvil.
- Modifica algo. Cambia los textos y los colores de los botones.
- Mide. Anota el RSSI a distintas distancias del router en una tabla y represéntala.
- Provoca el fallo. Quita
Cache-Controly describe qué ocurre en tu navegador. - Reto final: un segundo LED. Conecta un LED externo con una resistencia de 220 Ω a un pin como D1 (GPIO5), y su pata corta a GND. Añade las rutas
/on2y/off2. Piensa antes: con este LED, ¿qué nivel lo enciende? Evita los pines de arranque D3, D4 y D8.
Qué hemos aprendido
- Un servidor web ESP8266 responde a peticiones GET con códigos de estado: 200, 303 y 404.
- Un 303 manda al navegador a otra ruta: así la página se recarga con el estado nuevo.
- Escanear redes y escribir el estado de cada intento convierte un «no funciona» en un diagnóstico.
- Un fallo en el móvil puede estar en la página, no en la placa.
- Un servidor sin autenticación solo es seguro en una red de confianza.
Y el Lince, esa noche, apagó la luz de la cueva sin levantarse.
—¿Ves? No hacía falta una app —dijo la Gata Montesa.
—Ya… ¿y si ahora la quiero apagar desde la otra punta de Sierra Bermeja?
—Eso —dijo la Gata, mirando al techo— es para otro chisme.
Para seguir investigando
- Enciende un LED sin cables de red en el Chisme 31, LED por Bluetooth.
- Mira qué redes ve tu placa con el radar de redes (Chisme 30).
- Documentación oficial: ESP8266WiFi en el núcleo de Arduino.
Prototipo educativo, no un producto comercial. Úsalo solo en tu red local.
Etiqueta:AIDARAC, ESP8266, HTTP, ies monterroso, led, montesteam, NodeMCU, servidor web ESP8266, wifi


