EL PRODUCTO
Las lámparas de acuario que encontraba no se podían ajustar a mi gusto. Yo quería mover cada color por separado, programar un día entero con su amanecer y su atardecer, y controlarlo desde el móvil sin depender de la app de un fabricante. Así que la hice yo: la diseñé en 3D, repartí los LEDs en cinco canales, monté los drivers y el ESP32, y escribí el firmware y la app. Está montada y funcionando sobre el acuario.
INFRAESTRUCTURA TÉCNICA
- PLACA: ESP32 con cinco salidas PWM de 10 bits a 1 kHz, una por driver de LED.
- FIRMWARE: C++ con el entorno de Arduino; versión 4.1.
- APP: HTML, CSS y JavaScript guardados en la memoria del ESP32 y servidos por HTTP.
- SENSORES: sonda de temperatura digital (bus OneWire) y ventilador regulado por PWM.
- DISEÑO: modelo 3D completo en SketchUp y esquema de cableado propio.
01 / HARDWARE
Cinco canales, cada uno con su driver
Cada canal tiene su propio driver y su propia salida en el ESP32, así que se regula por separado. Los colores mezclan LEDs de distinto tipo: azul real, azul, rojo, ultravioleta de 395 y 420 nm y blanco de 6500 K y 3000 K. Antes de soldar nada, dibujé qué LED iba a cada driver.
- Azul izquierdaAzul real, mitad izquierda de la barra.
- Azul derechaAzul real, mitad derecha.
- Ambiente marinoEl canal de «azul extra» del esquema.
- UltravioletaLEDs de 395 y 420 nm.
- Blancos6500 K (fríos) y 3000 K (cálidos).
// Esquema: cada color de cable es un canal; arriba, los cinco drivers.
02 / LA APP
La sirve la propia lámpara
No hay nada que descargar. El ESP32 lleva dentro un servidor web con la app: se abre en el navegador del móvil y se puede añadir a la pantalla de inicio, donde se abre a pantalla completa como una app más.
- Manual o automático: en manual, un deslizador por canal; en automático, la lámpara sigue el día programado con la hora de internet (NTP).
- 12 perfiles: cada uno con nombre propio y hasta 24 puntos de hora y brillo por canal. Entre punto y punto, la luz cambia poco a poco.
- Vista previa: al editar un perfil, mueves la hora y ves en la lámpara real cómo quedaría la luz.
- Efecto nubes: cada 4 a 12 segundos el brillo baja al azar hasta un 15 % y vuelve despacio.
// Simulador del portfolio: la misma app, pintando la luz sobre una pecera.
03 / PRIMER ARRANQUE
Conectarla sin tocar código
Quien la enchufa no tiene que saber qué es un ESP32 ni buscar su IP. La lámpara se configura desde el móvil y, si se va la luz, vuelve sola a como estaba: canales, perfiles, red y límites se guardan en la memoria flash.
Al arrancar, la lámpara crea su propia red Wi-Fi para configurarla.
El móvil muestra las redes que ve la lámpara; eliges la de casa.
Se conecta y sigue ofreciendo su red por si hay que cambiarla.
Con mDNS se encuentra por nombre, sin buscar la IP.
04 / PROTECCIÓN TÉRMICA
El calor manda sobre el diseño
Los LEDs de potencia se calientan, y el calor les acorta la vida. La sonda mide la temperatura cada dos segundos y la lámpara reacciona sola. La app muestra la temperatura y la velocidad del ventilador, y deja ajustar todos los límites.
- Ventilador con histéresis: arranca a 28 °C, no se apaga hasta bajar a 26 °C y llega al 100 % a 38 °C. Así no se enciende y apaga sin parar.
- Potencia mínima: cuando gira, nunca baja del 30 %, porque por debajo zumbaba sin moverse.
- Límite de brillo: a partir de 45 °C el brillo máximo baja al 50 % poco a poco, y se recupera al bajar de 40 °C.

// Arriba, ventilador y temperatura en vivo.
05 / DISEÑO 3D
Antes de montarla, la modelé
Modelé la lámpara completa en SketchUp, con sus perfiles, colgantes y tornillos, para ver medidas y montaje antes de construirla. Puedes girarla aquí.
Pesa unos 21 MB.
06 / ARQUITECTURA
Cómo encajan las piezas
- 3.211líneas de firmware
- 17rutas HTTP
- 5canales PWM de 10 bits
- 12perfiles en flash
- 2 sentre lecturas de temperatura
- 4.1versión del firmware
Móvillo que ve quien la usa
ESP32: redcómo se llega a ella
ESP32: controlel bucle principal
Hardwarelo que se calienta
// Decisiones técnicas y por qué
WiFi.setSleep(false): el PWM deja de parpadear.lampara.local.07 / EN EL CÓDIGO
Fragmentos reales del firmware
Brillo a cualquier hora
Cada canal guarda puntos de hora y brillo. Esta función busca entre qué dos puntos está la hora actual e interpola. Si el tramo cruza la medianoche (por ejemplo, de 22:00 a 07:00), suma 24 horas para que la cuenta salga bien.
La usan tanto el modo automático, con la hora de NTP, como la vista previa, con la hora que eliges en la app.
int currentMinutes = h * 60 + m;
// … busca p1 (último punto antes de ahora) y p2 (el siguiente)
// Ajuste para el ciclo de 24h
if (m2 < m1) m2 += 1440;
int adjustedCurrent = currentMinutes;
if (adjustedCurrent < m1) adjustedCurrent += 1440;
float diff = m2 - m1;
float pos = (float)(adjustedCurrent - m1) / diff;
int briPercent = pt1.bri + (int)(pos * (pt2.bri - pt1.bri));
if (briPercent <= 0) return 0;
return map(briPercent, 1, 100, 30, PWM_MAX);
Del brillo al pin
En cada vuelta del bucle, el valor de cada canal pasa por las nubes, el límite térmico y la corrección gamma antes de llegar al pin. El límite (safetyMultiplier) se mueve poco a poco, así que nunca hay un apagón brusco.
La salida va invertida porque los drivers apagan la luz con la señal alta.
// Aplicar el límite máximo de seguridad (Hard Cap)
int maxAllowed = (int)((float)PWM_MAX * safetyMultiplier);
int limitedValue = (finalValue > maxAllowed) ? maxAllowed : finalValue;
double input = (double)limitedValue / (double)PWM_MAX;
double output = pow(input, 1.5);
int valorGamma = (int)(output * PWM_MAX);
if (limitedValue == 0) valorGamma = 0;
int pwmFinal = PWM_MAX - valorGamma;
// … recorta pwmFinal entre 0 y PWM_MAX
ledcWrite(channels[i].pin, pwmFinal);
RESUMEN DEL IMPACTO
La lámpara es el proyecto donde mejor se ve cómo trabajo de principio a fin: una necesidad propia, un diseño en 3D, electrónica montada a mano y un software que esconde la complejidad. Quien la usa solo ve un deslizador por color y un día de luz que se repite solo; debajo hay control térmico, transiciones suaves y una app servida desde una placa de pocos euros.