//#include <Wire.h> // Librería para comunicación I2C (para RTC)
#include <RTClib.h> // Librería para el DS1307 RTC
#include <LedControl.h> // Librería para el MAX7219
// =====================================================================
// Definiciones de Pines para ESP32-C3-DevKitM-1 (y para tu ESP32-C3-MINI-1 físico)
// =====================================================================
// MAX7219 (Conectado a pines SPI)
#define MAX7219_DIN_PIN 6 // GPIO6 (DIN / MOSI)
#define MAX7219_CS_PIN 5 // GPIO5 (CS / Chip Select)
#define MAX7219_CLK_PIN 4 // GPIO4 (CLK / Clock)
const int numDisplays = 1; // Un módulo de 4 dígitos MAX7219 se considera 1 display para LedControl
// RTC DS1307 (Conectado a pines I2C)
// SDA: GPIO2 (Se inicializa en Wire.begin(SDA, SCL))
// SCL: GPIO3 (Se inicializa en Wire.begin(SDA, SCL))
// Botón para Temporizador/Encendido (Conectado a GPIO)
#define BUTTON_PIN 1 // GPIO1
#define BUTTON_DEBOUNCE 50 // Milisegundos para ignorar rebotes del botón
// Zumbador (Conectado a GPIO)
#define BUZZER_PIN 0 // GPIO0
// Fotorresistor (LDR) y LEDs de Sigilo
#define LDR_PIN 8 // GPIO8 (Pin con capacidad ADC para leer el LDR)
#define LED_GREEN_PIN 7 // GPIO7
#define LED_YELLOW_PIN 9 // GPIO9
#define LED_RED_PIN 10 // GPIO10
// =====================================================================
// Constantes y Variables Globales
// =====================================================================
// Objeto RTC
RTC_DS1307 rtc;
// Objeto LedControl para el MAX7219
// Parámetros: (PIN_DIN, PIN_CLK, PIN_CS, número de displays en cadena)
LedControl lc = LedControl(MAX7219_DIN_PIN, MAX7219_CLK_PIN, MAX7219_CS_PIN, numDisplays);
// Variables del temporizador de filtro
unsigned long filterTimerDuration = 5 * 60 * 1000; // Duración del temporizador: 5 minutos en milisegundos
unsigned long filterTimerStartTime = 0; // Momento en que el temporizador inició
bool filterTimerActive = false; // Estado: ¿Está el temporizador corriendo?
bool alarmSounding = false; // Estado: ¿Está sonando la alarma del zumbador?
// Variables para el modo de bajo consumo y visualización
unsigned long lastActivityTime = 0; // Último momento en que hubo actividad (botón presionado)
const unsigned long DISPLAY_TIMEOUT = 10 * 1000; // Tiempo de inactividad para apagar el display: 10 segundos
bool displayOn = true; // Estado actual del display (encendido/apagado)
// Variables para el botón (manejo de rebotes o "debounce")
unsigned long lastButtonPressTime = 0;
// =====================================================================
// Funciones de Ayuda
// =====================================================================
// Función para mostrar la hora en el display MAX7219
// Muestra HH:MM en los 4 dígitos
void displayTime(int hour, int minute, int second) {
// Configura los dígitos individuales en el display
// lc.setDigit(número_del_display, posición_del_dígito, valor, punto_decimal)
// Posiciones: 0 (más a la derecha), 1, 2, 3 (más a la izquierda)
// Decena de hora (ej. si hora es 12, muestra 1)
lc.setDigit(0, 3, hour / 10, false);
// Unidad de hora (ej. si hora es 12, muestra 2) y activa el punto decimal para separar HH.MM
lc.setDigit(0, 2, hour % 10, true);
// Decena de minuto (ej. si minuto es 34, muestra 3)
lc.setDigit(0, 1, minute / 10, false);
// Unidad de minuto (ej. si minuto es 34, muestra 4)
lc.setDigit(0, 0, minute % 10, false);
// Nota: No se muestran los segundos directamente en los 4 dígitos para mantener HH:MM
}
// Función para mostrar el temporizador en el display MAX7219
// Muestra MM:SS en los 4 dígitos
void displayTimer(long remainingMillis) {
int totalSeconds = remainingMillis / 1000;
int minutes = totalSeconds / 60;
int seconds = totalSeconds % 60; // Segundos restantes después de calcular los minutos
// Muestra minutos y segundos en el formato MM.SS
lc.setDigit(0, 3, minutes / 10, false);
lc.setDigit(0, 2, minutes % 10, true); // Punto decimal para separar MM.SS
lc.setDigit(0, 1, seconds / 10, false);
lc.setDigit(0, 0, seconds % 10, false);
}
// Función para controlar los LEDs de sigilo según la luz ambiental
void updateStealthLEDs() {
int lightValue = analogRead(LDR_PIN); // Leer el valor analógico del LDR (rango 0-4095 en ESP32)
// Umbrales de calibración para los niveles de luz
// Estos valores pueden necesitar ajuste en tu entorno físico o en Wokwi.
// Valores bajos del LDR_PIN = oscuridad (resistencia alta del LDR)
// Valores altos del LDR_PIN = mucha luz (resistencia baja del LDR)
const int DARK_THRESHOLD = 500; // Por debajo de este valor, es considerado muy oscuro (VERDE)
const int MEDIUM_THRESHOLD = 2000; // Por debajo de este valor y por encima de DARK_THRESHOLD, es luz media (AMARILLO)
// Por encima de MEDIUM_THRESHOLD, es mucha luz (ROJO)
// Apagar todos los LEDs primero para evitar estados mixtos
digitalWrite(LED_GREEN_PIN, LOW);
digitalWrite(LED_YELLOW_PIN, LOW);
digitalWrite(LED_RED_PIN, LOW);
if (lightValue < DARK_THRESHOLD) {
// Entorno muy oscuro: Alto sigilo
digitalWrite(LED_GREEN_PIN, HIGH);
Serial.println("Sigilo: Verde (Oscuro)");
} else if (lightValue < MEDIUM_THRESHOLD) {
// Entorno con luz media: Sigilo medio
digitalWrite(LED_YELLOW_PIN, HIGH);
Serial.println("Sigilo: Amarillo (Luz Media)");
} else {
// Entorno bien iluminado: Bajo sigilo
digitalWrite(LED_RED_PIN, HIGH);
Serial.println("Sigilo: Rojo (Mucha Luz)");
}
}
// =====================================================================
// Configuración Inicial (setup)
// Se ejecuta una sola vez al inicio del programa.
// =====================================================================
void setup() {
Serial.begin(115200); // Inicia comunicación serial para depuración (ver mensajes en Monitor Serial de Wokwi)
Serial.println("Iniciando Reloj de Metro...");
// --- Configuración del Display MAX7219 ---
lc.shutdown(0, false); // Sacar el display del modo de apagado (enciéndelo)
lc.setIntensity(0, 8); // Establece el brillo del display (0 = más tenue, 15 = más brillante)
lc.clearDisplay(0); // Limpia todos los dígitos del display
// --- Configuración del Módulo RTC DS1307 ---
// Inicia la comunicación I2C. Los pines SCL y SDA para el ESP32-C3-MINI-1 son GPIO3 y GPIO2.
Wire.begin(2, 3); // ESP32-C3: GPIO2 (SDA), GPIO3 (SCL)
if (!rtc.begin()) {
Serial.println("¡Error! No se pudo encontrar el módulo RTC.");
Serial.println("Verifica tus conexiones I2C (SDA a GPIO2, SCL a GPIO3) y alimentación.");
while (1); // Detiene el programa aquí si el RTC no se detecta (en Wokwi, indica un problema de conexión)
}
// --- Establecer la hora del RTC (¡Solo para la primera vez o si se pierde la hora!) ---
// IMPORTANTE: Después de cargar el código una vez y que el RTC tenga la hora,
// COMENTA O BORRA la línea 'rtc.adjust(...);' para que el RTC mantenga la hora
// y no se reinicie a esta cada vez que el ESP32 se encienda o reinicie.
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Establece la hora y fecha de compilación del programa
rtc.adjust(DateTime(2025, 5, 28, 10, 30, 0)); // Establece una hora específica: 28 de Mayo de 2025, 10:30:00 AM
// --- Configuración del Botón ---
// Configura el pin del botón como entrada. INPUT_PULLUP activa una resistencia interna
// que mantiene el pin en HIGH. Cuando el botón se presiona, lo conecta a GND (LOW).
pinMode(BUTTON_PIN, INPUT_PULLUP);
// --- Configuración del Zumbador ---
// Configura el pin del zumbador como salida.
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW); // Asegura que el zumbador esté apagado al inicio
// --- Configuración de los LEDs de Sigilo ---
// Configura los pines de los LEDs como salidas.
pinMode(LED_GREEN_PIN, OUTPUT);
pinMode(LED_YELLOW_PIN, OUTPUT);
pinMode(LED_RED_PIN, OUTPUT);
// Apaga todos los LEDs de sigilo al inicio.
digitalWrite(LED_GREEN_PIN, LOW);
digitalWrite(LED_YELLOW_PIN, LOW);
digitalWrite(LED_RED_PIN, LOW);
// Inicializa el tiempo de la última actividad para el control del display.
lastActivityTime = millis();
Serial.println("Setup completado. ¡Iniciando bucle principal!");
}
// =====================================================================
// Bucle Principal (loop)
// Esta función se ejecuta repetidamente después del setup.
// =====================================================================
void loop() {
unsigned long currentTime = millis(); // Obtiene el tiempo transcurrido desde que el ESP32 se encendió (en milisegundos)
// === Manejo del Botón (Activación y Control de Modos) ===
// Si el botón está presionado (el pin se lee como LOW debido a INPUT_PULLUP)
if (digitalRead(BUTTON_PIN) == LOW) {
// Implementa "debounce" para evitar múltiples lecturas por una sola pulsación
if (currentTime - lastButtonPressTime > BUTTON_DEBOUNCE) {
lastButtonPressTime = currentTime; // Actualiza el tiempo de la última pulsación
// Si el display está apagado (modo de bajo consumo), la primera pulsación lo enciende
if (!displayOn) {
lc.shutdown(0, false); // Enciende el display MAX7219
displayOn = true;
// Apaga los LEDs de sigilo temporalmente al encender el display, se actualizarán en el loop
digitalWrite(LED_GREEN_PIN, LOW);
digitalWrite(LED_YELLOW_PIN, LOW);
digitalWrite(LED_RED_PIN, LOW);
Serial.println("Display y LEDs encendidos por pulsación.");
lastActivityTime = currentTime; // Reinicia el temporizador de inactividad
} else {
// Si el display ya está encendido, el botón alterna el estado del temporizador de filtro
filterTimerActive = !filterTimerActive; // Cambia entre activo y pausado
if (filterTimerActive) {
filterTimerStartTime = currentTime; // Si se activa, reinicia el temporizador
alarmSounding = false; // Desactiva la alarma si estaba sonando
digitalWrite(BUZZER_PIN, LOW); // Asegura que el zumbador esté apagado
Serial.println("Temporizador de filtro iniciado/reiniciado.");
} else {
// Si se pausa el temporizador, asegúrate de que la alarma se detenga
alarmSounding = false;
digitalWrite(BUZZER_PIN, LOW);
Serial.println("Temporizador de filtro pausado.");
}
lastActivityTime = currentTime; // Reinicia el temporizador de inactividad
}
}
}
// === Control de Encendido/Apagado Automático del Display y LEDs por Inactividad ===
// Si el display está encendido y ha pasado el tiempo de inactividad establecido
if (displayOn && currentTime - lastActivityTime > DISPLAY_TIMEOUT) {
lc.shutdown(0, true); // Apaga el display MAX7219
displayOn = false;
// Apaga todos los LEDs de sigilo
digitalWrite(LED_GREEN_PIN, LOW);
digitalWrite(LED_YELLOW_PIN, LOW);
digitalWrite(LED_RED_PIN, LOW);
Serial.println("Display y LEDs apagados por inactividad.");
}
// === Lógica principal cuando el display está encendido ===
if (displayOn) {
if (filterTimerActive) {
// Si el temporizador de filtro está activo
long elapsedMillis = currentTime - filterTimerStartTime;
long remainingMillis = filterTimerDuration - elapsedMillis;
if (remainingMillis <= 0) {
// El temporizador ha terminado o ha expirado
remainingMillis = 0; // Asegura que no haya números negativos
filterTimerActive = false; // Desactiva el temporizador
alarmSounding = true; // Activa la alarma
// Lógica de parpadeo para indicar el fin del temporizador
if (currentTime % 1000 < 500) { // Muestra "00:00" por 0.5s
displayTimer(0);
} else {
lc.clearDisplay(0); // Limpia el display por 0.5s
}
} else {
// El temporizador sigue corriendo, muestra el tiempo restante
displayTimer(remainingMillis);
// Serial.print("Tiempo restante (s): "); Serial.println(remainingMillis / 1000);
// Lógica de alarma de filtro bajo (por ejemplo, cuando quedan 60 segundos o menos)
if (remainingMillis <= 60 * 1000 && !alarmSounding) {
alarmSounding = true; // Activa la bandera de alarma
Serial.println("¡Alarma de filtro bajo activada!");
}
}
// Control del zumbador para la alarma
if (alarmSounding) {
// Haz que el zumbador suene en pulsos cortos para simular una alarma
if (currentTime % 1000 < 100) { // Suena por 100ms cada segundo
digitalWrite(BUZZER_PIN, HIGH);
} else {
digitalWrite(BUZZER_PIN, LOW);
}
} else {
digitalWrite(BUZZER_PIN, LOW); // Asegura que el zumbador esté apagado si no hay alarma
}
} else {
// Si el temporizador NO está activo, muestra la hora actual del RTC
DateTime now = rtc.now(); // Lee la hora y fecha del módulo RTC
displayTime(now.hour(), now.minute(), now.second()); // Muestra HH:MM
// Serial.print("Hora RTC: "); Serial.print(now.hour()); Serial.print(":"); Serial.print(now.minute()); Serial.print(":"); Serial.println(now.second());
// Actualiza los LEDs de sigilo solo cuando se muestra la hora (temporizador inactivo)
updateStealthLEDs();
}
}
// Pequeño delay para evitar que el bucle corra demasiado rápido.
// Esto ayuda a reducir el consumo de CPU (y batería en el dispositivo físico) y a estabilizar la simulación.
delay(10);
}Loading
esp32-c3-devkitm-1
esp32-c3-devkitm-1