// HARDWARE · IOT · PROYECTO PERSONAL

Lámpara para acuario

Una lámpara LED de cinco canales que diseñé, monté y programé yo. Un ESP32 reproduce un día completo de luz, vigila la temperatura de los LEDs y sirve su propia app al móvil, sin nada que instalar.

La lámpara colgada sobre el acuario, encendida en azul y ultravioleta
5Canales de luz
1.024Niveles por canal
12Perfiles de luz
24Puntos por canal
0Apps que instalar
1Persona detrás

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 de cableado: cinco drivers conectados a los LEDs de la barra por colores

// 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.
ronaldherrera.es/proyectos/demo-lampara-led
Simulador: la lámpara sobre una pecera dibujada y, al lado, la app con un deslizador por canal

// 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.

01 · SU REDPunto de acceso

Al arrancar, la lámpara crea su propia red Wi-Fi para configurarla.

02 · ESCANEORedes cercanas

El móvil muestra las redes que ve la lámpara; eliges la de casa.

03 · CONEXIÓNRed de casa

Se conecta y sigue ofreciendo su red por si hay que cambiarla.

04 · NOMBRElampara.local

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.
App de la lámpara en el móvil: estado, ventilador al 15 %, 26,4 °C y un deslizador por canal

// 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
App webdeslizadores y editor de perfiles
Pantalla de iniciose abre como una app
Consulta /statuscada 2 segundos
ESP32: redcómo se llega a ella
WebServer17 rutas HTTP
Wi-Fi AP + STAsu red y la de casa a la vez
mDNSlampara.local
NTPla hora para el modo automático
ESP32: controlel bucle principal
Interpolaciónbrillo de cada canal a cada minuto
Nubes y límite térmicoatenuaciones suaves
Corrección gammabrillo percibido uniforme
Preferencestodo guardado en flash
Hardwarelo que se calienta
5 drivers de LEDuno por canal
Sonda OneWiretemperatura de los LEDs
Ventilador PWMvelocidad según el calor

// Decisiones técnicas y por qué

ProblemaCon el ahorro de energía del Wi-Fi, la luz daba microcortes.
DecisiónDesactivarlo con WiFi.setSleep(false): el PWM deja de parpadear.
En vez deDejar la configuración por defecto y vivir con el parpadeo.
ProblemaInstalar una app y mantenerla en dos tiendas para una sola lámpara.
DecisiónLa app vive en la memoria del ESP32 y se sirve por HTTP; basta un navegador.
En vez deUna app nativa que habría que publicar y actualizar aparte.
ProblemaConfigurar la red sin cables ni código.
DecisiónModo doble: la lámpara ofrece su propia red y a la vez se conecta a la de casa; se encuentra como lampara.local.
En vez deEscribir el nombre y la contraseña del Wi-Fi en el firmware.
ProblemaEl ojo no ve el brillo de forma lineal.
DecisiónCorrección gamma con exponente 1,5 antes de escribir el PWM, para que el deslizador se sienta uniforme.
En vez dePasar el porcentaje tal cual, con saltos bruscos en la parte baja.
ProblemaCambios bruscos al arrancar, al cambiar de modo o al calentarse.
DecisiónTodo se mueve en rampas: la luz sube desde cero al encender y el límite térmico baja poco a poco.
En vez deAplicar cada cambio de golpe.

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.

Lampara-Led.ino · getTargetFromSchedule()C++
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.

Lampara-Led.ino · loop()C++
// 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.