#include "myDisplay.h"
#include "modulos.h"
#include <Wire.h>
#define DIRECCION_EEPROM 0x50
// Ya NO usamos array de mediciones, leemos directo de EEPROM
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 debounce
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 horaMostrada = 0; // Hora actualmente mostrada en MEM
unsigned long lastMemChangeTime = 0;
const unsigned long MEM_DISPLAY_TIME = 2000000;
// Variables para modo CLOCK
unsigned long lastClockUpdate = 0;
bool showingValueInClock = false;
unsigned long valueShowStartTime = 0;
const unsigned long VALUE_DISPLAY_TIME = 3000000;
// Variables para alternancia en modo NOW
unsigned long lastAlternateTime = 0;
const unsigned long ALTERNATE_INTERVAL = 2000000; // 2 segundos
bool showCurrentValue = true; // true = valor actual, false = valor guardado
uint16_t currentRadiation = 0;
bool buttonPressed = false;
unsigned long buttonPressStartTime = 0;
// Variables para sistema de promedio
unsigned long lastPromedioUpdate = 0;
const unsigned long PROMEDIO_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);
}
// ------------------- ALTERNAR DISPLAY EN MODO NOW -------------------
void alternarDisplayModoNOW() {
unsigned long currentTime = micros();
if (currentTime - lastAlternateTime >= ALTERNATE_INTERVAL) {
lastAlternateTime = currentTime;
showCurrentValue = !showCurrentValue;
if (showCurrentValue) {
// Mostrar PROMEDIO ACUMULADO actual
uint16_t promedioActual = obtenerPromedioActual();
display.showNumber(promedioActual);
Serial.print("📊 NOW - Promedio acumulado: ");
Serial.print(promedioActual);
Serial.print(" lux (");
Serial.print(obtenerContadorActual());
Serial.println(" mediciones)");
} else {
// Mostrar valor guardado para la hora actual
uint8_t horaActual = obtenerHoraActual();
uint16_t valorGuardado = LeerMedida(horaActual);
display.showNumber(valorGuardado);
if (valorGuardado > 0) {
Serial.print("💾 NOW - Promedio guardado ");
Serial.print(horaActual);
Serial.print("H: ");
Serial.print(valorGuardado);
Serial.println(" lux");
} else {
Serial.print("📭 NOW - Sin datos para ");
Serial.print(horaActual);
Serial.println("H");
}
}
}
}
// ------------------- BUSCAR HORA CON DATOS -------------------
int8_t buscarSiguienteHoraConDatos(uint8_t horaActual, bool direccionAdelante) {
if (direccionAdelante) {
// Buscar hacia adelante (0 → 23)
for (uint8_t h = horaActual + 1; h < 24; h++) {
if (LeerMedida(h) > 0) return h;
}
// Si no encuentra, buscar desde el inicio
for (uint8_t h = 0; h <= horaActual; h++) {
if (LeerMedida(h) > 0) return h;
}
} else {
// Buscar hacia atrás (23 → 0)
for (int8_t h = horaActual - 1; h >= 0; h--) {
if (LeerMedida(h) > 0) return h;
}
// Si no encuentra, buscar desde el final
for (int8_t h = 23; h >= horaActual; h--) {
if (LeerMedida(h) > 0) return h;
}
}
return -1; // No hay datos
}
// ------------------- CAMBIAR MODO CON ENCODER -------------------
void cambiarModo(bool direccionDerecha) {
switch (currentState) {
case STATE_NOW:
if (direccionDerecha) {
currentState = STATE_MEM;
// Buscar primera hora con datos
int8_t primeraHora = buscarSiguienteHoraConDatos(23, true); // Buscar desde inicio
if (primeraHora >= 0) {
horaMostrada = primeraHora;
showHour = true;
display.showHour(horaMostrada);
Serial.print("NOW → MEM (Hora ");
Serial.print(horaMostrada);
Serial.println("H)");
} else {
display.showNumber(0);
Serial.println("NOW → MEM (Sin datos guardados)");
}
lastMemChangeTime = micros();
} else {
currentState = STATE_CLOCK;
showingValueInClock = false;
mostrarHoraReloj();
Serial.println("NOW ← CLOCK");
}
break;
case STATE_MEM:
if (direccionDerecha) {
// Buscar siguiente hora con datos
int8_t siguienteHora = buscarSiguienteHoraConDatos(horaMostrada, true);
if (siguienteHora >= 0) {
horaMostrada = siguienteHora;
showHour = true;
display.showHour(horaMostrada);
Serial.print("MEM → Hora ");
Serial.print(horaMostrada);
Serial.println("H");
lastMemChangeTime = micros();
} else {
// No hay más datos, ir a CLOCK
currentState = STATE_CLOCK;
showingValueInClock = false;
mostrarHoraReloj();
Serial.println("MEM → CLOCK (No más datos)");
}
} else {
// Buscar hora anterior con datos
int8_t horaAnterior = buscarSiguienteHoraConDatos(horaMostrada, false);
if (horaAnterior >= 0) {
horaMostrada = horaAnterior;
showHour = true;
display.showHour(horaMostrada);
Serial.print("MEM ← Hora ");
Serial.print(horaMostrada);
Serial.println("H");
lastMemChangeTime = micros();
} else {
// No hay datos anteriores, ir a NOW
currentState = STATE_NOW;
showCurrentValue = true;
lastAlternateTime = micros();
Serial.println("MEM ← NOW (No hay datos anteriores)");
}
}
break;
case STATE_CLOCK:
if (direccionDerecha) {
currentState = STATE_NOW;
showingValueInClock = false;
showCurrentValue = true;
lastAlternateTime = micros();
Serial.println("CLOCK → NOW");
} else {
currentState = STATE_MEM;
// Buscar última hora con datos
int8_t ultimaHora = buscarSiguienteHoraConDatos(0, false); // Buscar desde final
if (ultimaHora >= 0) {
horaMostrada = ultimaHora;
showHour = true;
display.showHour(horaMostrada);
Serial.print("CLOCK ← MEM (Hora ");
Serial.print(horaMostrada);
Serial.println("H)");
} else {
display.showNumber(0);
Serial.println("CLOCK ← MEM (Sin datos)");
}
lastMemChangeTime = micros();
}
break;
}
}
// ------------------- MANEJAR MODO MEM -------------------
void manejarModoMEM() {
unsigned long currentTime = micros();
if (currentTime - lastMemChangeTime >= MEM_DISPLAY_TIME) {
lastMemChangeTime = currentTime;
uint16_t valor = LeerMedida(horaMostrada);
if (showHour && valor > 0) {
showHour = false;
display.showNumber(valor);
Serial.print("MEM - Valor ");
Serial.print(horaMostrada);
Serial.print("H: ");
Serial.println(valor);
} else if (valor > 0) {
showHour = true;
display.showHour(horaMostrada);
Serial.print("MEM - Hora ");
Serial.print(horaMostrada);
Serial.println("H");
}
// Si no hay datos, se mantiene mostrando 0
}
}
// ------------------- 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);
if (valorGuardado > 0) {
display.showNumber(valorGuardado);
showingValueInClock = true;
valueShowStartTime = micros();
Serial.print("CLOCK - Valor para ");
Serial.print(horaActual);
Serial.print("H: ");
Serial.println(valorGuardado);
} else {
Serial.print("CLOCK - No hay datos guardados para ");
Serial.print(horaActual);
Serial.println("H");
}
}
// ------------------- 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 promedioActual = obtenerPromedioActual();
uint32_t contador = obtenerContadorActual();
uint32_t suma = obtenerSumaAcumuladaActual();
if (promedioActual == 0) {
Serial.println("❌ No hay datos para guardar (promedio 0)");
return;
}
if (EscribirMedida(horaActual, promedioActual)) {
Serial.print("💾 Promedio MANUAL guardado - ");
Serial.print(horaActual);
Serial.print("H: ");
Serial.print(promedioActual);
Serial.print(" lux (");
Serial.print(contador);
Serial.print(" mediciones, suma: ");
Serial.print(suma);
Serial.println(")");
} else {
Serial.println("❌ Error guardando promedio en EEPROM");
}
}
// ------------------- 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 EEPROM -------------------
void inicializarEEPROM() {
Serial.println("🗃️ Verificando EEPROM...");
// Contar horas con datos
uint8_t horasConDatos = 0;
for (int i = 0; i < 24; i++) {
if (LeerMedida(i) > 0) {
horasConDatos++;
}
}
Serial.print("📊 Horas con datos: ");
Serial.print(horasConDatos);
Serial.println("/24");
if (horasConDatos == 0) {
Serial.println("📭 EEPROM vacía - Empezando desde cero");
} else {
Serial.println("📂 EEPROM contiene datos existentes");
}
}
// ------------------- ACTUALIZAR SISTEMA DE PROMEDIO -------------------
void actualizarSistemaPromedio() {
unsigned long currentTime = micros();
// Actualizar promedio cada 5 segundos
if (currentTime - lastPromedioUpdate >= PROMEDIO_UPDATE_INTERVAL) {
lastPromedioUpdate = currentTime;
// Tomar nueva medición del sensor
uint16_t nueva_medicion = leerSensorRadiacion();
uint32_t contador_anterior = obtenerContadorActual();
// Actualizar promedio acumulado
actualizarPromedioAcumulado(nueva_medicion);
// Mostrar info cada 10 mediciones
if (contador_anterior % 10 == 0 && contador_anterior > 0) {
uint16_t promedio_actual = obtenerPromedioActual();
uint32_t suma_acumulada = obtenerSumaAcumuladaActual();
Serial.print("📈 Promedio: ");
Serial.print(promedio_actual);
Serial.print(" lux (");
Serial.print(contador_anterior);
Serial.print(" mediciones, suma: ");
Serial.print(suma_acumulada);
Serial.println(")");
}
// Verificar si cambió la hora para guardar automáticamente
if (verificarCambioHora()) {
guardarPromedioSiHoraCompleta();
}
}
}
// ------------------- 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);
// Inicializar sistema de PROMEDIO ACUMULADO
inicializarPromedioAcumulado();
Serial.println("🚀 SISTEMA DE RADIACIÓN SOLAR - INICIADO");
Serial.println("========================================");
Serial.println("🎯 SISTEMA DE PROMEDIO ACUMULADO POR HORA");
Serial.println("⏰ Medición cada 5 segundos");
Serial.println("📊 Promedio calculado en tiempo real");
Serial.println("💾 Guardado automático al cambiar de hora");
Serial.println("📊 NOW: Alterna cada 2s entre promedio actual y guardado");
Serial.println("========================================");
inicializarSensores();
inicializarEEPROM();
Serial.println();
Serial.println("✅ SISTEMA LISTO");
Serial.println("📊 NOW: Promedio actual + promedio guardado (alterna cada 2s)");
Serial.println("💾 MEM: Navegar promedios guardados por hora");
Serial.println("🕒 CLOCK: Hora actual + pulsación larga para ver promedio");
Serial.println();
}
// ------------------- LOOP PRINCIPAL -------------------
void loop() {
static unsigned long lastDisplayRefresh = 0;
static unsigned long lastSensorRead = 0;
unsigned long currentTime = micros();
// Refresh display cada 1ms
if (currentTime - lastDisplayRefresh >= 1000) {
lastDisplayRefresh = currentTime;
display.refresh();
}
// Actualizar sistema de PROMEDIO ACUMULADO
actualizarSistemaPromedio();
// Manejar cada modo
switch (currentState) {
case STATE_MEM:
manejarModoMEM();
break;
case STATE_CLOCK:
manejarModoCLOCK();
break;
case STATE_NOW:
// Alternar entre promedio actual y promedio guardado cada 2 segundos
alternarDisplayModoNOW();
break;
}
// Pulsación larga en MODO CLOCK para mostrar valor guardado
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();
}
}