vineri, 27 februarie 2026

Ceas cu termometru, display LCD, emitator WiFi

 Va salut !

Tot cu ajutorul AI am realizat un ceas, cu Wemos D1 mini, care afiseaza pe un dispay LCD si temperatura citita cu SHT40, transmisa din exteriorul locuintei, tot de un Wemos D1 mini . Dupa muuuulte teste si muuuuulte buguri rezolvate, pot spune ca  functioneaza foarte bine, nu am mai gasit nicio problema.Trecerea la ora de vara/iarna se face automat.

Ceasul propriu-zis il alimentez dintr-un alimentator de telefon mobil, de 5 volti. Emitatorul este alimentat dintr-un acumulator Li-Ion, a carui tensiune este citita periodic ; daca scade sub pragul critic, apare mesajul "ACCU" pe displayul ceasului. Consumul emitatorului este foarte mic pentru ca transmisia temperaturii se face o data la 5 minute, dupa care montajul intra in deep sleep. Alimentarea se face cu un IC specializat, HT7333. 

UPDATE 12/03/2026 (bug conectare corectat). 

UPDATE 28/03/2026 - Modificand putin codul si folosind aplicatia BLYNK pot vedea temperatura pe telefonul mobil, de oriunde. Inclusiv graficul evolutiei zilnice, saptamanala, lunara 


 

 


 

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <TFT_eSPI.h>
#include <TimeLib.h>
#include <Timezone.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

TFT_eSPI display = TFT_eSPI();
ESP8266WebServer server(80);
WiFiUDP ntpUDP;

// Actualizare NTP la 24 ore (86400000 ms) - offset 0, primim UTC
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 0, 86400000);

const char* ssid = "your_SSID";
const char* password = "your_PSW";

// Reguli DST pentru București - ORDINE CORECTĂ: Standard (EET) apoi Daylight (EEST)
TimeChangeRule EET = {"EET", Last, Sun, Oct, 3, 120}; // Standard: UTC+2 (iarnă)
TimeChangeRule EEST = {"EEST", Last, Sun, Mar, 3, 180}; // Daylight: UTC+3 (vară)
Timezone Bucharest(EET, EEST); // Constructor: (standard, daylight)

const char* zileAbrev[] = {"DU", "LU", "MA", "MI", "JO", "VI", "SA"};
const char* luni[] = {"IAN", "FEB", "MAR", "APR", "MAI", "IUN", "IUL", "AUG", "SEP", "OCT", "NOV", "DEC"};

float receivedTemp = 0.0;
bool lowBattery = false;
bool timeInitialized = false;

// Variabilă pentru tracking reconectare WiFi
unsigned long lastWiFiAttempt = 0;
const unsigned long WIFI_RECONNECT_INTERVAL = 10000; // 10 secunde între încercări

void handleUpdate() {
if (server.hasArg("temp")) {
float v = server.arg("temp").toFloat();
if (v >= -50 && v <= 100) receivedTemp = v;
}
lowBattery = (server.hasArg("bat") && server.arg("bat") == "LOW");
server.send(200, "text/plain", "OK");
}

unsigned long prevDisplay = 0;


void displayInfo() {
time_t utc = timeClient.getEpochTime();

// Dacă nu am obținut NICIODATĂ ora corectă, afișăm "Sincronizare..."
if (!timeInitialized) {
display.fillScreen(TFT_BLACK);
display.setTextColor(TFT_WHITE, TFT_BLACK);
display.setFreeFont(&FreeSansBold12pt7b);
display.setTextDatum(MC_DATUM);
display.drawString("Sincronizare...", 120, 100);
return;
}

// AVEM ORA VALIDĂ - afișăm ceasul (chiar dacă WiFi pică între timp)
time_t local = Bucharest.toLocal(utc);
tmElements_t tm;
breakTime(local, tm);

int dd=tm.Day, mm=tm.Month-1, wd=tm.Wday-1, h=tm.Hour, m=tm.Minute, s=tm.Second;

static int lh=-1, lm=-1;
if (h!=lh || m!=lm) {
display.fillRect(20,80,220,80,TFT_BLACK);
display.setTextFont(7);
display.setTextColor(TFT_CYAN,TFT_BLACK);
display.setTextDatum(TR_DATUM);
display.drawString(String(h)+":"+(m<10?"0":"")+String(m), 175, 95);
lh=h; lm=m;
}

static String ls="";
String sec=(s<10?"0":"")+String(s);
if (sec!=ls) {
display.setFreeFont(&FreeSansBold12pt7b);
display.setTextDatum(MC_DATUM);
display.setTextColor(TFT_CYAN,TFT_BLACK);
display.fillRect(173,100,60,30,TFT_BLACK);
display.drawString(sec,190,105);
ls=sec;
}

static String ld="";
String dat=String(dd)+"-"+String(luni[mm]);
if (dat!=ld) {
display.setFreeFont(&FreeSansBold12pt7b);
display.setTextDatum(TL_DATUM);
display.setTextColor(TFT_GREEN,TFT_BLACK);
display.fillRect(10,25,100,25,TFT_BLACK);
display.drawString(dat,0,25);
ld=dat;
}

static String lt="";
String tmp=String(receivedTemp,1)+"'C";
if (tmp!=lt || receivedTemp==0.0) {
display.fillRect(233-90,25-2,90,25,TFT_BLACK);
display.setFreeFont(&FreeSansBold12pt7b);
display.setTextDatum(TR_DATUM);
display.setTextColor(TFT_PURPLE,TFT_BLACK);
display.drawString("'C",233,25);
display.setTextColor(TFT_YELLOW,TFT_BLACK);
display.drawString(String(receivedTemp,1),233-display.textWidth("'C"),25);
lt=tmp;
}

static bool lb=false;
if (lowBattery!=lb) {
display.fillRect(95,17,50,20,TFT_BLACK);
if (lowBattery) {
display.setTextFont(2);
display.setTextDatum(MC_DATUM);
display.setTextColor(TFT_RED,TFT_BLACK);
display.drawString("ACCU",120,25);
}
lb=lowBattery;
}

static int lw=-1;
int cw=(wd==0)?6:wd-1;
if (cw!=lw) {
display.fillRect(0,185,240,30,TFT_BLACK);
display.setFreeFont(&FreeSansBold9pt7b);
display.setTextDatum(MC_DATUM);
int pz[]={10,50,90,125,155,185,220};
for (int i=0;i<7;i++) {
int zi=(i+1)%7;
uint16_t c=(i==cw)?(zi==0||zi==6?TFT_RED:TFT_GREEN):TFT_DARKGREY;
display.setTextColor(c,TFT_BLACK);
display.drawString(zileAbrev[zi],pz[i],200);
}
lw=cw;
}
}

// =====================================================
// SETUP() - CORECTAT PENTRU ROUTER LENT
// =====================================================
void setup() {
display.init();
display.setRotation(3);
display.fillScreen(TFT_BLACK);
display.setTextColor(TFT_WHITE, TFT_BLACK);
display.setFreeFont(&FreeSansBold12pt7b);
display.setTextDatum(MC_DATUM);
display.drawString("Starting...", 120, 100);

// Curățare credențiale WiFi pentru conexiune proaspătă
WiFi.persistent(false);
delay(1000);
WiFi.disconnect(true);
delay(1000);
WiFi.mode(WIFI_OFF);
delay(1000);

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);

// 1. Conectare la Router (WiFi) - EXTINS LA 60 SECUNDE PENTRU ROUTER LENT
int attempts = 0;
const int maxAttempts = 15; // 15 x 4s = 60 secunde (suficient pentru router lent)
while (WiFi.status() != WL_CONNECTED && attempts < maxAttempts) {
attempts++;
display.fillScreen(TFT_BLACK);
display.drawString("Conectare WiFi", 120, 100);
// display.drawString(String(attempts) + "/" + String(maxAttempts), 120, 140);
display.drawString("Astept router...", 120, 170);
delay(4000);
yield(); // ESP8266 WDT friendly
}

if (WiFi.status() != WL_CONNECTED) {
display.fillScreen(TFT_BLACK);
display.setTextColor(TFT_RED, TFT_BLACK);
display.drawString("Eroare WiFi", 120, 100);
display.drawString("Reboot in 10s...", 120, 140);
// Așteptăm 10 secunde înainte de reboot (poate routerul a pornit între timp)
for (int i = 10; i > 0; i--) {
display.drawString(String(i) + "s", 120, 170);
delay(4000);
yield();
}
ESP.restart();
}

// 2. WiFi conectat - Începem verificarea INTERNET (NTP)
display.fillScreen(TFT_BLACK);
display.drawString("Verificare NET", 120, 100);
display.drawString(" Astept NTP", 120, 140);
delay(4000);
timeClient.begin();

// 3. VERIFICARE INTERNET REAL prin NTP (maxim 20 secunde)
int ntpAttempts = 0;
while (!timeInitialized && ntpAttempts < 20) {
if (timeClient.update()) {
time_t epoch = timeClient.getEpochTime();
// Validare epoch : după 1 Ian 2020 și înainte de overflow 2038
if (epoch > 1577836800 && epoch < 2147483647) {
setTime(epoch); // Sincronizează TimeLib cu NTP!
timeInitialized = true;
break;
}
}
ntpAttempts++;
display.fillScreen(TFT_BLACK);
display.drawString("Sincronizare NTP", 120, 100);
display.drawString(String(ntpAttempts) + "/20", 120, 140);
delay(1000);
yield(); // ESP8266 WDT friendly
}

if (!timeInitialized) {
// NTP a eșuat - nu avem internet real
display.fillScreen(TFT_BLACK);
display.setTextColor(TFT_RED, TFT_BLACK);
display.drawString("Eroare NET", 120, 100);
display.drawString("NTP Esuat", 120, 130);
display.drawString("Reboot in 5s", 120, 160);
for (int i = 5; i > 0; i--) {
display.drawString(String(i) + "s", 120, 190);
delay(1000);
yield();
}
ESP.restart();
}

// 4. SUCCES - Acum afișăm "WiFi Conectat" și IP (DOAR după NTP OK)
display.fillScreen(TFT_BLACK);
display.setTextColor(TFT_GREEN, TFT_BLACK);
display.drawString("WiFi Conectat", 120, 80);
display.setTextColor(TFT_WHITE, TFT_BLACK);
display.drawString(WiFi.SSID(), 120, 110);
display.drawString(WiFi.localIP().toString(), 120, 140);
display.drawString("Start ceas...", 120, 180);

server.on("/update", handleUpdate);
server.begin();
delay(4000);

// 5. Curățare ecran înainte de loop
display.fillScreen(TFT_BLACK);
}

// =====================================================
// LOOP() - ADĂUGATĂ RECONECTARE WIFI AUTOMATĂ
// =====================================================
void loop() {
server.handleClient();

// Reconectare automată dacă WiFi se pierde
if (WiFi.status() != WL_CONNECTED) {
if (millis() - lastWiFiAttempt > WIFI_RECONNECT_INTERVAL) {
display.setTextColor(TFT_ORANGE, TFT_BLACK);
display.setTextDatum(MC_DATUM);
display.setFreeFont(&FreeSansBold9pt7b);
display.drawString("Reconectare...", 120, 220);

WiFi.reconnect(); // Încearcă reconectarea
lastWiFiAttempt = millis();
}
yield(); // ESP8266 WDT friendly
return; // Skip rest until reconnected
}

// WiFi este conectat - actualizăm NTP (librăria gestionează intervalul de 24h)
if (timeClient.update()) {
time_t epoch = timeClient.getEpochTime();
if (epoch > 1577836800 && epoch < 2147483647) {
setTime(epoch); // Menține TimeLib sincronizat
timeInitialized = true;
}
}

// Afișare ceas la fiecare secundă
if (millis() - prevDisplay >= 1000) {
prevDisplay = millis();
displayInfo();
}

yield(); // ESP8266 WDT friendly - obligatoriu în loop
}
<10 175="" 95="" lh="h;" lm="m;" ls="" m="" sec="(utc" secunde="" static="" string="" tring=""><10 dat="(utc" display.drawstring="" display.fillrect="" display.setfreefont="" display.settextcolor="" display.settextdatum="" if="" ld="" ls="sec;" reesansbold12pt7b="" s="" sec="" static="" string="" tring=""><7 -="" 100="" 120="" 140="" 1="" 1ms="" 2="" a="" actualizare="" afi="" am="" attempts="" blocheaz="" bucla="" c="" conectare="" const="" critic:="" cu="" curent="" da="" dac="" de="" delay="" display.drawstring="" display.fillscreen="" display.init="" display.setfreefont="" display.setrotation="" display.settextcolor="" display.settextdatum="" doar="" eaz="" eboot...="" ecran="" else="" epuizat="" erial.print="" eroare="" esp.restart="" ex:="" excep="" f="" fiecare="" handleupdate="" i="" if="" ifi.localip="" ifi.status="" ii="" int="" intervale="" ional:="" it="" itera="" la="" loop="" lw="cw;" m="" mai="" maxattempts="" millis="" mult="" ncerc="" ncercarea:="" ncercarea="" niciodat="" non-blocking="" ntp="" ntre="" nu="" onectare="" onectat="" op="" password="" pe="" pentru="" prevntp="" pz="" r="" reesansbold12pt7b="" reu="" ri="" ro="" roare="" routerului="" sau="" secund="" serial.begin="" serial.println="" server.begin="" server.handleclient="" server.on="" setup="" ssid="" string="" teapt="" tentativele="" tft_black="" timeclient.begin="" timeclient.update="" timp="" tostring="" tring="" u="" uint16_t="" update="" verific="" void="" while="" wifi...="" wifi.begin="" wifi.mode="" wifi="" wl_connected="" zi="" zileabrev="">

Niciun comentariu:

Trimiteți un comentariu