#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include "time.h"
#include <WebServer.h>
#include <Preferences.h>
// --- CONFIGURACIÓN DE IP ESTÁTICA ---
IPAddress local_IP(192, 168, 0, 160); // Tu IP fija para el servidor web de ESP32 C3
IPAddress gateway(192, 168, 0, 1); // IP de tu router (ajusta si es 192.168.1.1)
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(212, 166, 132, 118); //8, 8, 8, 8 // Necesario para NTP y OpenWeather
// ESP32 C3 SUPERMINI Pins
#define TFT_CS 7
#define TFT_RST 9
#define TFT_DC 10
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
WebServer server(80);
Preferences preferences;
const char* ssid = "SSID";//Tu SSID
const char* password = "password";//Wifi password
const String apiKey = "AIKEY";
const String ciudad_fija = "ciudad";
const char* ntpServer = "hora.roa.es";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 0;
// Variables de datos
float temp = 0;
float veloc = 0;
int hum = 0;
String climaDesc = "...";
String site = "...";
String country = "..";
unsigned long ultimaActualizacionClima = 0;
const long intervaloClima = 600000; // Actualizar cada 10 minutos (600,000 ms)
int secuencia = 1;
unsigned long ultimaActualizacionInfo = 0;
const long intervaloInfo = 10000; // Actualizar info 10 segundos
int alarmaHora = -1;
int alarmaMinuto = -1;
bool alarmaActiva = false;
// --- FUNCIONES WEB ---
void handleRoot() {
String html = "<html><head><meta charset='UTF-8'><title>Alarma ESP32</title>";
html += "<style>body{font-family:sans-serif; text-align:center; background:#222; color:white;} .btn{background:#00adb5; border:none; color:white; padding:15px; border-radius:5px;}</style></head><body>";
html += "<h1>Reloj NTP - Alarma</h1>";
html += "<form action='/set' method='GET'>Hora: <input type='time' name='t' style='padding:10px;' required><br><br>";
html += "<input type='submit' class='btn' value='Establecer Alarma'></form>";
if(alarmaActiva) {
html += "<h3>Alarma programada: " + String(alarmaHora < 10 ? "0" : "") + String(alarmaHora) + ":" + String(alarmaMinuto < 10 ? "0" : "") + String(alarmaMinuto) + "</h3>";
html += "<a href='/clear'><button style='background:red; color:white; padding:10px;'>Borrar Alarma</button></a>";
}
html += "</body></html>";
server.send(200, "text/html", html);
}
void handleSetAlarm() {
String t = server.arg("t");
alarmaHora = t.substring(0, 2).toInt();
alarmaMinuto = t.substring(3, 5).toInt();
alarmaActiva = true;
preferences.begin("reloj", false);
preferences.putInt("h", alarmaHora);
preferences.putInt("m", alarmaMinuto);
preferences.end();
server.send(200, "text/html", "<h1>OK! Alarma configurada.</h1><a href='/'>Volver</a>");
}
void handleClearAlarm() {
alarmaActiva = false;
alarmaHora = -1;
preferences.begin("reloj", false);
preferences.putInt("h", -1);
preferences.end();
server.send(200, "text/html", "<h1>Alarma desactivada.</h1><a href='/'>Volver</a>");
}
void setup() {
Serial.begin(115200);
tft.init(240, 320);
tft.setRotation(3);
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
tft.setTextSize(3);
tft.setTextWrap(true);
tft.setCursor(10, 50);
tft.print("Inicializando la pantalla.");
delay(2000);
tft.fillScreen(ST77XX_BLACK);
Serial.println(F("TFT Initialized"));
// 1. Configurar IP Estática ANTES de WiFi.begin
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS)) {
Serial.println("Error al configurar IP Estática");
}
// 2. Conexión WiFi
tft.setCursor(10, 50);
tft.print("Conectando WIFI..");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
tft.setCursor(10, 50);
tft.print("Conectando WIFI..");
delay(500);
tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);
tft.setCursor(10, 50);
tft.print("Conectando WIFI..");
delay(500);
Serial.println(F("Conectando WIFI.."));
}
Serial.println("Conectado! IP: " + WiFi.localIP().toString());
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
tft.setCursor(10, 70);
tft.print("Conectado.");
delay(500);
tft.fillScreen(ST77XX_BLACK);
// 3. Cargar alarma de memoria
preferences.begin("reloj", true);
alarmaHora = preferences.getInt("h", -1);
alarmaMinuto = preferences.getInt("m", -1);
alarmaActiva = (alarmaHora != -1);
preferences.end();
server.on("/", handleRoot);
server.on("/set", handleSetAlarm);
server.on("/clear", handleClearAlarm);
server.begin();
// 4.. Configurar NTP
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
obtenerClima();
}
void loop() {
server.handleClient();
// 1. Gestionar Tiempo (NTP)
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
tft.setCursor(10, 10);
tft.print("Error Hora");
delay(3000);
tft.fillScreen(ST77XX_BLACK);
return;
}
// 2. Actualizar clima cada 10 minutos sin bloquear el reloj
if (millis() - ultimaActualizacionClima > intervaloClima) {
obtenerClima();
ultimaActualizacionClima = millis();
}
// 3. Comprobar Alarma
if(alarmaActiva && timeinfo.tm_hour == alarmaHora && timeinfo.tm_min == alarmaMinuto) {
if(timeinfo.tm_sec % 2 == 0){
//tft.fillScreen(ST77XX_RED);
tft.setCursor(20, 20);
tft.setTextSize(6);
tft.setTextColor(ST77XX_GREEN, ST77XX_BLACK);
char timeBuff[10];
sprintf(timeBuff, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
tft.print(timeBuff);
delay(500);
}
else{
//tft.fillScreen(ST77XX_BLACK);
tft.setCursor(20, 20);
tft.setTextSize(6);
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
char timeBuff[10];
sprintf(timeBuff, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
tft.print(timeBuff);
delay(500);
}
}
// 4. Dibujar Hora
tft.setCursor(20, 20);
tft.setTextSize(6);
tft.setTextColor(ST77XX_RED, ST77XX_BLACK);
char timeBuff[10];
sprintf(timeBuff, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
tft.print(timeBuff);
// 5. Dibujar Fecha debajo
tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);
tft.setCursor(10, 75);
tft.setTextSize(2);
char fecha[12];
sprintf(fecha, "%02d/%02d/%04d", timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
tft.print(fecha);
// 6. Dibujar Info Alarma
tft.setTextSize(2);
tft.setCursor(160, 75);
if(alarmaActiva) {
tft.setTextColor(ST77XX_GREEN, ST77XX_BLACK);
char alMsg[20];
sprintf(alMsg, "ALR ON: %02d:%02d", alarmaHora, alarmaMinuto);
tft.print(alMsg);
} else {
tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);
tft.print("ALR: OFF ");
}
// 7. Actualizar linea de info meteo cada 60 segundows
if (millis() - ultimaActualizacionInfo > intervaloInfo) {
tft.setTextSize(3);
tft.setCursor(1, 165);
tft.setTextColor(ST77XX_CYAN, ST77XX_BLACK);
tft.setTextWrap(false);
tft.print(" "); // Espacios para limpiar texto anterior
if (secuencia >= 3) {secuencia = 1;}
if (secuencia == 1) {
// DESCRIPCIÓN CLIMA (Usamos setTextWrap(false) para evitar saltos raros)
tft.setCursor(1, 165);
tft.setTextColor(ST77XX_CYAN, ST77XX_BLACK);
tft.setTextWrap(false);
tft.print(climaDesc + " "); // Espacios para limpiar texto anterior
}
if (secuencia == 2) {
// DESCRIPCIÓN CLIMA (Usamos setTextWrap(false) para evitar saltos raros)
tft.setCursor(1, 165);
tft.setTextColor(ST77XX_CYAN, ST77XX_BLACK);
tft.setTextWrap(false);
tft.print("Viento: "); tft.print(veloc); tft.print(" Km/h");
}
ultimaActualizacionInfo = millis();
secuencia = secuencia + 1;
}
// TEMPERATURA Y HUMEDAD
tft.setTextSize(3);
tft.setTextColor(ST77XX_WHITE, ST77XX_BLACK);
tft.setCursor(10, 110);
tft.print("Temperat: "); tft.print(temp, 1); tft.print(" C");
tft.setCursor(10, 135);
tft.print("Humedad: "); tft.print(hum); tft.print(" %");
// 8. CIUDAD Y FECHA (Base)
tft.setTextSize(3);
tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);
tft.setCursor(10, 200);
//tft.print("Site: " + ciudad_fija);
tft.print("Site:"); tft.print(site);tft.print(",");tft.print(country);
delay(500);
}
void obtenerClima() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
String url = "https://api.openweathermap.org/data/2.5/weather?q=" + ciudad_fija + "&appid=" + apiKey + "&units=metric&lang=es";
http.begin(url);
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
StaticJsonDocument<1024> doc;
deserializeJson(doc, payload);
temp = doc["main"]["temp"];
hum = doc["main"]["humidity"];
veloc = doc["wind"]["speed"];
climaDesc = doc["weather"][0]["description"].as<String>();
site = doc["name"].as<String>();
country = doc["sys"]["country"].as<String>();
climaDesc.toUpperCase();
}
http.end();
}
}
No hay comentarios:
Publicar un comentario