Proyecto 005 – Despertador y lámpara Arduino

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

AZDelivery NodeMCU ESP8266 Heltec con Pantalla OLED ( Enlace )
Buzzer activo ( Enlace )
Pulsadores ( Enlace )
Tornillo D2x8 ( Enlace )

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 )

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *