#include "myDisplay.h"
#include "modulos.h"
#include <Wire.h>
#define DIRECCION_EEPROM 0x50
// Estructura para almacenar hora y valor juntos
struct Medicion {
uint16_t hora;
uint16_t valor;
};
Medicion mediciones[13];
myDisplay display;
// Pines del encoder
const int pinCLK = 25;
const int pinDT = 26;
const int pinSW = 27;
// Estados del sistema
enum SystemState {
STATE_NOW, // Modo NOW (medición en tiempo real)
STATE_MEM, // Modo MEM mostrando hora/valor
STATE_CLOCK // Modo CLOCK
};
SystemState currentState = STATE_NOW;
// Flags atómicos para interrupciones
volatile bool cambioEncoder = false;
volatile bool botonPresionado = false;
// Variables para Rebote
volatile unsigned long ultimoTiempoEncoder = 0;
volatile unsigned long ultimoTiempoBoton = 0;
const unsigned long DEBOUNCE_ENCODER = 100000;
const unsigned long DEBOUNCE_BOTON = 200000;
// Variables para modo MEM
bool showHour = true;
uint8_t displayIndex = 0;
unsigned long lastMemChangeTime = 0;//última hora de cambio de memoria
const unsigned long MEM_DISPLAY_TIME = 2000000;
// Variables para modo CLOCK
unsigned long lastClockUpdate = 0;//última actualización del reloj
bool showingValueInClock = false;//mostrandoValorEnElReloj
unsigned long valueShowStartTime = 0;//valorMostrarHoraInicio
const unsigned long VALUE_DISPLAY_TIME = 3000000;
uint16_t currentRadiation = 0;//radiación actual
bool buttonPressed = false;
unsigned long buttonPressStartTime = 0;
// Variables para sistema de filtro
unsigned long lastFilterUpdate = 0;//Actualización de filtro ast
const unsigned long FILTER_UPDATE_INTERVAL = 5000000; // 5 segundos
// ------------------- INTERRUPCIONES -------------------
void IRAM_ATTR interrupcionEncoder() {
static unsigned long lastEncoderTime = 0;
unsigned long tiempoActual = micros();
if (tiempoActual - lastEncoderTime < DEBOUNCE_ENCODER) return;
lastEncoderTime = tiempoActual;
if ((REG_READ(GPIO_IN_REG) & (1 << pinCLK)) == 0) {
cambioEncoder = true;
}
}
void IRAM_ATTR interrupcionBoton() {
unsigned long tiempoActual = micros();
if (tiempoActual - ultimoTiempoBoton < DEBOUNCE_BOTON) return;
ultimoTiempoBoton = tiempoActual;
botonPresionado = true;
}
// ------------------- FUNCIONES BÁSICAS -------------------
uint16_t leerSensorRadiacion() {
uint16_t valor = readLightLevel();
return (valor > 9999) ? 9999 : valor;
}
uint8_t obtenerHoraActual() {
return HoraActual();
}
void mostrarHoraReloj() {
uint8_t horaActual = obtenerHoraActual();
uint8_t minutoActual = obtenerMinutoActual();
uint16_t horaFormateada = (horaActual * 100) + minutoActual;
display.showNumber(horaFormateada);
}
// ------------------- CAMBIAR MODO CON ENCODER -------------------
void cambiarModo(bool direccionDerecha) {
switch (currentState) {
case STATE_NOW:
if (direccionDerecha) {
currentState = STATE_MEM;
displayIndex = 0;
showHour = true;
display.showHour(mediciones[displayIndex].hora);
lastMemChangeTime = micros();
Serial.println("NOW → MEM (6H)");
} else {
currentState = STATE_CLOCK;
showingValueInClock = false;
mostrarHoraReloj();
Serial.println("NOW ← CLOCK");
}
break;
case STATE_MEM:
if (direccionDerecha) {
if (displayIndex < 12) {
displayIndex++;
showHour = true;
lastMemChangeTime = micros();
display.showHour(mediciones[displayIndex].hora);
Serial.print("MEM → ");
Serial.print(mediciones[displayIndex].hora);
Serial.println("H");
} else {
currentState = STATE_CLOCK;
showingValueInClock = false;
mostrarHoraReloj();
Serial.println("MEM (18H) → CLOCK");
}
} else {
if (displayIndex > 0) {
displayIndex--;
showHour = true;
lastMemChangeTime = micros();
display.showHour(mediciones[displayIndex].hora);
Serial.print("MEM ← ");
Serial.print(mediciones[displayIndex].hora);
Serial.println("H");
} else {
currentState = STATE_NOW;
Serial.println("MEM (6H) ← NOW");
}
}
break;
case STATE_CLOCK:
if (direccionDerecha) {
currentState = STATE_NOW;
showingValueInClock = false;
Serial.println("CLOCK → NOW");
} else {
currentState = STATE_MEM;
displayIndex = 12;
showHour = true;
display.showHour(mediciones[displayIndex].hora);
lastMemChangeTime = micros();
Serial.println("CLOCK ← MEM (18H)");
}
break;
}
}
// ------------------- MANEJAR MODO MEM -------------------
void manejarModoMEM() {
unsigned long currentTime = micros();
if (currentTime - lastMemChangeTime >= MEM_DISPLAY_TIME) {
lastMemChangeTime = currentTime;
if (showHour) {
showHour = false;
display.showNumber(mediciones[displayIndex].valor);
Serial.print("MEM - Valor ");
Serial.print(mediciones[displayIndex].hora);
Serial.print("H: ");
Serial.println(mediciones[displayIndex].valor);
} else {
showHour = true;
display.showHour(mediciones[displayIndex].hora);
Serial.print("MEM - Hora ");
Serial.print(mediciones[displayIndex].hora);
Serial.println("H");
}
}
}
// ------------------- MANEJAR MODO CLOCK -------------------
void manejarModoCLOCK() {
unsigned long currentTime = micros();
if (currentTime - lastClockUpdate >= 1000000) {
lastClockUpdate = currentTime;
if (!showingValueInClock) {
mostrarHoraReloj();
}
}
if (showingValueInClock && (currentTime - valueShowStartTime >= VALUE_DISPLAY_TIME)) {
showingValueInClock = false;
mostrarHoraReloj();
Serial.println("CLOCK - Volviendo a mostrar hora");
}
}
// ------------------- MOSTRAR VALOR EN MODO CLOCK -------------------
void mostrarValorEnClock() {
uint8_t horaActual = obtenerHoraActual();
uint16_t valorGuardado = LeerMedida(horaActual);
display.showNumber(valorGuardado);
showingValueInClock = true;
valueShowStartTime = micros();
Serial.print("CLOCK - Valor para ");
Serial.print(horaActual);
Serial.print("H: ");
Serial.println(valorGuardado);
}
// ------------------- DETECTAR PULSACIÓN LARGA -------------------
bool detectarPulsacionLarga() {
static bool lastButtonState = HIGH;
bool currentButtonState = (REG_READ(GPIO_IN_REG) & (1 << pinSW)) ? HIGH : LOW;
if (currentButtonState == LOW && lastButtonState == HIGH) {
buttonPressStartTime = micros();
buttonPressed = true;
}
if (buttonPressed && currentButtonState == LOW) {
unsigned long pressDuration = (micros() - buttonPressStartTime) / 1000;
if (pressDuration >= 1000) {
buttonPressed = false;
return true;
}
}
if (currentButtonState == HIGH) {
buttonPressed = false;
}
lastButtonState = currentButtonState;
return false;
}
// ------------------- GUARDAR MEDICIÓN MANUAL -------------------
void guardarMedicionManual() {
uint8_t horaActual = obtenerHoraActual();
uint16_t valorActual = (uint16_t)obtenerValorFiltradoActual();
uint32_t contador = obtenerContadorActual();
for (int i = 0; i < 13; i++) {
if (mediciones[i].hora == horaActual) {
mediciones[i].valor = valorActual;
EscribirMedida(horaActual, valorActual);
Serial.print("💾 Medición manual guardada - ");
Serial.print(horaActual);
Serial.print("H: ");
Serial.print(valorActual);
Serial.print(" (");
Serial.print(contador);
Serial.println(" mediciones)");
return;
}
}
for (int i = 0; i < 13; i++) {
if (mediciones[i].hora == 0) {
mediciones[i].hora = horaActual;
mediciones[i].valor = valorActual;
EscribirMedida(horaActual, valorActual);
Serial.print("💾 Nueva medición manual - ");
Serial.print(horaActual);
Serial.print("H: ");
Serial.print(valorActual);
Serial.print(" (");
Serial.print(contador);
Serial.println(" mediciones)");
return;
}
}
Serial.println("❌ No hay espacio para nueva medición");
}
// ------------------- INICIALIZAR SENSORES -------------------
bool inicializarSensores() {
Serial.println("🔧 Inicializando sensores...");
if (!bh1750Begin()) {
Serial.println("❌ Error: BH1750 no responde");
return false;
}
Serial.println("✅ Sensores inicializados correctamente");
return true;
}
// ------------------- INICIALIZAR DATOS DE EJEMPLO -------------------
void inicializarDatosEjemplo() {
bool eepromVacia = true;
for (int i = 0; i < 13; i++) {
if (LeerMedida(i + 6) != 0) {
eepromVacia = false;
break;
}
}
if (eepromVacia) {
Serial.println("📊 Inicializando datos de ejemplo...");
uint16_t horasEjemplo[] = {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
uint16_t valoresEjemplo[] = {6666, 7777, 8888, 9999, 1010, 1111, 1212, 101, 202, 303, 404, 505, 606};
for (int i = 0; i < 13; i++) {
EscribirMedida(horasEjemplo[i], valoresEjemplo[i]);
mediciones[i].hora = horasEjemplo[i];
mediciones[i].valor = valoresEjemplo[i];
}
Serial.println("✅ Datos de ejemplo inicializados");
} else {
Serial.println("ℹ️ EEPROM ya contiene datos");
}
}
// ------------------- CARGAR DATOS DESDE EEPROM -------------------
void cargarDatosDesdeEEPROM() {
Serial.println("📂 Cargando datos desde EEPROM...");
uint16_t horas[] = {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
for (int i = 0; i < 13; i++) {
uint16_t valor = LeerMedida(horas[i]);
mediciones[i].hora = horas[i];
mediciones[i].valor = valor;
Serial.print(" Hora ");
Serial.print(horas[i]);
Serial.print("H: ");
Serial.println(valor);
}
Serial.println("✅ Datos cargados correctamente");
}
// ------------------- ACTUALIZAR SISTEMA DE FILTRO -------------------
void actualizarSistemaFiltro() {
unsigned long currentTime = micros();
// Actualizar filtro cada 5 segundos
if (currentTime - lastFilterUpdate >= FILTER_UPDATE_INTERVAL) {
lastFilterUpdate = currentTime;
// Tomar nueva medición del sensor
uint16_t nueva_medicion = leerSensorRadiacion();
uint32_t contador_anterior = obtenerContadorActual();
float valor_anterior = obtenerValorFiltradoActual();
Serial.println("═══════════════════════════════════════");
Serial.print("🕒 Nueva medición #");
Serial.println(contador_anterior + 1);
Serial.print("📊 Valor del sensor: ");
Serial.println(nueva_medicion);
if (contador_anterior > 0) {
Serial.print("📈 Valor filtrado anterior: ");
Serial.println(valor_anterior, 2);
// Mostrar cálculo paso a paso
Serial.println("🔢 CÁLCULO DEL FILTRO:");
Serial.print(" y[n] = (1-α)*y[n-1] + α*x[n]");
Serial.print(" = (1-0.1)*");
Serial.print(valor_anterior, 2);
Serial.print(" + 0.1*");
Serial.println(nueva_medicion);
float termino1 = (1.0 - 0.1) * valor_anterior;
float termino2 = 0.1 * nueva_medicion;
Serial.print(" = 0.9*");
Serial.print(valor_anterior, 2);
Serial.print(" + 0.1*");
Serial.println(nueva_medicion);
Serial.print(" = ");
Serial.print(termino1, 2);
Serial.print(" + ");
Serial.print(termino2, 2);
Serial.print(" = ");
Serial.println(termino1 + termino2, 2);
} else {
Serial.println("🔢 Primera medición - Valor inicial del filtro");
}
// Aplicar filtro exponencial
actualizarFiltroExponencial(nueva_medicion);
// Mostrar resultado
float nuevo_valor = obtenerValorFiltradoActual();
uint32_t nuevo_contador = obtenerContadorActual();
Serial.print("✅ Nuevo valor filtrado: ");
Serial.print(nuevo_valor, 2);
Serial.print(" (");
Serial.print(nuevo_contador);
Serial.println(" mediciones acumuladas)");
Serial.println("═══════════════════════════════════════");
// Verificar si cambió la hora para guardar automáticamente
if (verificarCambioHora()) {
guardarFiltroSiHoraCompleta();
}
}
}
// ------------------- SETUP -------------------
void setup() {
Wire.begin();
Serial.begin(115200);
display.begin();
// Configurar pines
REG_WRITE(GPIO_ENABLE_W1TC_REG, (1 << pinCLK) | (1 << pinDT) | (1 << pinSW));
gpio_config_t io_conf = {};
io_conf.pin_bit_mask = (1ULL << pinCLK) | (1ULL << pinDT) | (1ULL << pinSW);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&io_conf);
attachInterrupt(digitalPinToInterrupt(pinCLK), interrupcionEncoder, FALLING);
attachInterrupt(digitalPinToInterrupt(pinSW), interrupcionBoton, FALLING);
/*// SOLO AGREGAR VERIFICACIÓN DE HORA
uint8_t hora = HoraActual();
uint8_t minuto = obtenerMinutoActual();
Serial.print("🕒 Hora actual del RTC: ");
Serial.print(hora);
Serial.print(":");
Serial.println(minuto);
if (hora > 23 || minuto > 59) {
Serial.println("⚠️ Hora inválida! Estableciendo 12:00...");
// Aquí llamarías a setRTCTime si la tienes implementada
// setRTCTime(12, 0, 0);
}*/
// FIN DE LA MODIFICACIÓN
/* Serial.println("🚀 Sistema listo - Modo NOW");
Serial.println("🎯 FILTRO EXPONENCIAL ACTIVADO");
Serial.println("📖 Fórmula: y[n] = (1-α)*y[n-1] + α*x[n]");
Serial.println("⚙️ Parámetros: α=0.1, medición cada 5 segundos");
Serial.println("🔄 Encoder derecha: NOW → MEM → CLOCK → NOW");
Serial.println("🔄 Encoder izquierda: NOW ← CLOCK ← MEM ← NOW");
Serial.println();*/
// Inicializar sistema de filtro exponencial (α = 0.1 para buen suavizado)
inicializarFiltroExponencial(0.1);
inicializarSensores();
inicializarDatosEjemplo();
cargarDatosDesdeEEPROM();
}
// ------------------- LOOP PRINCIPAL -------------------
void loop() {
static unsigned long lastDisplayRefresh = 0;//última actualización de pantalla
static unsigned long lastSensorRead = 0;
unsigned long currentTime = micros();
// Refresh display cada 1ms - T
if (currentTime - lastDisplayRefresh >= 10000) {
lastDisplayRefresh = currentTime;
display.refresh();
}
// Actualizar sistema de filtro exponencial
actualizarSistemaFiltro();
// Manejar cada modo
switch (currentState) {
case STATE_MEM:
manejarModoMEM();
break;
case STATE_CLOCK:
manejarModoCLOCK();
break;
case STATE_NOW:
// Mostrar valor filtrado en tiempo real
display.showNumber((uint16_t)obtenerValorFiltradoActual());
break;
}
// Pulsación larga en MODO CLOCK para mostrar valor
if (currentState == STATE_CLOCK && detectarPulsacionLarga() && !showingValueInClock) {
mostrarValorEnClock();
}
// Procesar encoder
if (cambioEncoder) {
cambioEncoder = false;
int clkState = (REG_READ(GPIO_IN_REG) & (1 << pinCLK)) ? 1 : 0;
int dtState = (REG_READ(GPIO_IN_REG) & (1 << pinDT)) ? 1 : 0;
bool direccionDerecha = (clkState != dtState);
cambiarModo(direccionDerecha);
}
// Botón corto en modo NOW para guardar manualmente
if (botonPresionado && currentState == STATE_NOW) {
botonPresionado = false;
guardarMedicionManual();
}
// Lectura rápida del sensor para actualización visual
if (currentTime - lastSensorRead >= 1000000) {
lastSensorRead = currentTime;
currentRadiation = leerSensorRadiacion();
}
}