/*
*
=========================================================
*
COLEGIO GIMNASIO BILINGÜE MARIE CURIE
*
Proyecto de Tecnología y Automatización
*
Sistema: Control Automático de Luz (LED + LDR)
*
Desarrollado por: Centro Prisma
*
Versión: 1.0 (Control proporcional con Kp + unidades físicas)
*
Fecha: 19 de Febrero de 2026
*
=========================================================
*/
#include <Arduino.h>
/*
=========================================================
CONTROL AUTOMÁTICO DE LUZ CON LAZO CERRADO
(LED + LDR)
=========================================================
¿QUÉ HACE ESTE PROYECTO?
Este sistema usa:
- 1 sensor de luz (LDR) -> mide la luz como un número (ADC)
- 1 LED (actuador) -> cambia su brillo con PWM
- Control proporcional (Kp)
El sistema hace esto todo el tiempo:
1) Mide la luz (ENTRADA).
2) La compara con una meta llamada
SETPOINT.
3) Calcula la diferencia (ERROR).
4) Decide el brillo del LED con una regla
proporcional: PWM = Kp × ERROR.
5) Enciende el LED con ese brillo
(SALIDA).
6) Vuelve a medir y repite.
Eso es un sistema de LAZO CERRADO:
mide
→ piensa → actúa → vuelve a medir.
=========================================================
*/
// ================= SENSOR DE LUZ (LDR)
=================
//
// El LDR cambia su resistencia según la
luz.
// El ESP32 convierte esa señal en un
número digital con su ADC.
//
// ADC del ESP32 (típico): 12 bits
// Esto significa que puede leer:
//
// 0
-> 0.0V (voltaje mínimo)
// 4095 -> 3.3V (voltaje máximo)
//
// OJO: Este número NO son lúmenes reales.
// Es una lectura digital del sensor.
//
// IMPORTANTE EN ESTE MONTAJE:
//
// Más luz sobre el LDR -> el número BAJA
// Menos luz (si lo tapas)-> el número
SUBE
const int PIN_LDR = 34;
// ================= SALIDA (LED)
=================
//
// Este LED será el “músculo” del sistema.
// Su brillo se controla con PWM (0 a
255).
const int PIN_LED = 12;
// ================= CONFIGURACIÓN DEL PWM
=================
//
// PWM = Modulación por Ancho de Pulso
//
// El ESP32 no cambia el voltaje del LED.
// En cambio, prende y apaga el pin muy
rápido.
//
// FRECUENCIA_PWM = 5000 Hz
// Esto significa que la señal se repite
// 5000 veces por segundo.
//
// RESOLUCION_PWM = 8 bits
// Esto significa 256 niveles de potencia:
//
// 0
-> 0% (apagado)
// 255 -> 100% (máximo brillo)
const int CANAL_PWM = 0;
const int FRECUENCIA_PWM = 5000;
const int RESOLUCION_PWM = 8;
// ================= AJUSTES DEL SISTEMA
(CONTROL) =================
//
// SETPOINT es la meta de luz que queremos
mantener.
// Es un número del ADC (0 a 4095), no
lúmenes reales.
const int SETPOINT = 2000;
// Kp es la “fuerza” del control
proporcional.
// Entre más grande sea Kp, más fuerte
reacciona el LED.
const float KP = 0.12;
// ================= TIEMPO ENTRE CICLOS
=================
//
// delay(100) = 100 milisegundos = 0.1
segundos
// Eso significa que el sistema repite el
lazo:
// 10 veces por segundo (aprox.)
const int TIEMPO_ENTRE_CICLOS_MS = 300;
// ================= VARIABLES (DATOS QUE
CAMBIAN) =================
//
// Estas variables guardan lo que el
sistema mide y calcula
// en cada vuelta del lazo cerrado.
int luzActual = 0; // Lectura ADC (0 a 4095)
int errorLuz = 0; // Diferencia entre lectura y meta
int pwmCalculado = 0; // Salida PWM (0 a 255)
//
=================================================
// FUNCIONES DEL SISTEMA
//
=================================================
//
-------------------------------------------------
// Función: medirLuz()
//
-------------------------------------------------
// Lee el valor del LDR con analogRead().
//
// Resultado:
// - Guarda el valor en
"luzActual"
//
// Unidad real:
// - Es un número ADC (0 a 4095)
// - 0 equivale a 0.0V y 4095 equivale a
3.3V (aprox.)
//
-------------------------------------------------
void medirLuz() {
luzActual = analogRead(PIN_LDR);
}
//
-------------------------------------------------
// Función: calcularError()
//
-------------------------------------------------
// Calcula qué tan lejos estamos de la
meta.
//
// Fórmula:
// ERROR = Luz actual - SETPOINT
//
// Ejemplos:
//
// Si ERROR = 0
// -> estamos exactamente en la meta.
//
// Si ERROR es grande
// -> estamos lejos de la meta.
//
// Si ERROR es negativo
// -> significa que ya hay suficiente
luz.
// -> lo convertimos en 0 porque el LED
no puede
//
tener “potencia negativa”.
//
-------------------------------------------------
void calcularError() {
errorLuz = luzActual - SETPOINT;
if
(errorLuz < 0) {
errorLuz = 0;
}
}
//
-------------------------------------------------
// Función: calcularBrillo()
//
-------------------------------------------------
// Decide qué brillo debe tener el LED.
//
// Regla proporcional:
// PWM = Kp × ERROR
//
// - ERROR pequeño -> PWM pequeño ->
LED suave.
// - ERROR grande -> PWM grande -> LED fuerte.
//
// Regla de seguridad:
// PWM máximo con 8 bits = 255.
// Si el cálculo supera 255, lo dejamos en
255.
//
-------------------------------------------------
void calcularBrillo() {
pwmCalculado = (int)(KP * errorLuz);
if
(pwmCalculado > 255) {
pwmCalculado = 255;
}
}
//
-------------------------------------------------
// Función: aplicarSalida()
//
-------------------------------------------------
// Envía el PWM al LED.
//
// Unidad real:
// - PWM 0 -> 0%
de potencia (apagado)
// - PWM 255 -> 100% de potencia
(máximo brillo)
//
-------------------------------------------------
void aplicarSalida() {
ledcWrite(CANAL_PWM, pwmCalculado);
}
//
-------------------------------------------------
// Función: mostrarDatos()
// -------------------------------------------------
// Muestra lo que está pasando en el lazo
cerrado.
//
// Mostramos unidades físicas y rangos:
//
// - ADC (0 a 4095) y su voltaje
aproximado (0.0V a 3.3V)
// - PWM (0 a 255) y su porcentaje
aproximado (0% a 100%)
// - Tiempo del ciclo en milisegundos (ms)
//
-------------------------------------------------
void mostrarDatos() {
//
Convertimos la lectura ADC a voltaje aproximado (0.0V a 3.3V)
//
Fórmula: voltaje = (ADC / 4095) * 3.3
float voltajeAprox = ( (float)luzActual / 4095.0 ) * 3.3;
//
Convertimos PWM a porcentaje aproximado
//
Fórmula: porcentaje = (PWM / 255) * 100
float porcentajePWM = ( (float)pwmCalculado / 255.0 ) * 100.0;
Serial.println("=================================");
Serial.println(" LAZO
CERRADO DE LUZ (LED+LDR) ");
Serial.println("=================================");
Serial.print("Luz medida (ADC 0-4095) Lumenes: ");
Serial.print(4095 - luzActual);
Serial.print("Meta (SETPOINT) (ADC): ");
Serial.println(SETPOINT);
Serial.print("Error (ADC): ");
Serial.println(errorLuz);
Serial.print("Salida LED (PWM 0-255): ");
Serial.print(pwmCalculado);
}
// =================================================
// INICIO DEL PROGRAMA
//
=================================================
void setup() {
//
Comunicación con el computador para ver mensajes.
Serial.begin(115200);
//
Configuramos el PWM:
ledcSetup(CANAL_PWM, FRECUENCIA_PWM, RESOLUCION_PWM);
//
Conectamos el canal PWM al pin del LED.
ledcAttachPin(PIN_LED, CANAL_PWM);
//
Iniciamos el LED apagado.
ledcWrite(CANAL_PWM, 0);
Serial.println("CONTROL DE LUZ INICIADO");
}
//
=================================================
// CICLO PRINCIPAL (LAZO CERRADO)
//
=================================================
void loop() {
/*
==========================================================
================== CICLO DE LAZO CERRADO =================
==========================================================
1)
MEDIR -> medirLuz()
2)
COMPARAR -> calcularError()
3)
DECIDIR -> calcularBrillo() (PWM = Kp × ERROR)
4)
ACTUAR -> aplicarSalida()
5)
OBSERVAR -> mostrarDatos()
6)
ESPERAR -> delay(ms)
7)
REPETIR -> vuelve al paso 1
automáticamente
Este ciclo se repite muchas veces.
Eso es el LAZO CERRADO.
==========================================================
*/
//
1️⃣ MEDIR (ENTRADA)
medirLuz();
//
2️⃣ COMPARAR (ERROR)
calcularError();
//
3️⃣ DECIDIR (CONTROL PROPORCIONAL)
calcularBrillo();
//
4️⃣ ACTUAR (SALIDA)
aplicarSalida();
//
5️⃣ OBSERVAR (RETROALIMENTACIÓN)
mostrarDatos();
//
6️⃣ ESPERAR Y REPETIR
delay(TIEMPO_ENTRE_CICLOS_MS);
}
No hay comentarios:
Publicar un comentario