sábado, 3 de enero de 2026

RELOJ DIGITAL NPT Y CLIMA CON ESP32 C3 SUPERMINI Y TFT ST7789

 Proyecto de reloj IOT que muestra hora y fecha, temperatura, humedad ambiente y estado del tiempo exterior en la ciudad elegida. Se ha usado un ESP32 C3 Supermini y un display GMT020-02-7P 240X320 con chipset ST7789. El código siguiente está realizado con Arduino IDE ver. 2.3.6

Conexion de pines:

TFT.........................ESP32 C3

CS .............................7

DC ............................20

RST ..........................21

SDA .........................9

SCL .........................4

VCC........................3V.

GND .......................GND


#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>
#include "time.h"

// ESP32 C3 SUPERMINI y TFT 2.0 TFTSPI GMT020-02-7P
#define TFT_CS    7
#define TFT_RST   21
#define TFT_DC    20
//#define TFT_MOSI  6  // SDA, HW MOSI
//#define TFT_SCLK  4  // SCL, HW SCLK
//#define TFT_MISO  5  // not used

Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

const char* ssid = "SSID WIFI"; //"LowiCD10"
const char* password = "PASSWORD WIFI"; //"2P3WHFC259TK3B"
const String apiKey = "APIKEY OPENWEATHERMAP"; // Tu clave de OpenWeatherMap
//const String ciudad_fija = "Granada,ES"; // Puedes fijarla o usar la detectada
const String ciudad_fija = "TU CIUDAD";
const char* ntpServer = "hora.roa.es"; //NPT HORA ESPAÑA
const long  gmtOffset_sec = 3600;      // Ajusta según tu país
const int   daylightOffset_sec = 0;    // 3600 si hay horario de verano

// 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

void obtenerClima() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    // URL para obtener clima por ciudad
    String url = "https://api.openweathermap.org/data/2.5/weather?q=" + ciudad_fija + "&appid=" + apiKey + "&units=metric&lang=es";
    //String url = "https://api.openweathermap.org/data/2.5/weather?q=granada&appid=20e3cd6f640b1ae47e37932983b07a09&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();
  }
}

void setup() {
  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"));

  // 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);
     }
 
  tft.fillScreen(ST77XX_BLACK);

  // Configurar NTP
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  obtenerClima();
 
}

void loop() {
  // 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();
  }

  // --- DIBUJAR EN PANTALLA ---
  tft.setTextColor(ST77XX_RED, ST77XX_BLACK);

  // HORA
  tft.setCursor(20, 20);
  tft.setTextSize(6);
 
  // Formato HH:MM:SS
  char buffer[10];
  sprintf(buffer, "%02d:%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
  tft.print(buffer);

  // Dibujar Fecha debajo
   tft.setTextColor(ST77XX_YELLOW, ST77XX_BLACK);
  tft.setCursor(55, 70);
  tft.setTextSize(3);
  char fecha[12];
  sprintf(fecha, "%02d/%02d/%04d", timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
  tft.print(fecha);

  //delay(1000); // Actualiza cada segundo

  // 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(" %");

  // 3. Actualizar linea de info meteo cada 60 segundows
  if (millis() - ultimaActualizacionInfo > intervaloInfo) {
 
  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;
  }

  // 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(1000); // Actualiza cada segundo
}

No hay comentarios:

Publicar un comentario