Initial version
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.theia/
|
||||||
417
RelojArduino.ino
Normal file
417
RelojArduino.ino
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include "RTClib.h"
|
||||||
|
#include "DHT.h"
|
||||||
|
|
||||||
|
// --- LIBRERÍAS ADICIONALES PARA SINCRONIZACIÓN ---
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
|
||||||
|
|
||||||
|
// --- Definiciones de hardware ---
|
||||||
|
#define SCREEN_WIDTH 128
|
||||||
|
#define SCREEN_HEIGHT 64
|
||||||
|
#define OLED_RESET -1
|
||||||
|
#define SCREEN_ADDRESS 0x3C
|
||||||
|
#define DHTPIN 4 // Pin donde conectaste el DHT11
|
||||||
|
#define DHTTYPE DHT11
|
||||||
|
|
||||||
|
|
||||||
|
// --- Constantes de tiempo ---
|
||||||
|
#define DHT_READ_INTERVAL 10 // Leer DHT cada 10 segundos
|
||||||
|
|
||||||
|
|
||||||
|
// --- Credenciales y NTP (Network Time Protocol) ---
|
||||||
|
const char* ntpServer = "pool.ntp.org";
|
||||||
|
const long gmtOffset_sec = 0; // NTP configurado a UTC (Offset 0)
|
||||||
|
const int daylightOffset_sec = 0; // Se ignora aquí
|
||||||
|
|
||||||
|
|
||||||
|
// --- Inicialización de objetos ---
|
||||||
|
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||||
|
RTC_DS3231 rtc;
|
||||||
|
DHT dht(DHTPIN, DHTTYPE);
|
||||||
|
WiFiManager wm; // Objeto de WiFiManager
|
||||||
|
|
||||||
|
|
||||||
|
// --- Variables de estado ---
|
||||||
|
int lastSecond = -1;
|
||||||
|
float currentTempC = 0.0;
|
||||||
|
int lastDHTReadSecond = -100;
|
||||||
|
bool rtcIsPresent = true;
|
||||||
|
int lastSyncHour = -1;
|
||||||
|
bool wifiModuleOn = false;
|
||||||
|
|
||||||
|
// NUEVA VARIABLE: Para rastrear si la conexión fue automática (credenciales encontradas)
|
||||||
|
bool wasAutoConnected = false;
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================
|
||||||
|
// Determina si Madrid está en horario de verano (CEST: UTC+2)
|
||||||
|
// =========================================
|
||||||
|
bool isDaylightSaving(const DateTime& dt) {
|
||||||
|
int year = dt.year();
|
||||||
|
DateTime lastSundayMarch(year, 3, 31, 0, 0, 0);
|
||||||
|
while (lastSundayMarch.dayOfTheWeek() != 0) lastSundayMarch = lastSundayMarch - TimeSpan(1, 0, 0, 0);
|
||||||
|
DateTime lastSundayOctober(year, 10, 31, 0, 0, 0);
|
||||||
|
while (lastSundayOctober.dayOfTheWeek() != 0) lastSundayOctober = lastSundayOctober - TimeSpan(1, 0, 0, 0);
|
||||||
|
|
||||||
|
DateTime startDST_UTC = lastSundayMarch + TimeSpan(0, 2, 0, 0);
|
||||||
|
DateTime endDST_UTC = lastSundayOctober + TimeSpan(0, 1, 0, 0);
|
||||||
|
|
||||||
|
return (dt >= startDST_UTC && dt < endDST_UTC);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================
|
||||||
|
// Obtiene la hora de Madrid (UTC + offset)
|
||||||
|
// =========================================
|
||||||
|
DateTime getMadridTime(const DateTime& rtcTime) {
|
||||||
|
bool summer = isDaylightSaving(rtcTime);
|
||||||
|
int offset = summer ? 2 : 1;
|
||||||
|
return rtcTime + TimeSpan(0, offset, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================
|
||||||
|
// Función: mostrar hora, segundos, fecha, DST y temperatura
|
||||||
|
// =========================================
|
||||||
|
void printTime(const char* timeStr, const char* secondsStr, const char* dateStr, const char* dstStr, float tempC, bool wifiIsOn) {
|
||||||
|
int16_t x1, y1;
|
||||||
|
uint16_t w, h;
|
||||||
|
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
|
||||||
|
// --- Hora grande (HH:MM) ---
|
||||||
|
display.setTextSize(3);
|
||||||
|
display.getTextBounds(timeStr, 0, 0, &x1, &y1, &w, &h);
|
||||||
|
int xHour = (SCREEN_WIDTH - (w + 6 + 6)) / 2;
|
||||||
|
int yHour = (SCREEN_HEIGHT - h) / 2 - 4;
|
||||||
|
|
||||||
|
display.setCursor(xHour, yHour);
|
||||||
|
display.print(timeStr);
|
||||||
|
|
||||||
|
// --- Segundos (SS) ---
|
||||||
|
display.setTextSize(1);
|
||||||
|
int16_t xS, yS;
|
||||||
|
uint16_t wS, hS;
|
||||||
|
display.getTextBounds(secondsStr, 0, 0, &xS, &yS, &wS, &hS);
|
||||||
|
int xSec = xHour + w + 2;
|
||||||
|
int ySec = yHour + (h - hS) / 2;
|
||||||
|
display.setCursor(xSec, ySec);
|
||||||
|
display.print(secondsStr);
|
||||||
|
|
||||||
|
// --- Fecha (DD/MM/YYYY) ---
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.getTextBounds(dateStr, 0, 0, &x1, &y1, &w, &h);
|
||||||
|
int xDate = SCREEN_WIDTH - w - 1;
|
||||||
|
int yDate = SCREEN_HEIGHT - h - 1;
|
||||||
|
display.setCursor(xDate, yDate);
|
||||||
|
display.print(dateStr);
|
||||||
|
|
||||||
|
// --- DST (CET/CEST) ---
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.getTextBounds(dstStr, 0, 0, &x1, &y1, &w, &h);
|
||||||
|
int xDst = SCREEN_WIDTH - w - 1;
|
||||||
|
int yDst = 1;
|
||||||
|
display.setCursor(xDst, yDst);
|
||||||
|
display.print(dstStr);
|
||||||
|
|
||||||
|
// --- Temperatura (X.XC) ---
|
||||||
|
display.setTextSize(1);
|
||||||
|
char tempStr[8];
|
||||||
|
snprintf(tempStr, sizeof(tempStr), "%.1fC", tempC);
|
||||||
|
display.getTextBounds(tempStr, 0, 0, &x1, &y1, &w, &h);
|
||||||
|
int xTemp = 1;
|
||||||
|
int yTemp = SCREEN_HEIGHT - h - 1;
|
||||||
|
display.setCursor(xTemp, yTemp);
|
||||||
|
display.print(tempStr);
|
||||||
|
|
||||||
|
// --- MENSAJE DE ESTADO WIFI (Encima de la temperatura) ---
|
||||||
|
display.setTextSize(1);
|
||||||
|
const char* wifiStr = wifiIsOn ? "WiFi: ON" : "WiFi: OFF";
|
||||||
|
|
||||||
|
display.getTextBounds(wifiStr, 0, 0, &x1, &y1, &w, &h);
|
||||||
|
int xWifi = xTemp; // Misma X que la temperatura
|
||||||
|
int yWifi = yTemp - h - 1; // Una línea encima de la temperatura
|
||||||
|
|
||||||
|
display.setCursor(xWifi, yWifi);
|
||||||
|
display.print(wifiStr);
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================
|
||||||
|
// Maneja la conexión WiFi y el portal cautivo
|
||||||
|
// Retorna true si está conectado, false si no.
|
||||||
|
// =========================================
|
||||||
|
bool handleWifi(bool forceConfig) {
|
||||||
|
|
||||||
|
// 1. Encender el módulo Wi-Fi
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
wifiModuleOn = true;
|
||||||
|
wasAutoConnected = false; // Resetear el estado de conexión
|
||||||
|
|
||||||
|
// Mensaje de estado inicial
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.println("Iniciando WiFi...");
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
bool connected = false;
|
||||||
|
|
||||||
|
if (forceConfig) {
|
||||||
|
// Modo de Configuración Forzada (Portal Cautivo)
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.println("CONFIG. FORZADA.");
|
||||||
|
display.println("Conecte a 'AutoConnectAP'");
|
||||||
|
display.println("Clave: 12345678");
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
// Bloquea aquí hasta que se configure y conecte
|
||||||
|
wm.setConfigPortalBlocking(true);
|
||||||
|
wm.setDebugOutput(false);
|
||||||
|
connected = wm.startConfigPortal("AutoConnectAP", "12345678");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Modo de Conexión Normal (Intenta usar credenciales guardadas)
|
||||||
|
wm.setConfigPortalBlocking(false);
|
||||||
|
wm.setConnectRetries(3);
|
||||||
|
wm.setConnectTimeout(10);
|
||||||
|
|
||||||
|
// autoConnect devuelve true si se conecta.
|
||||||
|
// Si no pudo conectar y no necesita portal, devolverá false.
|
||||||
|
connected = wm.autoConnect("AutoConnectAP", "12345678");
|
||||||
|
|
||||||
|
// Si se conectó y NO es una configuración forzada, asumimos que usó credenciales guardadas.
|
||||||
|
if (connected) {
|
||||||
|
wasAutoConnected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Apagar Wi-Fi si no está conectado
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
btStop();
|
||||||
|
wifiModuleOn = false;
|
||||||
|
wasAutoConnected = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si llegamos aquí, está conectado.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================
|
||||||
|
// Sincroniza la hora con NTP.
|
||||||
|
// =========================================
|
||||||
|
void syncRtcFromNtp(bool adjustRtc) {
|
||||||
|
|
||||||
|
// 1. Conectar/Configurar WiFi. Si falla, salimos.
|
||||||
|
if (!handleWifi(false)) {
|
||||||
|
display.println("\nNO HAY CONEXION.");
|
||||||
|
display.display();
|
||||||
|
delay(3000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🚩 IMPLEMENTACIÓN DEL REQUISITO: Mensaje de éxito de la conexión SOLO si fue conexión automática
|
||||||
|
if (wasAutoConnected) {
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.println("Credenciales encontradas!");
|
||||||
|
display.println("Conexion WiFi OK.");
|
||||||
|
display.println("Sincronizando hora...");
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
// HACEMOS UNA ESPERA DE 10 SEGUNDOS
|
||||||
|
unsigned long startWait = millis();
|
||||||
|
// Bucle de espera para mostrar el mensaje durante 10 segundos
|
||||||
|
while (millis() - startWait < 10000) {
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 2. Configurar y sincronizar hora NTP (UTC) con el reloj interno del ESP32
|
||||||
|
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
|
||||||
|
|
||||||
|
struct tm timeinfo;
|
||||||
|
if (!getLocalTime(&timeinfo)) {
|
||||||
|
display.clearDisplay(); // Limpiar y mostrar error
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.println("WiFi OK, pero ERROR NTP!");
|
||||||
|
display.display();
|
||||||
|
delay(3000);
|
||||||
|
goto end_sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Ajustar el RTC con la hora UTC obtenida
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.setTextSize(1);
|
||||||
|
|
||||||
|
if (adjustRtc && rtcIsPresent) {
|
||||||
|
DateTime now(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
|
||||||
|
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
|
||||||
|
rtc.adjust(now);
|
||||||
|
display.println("RTC Sincronizado (UTC).");
|
||||||
|
} else {
|
||||||
|
display.println("Sistema Sincronizado.");
|
||||||
|
}
|
||||||
|
display.display();
|
||||||
|
delay(3000);
|
||||||
|
|
||||||
|
end_sync:
|
||||||
|
// 4. Apagar Wi-Fi para ahorrar energía después de la sincronización
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
btStop();
|
||||||
|
wifiModuleOn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================
|
||||||
|
// Comprueba si el RTC está presente e intenta reanudarlo
|
||||||
|
// =========================================
|
||||||
|
void checkRtcStatusAndSync() {
|
||||||
|
bool rtcPreviouslyPresent = rtcIsPresent;
|
||||||
|
|
||||||
|
// Intentar re-inicializar el RTC
|
||||||
|
if (!rtc.begin()) {
|
||||||
|
if (rtcPreviouslyPresent) { // Si acaba de desconectarse
|
||||||
|
rtcIsPresent = false;
|
||||||
|
// Sincroniza solo el sistema para obtener la hora actual
|
||||||
|
syncRtcFromNtp(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!rtcPreviouslyPresent) { // Si acaba de conectarse
|
||||||
|
// Sincroniza el RTC con NTP para reanudar el reloj
|
||||||
|
syncRtcFromNtp(true);
|
||||||
|
}
|
||||||
|
rtcIsPresent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================
|
||||||
|
// setup()
|
||||||
|
// =========================================
|
||||||
|
void setup() {
|
||||||
|
Wire.begin(21, 22);
|
||||||
|
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
|
||||||
|
display.clearDisplay();
|
||||||
|
display.display();
|
||||||
|
dht.begin();
|
||||||
|
|
||||||
|
// 1. Inicialización y comprobación del RTC
|
||||||
|
if (!rtc.begin()) {
|
||||||
|
rtcIsPresent = false;
|
||||||
|
display.ssd1306_command(0x81);
|
||||||
|
display.ssd1306_command(0); // Valor de contraste (brillo)
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.println("RTC NO ENCONTRADO EN ARRANQUE!");
|
||||||
|
display.display();
|
||||||
|
delay(3000);
|
||||||
|
|
||||||
|
// RTC no está presente: Borramos credenciales y forzamos el portal.
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.println("Borrando WiFi guardado...");
|
||||||
|
display.display();
|
||||||
|
delay(1000);
|
||||||
|
wm.resetSettings(); // Borra las credenciales de Wi-Fi guardadas
|
||||||
|
|
||||||
|
// Continúa con la configuración del WiFi Manager (Bloqueante)
|
||||||
|
handleWifi(true);
|
||||||
|
|
||||||
|
// Sincroniza el sistema (no el RTC) después de la configuración.
|
||||||
|
syncRtcFromNtp(false);
|
||||||
|
|
||||||
|
// NOTA: wasAutoConnected se pone a false en handleWifi(true), por lo que la espera de 30s no se ejecutará aquí.
|
||||||
|
|
||||||
|
} else if (rtc.lostPower()) {
|
||||||
|
// RTC presente pero perdió energía: sincroniza con la configuración guardada.
|
||||||
|
syncRtcFromNtp(true);
|
||||||
|
// NOTA: Si autoConnect tiene éxito, wasAutoConnected será true y se ejecutará la espera de 30s.
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// RTC OK, apagamos el módulo Wi-Fi por defecto
|
||||||
|
WiFi.mode(WIFI_OFF);
|
||||||
|
btStop();
|
||||||
|
wifiModuleOn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =========================================
|
||||||
|
// loop()
|
||||||
|
// =========================================
|
||||||
|
void loop() {
|
||||||
|
|
||||||
|
// 1. OBTENCIÓN DE HORA (depende de si el RTC está presente o no)
|
||||||
|
DateTime nowUTC;
|
||||||
|
if (rtcIsPresent) {
|
||||||
|
nowUTC = rtc.now();
|
||||||
|
} else {
|
||||||
|
struct tm timeinfo;
|
||||||
|
if (getLocalTime(&timeinfo)) {
|
||||||
|
nowUTC = DateTime(timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
|
||||||
|
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
|
||||||
|
} else {
|
||||||
|
nowUTC = DateTime(2000, 1, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. CONTROL HORARIO: Comprobar RTC y Sincronizar con NTP una vez cada hora (basado en la hora UTC)
|
||||||
|
if (nowUTC.hour() != lastSyncHour) {
|
||||||
|
if (lastSyncHour != -1) {
|
||||||
|
// NOTA: En la comprobación horaria (loop), wasAutoConnected no se usará para la espera.
|
||||||
|
checkRtcStatusAndSync();
|
||||||
|
}
|
||||||
|
lastSyncHour = nowUTC.hour();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Control para leer la temperatura solo cada N segundos (10s)
|
||||||
|
if (nowUTC.second() % DHT_READ_INTERVAL == 0 && nowUTC.second() != lastDHTReadSecond) {
|
||||||
|
|
||||||
|
float tempC = dht.readTemperature();
|
||||||
|
if (!isnan(tempC)) {
|
||||||
|
currentTempC = tempC;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastDHTReadSecond = nowUTC.second();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Actualizar la pantalla solo si el segundo ha cambiado
|
||||||
|
if (nowUTC.second() != lastSecond) {
|
||||||
|
lastSecond = nowUTC.second();
|
||||||
|
|
||||||
|
// 5. Calcular la hora de Madrid aplicando la zona horaria/DST
|
||||||
|
DateTime nowMadrid = getMadridTime(nowUTC);
|
||||||
|
|
||||||
|
char timeStr[6]; // HH:MM
|
||||||
|
char secStr[3]; // SS
|
||||||
|
char dateStr[11]; // DD/MM/YYYY
|
||||||
|
char dstStr[5]; // CET / CEST
|
||||||
|
|
||||||
|
snprintf(timeStr, sizeof(timeStr), "%02d:%02d", nowMadrid.hour(), nowMadrid.minute());
|
||||||
|
snprintf(secStr, sizeof(secStr), "%02d", nowMadrid.second());
|
||||||
|
snprintf(dateStr, sizeof(dateStr), "%02d/%02d/%04d", nowMadrid.day(), nowMadrid.month(), nowMadrid.year());
|
||||||
|
|
||||||
|
bool summer = isDaylightSaving(nowUTC);
|
||||||
|
snprintf(dstStr, sizeof(dstStr), "%s", summer ? "CEST" : "CET");
|
||||||
|
|
||||||
|
printTime(timeStr, secStr, dateStr, dstStr, currentTempC, wifiModuleOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user