La cererea prietenului Doru, am modificat ceasul prezentat anterior, astfel incat afisarea orei sa se faca folosind caractere mai mari. Iata ce a iesit.
Si inca un montaj cu Wemos D1 mini (sper ca nu v-am plictisit !). Un ceas cu termometru (DS18B20), cu display LCD 2x16, conectare la WiFi. Functioneaza perfect, de multa vreme.
//ceas cu termometru, ora iarna/vara, cu modificari AI
// Global variables tm timeinfo; time_t now; long unsigned lastNTPtime; unsigned long lastEntryTime; String StrTempC;
// Add these variables to store previous values for comparison int prevHour = -1; int prevMin = -1; int prevSec = -1; int prevDay = -1; int prevMonth = -1; int prevWDay = -1; String prevTemp = "";
// Only update time if it has changed if (localTime->tm_hour != prevHour || localTime->tm_min != prevMin || localTime->tm_sec != prevSec) { lcd.setCursor(0, 0); sprintf(time_output, "%02d:%02d:%02d", localTime->tm_hour, localTime->tm_min, localTime->tm_sec); lcd.print(time_output);
// Clear day of week at midnight if (localTime->tm_hour == 0 && localTime->tm_min == 0 && localTime->tm_sec == 0) { lcd.setCursor(9, 1); lcd.print(" "); } }
// Only update date if it has changed if (localTime->tm_mday != prevDay || localTime->tm_mon != prevMonth) { lcd.setCursor(0, 1); sprintf(time_output, "%02d/%02d", localTime->tm_mday, localTime->tm_mon + 1); lcd.print(time_output);
// Only update day of week if it has changed if (localTime->tm_wday != prevWDay) { char* dow = getDOW(localTime->tm_wday); lcd.setCursor(8 + (8 - strlen(dow)), 1); lcd.print(dow);
prevWDay = localTime->tm_wday; }
// Only update temperature if it has changed if (StrTempC != prevTemp) { lcd.setCursor(10, 0); lcd.print(StrTempC);
// Display degree symbol (only if temperature changed) lcd.setCursor(15, 0); lcd.write(0);
prevTemp = StrTempC; tempChanged = true; } }
char * getDOW(uint8_t tm_wday) { switch (tm_wday) { case 1: return " Luni"; case 2: return " Marti"; case 3: return "Miercuri"; case 4: return " Joi"; case 5: return " Vineri"; case 6: return " Sambata"; case 0: return "Duminica"; default: return " Error "; } }
// The loop function to update temperature less frequently unsigned long lastTempUpdate = 0; const unsigned long TEMP_UPDATE_INTERVAL = 10000; // Update temperature every 10 seconds
void setup() { // Initialize sensors and communication sensors.begin(); Serial.begin(115200);
// Configure time configTime(0, 0, NTP_SERVER); setenv("TZ", TZ_INFO, 1);
// Get initial time if (!getTimeReducedTraffic(30000)) { // Wait up to 30 seconds for initial time sync Serial.println("Time not set"); ESP.restart(); }
// Display initial time showTime(&timeinfo);
// Store time reference lastNTPtime = time(&now); lastEntryTime = millis();
// Create custom character for degree symbol lcd.createChar(0, customChar);
void loop() { unsigned long currentMillis = millis();
// 1. Re-sincronizare NTP (o dată pe oră) if (currentMillis - lastNTPsync >= NTP_REFRESH_INTERVAL) { Serial.println("[NTP] Re-sincronizare..."); configTime(0, 0, NTP_SERVER); setenv("TZ", TZ_INFO, 1); tzset(); // Nu folosim delay mare aici, getTime va rula în background getTimeReducedTraffic(5000); lastNTPsync = currentMillis; }
// 2. Actualizare structură timp (fără contact NTP) // Această funcție trebuie să ruleze des pentru a menține timeinfo actualizat time(&now); localtime_r(&now, &timeinfo);
// 4. Actualizare afișaj DOAR când se schimbă secunda // Aceasta elimină flicker-ul cauzat de scrierea repetată pe LCD if (timeinfo.tm_sec != prevSec) { showTime(&timeinfo); // prevSec este actualizat deja în interiorul funcției showTime } }
Si inca un montaj cu excelentul Wemos D1 mini : ceas cu matrice de leduri, cu senzor de temperatura DS18B20. Are efect de scroll a afisarii. Testat, functioneaza perfect. L-am montat in bucatarie, iar senzorul de temperatura l-am scos in balcon. Succes !
// Ceas matrice LED cu ora automata vara/iarna + DS18B20
// Conexiuni : DS18B20 la D2 ; matrice DIN - D8 ; CS - D7 ; CLK - D6
#include <ESP8266WiFi.h> #include <time.h> #include <MD_MAX72xx.h> #include <SPI.h> #include <OneWire.h> #include <DallasTemperature.h>
Va salut !
Actualizez blogul cu o simpla "statie meteo" (denumire cam pompoasa...), creata cu un Wemos D1 mini (da, am multe montaje cu aceasta mica bijuterie !) si un display OLED SH1106. Datele meteo sunt furnizate de OpenWeather.
Necesita imbunatatiri (ora automata vara/iarna), dar este un punct de plecare pentru cine vrea sa experimenteze. Succes !!
//cu date meteo de la OpenWeather
#include "Arduino.h" #include <ESP8266WiFi.h> #include <WiFiManager.h> #include <NTPClient.h> #include <WiFiUdp.h> #include <ArduinoJson.h> #include <SH1106Wire.h> #include <ESP8266HTTPClient.h>
// Definirea pinilor pentru display OLED
#define OLED_SDA D2
#define OLED_SCL D1
// Inițializarea display-ului OLED
SH1106Wire display(0x3c, OLED_SDA, OLED_SCL);
// Definirea serverului NTP și offset-ului de timp
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 7200, 60000);
const char* ssid = "your_ssid"; // Introduceti numele retelei WiFi
const char* password = "your_psw"; // Introduceti parola retelei WiFi
const char* zileleSaptamanii[] = {"Duminica", "Luni", "Marti", "Miercuri", "Joi", "Vineri", "Sambata"};
const String apiKey = "6d34f8b43ddcbe0377b78a29108d2196"; //creati-va propriu apiKey !!!
const String city = "Ploiesti"; // setati-va locatia !!!
String temperatura = "";
String stareVreme = "";
String formatTime(int hh, int mm, int ss) {
String timeStr = "";
if (hh < 10) timeStr += "0";
timeStr += String(hh) + ":";
if (mm < 10) timeStr += "0";
timeStr += String(mm) + ":";
if (ss < 10) timeStr += "0";
timeStr += String(ss);
return timeStr;
}
void setup() {
Serial.begin(115200);
// Conectare la WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Se conecteaza la WiFi...");
}
Serial.println("Conectat la WiFi");
timeClient.begin();
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_10);
}
void obtineVremea() {
WiFiClient client;
HTTPClient http;
String url = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "&appid=" + apiKey + "&units=metric&lang=ro";
http.begin(client, url);
int httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload);
temperatura = String(doc["main"]["temp"].as(), 1) + " °C";
stareVreme = doc["weather"][0]["description"].as();
}
http.end();
}
void loop() {
timeClient.update();
int hh = timeClient.getHours();
int mm = timeClient.getMinutes();
int ss = timeClient.getSeconds();
int zi = timeClient.getDay();
String currentTime = formatTime(hh, mm, ss);
obtineVremea();
display.clear();
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.setFont(ArialMT_Plain_10);
display.drawString(64, 0, zileleSaptamanii[zi]);
display.setFont(ArialMT_Plain_24);
display.drawString(64, 10, currentTime);
display.setFont(ArialMT_Plain_16);
display.drawString(64, 35, temperatura);
display.setFont(ArialMT_Plain_10);
display.drawString(64, 50, stareVreme);
display.setFont(ArialMT_Plain_16);
display.display();
delay(1000);
}
Va salut !
Fiindca o lunga perioada de timp am fost in concediu medical, dupa o afectiune oncologica ce a necesitat operatie, in timpul liber disponibil am lucrat la o serie de proiecte, care sa-mi tina mintea distrasa de la alte ganduri ...
Cu ajutorul AI (Vercel, Perplexity, Gemini, Qwen, Le Chat) am conceput si finalizat, dupa multe, multe incercari si aceasta statie meteo color. Functioneaza foarte bine, prietenul meu, Dragos, o are "activa" si e multumit de ea.
Nota : am experimentat cu datele meteo de pe toate siteurile care pun la dispozitie apiKey. Nu sunt doua la fel !! Exista mari variatii, in special in privinta temperaturii, dar, la unele siteuri, si la starea vremii propriu-zisa (afara era soare, senin si prognoza era innorat, sanse de ploaie :( ). Acesta este si motivul pentru care eu nu folosesc aceasta statie, prefer ceasul cu termometru (ce poate fi vazut tot aici, pe blog).
Cine doreste poate face statia, ideea este sa va obtineti propriul apiKey (google_it) si sa va setati locatia. Succes !
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 multe teste si multe buguri rezolvate, pot spune ca acum 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.
// DATĂ static String ld=""; String dat=(utc>100000000)?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; }
// TEMPERATURĂ - FOARTE IMPORTANT: afișează mereu, chiar dacă este 0.0 static String lt=""; String tmp=String(receivedTemp,1)+"'C"; if (tmp!=lt || receivedTemp==0.0) { // 🔑 FORȚEAZĂ afișarea inițială! 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; }
// BATERIE 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; }
// ZILE static int lw=-1; int cw=(wd==0)?6:wd-1; if (cw!=lw || utc<=100000000) { display.fillRect(0,185,240,30,TFT_BLACK); display.setFreeFont(&FreeSansBold9pt7b); display.setTextDatum(MC_DATUM); int pz[7]={10,50,90,125,155,185,220}; for (int i=0;i<7;i++) { int zi=(i+1)%7; uint16_t c=(utc<=100000000)?TFT_DARKGREY:(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; } }
//Serial.print("Incercarea: "); Serial.println(attempts); delay(1000); // Așteaptă 1 secundă între încercări pentru a da timp routerului }
// Verificăm dacă am reușit sau am epuizat tentativele if (WiFi.status() == WL_CONNECTED) { display.fillScreen(TFT_BLACK); display.drawString("Conectat !", 120, 100); display.drawString(WiFi.localIP().toString(), 120, 140);
Va prezint statia mea de lipit cu Arduino. Realizata cu AI Gemini ; eu
doar am formulat cerintele si am testat codul, deci nu am prea multe
merite. Inspiratia mea au fost nenumaratele exemple existente.
Pe scurt : ciocan Pensol SL-10 (cel pe care-l am de mai bine de 15 ani
si nu renunt la el, e prea bun !), Arduino Nano, display LED 7 segmente 3
digiti, encoder rotativ, 2 butoane pentru memorare 2 temperaturi de
lucru, stand-by dupa 30 minute.
Am testat-o cateva zile, pe parcursul carora am rezolvat diverse
buguri. In prezent functioneza destul de bine, zic eu. Am vrut initial
cu PID, dar inertia termica mare a ciocanului m-a facut sa renunt.
Orice comentariu sau sugestie sunt binevenite. Statia este prezentata si pe Elforum (https://www.elforum.info/topic/165506-statie-de-lipit-cu-arduino/),
cine vrea o poate modifica dupa cum doreste (recomand
Vercel / Perplexity / Qwen / Gemini, nu neaparat in aceasta ordine). Numai
bine !
Dupa cateva zile de folosire mai intensa, am mai facut cateva modificari in soft. Atasez ultima versiune (25.02.2026)
/* // // STATIE DE LIPIT CU ARDUINO // DISPLAY LED CU 7 SEGMENTE // ENCODER SI 2 MEMORII // STAND-BY DUPA 30 MINUTE // Vers.finala (?) */ #include "max6675.h" #include <Encoder.h> #include <EEPROM.h> #include <EasyButton.h> #include <avr/wdt.h>
// Functie gestionare trezirea din stand-by void wakeSystem() { if (isSystemOff) { isSystemOff = false; // Forțăm o citire imediată a senzorului pentru a evita afișarea de valori vechi double r = thermocouple.readCelsius(); if (isnan(r) || r <= 0) sensorError = true; else { sensorError = false; Input = r + TEMP_OFFSET; } lastUpdate = millis(); // Actualizam si timestamp-ul ultimei citiri } resetActivity(); }
void setup() { Serial.begin(9600); for (int i = 0; i < 7; i++) pinMode(segPins[i], OUTPUT); for (int i = 0; i < 3; i++) pinMode(digitPins[i], OUTPUT); pinMode(11, OUTPUT); analogWrite(11, 0);
for (int i = 0; i < 2; i++) { int val; EEPROM.get(EEPROM_ADDR[i], val); // Verificam si noul prag minim de 200 grade if (val >= 200 && val <= 400) savedTemps[i] = val; else savedTemps[i] = 0; } delay(500); myEnc.write(0); resetActivity(); wdt_enable(WDTO_2S); // Activează Watchdog cu interval de 2 secunde }