01 · Punto de partida
Qué vamos a construir
Una luz nocturna que se enciende sola cuando la habitación queda a oscuras y se apaga cuando amanece. La especificación parece de una línea, y por eso sirve para ver cuánto hay detrás de un comportamiento «obvio». El taller usa lo que ya sabemos: el LDR y su divisor (tema 14), la histéresis (temas 7 y 13), el filtrado (tema 5), los eventos de un botón (tema 8) y el PWM del curso de Microcontroladores. Los requisitos son:
- Medir la luz ambiente con un LDR y estimarla en lux.
- Encender cuando haya menos de 10 lux y apagar cuando haya más de 30 lux, sin temblar entre ambos.
- No reaccionar a sombras pasajeras, faros de autos o una linterna: exigir que la condición se mantenga 10 segundos.
- Regular el brillo con PWM: tenue en penumbra, más intenso en oscuridad total, con la corrección que exige la percepción del ojo.
- Pasar de un brillo a otro de forma gradual, nunca de golpe.
- No confundir su propia luz con la del ambiente.
- Admitir un botón para forzar el encendido y volver al modo automático.
- No bloquear el programa en ningún momento.
02 · Paso 1
El comportamiento como máquina de estados
Antes de escribir código, se dibuja el comportamiento. «Encender si oscuro» no basta: entre oscuro y claro hay decisiones que se confirman con el tiempo, y eso se resuelve con cuatro estados:
| Estado | Lámpara | Pasa a… | Cuando… |
|---|---|---|---|
| DÍA | Apagada | CONFIRMANDO NOCHE | La luz estimada baja de 10 lux |
| CONFIRMANDO NOCHE | Apagada | NOCHE DÍA | Pasan 10 s sin que la luz vuelva a subir de 10 lux La luz vuelve a 10 lux o más (era una sombra) |
| NOCHE | Encendida (brillo según la oscuridad) | CONFIRMANDO DÍA | La luz estimada supera 30 lux |
| CONFIRMANDO DÍA | Encendida | DÍA NOCHE | Pasan 10 s sin que la luz baje de 30 lux La luz vuelve a 30 lux o menos (era un destello) |
Los dos estados de confirmación son los que dan robustez: una condición debe sostenerse antes de actuar. Los umbrales 10 y 30 lux forman la banda de histéresis, y el tiempo de confirmación elimina los destellos breves. Se combinan, y cada uno cubre un tipo de perturbación: la histéresis, el ruido pequeño y continuo; la confirmación, los eventos grandes pero cortos.
Una pregunta de diseño central, que el taller deja para el paso 4: ¿qué luz se compara con los umbrales, la que mide el sensor o la del ambiente? Son distintas cuando la lámpara está encendida.
| Tarea | Período | Qué hace |
|---|---|---|
| Muestreo de luz y máquina de estados | 1 s | Lee el LDR, filtra, estima los lux y avanza el estado |
| Transición de brillo | 20 ms | Mueve el brillo hacia el objetivo y actualiza el PWM |
| Botón | cada vuelta del bucle | Detecta clic y pulsación larga |
03 · Paso 2
El cableado
| Elemento | Conexión | Observaciones |
|---|---|---|
| LDR | 5 V → LDR → A0 | Más luz, más tensión (tema 14) |
| Resistencia fija | A0 → 10 kΩ → masa | Su valor fija la zona útil del divisor (10 a 1000 lux) |
| LED | D9 → resistencia de 220 Ω → LED → masa | D9 tiene PWM; para una tira o un LED de potencia se necesita un MOSFET (tema 27) |
| Botón | D2 → botón → masa | Con el pull-up interno (tema 6) |
El montaje físico concentra el principal problema del taller: el LDR no debe «ver» al LED. Conviene separarlos, orientar el LDR hacia la ventana o la habitación y el LED hacia el suelo o la pared opuesta, o aislarlos con una pantalla opaca. Cuanto menos luz propia llegue al sensor, más fácil es el control (paso 4). Y el LDR necesita un pequeño difusor para que no lo engañe un único rayo directo.
04 · Paso 3
Medir la luz con confianza
La cadena de medición es la del tema 14, con un filtro encima. Cada segundo se toma una muestra, se la guarda en una ventana de tres y se usa la mediana para rechazar valores aislados (un destello de una sola muestra no pasa); la mediana pasa por un filtro exponencial que suaviza el resto (tema 5). Luego se estima la luz con la ley de potencia del LDR:
const uint8_t PIN_LDR = A0;
const float R_FIJA = 10000.0; // a masa; el LDR va a Vcc
const float R10 = 15000.0, GAMMA_LDR = 0.6; // parámetros del LDR (hoja de datos)
float ventana[3] = {50, 50, 50}; // últimas tres lecturas, en lux
uint8_t indiceVentana = 0;
float luxFiltrado = 50;
float luxDesdeAdc() {
int c = analogRead(PIN_LDR);
if (c <= 1) return NAN; // LDR desconectado o oscuridad total
if (c >= 1022) return 100000.0; // saturado: mucha luz
float rLdr = R_FIJA * (1023.0 - c) / c;
return 10.0 * pow(R10 / rLdr, 1.0 / GAMMA_LDR);
}
float mediana3(float a, float b, float c) {
return max(min(a, b), min(max(a, b), c));
}
void muestrearLuz() { // se llama una vez por segundo
float lux = luxDesdeAdc();
if (isnan(lux)) lux = 0.1; // sin LDR o a oscuras: se trata como oscuridad
ventana[indiceVentana] = lux;
indiceVentana = (indiceVentana + 1) % 3;
float m = mediana3(ventana[0], ventana[1], ventana[2]);
luxFiltrado += 0.3 * (m - luxFiltrado); // filtro exponencial, alfa = 0,3
}
Dos decisiones merecen atención. El filtro se aplica a los lux (no a las cuentas): como la luz abarca décadas, un promedio lineal está dominado por los valores altos. Y un LDR desconectado (cuentas 0) se trata como oscuridad: en una luz nocturna, encenderse ante la duda es el comportamiento seguro, y coincide con el caso más probable (una habitación oscura). Elegir cómo se comporta el sistema ante una falla de modo que coincida con la acción correcta del caso normal simplifica el diseño.
05 · Paso 4
La lámpara ve su propia luz
Ahora, el problema central. Cuando la lámpara está encendida, parte de su luz llega al LDR, de modo que la luz medida es la del ambiente más el aporte de la lámpara:
donde A es la luz que la lámpara a pleno brillo aporta al sensor, en lux. Es una realimentación positiva de signo problemático: al encender, la luz medida sube; si sube por encima del umbral de apagado, la lámpara se apaga; entonces la luz medida baja de nuevo por debajo del umbral de encendido, y se vuelve a encender. El resultado es una oscilación permanente, aunque la habitación esté oscura y estable. Analicemos cuándo ocurre.
Con la lámpara encendida y el ambiente casi a oscuras (Lambiente ≈ 0), la luz medida es A. Para que no se apague hace falta A menor que el umbral de apagado. Pero con histéresis la condición es más fuerte: un ambiente apenas por debajo del umbral de encendido (10 lux) tiene que seguir dando una luz medida menor que 30 lux con la lámpara encendida:
Si el aporte de la lámpara es mayor que el ancho de la banda de histéresis, el sistema oscila en el entorno del anochecer. Hay cuatro remedios, que se combinan:
| Remedio | Cómo funciona | Límite |
|---|---|---|
| Aislamiento óptico | Pantalla, orientación y distancia que reduzcan A | No siempre es posible en un producto compacto |
| Banda de histéresis mayor | Subir el umbral de apagado hasta que supere A con margen | Se enciende a oscuras pero tarda en apagarse; reduce la sensibilidad |
| Confirmación temporal | Exige que la condición se sostenga varios segundos | Convierte una oscilación rápida en una lenta, pero no la elimina |
| Compensación de la luz propia | Restar el aporte conocido: Lambiente = Lmedida − A × brillo | Exige conocer A (se mide en la instalación) y el brillo real |
La compensación es el remedio de fondo, porque ataca la causa. Se mide A una vez, en la instalación, con el ambiente a oscuras: se enciende la lámpara a pleno y se anota la luz medida. Después, en cada ciclo, se estima el ambiente restando A × brillo, con el brillo realmente aplicado en ese momento. Hay que tener en cuenta el retardo: el sensor mide el efecto de un brillo que ya cambió, por eso conviene usar el brillo del ciclo anterior.
06 · Paso 5
Brillo percibido y PWM
El PWM regula la potencia media del LED, y un LED emite una luz aproximadamente proporcional a su corriente media. Pero el ojo no percibe la luz de forma lineal: es sensible a las relaciones, no a las diferencias (tema 14). Un PWM de 50 % no se ve «la mitad de brillante» que uno de 100 %, sino bastante más intenso; el brillo percibido sigue aproximadamente una potencia de 0,4 de la luz emitida. Si el programa varía el PWM linealmente, la lámpara parece cambiar mucho al principio y casi nada al final.
La solución es la corrección gamma: se define un nivel de brillo percibido entre 0 y 1 y se lo convierte en ciclo de trabajo elevándolo a una potencia (alrededor de 2,2):
| Brillo percibido | Sin corrección (duty) | Con gamma 2,2 (duty) |
|---|---|---|
| 10 % | 26 | 2 |
| 20 % | 51 | 7 |
| 50 % | 128 | 56 |
| 80 % | 204 | 156 |
| 100 % | 255 | 255 |
La tabla muestra un detalle con consecuencias: para un brillo del 10 % bastan 2 niveles de 255. Con un PWM de 8 bits, los brillos bajos tienen muy pocos escalones y se ven a saltos; es el precio de la corrección gamma. Por eso las lámparas de buena calidad usan 10 a 16 bits de resolución o dithering (alternar valores vecinos) en los niveles bajos. En una luz nocturna, que opera sobre todo a niveles bajos, conviene un PWM de mayor resolución (el Timer1 del ATmega328P admite hasta 16 bits). Otra cuestión: la frecuencia del PWM debe ser lo bastante alta (cientos de hertz como mínimo; mejor kilohertz) para que no se perciba el parpadeo ni interfiera con cámaras.
07 · Paso 6
Transiciones suaves
Una luz que se enciende de golpe a plena potencia en la oscuridad molesta, y una que se apaga bruscamente desconcierta. La solución es un limitador de velocidad (slew-rate limiter, tema 5): el brillo real persigue al brillo objetivo avanzando un paso máximo por unidad de tiempo.
const float PASO_FADE = 0.01; // 1 % cada 20 ms: de 0 a 100 % en 2 s
float brillo = 0.0; // brillo percibido actual (0 a 1)
float objetivo = 0.0; // al que se quiere llegar
uint8_t dutyConGamma(float b) {
return (uint8_t)(255.0 * pow(b, 2.2) + 0.5);
}
void actualizarBrillo() { // se llama cada 20 ms
float diferencia = objetivo - brillo;
if (diferencia > PASO_FADE) brillo += PASO_FADE;
else if (diferencia < -PASO_FADE) brillo -= PASO_FADE;
else brillo = objetivo;
analogWrite(PIN_LED, dutyConGamma(brillo));
}
Una consecuencia sutil: el brillo que mide el sensor (y que se usa en la compensación) es el brillo actual, no el objetivo. Por eso el programa guarda ambos: el objetivo decide, el actual se aplica y se descuenta.
08 · Paso 7
El programa completo
Con las piezas anteriores, el programa es la máquina de estados del paso 1 y dos tareas periódicas. El estado se actualiza una vez por segundo con la luz del ambiente (medida menos el aporte propio), y el brillo se mueve cada 20 ms:
const float U_ENCENDER = 10.0; // lux: por debajo, oscuro
const float U_APAGAR = 30.0; // lux: por encima, claro
const uint32_t T_CONFIRMAR = 10000; // ms que debe sostenerse la condición
const float A_LAMPARA = 15.0; // lux que aporta la lámpara a pleno (medido en la instalación)
enum Estado { DIA, CONFIRMANDO_NOCHE, NOCHE, CONFIRMANDO_DIA };
Estado estado = DIA;
uint32_t tEstado = 0;
bool forzado = false; // modo manual (luz de lectura)
float luzAmbiente() {
return max(0.0, luxFiltrado - A_LAMPARA * brillo); // se resta lo que aporta la propia lámpara
}
float brilloSegunOscuridad(float lux) { // 20 % en penumbra hasta 100 % a oscuras
float t = constrain((U_ENCENDER - lux) / (U_ENCENDER - 0.5), 0.0, 1.0);
return 0.2 + 0.8 * t;
}
void avanzarEstado(uint32_t ahora) {
float lux = luzAmbiente();
switch (estado) {
case DIA:
if (lux < U_ENCENDER) { estado = CONFIRMANDO_NOCHE; tEstado = ahora; }
break;
case CONFIRMANDO_NOCHE:
if (lux >= U_ENCENDER) estado = DIA; // era una sombra
else if (ahora - tEstado >= T_CONFIRMAR) estado = NOCHE;
break;
case NOCHE:
if (lux > U_APAGAR) { estado = CONFIRMANDO_DIA; tEstado = ahora; }
break;
case CONFIRMANDO_DIA:
if (lux <= U_APAGAR) estado = NOCHE; // era un destello
else if (ahora - tEstado >= T_CONFIRMAR) estado = DIA;
break;
}
bool encendida = (estado == NOCHE || estado == CONFIRMANDO_DIA);
objetivo = forzado ? 0.6 : (encendida ? brilloSegunOscuridad(lux) : 0.0);
}
uint32_t tMuestra = 0, tFade = 0;
void loop() {
uint32_t ahora = millis();
if (ahora - tMuestra >= 1000) { // tarea de 1 s
tMuestra = ahora;
muestrearLuz();
avanzarEstado(ahora);
}
if (ahora - tFade >= 20) { // tarea de 20 ms
tFade = ahora;
actualizarBrillo();
}
uint8_t ev = actualizar(boton); // detector de eventos del tema 8
if (ev & EV_CLICK) forzado = !forzado; // un clic alterna la luz de lectura
if (ev & EV_LARGA) forzado = false; // pulsación larga: vuelve al automático
}
Fijate en las propiedades que se heredan del resto del curso: nada bloquea (todo son comparaciones con millis()), cada tarea tiene su período, el estado de confirmación usa restas sin signo, y el botón es el detector reutilizable del tema 8. La lógica de decisión está separada del PWM y del sensor, lo que permite probar la máquina de estados con luces simuladas, como hace el simulador de este tema.
09 · Paso 8
Verificar
| Prueba | Cómo se hace | Resultado esperado |
|---|---|---|
| Anochecer | Bajar lentamente la luz (con una cartulina o un regulador) | Tras 10 s por debajo de 10 lux, se enciende con una subida suave |
| Amanecer | Subir la luz más allá de 30 lux | Tras 10 s, se apaga con una bajada suave |
| Sombra pasajera | Tapar el LDR 3 s con la mano durante el día | No se enciende (la confirmación se cancela) |
| Destello nocturno | Iluminar el LDR con una linterna 3 s por la noche | No se apaga |
| Realimentación | Encender con el ambiente oscuro y estable, 10 minutos | Se mantiene encendida sin parpadear |
| Medir A | A oscuras, lámpara a pleno, anotar la luz medida; repetir al 50 % | La luz medida es proporcional al brillo; A se carga en el programa |
| Banda de histéresis | Dejar la luz entre 10 y 30 lux | Mantiene el estado en que estaba, encendida o apagada |
| Botón | Clic, otro clic y pulsación larga | Enciende al 60 %, apaga (auto) y vuelve al automático |
| LDR desconectado | Desenchufarlo | Se trata como oscuridad: se enciende (acción segura) |
| Brillos bajos | Mirar la lámpara al 5–10 % en la oscuridad | Sin saltos visibles; si los hay, aumentar la resolución del PWM |
La prueba más reveladora es la de la realimentación: con el sistema sin compensar, una lámpara que aporta más luz que el ancho de la banda oscila durante la noche, y el fallo se ve a simple vista, con una luz que parpadea sin cesar, aunque nada cambie en la habitación.
10 · Paso 9
Fallas y robustez
- LDR desconectado o en corto. Desconectado: cuentas cercanas a 0, tratado como oscuridad (la lámpara se enciende). En corto con Vcc: cuentas de 1023, tratado como mucha luz (la lámpara se apaga, con un día aparente permanente). Es la falla menos segura, y se detecta con un control de coherencia: una «mucha luz» que no varía nunca en 24 horas es sospechosa.
- Envejecimiento del LED. Con el tiempo emite menos, y A cambia. Conviene revisar el valor de A en cada mantenimiento, o medirlo automáticamente en la primera noche tras la instalación.
- Suciedad en el sensor. Un LDR cubierto de polvo ve menos luz y mantiene la lámpara encendida de día. Un difusor limpiable y una revisión periódica lo evitan.
- Luz parásita nocturna. Farolas, pantallas y LED de otros equipos elevan la luz «nocturna» por encima de 10 lux. Los umbrales se ajustan en la instalación, no en el escritorio.
- Desborde de
millis(). Todas las comparaciones de tiempo usan restas sin signo y toleran los 49 días. - Corte de energía. Al volver, el programa arranca en DÍA con los filtros en un valor neutro; en la primera noche, tarda un ciclo de confirmación en reaccionar. Un valor inicial de los filtros cercano al umbral evita que la lámpara se encienda sin motivo al arrancar de día.
11 · Analogía con programación
Simular un día completo con cuatro estrategias
La máquina de estados no necesita hardware para probarse. Simulamos 24 horas, con una muestra por minuto: una curva de luz diaria con ruido del 20 %, tres destellos nocturnos de 150 lux (faros de autos) y una lámpara que aporta 15, 25 o 40 lux al sensor cuando está encendida. Comparamos cuántas veces cambia de estado cada estrategia. El ideal es 3: un encendido al comenzar la simulación (de noche), un apagado al amanecer y un encendido al anochecer.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
import math
def azar(semilla):
estado = semilla
while True:
estado = (1103515245 * estado + 12345) % 2**31
yield estado / 2**31
def luz_del_dia():
"""Luz ambiente cada minuto: día de 6 a 18 h, noche de 0,3 lux, ruido de ±10 % y tres destellos."""
g = azar(21)
luz = []
for minuto in range(1440):
hora = minuto / 60
base = 300 * math.sin(math.pi * (hora - 6) / 12) + 0.3 if 6 <= hora < 18 else 0.3
luz.append(base * (1 + 0.2 * (next(g) - 0.5)))
for minuto in (60, 200, 1380): # faros de autos: 150 lux durante un minuto
luz[minuto] += 150
return luz
def simular(luz, aporte, estrategia, confirmar=5):
"""Cuenta los cambios de estado de la lámpara.
A: un solo umbral de 20 lux. B: histéresis 10/30.
C: histéresis + confirmación. D: C + compensación de la luz propia."""
encendida, cambios, cuenta = False, 0, 0
for ambiente in luz:
medida = ambiente + (aporte if encendida else 0) # el sensor ve la lámpara
estimada = max(0, medida - (aporte if encendida else 0)) if estrategia == "D" else medida
if estrategia == "A":
deseada = estimada < 20
elif not encendida and estimada < 10:
deseada = True
elif encendida and estimada > 30:
deseada = False
else:
deseada = encendida
if estrategia in "AB": # sin confirmación: cambia al instante
if deseada != encendida:
encendida, cambios = deseada, cambios + 1
elif deseada != encendida: # con confirmación: debe sostenerse
cuenta += 1
if cuenta >= confirmar:
encendida, cambios, cuenta = deseada, cambios + 1, 0
else:
cuenta = 0
return cambios
luz = luz_del_dia()
print("aporte de la lámpara A B C D")
for aporte in (15, 25, 40):
resultados = [simular(luz, aporte, e) for e in "ABCD"]
print(f"{aporte:10d} lux " + " ".join(f"{r:5d}" for r in resultados))
Resultado: con un aporte de 15 lux (menor que la banda de 20 lux), el umbral único (A) cambia 33 veces y la histéresis sola (B) 9 veces, por los tres destellos; con confirmación (C) y con compensación (D), 3 veces, el ideal. Con 25 lux, mayor que la banda, el umbral único cambia 751 veces (oscila toda la noche), B 17 y C y D, 3. Con 40 lux, incluso la confirmación (C) sufre 147 cambios y B 735: solo la compensación (D) mantiene los 3 cambios ideales. Probá cambiar confirmar a 15 y el aporte a 40 para ver cómo se comporta C.
La tabla resume el taller: cada técnica resuelve un tipo de problema y solo la compensación ataca la realimentación de fondo. El modelo ignora la dinámica del brillo (el fade) y supone que la lámpara aporta su luz de inmediato.
12 · Exploración interactiva
El simulador de la luz nocturna
El simulador ejecuta el control con pasos de un segundo. Fijá la luz del ambiente (en escala logarítmica) y el aporte de la lámpara al sensor, elegí una de las cuatro estrategias y mirá el gráfico de los últimos 120 segundos: la luz que mide el sensor y el brillo de la lámpara. Podés inyectar un destello de faros, forzar la luz con los botones y dejar que corra solo. Para aislar el efecto de la realimentación, el simulador usa un brillo fijo (el que elijas) en lugar del brillo adaptativo del programa completo, y las estrategias 1 a 3 no compensan la luz propia.
Luz nocturna: control, sensor y lámpara
Elegí una estrategia y presioná «Un segundo» o «Ejecutar solo».
En el gráfico, la línea tenue es la luz medida por el sensor (escala logarítmica de 0,1 a 1000 lux), las líneas punteadas son los umbrales de 10 y 30 lux y la línea gruesa es el brillo de la lámpara. Una lámpara que oscila se ve como un diente de sierra.
Recorrido guiado
- Con 1 lux de ambiente, aporte de 25 lux, brillo del 100 % y la estrategia 3 (histéresis y confirmación), presioná «Ejecutar solo». Pasados unos 10 s se enciende de forma suave. La luz medida sube a unos 26 lux (1 del ambiente más 25 de la lámpara), que sigue por debajo de 30 lux: la lámpara se mantiene encendida.
- Cambiá a la estrategia 1 (umbral único de 20 lux): con la lámpara encendida, la luz medida (26 lux) supera 20 lux, se apaga, vuelve a 1 lux, se enciende… oscila. Mirá el contador de cambios y el diente de sierra del brillo.
- Pasá a la estrategia 2 (histéresis 10/30): con 1 lux de ambiente es estable. Subí el ambiente a unos 7 lux: encendida, la luz medida es 7 + 25 = 32 lux, mayor que 30; se apaga, y entonces 7 lux es menor que 10 y se enciende otra vez. Oscila en la zona de 5 a 10 lux. Bajá el aporte a 15 lux: 7 + 15 = 22 lux, y se estabiliza. El aporte debe ser menor que el ancho de la banda.
- Con aporte de 40 lux, ambiente de 1 lux y la estrategia 3, la lámpara oscila lentamente (encendida: 41 lux, tras 10 s se apaga; apagada: 1 lux, tras 10 s se enciende). Pasá a la estrategia 4 (compensación): la luz estimada del ambiente vuelve a 1 lux y la lámpara queda quieta. Solo la compensación resuelve la realimentación de fondo.
- Con la estrategia 4 y la lámpara encendida, inyectá un destello de faros (5 s): no pasa nada, porque el destello dura menos de 10 s. Con la estrategia 2, la lámpara se apaga de inmediato y se vuelve a encender al terminar el destello.
- Con la estrategia 3 y aporte 0, dejá la lámpara encendida y subí el ambiente a 20 lux: sigue encendida (está dentro de la banda). Subilo a 50 lux y esperá 10 s: se apaga. Volvé a bajarlo a 20 lux: sigue apagada hasta que baje de 10 lux.
- Con la estrategia 1, aporte 25 y ambiente de 1 lux, bajá el brillo al 40 %: la lámpara aporta 10 lux, la luz medida es 11 lux, menor que 20, y deja de oscilar. Atenuar la lámpara también reduce la realimentación.
- Probá «Forzar encendida»: la lámpara pasa al 60 % sin tener en cuenta el ambiente. «Automático» devuelve el control.
Desafío: el aporte de la lámpara
Con la estrategia 4 el control compensa exactamente el aporte que le indica el parámetro, que en el simulador coincide con la realidad. En una instalación real, el valor de A se mide y puede equivocarse. Pensá qué ocurriría si el programa supone 15 lux pero la lámpara aporta 25: la luz del ambiente estimada queda 10 lux por encima de la real cuando la lámpara está a pleno. ¿La lámpara oscilaría? Probablemente no, porque 10 lux de error caben dentro de la banda de 20 lux; pero el margen de seguridad se reduce a la mitad. La compensación no tiene que ser perfecta, solo mucho mejor que nada.
13 · Método de trabajo
Lo aprendido, para cualquier control con un sensor afectado por el actuador
- Escribí el comportamiento como una máquina de estados, con estados de confirmación donde una decisión deba sostenerse.
- Combiná histéresis y confirmación temporal: la primera contra el ruido continuo; la segunda, contra los eventos cortos.
- Preguntate qué ve realmente el sensor. Si el actuador lo perturba, calculá el aporte A y compará con el ancho de la banda.
- Atacá la realimentación en este orden: aislar físicamente, ensanchar la banda, confirmar en el tiempo y, por último, restar el efecto conocido.
- Mide A en la instalación y dejalo como parámetro ajustable, no como una constante fija.
- Pensá en la percepción: corrección gamma y resolución suficiente para brillos bajos.
- Suavizá las transiciones con un limitador de velocidad y guardá el valor aplicado, que es el que influye en el sensor.
- Decidí la acción ante una falla de modo que coincida con la acción correcta del caso más probable.
El mismo esquema se repite en el resto del curso, sobre todo en los controles de lazo cerrado (tema 42): un sensor, una decisión con banda y confirmación, un actuador suave y un análisis honesto de cómo el actuador afecta al sensor.
14 · Comprensión
Confusiones frecuentes
«Con histéresis alcanza para que no parpadee»
Solo si el aporte de la lámpara al sensor es menor que el ancho de la banda. Si es mayor, la histéresis no impide la oscilación.
«Para atenuar, basta con variar el PWM linealmente»
El ojo percibe la luz de forma aproximadamente logarítmica. Sin corrección gamma, los cambios se perciben desiguales: grandes en los brillos bajos y casi nulos en los altos.
«La confirmación temporal elimina la oscilación por realimentación»
La hace más lenta, no la elimina: la lámpara sigue encendiéndose y apagándose, solo que con un período de varios segundos.
«Compensar es restar siempre el brillo máximo»
Hay que restar el aporte correspondiente al brillo real en ese momento (A × brillo), porque la lámpara está a media potencia durante las transiciones y en penumbra.
«Un PWM de 8 bits alcanza para todo»
Con corrección gamma, un brillo del 10 % usa solo 2 niveles de 255. En los brillos bajos, que son los de una luz nocturna, se ven escalones: conviene mayor resolución.
«Si falla el sensor, lo mejor es apagar todo»
Depende del uso. En una luz nocturna, encenderse ante la duda es lo seguro. El comportamiento ante fallas debe pensarse para cada aplicación.
15 · Práctica guiada
Predecí antes de comprobar
1. Condición de estabilidad
Los umbrales son 10 y 30 lux. ¿Cuál es el aporte máximo de la lámpara al sensor para que la histéresis, sola, evite la oscilación? ¿Qué pasa con 22 lux?
Ver solución
El aporte debe ser menor que el ancho de la banda: 30 − 10 = 20 lux. Con 22 lux, cuando el ambiente baja a unos 9 lux y la lámpara se enciende, la luz medida es 9 + 22 = 31 lux, mayor que 30: se apaga; entonces mide 9 lux y vuelve a encender. Oscila mientras el ambiente esté entre 8 y 10 lux.
2. Compensación
El sensor mide 34 lux con la lámpara a un brillo de 0,6, y el aporte a pleno es 25 lux. ¿Cuánta luz de ambiente hay? ¿Y si la lámpara estuviera apagada y midiera 34?
Ver solución
Aporte actual = 25 × 0,6 = 15 lux; ambiente = 34 − 15 = 19 lux. Con la lámpara apagada no hay nada que restar: el ambiente es 34 lux, ya por encima del umbral de apagado.
3. Gamma
Se quiere un brillo percibido del 30 % con γ = 2,2 y un PWM de 8 bits. ¿Qué duty se aplica? ¿Y sin corrección?
Ver solución
Con gamma: 255 × 0,32,2 = 255 × 0,0707 ≈ 18. Sin corrección: 255 × 0,3 = 77. Sin la corrección, la lámpara luciría mucho más brillante que el 30 % deseado.
4. Duración del fade
El brillo avanza 0,01 cada 20 ms. ¿Cuánto tarda en ir de 0 a 1? ¿Y si se quisiera una transición de 4 segundos con la misma tarea de 20 ms?
Ver solución
De 0 a 1 son 100 pasos × 20 ms = 2 s. Para 4 s hacen falta 200 pasos de 20 ms, es decir, un paso de 0,005.
5. Confirmación frente a destello
La lámpara está encendida (estado NOCHE) y pasa un auto cuyos faros dan 150 lux durante 6 s. El tiempo de confirmación es 10 s. ¿Qué hace el control? ¿Y si los faros duraran 12 s?
Ver solución
Con 6 s: la luz sube a más de 30 lux, el estado pasa a CONFIRMANDO DÍA, y cuando los faros se van (antes de los 10 s) la luz vuelve a 30 lux o menos y regresa a NOCHE: la lámpara no se apaga. Con 12 s: se cumple la confirmación y pasa a DÍA, se apaga; al terminar el destello, la luz baja de 10 lux, se confirma otros 10 s y se vuelve a encender. Un destello mayor que el tiempo de confirmación sí se atiende.
6. Falla del sensor
El cable del LDR se corta y las cuentas quedan en 0. ¿Qué hace la luz nocturna? ¿Y si el LDR se pone en cortocircuito con Vcc?
Ver solución
Cuentas 0: se interpreta como oscuridad y la lámpara se enciende (el fallo seguro). En cortocircuito: 1023 cuentas, «mucha luz», la lámpara se apaga permanentemente, una falla peligrosa por silenciosa. Se detecta con un control de coherencia: una luz que no varía nunca en 24 h es sospechosa.
16 · Síntesis
Ideas para recordar
- Un control automático de luz es una máquina de estados con estados de confirmación: una decisión debe sostenerse antes de actuar.
- La histéresis (10 y 30 lux) combate el ruido continuo; la confirmación temporal, los eventos cortos como sombras y destellos.
- La luz medida es la del ambiente más A × brillo: la lámpara perturba a su propio sensor.
- La histéresis sola evita la oscilación solo si A es menor que el ancho de la banda (aquí 20 lux).
- Los remedios son aislar, ensanchar la banda, confirmar y, sobre todo, restar el efecto conocido (compensación).
- El brillo percibido es aproximadamente logarítmico: el PWM debe llevar una corrección gamma (duty = brillo2,2).
- Con la corrección gamma los brillos bajos usan pocos niveles; conviene un PWM de mayor resolución.
- Las transiciones se suavizan con un limitador de velocidad; la compensación usa el brillo realmente aplicado.
- El comportamiento ante fallas debe coincidir con la acción segura del caso más probable.
¿Listo para el bloque 5?
Antes de avanzar, calculá el aporte máximo de la lámpara que tolera una banda de 10 a 25 lux, explicá por qué un PWM lineal da un brillo mal percibido y describí qué hace la compensación con A × brillo. Si podés justificar las tres respuestas, ya sabés diseñar un control con realimentación óptica.
En el próximo bloque pasamos a la distancia y el movimiento, empezando por los sensores ultrasónicos (HC-SR04): tiempo de vuelo, velocidad del sonido y zonas ciegas.