Buenos dias Makers !!
Hoy traigo un despertador realizado con una placa de Arduino. Podremos interactuar con este despertador desde cualquier navegador web, haciendo que sea muy fácil poner alarmas, desactivarlas o encender la lámpara.
Materiales
Con este despertador inteligente, podrás disfrutar de una experiencia completamente conectada y personalizable. Gracias a su conexión WiFi, el dispositivo sincroniza automáticamente la hora con servidores NTP, asegurando siempre una precisión absoluta. Además, su pantalla OLED no solo muestra la hora actual de manera clara y elegante, sino que también te informa sobre el estado de tus alarmas configuradas. Este despertador te permite establecer hasta seis alarmas diferentes, cada una ajustable a los días de la semana que prefieras, con la opción de activar un efecto de amanecer que simula gradualmente la luz natural para un despertar más suave y agradable.
A través de una interfaz web accesible desde cualquier dispositivo conectado, podrás controlar todas las funciones del despertador, incluyendo el encendido, apagado y ajuste del brillo del LED incorporado. Este LED también se utiliza para el efecto de amanecer, aumentando progresivamente su intensidad durante cinco minutos antes de que suene la alarma. Además, un buzzer incorporado emite tonos progresivos para asegurarte de que nunca pases por alto una alarma importante.
El diseño incluye botones físicos que permiten encender y apagar la pantalla o controlar manualmente el LED, brindándote opciones rápidas y cómodas sin necesidad de acceder a la web. Todas tus configuraciones se guardan en la memoria del dispositivo, asegurando que tus preferencias no se pierdan, incluso si se reinicia el sistema. Para mayor comodidad, el despertador ajusta automáticamente la hora según el horario de verano o estándar, ofreciendo siempre la mejor precisión sin que tengas que preocuparte por cambios manuales. Con un diseño modular y un código claro, este proyecto no solo es funcional, sino que también es ideal para quienes desean explorar y aprender más sobre el mundo de la programación y los microcontroladores.



Código
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <U8g2lib.h>
#include <ESP8266WebServer.h>
#include <TimeLib.h> // Añade esta librería para manejar funciones de tiempo
#include <EEPROM.h>
// Configuración de la red WiFi
const char* ssid = "";
const char* password = "";
unsigned long ultimoTiempoDePulsacion = 0;
const int debounceDelay = 500; // Retraso para el debounce, en milisegundos
const int debounceDelay_2 = 500; // Retraso para el debounce, en milisegundos
// Configuración de la pantalla OLED
U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, 5, 4, 16);
// Configuración del cliente NTP
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 3600, 1800000); // Offset de 1 hora
// Configuración de IP estática
IPAddress ipLocal(192, 168, 1, 102);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress dns(192, 168, 1, 1);
// Configuración del servidor web
ESP8266WebServer server(80);
// Pines
const int buzzerPin = 14;
const int buttonPin_1 = 12;
const int buttonPin_2 = 13;
// Estructura de la alarma
struct Alarma {
String hora;
bool dias[7];
bool activa;
bool amanecer; // Campo nuevo para la función de amanecer
} alarmas[6]; // alarmas
unsigned long pantallaEncendidaTiempo = 0;
bool pantallaEncendida = true;
bool alarmaActiva = false;
unsigned long inicioAlarma = 0;
const unsigned long duracionAlarma = 60000; // Duración total de la alarma en milisegundos
unsigned long ultimaActualizacionHora = 0;
const unsigned long intervaloActualizacionHora = 3600000; // 1 hora en milisegundos
const long offsetHorarioEstandar = 3600; // Cambia según tu zona horaria estándar en segundos
const long offsetHorarioVerano = 7200; // Cambia según tu zona horaria de verano en segundos
unsigned long ultimaVerificacionHorarioVerano = 0;
const unsigned long intervaloVerificacionHorarioVerano = 60000; // 1 minuto en milisegundos
unsigned long tiempoDesactivacionAlarmas[4] = {0, 0, 0, 0}; // Inicializa con ceros
const int ledPin = 15; // GPIO15 para el LED
int brilloLED = 0; // Valor de brillo del LED (0-255)
int lampara = 0;
// Variables globales para manejar el amanecer
bool amanecerActivo = false;
unsigned long inicioAmanecer = 0;
const int duracionAmanecer = 5 * 60 * 1000; // Duración del amanecer, por ejemplo, 5 minutos
int indiceAlarmaAmanecer = -1;
bool estadoLED = false; // false indica que el LED está apagado, true indica que está encendido
bool alarmasDesactivadas = false; // Nuevo estado global
void setup() {
Serial.begin(115200);
Serial.println("Iniciando...");
// Configurar IP estática
WiFi.config(ipLocal, gateway, subnet, dns);
Serial.begin(115200);
Serial.println("Iniciando...");
// Conectar a WiFi (utiliza DHCP por defecto)
WiFi.begin(ssid, password);
Serial.print("Conectando a WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi conectado");
Serial.print("Dirección IP asignada: ");
Serial.println(WiFi.localIP());
// Inicializar pantalla OLED
u8g2.begin();
u8g2.setContrast(0);
// Iniciar NTP Client
timeClient.begin();
// Iniciar servidor web
server.on("/", HTTP_GET, handleRoot);
server.on("/setAlarm", HTTP_POST, handleSetAlarm);
server.on("/alarmas", HTTP_GET, handleAlarmStatus); //PARA PODER VER LAS ALARMAS QUE TENGO CONFIGURADAS
server.on("/led/toggle", HTTP_GET, []() {
server.send(200, "text/plain", "OK");
});
server.on("/toggleAlarmas", HTTP_GET, []() {
alarmasDesactivadas = !alarmasDesactivadas;
if (alarmasDesactivadas) {
u8g2.setDrawColor(0); // Invertir colores
} else {
u8g2.setDrawColor(1); // Restaurar colores normales
}
// En lugar de texto plano, envía una página HTML básica
String respuesta = "<html><body><h1>";
respuesta += (alarmasDesactivadas ? "Alarmas Desactivadas" : "Alarmas Activadas");
respuesta += "</h1><a href=\"/\">Volver</a></body></html>";
server.send(200, "text/html", respuesta); // Corrige el formato de respuesta
});
server.begin();
// Inicializar pines
//pinMode(buzzerPin, OUTPUT);
pinMode(buttonPin_1, INPUT_PULLUP);
pinMode(buttonPin_2, INPUT_PULLUP); // Asumiendo que usas una resistencia pull-up interna
// Inicializar alarmas
for (int i = 0; i < 6; i++) {
alarmas[i].activa = false;
alarmas[i].amanecer = false;
for (int j = 0; j < 7; j++) {
alarmas[i].dias[j] = false;
}
}
//CONTROLAR LED
pinMode(ledPin, OUTPUT); // Configura el pin del LED como salida
brilloLED = 128; // Valor inicial de brillo (50%)
analogWrite(ledPin, brilloLED);
server.on("/led", HTTP_GET, []() {
String estado = server.arg("estado");
if (estado == "on") {
analogWrite(ledPin, brilloLED); // Enciende el LED con el último brillo configurado
} else if (estado == "off") {
analogWrite(ledPin, 0); // Apaga el LED
}
server.send(200, "text/plain", "OK");
});
server.on("/brillo", HTTP_GET, []() {
brilloLED = server.arg("valor").toInt();
analogWrite(ledPin, brilloLED); // Ajusta el brillo del LED
server.send(200, "text/plain", "OK");
});
ajustarOffsetHorarioVerano(); // Ajusta el offset inicial para el cliente NTP
//iniciamos el EPROM
EEPROM.begin(512); // Tamaño en bytes
cargarAlarmasDesdeEEPROM(); // Cargar alarmas al inicio
}
void loop() {
// Verifica y reconecta WiFi si es necesario
revisarYReconectarWiFi();
// Actualizar la hora y ajustar por horario de verano si es necesario
if (millis() - ultimaActualizacionHora > intervaloActualizacionHora) {
if (!timeClient.update()) {
timeClient.forceUpdate();
}
ultimaActualizacionHora = millis();
}
// Verificar y ajustar el offset del horario de verano cada minuto
if (millis() - ultimaVerificacionHorarioVerano > intervaloVerificacionHorarioVerano) {
ajustarOffsetHorarioVerano(); // Verifica y ajusta el offset según sea necesario
ultimaVerificacionHorarioVerano = millis();
}
// Manejar solicitudes del servidor web
server.handleClient();
// Actualizar la hora actual desde el cliente NTP
timeClient.update();
String horaActual = timeClient.getFormattedTime().substring(0, 5);
int diaSemana = timeClient.getDay();
// Manejo del botón de la luz
int estadoLecturaBoton = digitalRead(buttonPin_2);
unsigned long tiempoActual = millis();
// Verificar si se ha presionado el botón
if (estadoLecturaBoton == LOW && (tiempoActual - ultimoTiempoDePulsacion) > debounceDelay_2) {
ultimoTiempoDePulsacion = tiempoActual; // Actualizar el último tiempo de pulsación
// Si el amanecer está activo, apaga el LED y detiene el amanecer
if (amanecerActivo) {
analogWrite(ledPin, 0); // Apaga el LED
amanecerActivo = false; // Detiene el amanecer
Serial.println("Amanecer detenido manualmente");
}
// Si el amanecer no está activo, cambia el estado del LED
else {
if (estadoLED) {
analogWrite(ledPin, 0); // Apaga el LED
estadoLED = false; // Actualiza el estado del LED a apagado
} else {
analogWrite(ledPin, brilloLED); // Enciende el LED al brillo previamente configurado
estadoLED = true; // Actualiza el estado del LED a encendido
}
}
desactivarBuzzer(); // Desactiva la alarma si está activa
}
// Si las alarmas están desactivadas, no ejecutar más lógica de alarmas
if (!alarmasDesactivadas) {
for (int i = 0; i < 6; i++) {
// Reactivar alarma después de 1 minuto si fue desactivada
if (!alarmas[i].activa && (millis() - tiempoDesactivacionAlarmas[i]) > 60000) {
alarmas[i].activa = true;
}
// Manejar alarmas y efecto amanecer
if (alarmas[i].activa && alarmas[i].dias[diaSemana]) {
if (horaActual == alarmas[i].hora) {
activarBuzzerProgresivo();
} else if (esHoraDeAmanecer(alarmas[i].hora) && alarmas[i].amanecer && !amanecerActivo) {
iniciarAmanecer(i);
}
}
}
// Manejar el efecto de amanecer si está activo
manejarAmanecer();
}
// Mostrar la hora actual en la pantalla OLED
mostrarHora(horaActual);
// Manejo del botón de la pantalla OLED
int estadoBoton = digitalRead(buttonPin_1);
if (estadoBoton == LOW && (millis() - ultimoTiempoDePulsacion) > debounceDelay) {
if (pantallaEncendida) {
apagarPantalla();
} else {
encenderPantalla();
}
pantallaEncendida = !pantallaEncendida;
ultimoTiempoDePulsacion = millis();
}
}
bool esHoraDeAmanecer(String horaAlarma) {
// Obtén la hora actual en minutos desde medianoche
int hora = timeClient.getHours();
int minuto = timeClient.getMinutes();
int minutosActuales = hora * 60 + minuto;
// Convierte la hora de la alarma a minutos desde medianoche
int horaAlarmaInt = horaAlarma.substring(0, 2).toInt();
int minutoAlarma = horaAlarma.substring(3, 5).toInt();
int minutosAlarma = horaAlarmaInt * 60 + minutoAlarma;
// Comprueba si faltan exactamente 5 minutos para la alarma
return (minutosAlarma - minutosActuales == 5);
}
void mostrarHora(String hora) {
if (pantallaEncendida == true) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_fub30_tr);
int anchoTexto = u8g2.getStrWidth(hora.c_str());
int x = (128 - anchoTexto) / 2;
u8g2.drawStr(x, 32, hora.c_str());
u8g2.sendBuffer();
pantallaEncendidaTiempo = millis();
}
}
void apagarPantalla() {
const unsigned long tiempoMostrarTexto = 2000; // Esperar 2 segundos
for (int i = 0; i < 6; i++) {
mostrarAlarma(i);
delay(tiempoMostrarTexto);
}
u8g2.setPowerSave(1); // Apaga la pantalla
Serial.println("Pantalla apagada");
}
void encenderPantalla() {
// Muestra la hora actual
mostrarHora(timeClient.getFormattedTime().substring(0, 5));
// Enciende la pantalla
u8g2.setPowerSave(0);
Serial.println("Pantalla Encendida");
}
void mostrarAlarma(int indice) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_8x13_tr); // Elige una fuente más pequeña para mostrar varias líneas
// Primera línea con el índice y la hora
String texto = "Alarma " + String(indice + 1) + ": " + alarmas[indice].hora;
u8g2.drawStr(0, 10, texto.c_str());
// Segunda línea con los días
u8g2.setFont(u8g2_font_6x10_tr);
texto = "Dias: ";
for (int j = 0; j < 7; j++) {
if (alarmas[indice].dias[j]) {
texto += String(j) + " ";
}
}
u8g2.drawStr(0, 20, texto.c_str());
// Tercera línea con el estado del amanecer
texto = "Amanecer: " + String(alarmas[indice].amanecer ? "Si" : "No");
u8g2.drawStr(0, 30, texto.c_str()); // Ajustar la posición Y según sea necesario
u8g2.sendBuffer();
}
void activarBuzzerProgresivo() {
if (!alarmaActiva) {
// Iniciar la alarma
alarmaActiva = true;
inicioAlarma = millis();
// Iniciar con el tono de 21000 Hz
tone(buzzerPin, 21000);
}
if (alarmaActiva) {
// Controlar el estado de la alarma basado en el tiempo
Serial.println("Alarma activada");
unsigned long tiempoActual = millis();
unsigned long tiempoTranscurrido = tiempoActual - inicioAlarma;
if (tiempoTranscurrido < duracionAlarma) {
// Cambiar la frecuencia del tono según el tiempo transcurrido
if (tiempoTranscurrido < 30000) { // Primeros 20 segundos a 21000 Hz
if (tiempoTranscurrido % 800 < 100) {
tone(buzzerPin, 14000); // Tono activo
} else {
noTone(buzzerPin); // Tono inactivo
}
} else if (tiempoTranscurrido < 30000) { // Siguientes 20 segundos a 20000 Hz
if (tiempoTranscurrido % 400 < 100) {
tone(buzzerPin, 13000); // Tono activo
} else {
noTone(buzzerPin); // Tono inactivo
}
} else { // Después de 30 segundos, 18000 Hz
if (tiempoTranscurrido % 600 < 100) {
tone(buzzerPin, 10000); // Tono activo
} else {
noTone(buzzerPin); // Tono inactivo
}
}
} else {
// Detener la alarma después de su duración
noTone(buzzerPin);
alarmaActiva = false;
}
}
}
void desactivarBuzzer() {
Serial.println("Alarma desactivada");
//analogWrite(ledPin, 0); // lo añado para asegurarme que cuando apague el buzer, se apague la luz
digitalWrite(buzzerPin, LOW);
for (int i = 0; i < 6; i++) {
if (alarmas[i].activa) {
alarmas[i].activa = false;
analogWrite(ledPin, 0); // lo añado para asegurarme que cuando apague el buzer, se apague la luz
tiempoDesactivacionAlarmas[i] = millis(); // Registra el tiempo de desactivación
}
}
}
void handleRoot() {
String html = "<html><head>"
"<style>"
"body {"
" font-family: Arial, sans-serif;"
" background-color: #f0f0f0;"
" color: #333;"
" display: flex;"
" flex-direction: column;"
" justify-content: flex-start;"
" align-items: center;"
" height: 100vh;"
" margin: 0;"
" padding: 10px;"
" box-sizing: border-box;"
"}"
"h1 {"
" margin: 20px 0;"
"}"
".container {"
" background-color: #fff;"
" padding: 20px;"
" border-radius: 10px;"
" box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);"
" text-align: center;"
" max-width: 400px;"
" width: 100%;"
" margin-top: 40px;"
" box-sizing: border-box;"
"}"
"button {"
" background-color: #007bff;"
" color: white;"
" border: none;"
" padding: 10px 20px;"
" margin: 10px;"
" border-radius: 5px;"
" cursor: pointer;"
" font-size: 16px;"
"}"
"button:hover {"
" background-color: #0056b3;"
"}"
"input[type='range'] {"
" width: 100%;"
" margin: 10px 0;"
" height: 30px;"
"}"
"label {"
" display: block;"
" margin-top: 10px;"
" font-weight: bold;"
"}"
".alarm {"
" margin-bottom: 20px;"
" min-height: 100px;"
"}"
"</style>"
"</head><body>";
html += "<div class='container'>"
"<h1>Control de LED y Alarmas</h1>"
"<button onclick=\"encenderLed()\">Encender LED</button>"
"<button onclick=\"apagarLed()\">Apagar LED</button><br>"
"<input type=\"range\" id=\"brillo\" min=\"0\" max=\"255\" onchange=\"ajustarBrillo(this.value)\" /><br>"
"<h1>Configurar Alarmas</h1>"
"<form action='/setAlarm' method='post'>";
html += "<button onclick=\"toggleAlarmas()\">Des/Act Alarmas</button>";
html += "<script>"
"function toggleAlarmas() {"
" var xhr = new XMLHttpRequest();"
" xhr.open('GET', '/toggleAlarmas', true);"
" xhr.onreadystatechange = function() {"
" if (xhr.readyState === 4 && xhr.status === 200) {"
" document.body.innerHTML = xhr.responseText;"
" boton.disabled = false;"
" }"
" };"
" xhr.send();"
"}"
"</script>";
for (int i = 0; i < 6; i++) {
html += "<div class='alarm'>"
"<h2>Alarma " + String(i + 1) + "</h2>"
"<label for='hora" + String(i + 1) + "'>Hora ( HH:MM ):</label>"
"<input type='text' id='hora" + String(i + 1) + "' name='hora" + String(i + 1) + "' value='" + alarmas[i].hora + "' placeholder='HH:MM'><br>"
"<label for='dias" + String(i + 1) + "'>Dias (0-6, 0=Do):</label>"
"<input type='text' id='dias" + String(i + 1) + "' name='dias" + String(i + 1) + "' value='" + diasAlarmaComoCadena(i) + "' placeholder='0,1,2...'><br>"
"<label for='amanecer" + String(i + 1) + "'>Amanecer:</label>"
"<input type='checkbox' id='amanecer" + String(i + 1) + "' name='amanecer" + String(i + 1) + "'" + (alarmas[i].amanecer ? " checked" : "") + "><br>"
"</div>";
}
html += "<br><input type='submit' value='Configurar Alarmas' style='width:100%; background-color:#007bff; color:white; padding:10px 0; border:none; border-radius:5px;'></form></div>";
html += "<script>"
"function encenderLed() {"
" var xhr = new XMLHttpRequest();"
" xhr.open('GET', '/led?estado=on', true);"
" xhr.send();"
"}"
"function apagarLed() {"
" var xhr = new XMLHttpRequest();"
" xhr.open('GET', '/led?estado=off', true);"
" xhr.send();"
"}"
"function ajustarBrillo(val) {"
" var xhr = new XMLHttpRequest();"
" xhr.open('GET', '/brillo?valor=' + val, true);"
" xhr.send();"
"}"
"function saltarAlarma(index) {"
" var xhr = new XMLHttpRequest();"
" xhr.open('GET', '/skipAlarm?index=' + index, true);"
" xhr.send();"
"}"
"</script></body></html>";
server.send(200, "text/html", html);
}
// Función para convertir los días de la alarma a una cadena
String diasAlarmaComoCadena(int indiceAlarma) {
String dias = "";
for (int i = 0; i < 7; i++) {
if (alarmas[indiceAlarma].dias[i]) {
if (dias.length() > 0) dias += ",";
dias += String(i);
}
}
return dias;
}
void handleSetAlarm() {
for (int i = 0; i < 6; i++) {
String hora = server.arg("hora" + String(i + 1));
String dias = server.arg("dias" + String(i + 1));
String amanecer = server.arg("amanecer" + String(i + 1)); // Nuevo
configurarAlarma(i, hora, dias, amanecer == "on"); // Nuevo
}
server.sendHeader("Location", "/", true);
server.send(302, "text/plain", "");
guardarAlarmasEnEEPROM();
}
void configurarAlarma(int indice, String hora, String dias, bool amanecer) {
alarmas[indice].hora = hora;
alarmas[indice].activa = true;
alarmas[indice].amanecer = amanecer;
// Limpia el arreglo de días
memset(alarmas[indice].dias, 0, sizeof(alarmas[indice].dias));
// Convierte la cadena de días en un array booleano
for (unsigned int j = 0; j < dias.length(); j++) {
char diaChar = dias.charAt(j);
if (isDigit(diaChar)) {
int dia = diaChar - '0'; // Convertir el carácter a un número
if (dia >= 0 && dia < 7) {
alarmas[indice].dias[dia] = true;
}
}
}
// Verificación adicional para evitar errores con el día "0" (domingo)
if (dias.indexOf('0') == -1) {
alarmas[indice].dias[0] = false; // Desactiva el domingo si no está en la cadena
}
}
void handleAlarmStatus() {
String html = "<html><body><h1>Estado de las Alarmas</h1>";
for (int i = 0; i < 6; i++) {
html += "<h2>Alarma " + String(i + 1) + "</h2>"
"<p>Hora: " + alarmas[i].hora + "</p>"
"<p>Dias Activos: ";
for (int j = 0; j < 7; j++) {
if (alarmas[i].dias[j]) {
html += String(j) + " ";
}
}
html += "</p><p>Activa: " + String(alarmas[i].activa ? "Si" : "No") + "</p>";
}
html += "</body></html>";
server.send(200, "text/html", html);
}
void revisarYReconectarWiFi() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Desconectado de WiFi. Intentando reconectar...");
WiFi.disconnect();
WiFi.begin(ssid, password);
unsigned long tiempoInicio = millis();
while (WiFi.status() != WL_CONNECTED && (millis() - tiempoInicio) < 10000) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi reconectado con éxito.");
Serial.println("Dirección IP: " + WiFi.localIP().toString());
} else {
Serial.println("\nNo se pudo reconectar a WiFi.");
}
}
}
void iniciarAmanecer(int indiceAlarma) {
if (!amanecerActivo) {
amanecerActivo = true;
inicioAmanecer = millis();
indiceAlarmaAmanecer = indiceAlarma;
Serial.println("Iniciando amanecer para alarma " + String(indiceAlarma + 1));
}
}
String calcularHoraAmanecer(String horaAlarma) {
int hora = horaAlarma.substring(0, 2).toInt();
int minuto = horaAlarma.substring(3, 5).toInt();
minuto -= 5;
if (minuto < 0) {
minuto += 60;
hora--;
if (hora < 0) {
hora += 24;
}
}
char buffer[6];
sprintf(buffer, "%02d:%02d:00", hora, minuto);
return String(buffer);
}
void manejarAmanecer() {
if (amanecerActivo) {
unsigned long tiempoTranscurrido = millis() - inicioAmanecer;
// Calcula el brillo del LED en función del tiempo transcurrido
int brillo = map(tiempoTranscurrido, 0, duracionAmanecer, 0, 255);
brillo = min(brillo, 255); // Asegúrate de que el brillo no exceda 255
analogWrite(ledPin, brillo);
// Finaliza el amanecer si se alcanza la duración total
if (tiempoTranscurrido > duracionAmanecer) {
amanecerActivo = false;
indiceAlarmaAmanecer = -1;
Serial.println("Amanecer finalizado");
}
}
}
void ajustarOffsetHorarioVerano() {
time_t now = timeClient.getEpochTime();
tmElements_t tm;
breakTime(now, tm); // Convierte el tiempo epoch a elementos de tiempo (año, mes, día, etc.)
bool esHorarioVerano = false; // Aquí debes implementar tu lógica para determinar si actualmente debería ser horario de verano
Serial.println("EsHorarioVerano False");
Serial.println("Mes: ");
Serial.print(tm.Month);
// Por ejemplo, para la UE, el horario de verano comienza el último domingo de marzo y termina el último domingo de octubre
if (tm.Month > 3 && tm.Month < 10) {
esHorarioVerano = true; // De abril a septiembre es definitivamente horario de verano
Serial.println("Horario de verano");
} else if (tm.Month == 3 && (lastSunday(tm.Year, 3) <= tm.Day)) {
esHorarioVerano = true; // Último domingo de marzo o después
Serial.println("Horario de verano 2");
} else if (tm.Month == 10 && (lastSunday(tm.Year, 10) > tm.Day)) {
esHorarioVerano = true; // Antes del último domingo de octubre
Serial.println("Horario de verano 3");
}
if (esHorarioVerano) {
timeClient.setTimeOffset(offsetHorarioVerano);
} else {
timeClient.setTimeOffset(offsetHorarioEstandar);
}
}
// Esta función encuentra el último domingo del mes dado
int lastSunday(int year, int month) {
tmElements_t tm; // Crea una instancia de tmElements_t
tm.Year = year - 1970; // TimeLib maneja los años como años desde 1970
tm.Month = month;
tm.Day = 31; // Empieza con el último día del mes y ve hacia atrás
int dayOfWeek;
do {
time_t time = makeTime(tm); // Convierte tmElements_t en time_t
dayOfWeek = weekday(time); // Obtiene el día de la semana
if (dayOfWeek != 1) { // 1 es domingo en TimeLib
tm.Day--; // Decrementa el día hasta encontrar el domingo
}
} while (dayOfWeek != 1);
return tm.Day; // Retorna el día del último domingo del mes
}
void guardarAlarmasEnEEPROM() {
for (int i = 0; i < 6; i++) {
int base = i * 20; // Espacio asignado para cada alarma
// Guardar hora (como texto de longitud fija "HH:MM")
for (int j = 0; j < 5; j++) {
EEPROM.write(base + j, alarmas[i].hora[j]);
}
// Guardar días activos
for (int j = 0; j < 7; j++) {
EEPROM.write(base + 5 + j, alarmas[i].dias[j]);
}
// Guardar estado de la alarma
EEPROM.write(base + 12, alarmas[i].activa);
EEPROM.write(base + 13, alarmas[i].amanecer);
}
// Guardar cambios
EEPROM.commit();
}
void cargarAlarmasDesdeEEPROM() {
for (int i = 0; i < 6; i++) {
int base = i * 20; // Espacio asignado para cada alarma
// Cargar hora
String hora = "";
for (int j = 0; j < 5; j++) {
hora += char(EEPROM.read(base + j));
}
alarmas[i].hora = hora;
// Cargar días activos
for (int j = 0; j < 7; j++) {
alarmas[i].dias[j] = EEPROM.read(base + 5 + j);
}
// Cargar estado de la alarma
alarmas[i].activa = EEPROM.read(base + 12);
alarmas[i].amanecer = EEPROM.read(base + 13);
}
}
Sólidos para descargar
Descargar el 3D ( Enlace )