Chisme 195 del Lince: semáforo NodeMCU controlado desde el móvil
A la entrada de la cueva del Lince de la Noche hay un semáforo. Verde: «estoy en casa, pasa». Amarillo: «estoy ocupado». Rojo: «hoy no molestes».
El problema es que para cambiarlo, el Lince tiene que levantarse del sofá, cruzar la cueva y mover un cable. Y lleva tres días en rojo sin querer.
—Voy a comprar un sistema domótico —anuncia—. Con hub, con nube y con una suscripción.
La Gata Montesa, que está mirando el móvil, ni levanta la vista.
—Tienes una NodeMCU, WiFi y un navegador. La placa puede ser la página web. Y el móvil, tu mando.
—¿La placa… sirve páginas?
—Desde el sofá. Gratis. Y sin suscripción.
Introducción: un semáforo NodeMCU desde el móvil
En este chisme construimos un semáforo NodeMCU que se controla desde el móvil. La NodeMCU se conecta a tu WiFi, crea una página web con cuatro botones y enciende cada luz cuando la pulsas.
Es el Chisme 195, para Bachillerato. Aprendes cómo funciona un servidor web por dentro, con un programa que puedes leer entero.
Para no equivocarte con los cables, tienes un simulador con la NodeMCU, el semáforo y un móvil dibujado que abre la página.
Por qué este chisme
Ya enciendes un LED desde el móvil en el chisme 182. Aquí damos un paso más: tres luces y una página con estado, donde cada botón se ve apagado si su luz está apagada.
Y hay una idea de fondo: la página la sirve la propia placa, dentro de tu red. No hay nube ni servidor externo, y los datos no salen de casa.
Cómo funciona un servidor web en la NodeMCU
Cuando escribes la IP en el navegador, el móvil le pide una página a la NodeMCU. La NodeMCU responde con un texto en HTML, y el navegador lo dibuja.
Cada botón es un enlace: /verde, /amarillo, /rojo o /todas. Al pulsarlo, el móvil pide esa ruta y la NodeMCU ejecuta una función que cambia la luz y devuelve al móvil a la página principal.
Para eso la placa necesita estar en tu WiFi. WiFi.begin(ssid, password) la conecta, y WiFi.localIP() te dice la IP que tienes que escribir en el móvil.
Qué necesitamos
- Una NodeMCU ESP8266.
- Un módulo semáforo de 3 luces.
- Cables de conexión.
- Un cable micro-USB.
- Una red WiFi de 2,4 GHz y un móvil.
- El IDE de Arduino con el paquete esp8266.
Montaje del semáforo NodeMCU
| Semáforo | NodeMCU |
|---|---|
| GND | G |
| Verde | D7 |
| Amarillo | D6 |
| Rojo | D5 |
Los pines D5, D6 y D7 evitan los de arranque (D3, D4 y D8) y funcionan a 3,3 V. Mira la serigrafía del módulo por si el orden de los pines es distinto.
El programa
Abre el IDE de Arduino, elige la placa «NodeMCU 1.0 (ESP-12E Module)» y escribe tu WiFi en las dos primeras líneas con datos.
// Semáforo controlado desde el móvil (servidor web)
// NodeMCU ESP8266 + semáforo de 3 luces
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
const char* ssid = "TU_WIFI"; // CAMBIA: nombre de tu WiFi
const char* password = "TU_CONTRASEÑA"; // CAMBIA: contraseña de tu WiFi
const int PIN_VERDE = D7; // si tu verde y tu rojo salen cambiados, intercámbialos
const int PIN_AMARILLO = D6;
const int PIN_ROJO = D5;
bool verdeOn = false;
bool amarilloOn = false;
bool rojoOn = false;
ESP8266WebServer server(80);
// Crea un botón: más apagado si su luz está apagada
String boton(String texto, String ruta, String fondo, String letra, bool encendida) {
String h = "<a class='btn' style='background:" + fondo + ";color:" + letra + ";";
if (!encendida) h += "opacity:0.35;";
h += "' href='" + ruta + "'>" + texto;
h += encendida ? " (encendida)" : " (apagada)";
h += "</a>";
return h;
}
void paginaInicio() {
String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<title>Semáforo NodeMCU</title>";
html += "<style>";
html += "body{text-align:center;font-family:sans-serif}";
html += ".btn{display:block;margin:20px auto;padding:25px;width:80%;max-width:300px;";
html += "font-size:1.6em;text-decoration:none;border-radius:12px}";
html += "</style></head><body>";
html += "<h1>Semáforo NodeMCU</h1>";
html += boton("Verde", "/verde", "#2e9e44", "#fff", verdeOn);
html += boton("Amarillo", "/amarillo", "#f1c40f", "#000", amarilloOn);
html += boton("Rojo", "/rojo", "#c0392b", "#fff", rojoOn);
html += "<a class='btn' style='background:#555;color:#fff' href='/todas'>Apagar todas</a>";
html += "</body></html>";
server.sendHeader("Cache-Control", "no-store");
server.send(200, "text/html", html);
}
// Vuelve a la página principal
void volver() {
server.sendHeader("Cache-Control", "no-store");
server.sendHeader("Location", "/");
server.send(303);
}
// Cambia una luz: si estaba apagada la enciende y al revés
void cambiar(int pin, bool &estado) {
estado = !estado;
digitalWrite(pin, estado ? HIGH : LOW); // HIGH = encendida
Serial.print("Pin ");
Serial.print(pin);
Serial.println(estado ? " ENCENDIDO" : " APAGADO");
volver();
}
void apagarTodas() {
verdeOn = amarilloOn = rojoOn = false;
digitalWrite(PIN_VERDE, LOW);
digitalWrite(PIN_AMARILLO, LOW);
digitalWrite(PIN_ROJO, LOW);
Serial.println("Todas apagadas");
volver();
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(PIN_VERDE, OUTPUT);
pinMode(PIN_AMARILLO, OUTPUT);
pinMode(PIN_ROJO, OUTPUT);
digitalWrite(PIN_VERDE, LOW);
digitalWrite(PIN_AMARILLO, LOW);
digitalWrite(PIN_ROJO, LOW);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Conectando");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Conectado. IP: ");
Serial.println(WiFi.localIP());
server.on("/", paginaInicio);
server.on("/verde", []() { cambiar(PIN_VERDE, verdeOn); });
server.on("/amarillo", []() { cambiar(PIN_AMARILLO, amarilloOn); });
server.on("/rojo", []() { cambiar(PIN_ROJO, rojoOn); });
server.on("/todas", apagarTodas);
server.begin();
Serial.println("Servidor listo");
}
void loop() {
server.handleClient();
}
Antes de compartir el programa con nadie, borra tu WiFi real y deja TU_WIFI y TU_CONTRASEÑA.
Cómo probarlo
- Sube el programa y abre el monitor serie a 115200.
- Espera a ver
Conectado. IP: ...y apunta la IP. - En el móvil, conectado a la misma WiFi, escribe esa IP en el navegador.
- Pulsa cada botón: la luz se enciende o se apaga, y el botón se ve más apagado cuando su luz está apagada.
- «Apagar todas» las apaga a la vez.
Si algo falla
- No conecta, solo salen puntos: revisa el nombre y la contraseña. La NodeMCU solo funciona en 2,4 GHz.
- Conecta, pero el móvil no abre la página: algunas WiFi de centro aíslan los dispositivos. Prueba con el punto de acceso del móvil.
- Una luz no corresponde con su botón: intercambia los números de pin en el programa.
- No se enciende ninguna: revisa el cable de GND.
Qué está pasando de verdad
Cada pulsación es una petición HTTP: el móvil pide una ruta y la NodeMCU responde. En el monitor serie verás Pin 13 ENCENDIDO: es el número de pin interno (GPIO) de D7. D6 es el 12 y D5 es el 14.
El estado de las luces vive en tres variables (verdeOn, amarilloOn, rojoOn). La página se dibuja de nuevo cada vez, con el botón más claro u oscuro según esas variables.
Y una advertencia de seguridad: esta página no tiene contraseña. Cualquiera conectado a tu WiFi y que conozca la IP puede cambiar las luces. Para un semáforo de clase no importa, pero conviene saberlo.
Para el aula
- ¿Qué diferencia hay entre una página que sirve la placa y una que está en internet?
- ¿Por qué la NodeMCU responde con una redirección (
303) después de cambiar una luz? - ¿Qué pasaría si dos móviles pulsan a la vez?
- ¿Por qué el móvil tiene que estar en la misma WiFi?
- ¿Quién podría abrir esta página? ¿Cómo la protegerías?
Retos
- Reto 1: haz que funcione con tu WiFi y enciende cada luz.
- Reto 2: cambia los colores y los textos de la página.
- Reto 3: añade un botón «Semáforo automático» que haga la secuencia verde, amarillo y rojo. Pista: no uses
delay(), porque la página dejaría de responder; usamillis(). - Reto 4: muestra en la página cuánto tiempo lleva encendida cada luz.
- Reto 5: añade una contraseña sencilla para abrir la página.
- Reto 6: piensa: ¿cómo conectarías esta página al sensor de humedad para que el semáforo se encienda solo? (ODS 6 y 15).
El semáforo NodeMCU y las ODS
Un semáforo que se enciende solo, según la humedad de la tierra, avisaría de cuándo regar sin que nadie lo vigile. Eso toca dos Objetivos de Desarrollo Sostenible:
- ODS 6, Agua limpia y saneamiento: regar solo cuando hace falta evita malgastar agua.
- ODS 15, Vida de ecosistemas terrestres: suelos y plantas sanos, la base de huertos y bosques como los de Sierra Bermeja.
Qué hemos aprendido
- Un servidor web es un programa que responde a peticiones con texto en HTML.
- Cada botón de la página es una ruta que ejecuta una función en la placa.
- La NodeMCU solo usa WiFi de 2,4 GHz, y el móvil debe estar en la misma red.
- Una página sin contraseña la puede abrir cualquiera de tu red.
Escena final
El Lince se tumbó en el sofá y pulsó el botón verde. A lo lejos, en la entrada de la cueva, se encendió una luz.
—Funciona —dijo, emocionado.
—Se llama servidor web —respondió la Gata, sin levantar la vista del móvil—. Y ahora te pongo en rojo.
Para seguir investigando
- Chisme 182: servidor web ESP8266, un LED desde el móvil
- Catálogo de los Chismes del Lince
- Servidor web, en Wikipedia
- Documentación de ESP8266WebServer
Este chisme es un prototipo educativo.
Etiqueta:AIDARAC, Arduino IDE, ESP8266, ies monterroso, montesteam, NodeMCU, ODS, Semáforo, servidor web, wifi

