#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#define DS1307_ADDRESS 0x68 // Dirección I2C del DS1307
#define i2c_Address 0x3c // Dirección I2C de la pantalla OLED
#define SCREEN_WIDTH 128 // Ancho de la pantalla OLED
#define SCREEN_HEIGHT 64 // Alto de la pantalla OLED
#define OLED_RESET -1
#define B_set 5 // Botón de configuración
#define B_up 3 // Botón de incremento
#define B_down 4 // Botón de decremento
#define LED_Alarm 6 // LED para la alarma
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Variables para los botones
int Boton_Config = 0;
bool estadoAnteriorConfig = HIGH;
bool estadoAnteriorArriba = HIGH;
bool estadoAnteriorAbajo = HIGH;
// Variables para la alarma
int hora_A = 0, minuto_A = 0; // Variables para la hora y minuto de la alarma
// Variables para almacenar la hora actual
int hora_actual = 0;
int minuto_actual = 0;
int segundo_actual = 0;
// Variables para la gestión de la duración de la alarma
//unsigned long alarmaInicioMillis = 0; // Tiempo de inicio de la alarma
const unsigned long DURACION_ALARMA = 60000; // Duración de la alarma en milisegundos
bool alarmaActiva = false; // Estado de la alarma
// Función para determinar si un año es bisiesto
bool esBisiesto(int year) {
int anoCompleto = 2000 + year; // Convertir a cuatro dígitos
return (anoCompleto % 4 == 0 && (anoCompleto % 100 != 0 || anoCompleto % 400 == 0));
}
// Función para obtener el número de días en un mes específico
int diasEnMes(int mes, int year) {
switch (mes) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
return 31;
case 4: case 6: case 9: case 11:
return 30;
case 2:
return esBisiesto(year) ? 29 : 28;
default:
return 0; // Mes inválido
}
}
// Función para convertir de BCD a decimal
int bcdADec(int val) {
return ((val >> 4) * 10) + (val & 0x0F);
}
// Función para convertir de decimal a BCD
int decABcd(int val) {
return ((val / 10) << 4) + (val % 10);
}
// Función para leer el estado de un botón con
bool botonPresionado(int pin, bool &estadoAnterior) {
bool estadoActual = digitalRead(pin);
bool presionado = (estadoAnterior == LOW && estadoActual == HIGH);
estadoAnterior = estadoActual;
if (presionado) {
delay(50);
}
return presionado;
}
// Arreglo de nombres de días de la semana
const char* dias[] = {"Inválido", "Domingo", "Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado"};
// Función para calcular el día de la semana (1 = Domingo, ..., 7 = Sábado)
int calcularDiaSemana(int dia, int mes, int year) {
int anio = 2000 + year; // Convertir a año de cuatro dígitos
// Ajuste para la Congruencia de Zeller
if (mes < 3) {
mes += 12;
anio -= 1;
}
int K = anio % 100; // Año del siglo
int J = anio / 100; // Siglo
// Aplicación de la Congruencia de Zeller
int h = (dia + (13 * (mes + 1)) / 5 + K + (K / 4) + (J / 4) + (5 * J)) % 7;
// Mapeo de 'h' al día de la semana (1 = Domingo, 7 = Sábado)
int diaSemana = ((h + 6) % 7) + 1;
return diaSemana;
}
void setup() {
Wire.begin(); // Inicializa la comunicación I2C
Serial.begin(9600);
// Configuración inicial de fecha y hora
establecerFechaHora(31, 9, 0, 0); // Hora: 00:31:00
establecerFechaHora(30, 10, 24, 4); // Fecha: Día 30, Mes 10, Año 24, PD=4
display.begin(i2c_Address, true);
pinMode(B_set, INPUT_PULLUP);
pinMode(B_up, INPUT_PULLUP);
pinMode(B_down, INPUT_PULLUP);
pinMode(LED_Alarm, OUTPUT);
digitalWrite(LED_Alarm, LOW);
}
void loop() {
display.clearDisplay();
imprimirHora();
imprimirFecha();
imprimirAlarma(hora_A, minuto_A);
// Lógica de la alarma: solo se activa cuando no estamos en modo de configuración
if (Boton_Config == 0) {
if (hora_actual == hora_A && minuto_actual == minuto_A && !alarmaActiva) {
digitalWrite(LED_Alarm, HIGH); // Enciende el LED
// alarmaInicioMillis = millis(); // Guarda el tiempo de inicio
alarmaActiva = true;
delay(200); // Activa la bandera de la alarma
//Serial.println("Alarma activada!");
}
// Verificar si la alarma está encendida y si ha pasado el tiempo suficiente
if (alarmaActiva) {
// if (millis() - alarmaInicioMillis >= DURACION_ALARMA) {
digitalWrite(LED_Alarm, LOW); // Apaga el LED después de la duración establecida
alarmaActiva = false; // Resetea la bandera de la alarma
// Serial.println("Alarma desactivada!");
// }
}
}
// Manejo del botón de configuración
if (botonPresionado(B_set, estadoAnteriorConfig)) {
Boton_Config++;
if (Boton_Config > 9) Boton_Config = 0; // Reiniciar el contador si excede el número de ajustes
}
if (Boton_Config > 0) {
configuracion();
}
display.display();
delay(20); // Retardo para evitar múltiples lecturas
}
// Función para mostrar el nombre del día de la semana
void diaSemana(int N) {
if (N >= 1 && N <= 7) {
display.print(dias[N]);
} else {
display.print(dias[0]);
}
}
// Función para establecer la hora y fecha inicial
void establecerFechaHora(int A, int B, int C, int PD) {
A = decABcd(A);
B = decABcd(B);
C = decABcd(C);
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(PD);
Wire.write(A);
Wire.write(B);
Wire.write(C);
Wire.endTransmission();
}
// Imprime la hora en pantalla y actualiza las variables actuales
void imprimirHora() {
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 3);
int8_t segundo = bcdADec(Wire.read() & 0x7F);
int8_t minuto = bcdADec(Wire.read());
int8_t hora = bcdADec(Wire.read());
// Actualizar las variables actuales
hora_actual = hora;
minuto_actual = minuto;
segundo_actual = segundo;
display.setTextColor(SH110X_WHITE);
display.setCursor(1, 1);
display.setTextSize(2);
if (hora < 10) display.print("0");
display.print(hora);
display.print(":");
if (minuto < 10) display.print("0");
display.print(minuto);
display.print(":");
if (segundo < 10) display.print("0");
display.print(segundo);
}
// Imprime la fecha en pantalla
void imprimirFecha() {
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(3);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 4);
int8_t dia_semana = Wire.read();
int8_t dia = bcdADec(Wire.read());
int8_t mes = bcdADec(Wire.read());
int8_t ano = bcdADec(Wire.read());
display.setCursor(2, 17);
display.setTextSize(1.7);
diaSemana(dia_semana);
display.print(" ");
if (dia < 10) display.print("0");
display.print(dia);
display.print("/");
if (mes < 10) display.print("0");
display.print(mes);
display.print("/");
if (ano < 10) display.print("0");
display.print(ano);
}
// Imprime la hora de la alarma en pantalla
void imprimirAlarma(int hora_A, int minuto_A) {
display.setCursor(2, 27);
display.setTextSize(2);
display.print("Alarma");
if (alarmaActiva) {
display.print(" *"); // Indicador de alarma activa
}
display.setCursor(2, 43);
if (hora_A < 10) display.print("0");
display.print(hora_A);
display.print(":");
if (minuto_A < 10) display.print("0");
display.print(minuto_A);
}
// Configuración de la hora, fecha y alarma
void configuracion() {
display.setCursor(110, 50); // Posición fija para mostrar la letra del parámetro
display.setTextSize(1.7);
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(0);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 7);
int8_t segundo = bcdADec(Wire.read() & 0x7F);
int8_t minuto = bcdADec(Wire.read());
int8_t hora = bcdADec(Wire.read());
int8_t dia_semana = Wire.read(); // Día de la semana (no necesita conversión)
int8_t dia = bcdADec(Wire.read());
int8_t mes = bcdADec(Wire.read());
int8_t ano = bcdADec(Wire.read());
switch (Boton_Config) {
case 1:
// Configurar hora
if (botonPresionado(B_up, estadoAnteriorArriba)) hora = (hora + 1) % 24;
if (botonPresionado(B_down, estadoAnteriorAbajo)) hora = (hora - 1 + 24) % 24;
display.print("h");
break;
case 2:
// Configurar minuto
if (botonPresionado(B_up, estadoAnteriorArriba)) minuto = (minuto + 1) % 60;
if (botonPresionado(B_down, estadoAnteriorAbajo)) minuto = (minuto - 1 + 60) % 60;
display.print("m");
break;
case 3:
// Configurar segundo
if (botonPresionado(B_up, estadoAnteriorArriba)) segundo = (segundo + 1) % 60;
if (botonPresionado(B_down, estadoAnteriorAbajo)) segundo = (segundo - 1 + 60) % 60;
display.print("s");
break;
case 4:
// Configurar día
if (botonPresionado(B_up, estadoAnteriorArriba)) {
dia++;
if (dia > diasEnMes(mes, ano)) {
dia = 1;
}
}
if (botonPresionado(B_down, estadoAnteriorAbajo)) {
dia--;
if (dia < 1) {
dia = diasEnMes(mes, ano);
}
}
display.print("D");
break;
case 5:
// Configurar mes
if (botonPresionado(B_up, estadoAnteriorArriba)) {
mes = (mes % 12) + 1;
if (dia > diasEnMes(mes, ano)) {
dia = diasEnMes(mes, ano);
}
}
if (botonPresionado(B_down, estadoAnteriorAbajo)) {
mes = (mes - 1 == 0) ? 12 : mes - 1;
if (dia > diasEnMes(mes, ano)) {
dia = diasEnMes(mes, ano);
}
}
display.print("M");
break;
case 6:
// Configurar año
if (botonPresionado(B_up, estadoAnteriorArriba)) ano = (ano + 1) % 100;
if (botonPresionado(B_down, estadoAnteriorAbajo)) ano = (ano - 1 + 100) % 100;
if (dia > diasEnMes(mes, ano)) {
dia = diasEnMes(mes, ano);
}
display.print("A");
break;
case 7:
// Configurar hora de alarma
if (botonPresionado(B_up, estadoAnteriorArriba)) hora_A = (hora_A + 1) % 24;
if (botonPresionado(B_down, estadoAnteriorAbajo)) hora_A = (hora_A - 1 + 24) % 24;
display.print("ha");
break;
case 8:
// Configurar minuto de alarma
if (botonPresionado(B_up, estadoAnteriorArriba)) minuto_A = (minuto_A + 1) % 60;
if (botonPresionado(B_down, estadoAnteriorAbajo)) minuto_A = (minuto_A - 1 + 60) % 60;
display.print("ma");
break;
case 9:
// Finalizar configuración
Boton_Config = 0;
break;
}
// Recalcular el día de la semana basado en la nueva fecha
int nuevoDiaSemana = calcularDiaSemana(dia, mes, ano);
if (Boton_Config <= 6) {
// Convertir a BCD antes de escribir al RTC
hora = decABcd(hora);
minuto = decABcd(minuto);
segundo = decABcd(segundo);
dia = decABcd(dia);
mes = decABcd(mes);
ano = decABcd(ano);
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(0);
Wire.write(segundo);
Wire.write(minuto);
Wire.write(hora);
Wire.write(nuevoDiaSemana); // Día de la semana actualizado
Wire.write(dia);
Wire.write(mes);
Wire.write(ano);
Wire.endTransmission();
}
delay(50);
}