Tabela electronica la H0

  • In perioada urmatoare o sa vi se solicite modificarea parolei. Pana la modificarea parolei, userul poate figura ca si blocat, odata modificata parola, userul este reactivat automat.
    Pentru orice problema va rog folositi butonul "Contact"

dac

Well-Known Member
Trenulist
15 Septembrie 2007
1.412
272
Bucuresti
LOCATION
Bucuresti
De-a lungul timpului, am tot cochetat cu ideea folosirii pe diorama a unor display-uri digitale ce se doreau a fi "panouri publicitare la scara". Chiar daca modulele alfanumerice folosite (de obicei 1602 - cu 2 randuri a 16 caractere) ofereau o buna lizibilitate a mesajelor afisate, erau totusi departe de realitatea la scara 1:1.

De ceva vreme au aparut insa mini-displayuri gandite parca anume pentru noi: module OLED cu diagonala de 0.96 inch, care s-ar traduce la scara ca "plasme de 2,12m", deci numai bune pentru, de exemplu, tabelele electronice din garile dioramelor!
Afisajele sunt grafice, nu alfanumerice, au o rezolutie de 128x64 pixeli, si sunt in general gandite pentru a fi folosite cu microcontrolere AVR mai potente pe platforme gen Arduino, care permit afisarea unor imagini grafice simple sau fonturi de diferite marimi.

Nu am vrut insa nimic "fancy", doar afisarea unor tabele de tip text, cu font cat mai mic dar clar si eventual ceva animatie care sa fie vizibila de la oarecare distanta. Ma intrebam daca as putea folosi controlere mai simplute gen PIC, pentru a mentine schema si intregul circuit cat mai "suple". Iata ce a iesit:

DISPLAY-OLED-LCD-LED-128X64-0-96-INCH.jpg
IMG_1594.jpg
IMG_1595.jpg

Schema este simpla, cu doar cateva componente si microcontrolerul PIC 16F648A, cu o memorie program de 4k. Ma intrebam daca o sa fie suficienta; a trebuit sa creez de la zero biblioteca de functii pentru comanda display-ului, precum si sa definesc fontul necesar afisarii mesajelor. Impreuna cu o tabela de 20 linii de text au ocupat in total 1.8k din cei 4. Exista asadar si potential pentru dezvoltare...

sdb1.png
sdb2.png
sdb3.png

Caracterele au fost create dupa fontul folosit de afisajele 1602 si sunt definite intr-o matrice de 6x8 pixeli ce include o coloana de spatiu intre caractere. Incap astfel 21 caractere pe rand (21x6=126 din 128) si 8 randuri pe ecran. Din cei 2 pixeli ramasi ("0") unul este afisat inainte si unul dupa text, pentru centrarea mesajului pe display.
Tehnologia OLED vine cu avantajele sale: unghi larg de vizualizare, contrast ridicat, dar si cu dezavantaje legate de uzura neuniforma a pixelilor in functie de gradul de utilizare. Am implementat asadar o rutina de scroll vertical pentru a evita pe cat posibil "pixel burn-in".

IMG_1596.jpg
IMG_1597.jpg
IMG_1598.jpg

Orarul trenurilor a fost inspirat de poze gasite pe net; codurile lor sunt insa relative, incercand sa ma incadrez in limita a 5 caractere.


[img=https://www.youtube.com/watch?v=w8zeNcpIB1w&ab_channel=dacrail]View: https://www.youtube.com/watch?v=w8zeNcpIB1w&ab_channel=dacrail[/img]


(flicker-ul din inregistrare nu se vede in realitate; este cauzat de framerate-ul display-ului: 103 fps)
 

Ata?amente

  • DISPLAY-OLED-LCD-LED-128X64-0-96-INCH.jpg
    DISPLAY-OLED-LCD-LED-128X64-0-96-INCH.jpg
    20,9 KB · Vizualiz?ri: 2
  • DISPLAY-OLED-LCD-LED-128X64-0-96-INCH.jpg
    DISPLAY-OLED-LCD-LED-128X64-0-96-INCH.jpg
    20,9 KB · Vizualiz?ri: 2
  • sdb2.png
    sdb2.png
    13,5 KB · Vizualiz?ri: 2
  • sdb1.png
    sdb1.png
    17,8 KB · Vizualiz?ri: 2
  • sdb3.png
    sdb3.png
    6,3 KB · Vizualiz?ri: 2
Super idee!
Se mai poate face asta si cu un MP4 ieftin si micut, doar ca trebuie sa muncnesti la editare video si sa pui un film sa ruleze sau daca suporta slide-show de poze, si mai bine, intercalezi partea de display publicitar cu orarul trenurilor.
 
  • Like
Reac?ii: mpursu ?i dac
Update:
Am compactat un pic codul, obtinand ceva spatiu in plus de care am profitat implementand si rutinele care transforma tabela in accesoriu DCC.
Astfel, poate fi pornita / oprita facil acum din multiMaus (incerc sa prezerv la maxim viata OLED, stingand afisajul cand nu este necesar).

CV-uri folosite (se pot programa usor din multiMaus in mod POM):
- CV101 = adresa decodor in intervalul 1-99, implicit 99.
- CV102 = viteza scroll-up, implicit 9. Valoarea indica numarul de pachete DCC dintre doua scroll-uri de un pixel. Viteza de scroll este deci invers proportionala cu valoarea CV102 si se poate regla "dupa gust" astfel incat miscarea sa fie fluida, dar sa nu ai totusi impresia de "movie credits".

Am marit si numarul de linii de text la 30, pentru un orar care sa acopere mai bine 24 ore. Totul incape acum in 2k memorie program, deci pot fi folosite si PIC-uri cu flash de 2k gen 16F628A.
 
Spuneam mai sus ca aceste afisaje grafice sunt gandite pentru a fi folosite cu microcontrolere AVR mai potente pe platforme gen Arduino. Un astfel de microcontroler SoC (system-on-chip) este ESP32, un device cu specificatii interesante, de exemplu 4MB memorie flash (de 1000x mai mult decat PIC-ul de mai sus!), sau modulul Wi-Fi ce ofera posibilitatea conectarii la reteaua locala, si de aici la internet. Exista afisaje OLED bicolore, cu aceeasi rezolutie si aceleasi dimensiuni, dar in care sfertul de ecran superior este galben, iar restul albastru. Zona galbena poate fi folosita pentru a scoate in evidenta un logo sau text.

res_b76a1cf29ac60fb257a9fdcbba77a040.jpgScreen Shot 2023-11-02 at 18.20.24.png

Mai sunt necesare doar cateva fire de conexiune, un "breadboard" si un incarcator tip USB (sau powerbank) si obtinem un gadget nu la fel de compact precum cel de mai sus, dar cu potential!

IMG_1616.jpg
IMG_1618.jpg

Tot ce avem de facut acum este sa incarcam in memoria ESP32 un program corespunzator (sau "sketch" in limbajul Arduino). De exemplu:

Screen Shot 2023-11-02 at 18.27.21.png
Programul se conecteaza la reteaua locala, apoi la un server NTP din internet pentru a sincroniza ceasul intern cu cel atomic, aplica ulterior corectiile de "time zone" si "daylight saving time" , dupa care afiseaza ora pana ce este oprit. Intrucat afisajul este OLED si ne pasa de soarta pixelilor folositi in exces, ecranul trece "in negativ" la secunda 30 si revine la normal la secunda 0.


[img=https://www.youtube.com/watch?v=cZII6mIxtqw&ab_channel=dacrail]View: https://www.youtube.com/watch?v=cZII6mIxtqw&ab_channel=dacrail[/img]


Display-ul este initializat la secunda 3 de la pornire, ceasul afisand ora 0 + corectia TZ si DST, apoi la secunda 8 primeste ora exacta din internet. Odata sincronizat, precizia orei afisate depinde de cea a ceasului intern ESP32. Dupa cateva ore de rulaj, ceasul nu pierduse nici o secunda. Daca modulul este alimentat odata cu diorama, pentru a functiona cateva ore, ceasul va fi precis afisat. Daca va fi pornit pentru o durata mai mare de timp, sau este alimentat permanent, este utila si o rutina de resincronizare la cateva ore.

Afisajul OLED poate fi montat pe un perete gol al unei cladiri, iar firele si modulul ESP32 pot fi gazduite in interior. Astfel, ceasul garii dioramei va arata nu doar o ora inghetata, ci chiar ora exacta!
 
Un mini-display mai potrivit pentru ceasul garii este poate cel cu format dreptunghiular si rezolutie verticala la jumatate (128 x 32 pixeli). Se poate afisa astfel, doar pe singur rand de text, ora sau ora si data alternativ.

IMG_1626.jpg
IMG_1627.jpg

Am modificat usor si sketch-ul prezentat mai sus: sincronizarea cu ceasul atomic se face la pornire si apoi la minutul 59 al fiecarei ore. In plus, afisarea timpului se face acum de 10x pe secunda.

Screen Shot 2023-11-05 at 18.02.11.png
 

Ata?amente

  • Screen Shot 2023-11-05 at 18.02.11.png
    Screen Shot 2023-11-05 at 18.02.11.png
    184,1 KB · Vizualiz?ri: 2
Toate mini-displayurile prezentate mai sus sunt de tip OLED. Exista insa unele cu aceeasi diagonala de 0.96 inch (2,12 m la scara H0), realizate in tehnologie LCD TFT. Ba mai mult, au o rezolutie usor crescuta (160 x 80) si pot afisa imagini full-color intr-o paleta de 65 mii de culori! In plus, sunt de tip IPS (unghi larg de vizualizare si acuratete a nuantelor) si nu sunt la fel de sensibile ca cele OLED la aparitia "burn-in" deci pot fi lasate sa ruleze ore in sir fara probleme. Sa cuplam un astfel de mini-display cu un modul ESP8266 (var mai tanar al lui ESP32):

Screen Shot 2023-11-03 at 18.21.34.pngScreen Shot 2023-11-03 at 18.24.38.png

IMG_1622.jpg

IMG_1623.jpg


[img=https://www.youtube.com/watch?v=GZHc3nBGAhs&ab_channel=dacrail]View: https://www.youtube.com/watch?v=GZHc3nBGAhs&ab_channel=dacrail[/img]


Evident, "panoul publicitar H0" are conectivitate WiFi, prin urmare reclamele pot fi schimbate relativ usor de la distanta.
 

[img=https://www.youtube.com/watch?v=VdNRi_Cc7K4&ab_channel=dacrail]View: https://www.youtube.com/watch?v=VdNRi_Cc7K4&ab_channel=dacrail[/img]


Afisaj electronic peron: ceas analogic (evident, sincronizat prin internet) si informatii despre trenul tras la peron, prezentate automat pentru cateva minute inainte de ora plecarii. In restul timpului se poate afisa data, un logo, sau un alt text scurt.
Sketch-ul arata cam asa:

#include <WiFi.h>
#include <time.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire1, -1);

const char* ssid = "*****";
const char* password = "*****";
float angle;

void setup() {
Serial.begin(115200);

Wire1.begin(33, 32); //I2C pins

if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.setTextColor(WHITE);

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}

Serial.println("Connected to Wi-Fi!");

configTime(0, 0, "de.pool.ntp.org", "time.nist.gov"); //sync at boot time
setenv("TZ", "EET-2EEST,M3.5.0/3,M10.5.0/4", 1); // Bucharest TZ & DST.
tzset();
}

void loop() {
time_t rawtime = time(nullptr);
struct tm* timeinfo = localtime(&rawtime);

display.clearDisplay();

// draw clock ticks
for(int z=0; z<360;z=z+30){
angle = (float)z / 57.3; //Convert degrees to radians
int x1=(16+(sin(angle)*15));
int y1=(15-(cos(angle)*15));
int x2=(16+(sin(angle)*(12)));
int y2=(15-(cos(angle)*(12)));
display.drawLine(x1,y1,x2,y2,WHITE);
}
// draw clock hour
angle=((float)timeinfo->tm_hour * 30 + (float)timeinfo->tm_min / 2) / 57.3 ; //Convert degrees to radians
int x2=(16+(sin(angle)*(9)));
int y2=(15-(cos(angle)*(9)));
display.drawLine(16,15,x2,y2,WHITE);

// draw clock minute
angle=((float)timeinfo->tm_min * 6 / 57.3) ; //Convert degrees to radians
x2=(16+(sin(angle)*(11)));
y2=(15-(cos(angle)*(11)));
display.drawLine(16,15,x2,y2,WHITE);

// draw clock second
angle=((float)timeinfo->tm_sec * 6 / 57.3) ; //Convert degrees to radians
x2=(16+(sin(angle)*(13)));
y2=(15-(cos(angle)*(13)));
display.drawLine(16,15,x2,y2,WHITE);

display.setTextSize(1);
display.setCursor(40, 3);
display.print("Powered by");
display.setTextSize(2);
display.setCursor(44, 14);
display.print("dacrail");

if (timeinfo->tm_hour == 20 && timeinfo->tm_min >= 51 && timeinfo->tm_min < 52) {
display.fillRect(40,0,127,31,BLACK);
display.setTextSize(1);
display.setCursor(40, 1);
display.print("IR 1692 20:52");
display.setCursor(40, 12);
display.print("TIMISOARA NORD");
display.setCursor(40, 23);
display.print("via Craiova ");
}
if (timeinfo->tm_hour == 20 && timeinfo->tm_min >= 52 && timeinfo->tm_min < 53) {
display.fillRect(40,0,127,31,BLACK);
display.setTextSize(1);
display.setCursor(40, 1);
display.print("IR 1896 20:53");
display.setCursor(40, 12);
display.print("CRAIOVA ");
display.setCursor(40, 23);
display.print("via Pitesti ");
}
display.display();

if (timeinfo->tm_sec == 0) display.invertDisplay(false);
if (timeinfo->tm_sec == 30) display.invertDisplay(true);

if (timeinfo->tm_min == 59 && timeinfo->tm_sec == 0) { //sync every hour at 59'0"
configTime(0, 0, "de.pool.ntp.org", "time.nist.gov");
setenv("TZ", "EET-2EEST,M3.5.0/3,M10.5.0/4", 1); // Bucharest TZ & DST.
tzset();
delay(900); //prevent multiple NTP requests
}
delay(86);
}
 
La cererea publicului, am sa postez aici sketch-urile de mai sus in format ce poate fi copiat si editat usor:

internet_time.ino (include sincronizare ceas prin internet la pornire si apoi la minutul 59 al fiecarei ore)
#include <WiFi.h>
#include <time.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire1, -1);

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

void setup() {
Serial.begin(115200);

Wire1.begin(33, 32); //I2C pins

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.setTextColor(WHITE);

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}

Serial.println("Connected to Wi-Fi!");

configTime(0, 0, "de.pool.ntp.org","time.nist.gov"); //sync at boot time
setenv("TZ","EET-2EEST,M3.5.0/3,M10.5.0/4",1); // Bucharest TZ & DST.
tzset();
}

void loop() {
time_t rawtime = time(nullptr);
struct tm* timeinfo = localtime(&rawtime);

display.clearDisplay();

display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(5,1);
//display.print(" dacrail");
if( timeinfo->tm_mday <10)
display.print(" ");
display.print(timeinfo->tm_mday);
display.print(".");
if( timeinfo->tm_mon <9)
display.print("0");
display.print(timeinfo->tm_mon + 1);
display.print(".");
display.print(timeinfo->tm_year + 1900);

display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(2,28);
if( timeinfo->tm_hour <10)
display.print(" ");
display.print(timeinfo->tm_hour);
display.print(":");
if( timeinfo->tm_min <10)
display.print("0");
display.print(timeinfo->tm_min);
display.setTextSize(2);
display.setCursor(92,35);
display.print(":");
if( timeinfo->tm_sec <10)
display.print("0");
display.print(timeinfo->tm_sec);

display.display();

if (timeinfo->tm_sec==0){
display.invertDisplay(false);
}
if (timeinfo->tm_sec==30){
display.invertDisplay(true);
}

if (timeinfo->tm_min==59 && timeinfo->tm_sec==0){ //sync every hour at 59'0"
configTime(0, 0, "de.pool.ntp.org","time.nist.gov");
setenv("TZ","EET-2EEST,M3.5.0/3,M10.5.0/4",1); // Bucharest TZ & DST.
tzset();
}

delay(1000);
}


dacrail_time.ino
#include <WiFi.h>
#include <time.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire1, -1);

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

void setup() {
Serial.begin(115200);

Wire1.begin(33, 32); //I2C pins

if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.setTextColor(WHITE);

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}

Serial.println("Connected to Wi-Fi!");

configTime(0, 0, "de.pool.ntp.org", "time.nist.gov"); //sync at boot time
setenv("TZ", "EET-2EEST,M3.5.0/3,M10.5.0/4", 1); // Bucharest TZ & DST.
tzset();
}


void loop() {
time_t rawtime = time(nullptr);
struct tm* timeinfo = localtime(&rawtime);

display.clearDisplay();
display.setTextSize(3);
display.setTextColor(WHITE);
display.setCursor(2, 5);
if ( timeinfo->tm_hour < 10) display.print(" ");
display.print(timeinfo->tm_hour);
display.print(":");
if ( timeinfo->tm_min < 10) display.print("0");
display.print(timeinfo->tm_min);
display.setTextSize(2);
display.setCursor(92, 12);
display.print(":");
if ( timeinfo->tm_sec < 10) display.print("0");
display.print(timeinfo->tm_sec);
display.display();

if (timeinfo->tm_sec == 0) display.invertDisplay(false);
if (timeinfo->tm_sec == 30) display.invertDisplay(true);

if (timeinfo->tm_min == 59 && timeinfo->tm_sec == 0) { //sync every hour at 59'0"
configTime(0, 0, "de.pool.ntp.org", "time.nist.gov");
setenv("TZ", "EET-2EEST,M3.5.0/3,M10.5.0/4", 1); // Bucharest TZ & DST.
tzset();
delay(900); //prevent multiple NTP requests
}
delay(87);
}


OTA_dacrail_time_analog.ino (panou peron cu ceas analogic si afisare data)
#include <WiFi.h>
#include <time.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ArduinoOTA.h>

Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire1, -1);

const char* ssid = "*****";
const char* password = "*****";
float angle;

void setup() {
Serial.begin(115200);

Wire1.begin(33, 32); //I2C pins

if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.setTextColor(WHITE);

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}
Serial.println("Connected to Wi-Fi!");

ArduinoOTA.onStart([]() {
Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());

configTime(0, 0, "de.pool.ntp.org", "time.nist.gov"); //sync at boot time
setenv("TZ", "EET-2EEST,M3.5.0/3,M10.5.0/4", 1); // Bucharest TZ & DST.
tzset();
}


void loop() {
// int t = millis();
ArduinoOTA.handle(); // Handles a code update request
time_t rawtime = time(nullptr);
struct tm* timeinfo = localtime(&rawtime);

display.clearDisplay();

// draw clock ticks
for(int z=0; z<360;z=z+30){
angle = (float)z / 57.3; //Convert degrees to radians
int x1=(16+(sin(angle)*15));
int y1=(15-(cos(angle)*15));
int x2=(16+(sin(angle)*(12)));
int y2=(15-(cos(angle)*(12)));
display.drawLine(x1,y1,x2,y2,WHITE);
}
// draw clock hour
angle=((float)timeinfo->tm_hour * 30 + (float)timeinfo->tm_min / 2) / 57.3 ; //Convert degrees to radians
int x2=(16+(sin(angle)*(9)));
int y2=(15-(cos(angle)*(9)));
display.drawLine(16,15,x2,y2,WHITE);

// draw clock minute
angle=((float)timeinfo->tm_min * 6 / 57.3) ; //Convert degrees to radians
x2=(16+(sin(angle)*(11)));
y2=(15-(cos(angle)*(11)));
display.drawLine(16,15,x2,y2,WHITE);

// draw clock second
angle=((float)timeinfo->tm_sec * 6 / 57.3) ; //Convert degrees to radians
x2=(16+(sin(angle)*(13)));
y2=(15-(cos(angle)*(13)));
display.drawLine(16,15,x2,y2,WHITE);

display.setTextSize(1);
display.setCursor(40, 3);
display.print("Powered by");
display.setTextSize(2);
display.setCursor(44, 14);
display.print("dacrail");

if ((timeinfo->tm_min & 1) == 1) {
display.fillRect(40,0,127,31,BLACK);
display.setTextSize(1);
display.setCursor(40, 6);
if (timeinfo->tm_wday == 0) display.print("Duminica");
if (timeinfo->tm_wday == 1) display.print("Luni");
if (timeinfo->tm_wday == 2) display.print("Marti");
if (timeinfo->tm_wday == 3) display.print("Miercuri");
if (timeinfo->tm_wday == 4) display.print("Joi");
if (timeinfo->tm_wday == 5) display.print("Vineri");
if (timeinfo->tm_wday == 6) display.print("Sambata");
display.print(",");
display.setCursor(100, 1);
if (timeinfo->tm_min == 59 && timeinfo->tm_sec == 0) display.print("sync");
display.setCursor(52, 19);
if (timeinfo->tm_mday < 10) display.print(" ");
display.print(timeinfo->tm_mday);
display.print(" ");
if (timeinfo->tm_mon == 0) display.print("IAN");
if (timeinfo->tm_mon == 1) display.print("FEB");
if (timeinfo->tm_mon == 2) display.print("MAR");
if (timeinfo->tm_mon == 3) display.print("APR");
if (timeinfo->tm_mon == 4) display.print("MAI");
if (timeinfo->tm_mon == 5) display.print("IUN");
if (timeinfo->tm_mon == 6) display.print("IUL");
if (timeinfo->tm_mon == 7) display.print("AUG");
if (timeinfo->tm_mon == 8) display.print("SEP");
if (timeinfo->tm_mon == 9) display.print("OCT");
if (timeinfo->tm_mon ==10) display.print("NOV");
if (timeinfo->tm_mon ==11) display.print("DEC");
display.print(". ");
display.print(timeinfo->tm_year + 1900);
}
display.display();

if (timeinfo->tm_sec == 0) display.invertDisplay(false);
if (timeinfo->tm_sec == 30) display.invertDisplay(true);

if (timeinfo->tm_min == 59 && timeinfo->tm_sec == 0) { //sync every hour at 59'0"
configTime(0, 0, "de.pool.ntp.org", "time.nist.gov");
setenv("TZ", "EET-2EEST,M3.5.0/3,M10.5.0/4", 1); // Bucharest TZ & DST.
tzset();
delay(900); //prevent multiple NTP requests
}
// Serial.println(millis()-t);
delay(86);
}