In questo tutorial imparerai come sincronizzare l’orologio dell’ESP32 con un server SNTP per avere sempre l’ora esatta e non dover mai più impostare l’orologio.
Sincronizzando l’orologio via internet con un server di tempo SNTP possiamo assicurarci che il nostro orologio mostri sempre l’ora corretta, indipendentemente dalla precisione dell’orologio interno dell’ESP32 o dai cambiamenti dell’ora legale.
Iniziamo con i componenti necessari.
Componenti necessari
Ti serviranno solo un ESP32, un LCD e qualche cavo. Invece della scheda ESP32-C3 Mini Development Board elencata sotto, potresti usare anche qualsiasi altro ESP32.

ESP32-C3 Mini

Cavo USB C

LCD 16×2

Set di cavi Dupont

Breadboard
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
Server di tempo NTP
NTP sta per Network Time Protocol, e un server NTP è un sistema informatico specializzato che fornisce informazioni di tempo accurate ad altri dispositivi connessi a internet.
Un client NTP interroga regolarmente uno o più server NTP per ricevere l’ora corrente e sincronizza il suo orologio interno basandosi sul timestamp ricevuto. Nel caso di un ESP32 come client, l’ESP32 si connette via router e Wi-Fi a internet e avvia una connessione a un pool di server NTP. Uno dei server NTP risponde con un timestamp che viene ricevuto dall’ESP32 e usato per regolare il suo orologio interno.

I server NTP stessi sono organizzati in un sistema gerarchico a livelli con dispositivi di cronometraggio ad alta precisione come orologi atomici, GNSS (incluso GPS) o altri orologi radiofonici come origine (fonte oraria autorevole). I singoli livelli sono chiamati Stratum.

Tipicamente, non ti connetti a un server NTP specifico (indirizzo IP) ma a un pool di molti server NTP. Ne parleremo meglio nella sezione successiva.
Pool NTP
Il NTP Pool è una raccolta dinamica di server NTP in rete. La tabella seguente mostra il numero di server NTP nel pool ntp.org in diverse località geografiche.

Richiedi informazioni di tempo a un server NTP nel pool usando il nome “pool.ntp.org”. Il sistema cercherà di trovare il server disponibile più vicino a te. Questo è il modo consigliato.
Tuttavia, puoi anche richiedere l’ora da server NTP in località o paesi specifici. Per esempio, l’indirizzo “de.pool.ntp.org” si riferisce a un pool di server NTP in Germania (DE). Puoi anche usare continental zones, come ad esempio europe, north-america, oceania o asia.pool.ntp.org. Infine, puoi aggiungere un prefisso numerico se ti servono più nomi di server, es. “0.de.pool.ntp.org”, “1.de.pool.ntp.org”.
STNP vs NTP
Esistono due protocolli per la sincronizzazione dell’ora. NTP (Network Time Protocol) e SNTP (Simple Network Time Protocol). NTP è più preciso e complesso, mentre SNTP è una versione semplificata di NTP.
SNTP è stato sviluppato specificamente per piccoli computer e microcontrollori. È progettato per richiedere meno memoria e potenza di calcolo rispetto a NTP. Pur essendo molto meno preciso di NTP, SNTP fornisce comunque l’ora con un margine di circa 100 millisecondi.
Tipicamente è usato da piccoli dispositivi di rete, come telecamere IP, DVR, telefoni IP, router, dispositivi consumer e anche da microcontrollori come l’ESP32.
Sincronizzare l’orologio ESP32 con un server SNTP
Il codice seguente mostra come sincronizzare l’orologio interno di un ESP32 con l’ora ricevuta da un server SNTP. Questo permette all’ESP32 di mantenere l’ora esatta aggiornandola periodicamente dal server. Dai un’occhiata veloce al codice completo prima, poi discuteremo i dettagli.
// makerguides.com: synchronizing ESP32 time with SNTP server
#include "WiFi.h"
#include "esp_sntp.h"
const char* ssid = "SSID";
const char* password = "PASSWORD";
void initWiFi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
}
}
void notify(struct timeval* t) {
Serial.println("synchronized");
}
void initSNTP() {
sntp_set_sync_interval(1 * 60 * 60 * 1000UL); // 1 hour
sntp_set_time_sync_notification_cb(notify);
esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
esp_sntp_setservername(0, "pool.ntp.org");
esp_sntp_init();
setTimezone();
}
void wait4SNTP() {
while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) {
delay(100);
Serial.println("waiting ...");
}
}
void setTimezone() {
setenv("TZ", "AEST-10AEDT,M10.1.0,M4.1.0/3", 1);
tzset();
}
void printTime() {
struct tm timeinfo;
getLocalTime(&timeinfo);
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
void setup() {
Serial.begin(115200);
initWiFi();
initSNTP();
wait4SNTP();
}
void loop() {
printTime();
delay(10000);
}
Librerie
Iniziamo includendo le librerie necessarie. WiFi.h per la connessione a internet, e esp_sntp.h per la comunicazione con un server SNTP. Entrambe sono librerie di default del core ESP32 e non devono essere installate.
#include "WiFi.h" #include "esp_sntp.h"
Costanti e Variabili
Definiamo quindi le costanti e variabili necessarie per il programma. Qui specifichiamo SSID e password della rete Wi-Fi a cui l’ESP32 si connetterà. Ovviamente dovrai sostituirli con le tue credenziali Wi-Fi.
const char* ssid = "SSID"; const char* password = "PASSWORD";
Inizializzazione WiFi
La funzione initWiFi() si occupa di connettere l’ESP32 alla rete WiFi specificata. Controlla continuamente lo stato della connessione finché non è stabilita con successo.
void initWiFi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
}
}
Inizializzazione SNTP
Nella funzione initSNTP() configuriamo le impostazioni SNTP per la sincronizzazione dell’ora. Questo include l’intervallo di sincronizzazione, il nome del server, la modalità operativa e il fuso orario.
void notify(struct timeval* t) {
Serial.println("synchronized");
}
void initSNTP() {
sntp_set_sync_interval(60 * 60 * 1000UL); // 1 hour
sntp_set_time_sync_notification_cb(notify);
esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
esp_sntp_setservername(0, "pool.ntp.org");
esp_sntp_init();
setTimezone();
}
Esaminiamo più da vicino le singole chiamate di funzione e cosa fanno.
sntp_set_sync_interval(interval_ms) determina la frequenza con cui sincronizziamo l’orologio interno dell’ESP32 con il server SNTP. L’intervallo è specificato in microsecondi e un intervallo di 60 * 60 * 1000UL microsecondi significa che sincronizziamo ogni ora. Non inviare richieste troppo frequenti. Intervalli ragionevoli sono tipicamente da una o due volte al giorno fino a 5 volte all’ora.
sntp_set_time_sync_notification_cb(callback) ti permette di specificare una funzione di notifica (callback) che viene chiamata ogni volta che avviene una sincronizzazione. Nel codice di esempio definiamo la funzione notify() a questo scopo, che stampa semplicemente “synchronized“. Nella maggior parte delle applicazioni non serve, ma è ottima per test e debug!
esp_sntp_setoperatingmode(operating_mode) imposta la operating mode. Tipicamente è ESP_SNTP_OPMODE_POLL – stiamo interrogando il server SNTP. Ma esiste anche ESP_SNTP_OPMODE_LISTENONLY.
esp_sntp_setservername(idx, server) serve per impostare il nome/indirizzo del server. Puoi specificare più server se vuoi. Per esempio:
esp_sntp_setservername(0, "pool.ntp.org"); esp_sntp_setservername(1, "de.pool.ntp.org"); esp_sntp_setservername(2, "time.nist.gov");
esp_sntp_init() avvia il servizio SNTP con i parametri sopra. Esiste anche una funzione per fermare il servizio: esp_sntp_stop().
Infine, dobbiamo impostare il fuso orario poiché il server SNTP restituisce l’ora in UTC. Questa parte è implementata in una funzione separata che vedremo nella prossima sezione.
Se vuoi approfondire le funzioni timer dell’ESP dai un’occhiata al ESP32 Programming Guide for System Time. Nota che esiste anche una funzione configTime() come parte del core Arduino che fa essenzialmente la stessa cosa della nostra funzione initSNTP(). Tuttavia, prende come parametri un gmtOffset_sec e un daylightOffset_sec ma non sono riuscito a farla funzionare correttamente con il fuso orario australiano e non permette di impostare l’intervallo di sincronizzazione.
Configurazione del fuso orario
La funzione setTimezone() imposta il fuso orario per l’ESP32 usando la variabile d’ambiente TZ. Poiché vivo in Australia, l’ho impostata su “AEST-10AEDT,M10.1.0,M4.1.0/3”, che corrisponde all’Australian Eastern Standard Time (AEST) con le regolazioni per l’ora legale.
void setTimezone() {
setenv("TZ", "AEST-10AEDT,M10.1.0,M4.1.0/3", 1);
tzset();
}
Le parti di questa definizione del fuso orario sono le seguenti
- AEST: Australian Eastern Standard Time
- -10: offset UTC di 10 ore avanti rispetto al Tempo Coordinato Universale (UTC)
- AEDT: Australian Eastern Daylight Time
- M10.1.0: il passaggio all’ora legale avviene la prima domenica di ottobre
- M4.1.0/3: il ritorno all’ora standard avviene la prima domenica di aprile, con una differenza di 3 ore da UTC.
Per altre definizioni di fuso orario dai un’occhiata al Posix Timezones Database. Basta copiare e incollare la stringa che trovi lì per il tuo fuso orario nella funzione setenv().
Attesa della sincronizzazione
La funzione wait4SNTP() controlla continuamente lo stato della sincronizzazione con il server SNTP finché non è completata. Stampa un messaggio mentre aspetta.
void wait4SNTP() {
while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) {
delay(100);
Serial.println("waiting ...");
}
}
Stampa dell’ora
La funzione printTime() recupera le informazioni di tempo correnti e le stampa in modo formattato usando la struttura dati struct tm.
void printTime() {
struct tm timeinfo;
getLocalTime(&timeinfo);
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
"%A, %B %d %Y %H:%M:%S" sono format specifiers che determinano come viene formattato il timeinfo e i membri della struttura tm struct sono i seguenti:
Member Type Meaning Range tm_sec int seconds after the minute 0-61* tm_min int minutes after the hour 0-59 tm_hour int hours since midnight 0-23 tm_mday int day of the month 1-31 tm_mon int months since January 0-11 tm_year int years since 1900 tm_wday int days since Sunday 0-6 tm_yday int days since January 1 0-365 tm_isdst int Daylight Saving Time flag
Funzione Setup
Nella funzione setup() inizializziamo la comunicazione seriale, connettiamo il Wi-Fi, inizializziamo SNTP e aspettiamo che la sincronizzazione dell’ora sia completata.
void setup() {
Serial.begin(115200);
initWiFi();
initSNTP();
wait4SNTP();
}
Nota che non puoi spegnere la connessione Wi-Fi dopo che il setup è completato. Alcuni tutorial sbagliano questo punto. Senza Wi-Fi l’ESP32 non può sincronizzarsi con il server SNTP. Spegnere il Wi-Fi ha senso solo se metti l’ESP32 in deep sleep e ti riconnetti a un server SNTP all’avvio.
Funzione Loop
La funzione loop() stampa ripetutamente l’ora corrente ogni 10 secondi.
void loop() {
printTime();
delay(10000);
}
Nota che la sincronizzazione avviene in background (thread separato). Non c’è nessuna funzione da chiamare regolarmente nel loop per interrogare il server SNTP.
Esempio di output
Per provare il codice, ti suggerisco di cambiare temporaneamente l’intervallo di sincronizzazione a 30 secondi (30 * 1000UL). Sul Monitor Seriale dovresti vedere la seguente sequenza di output:

Prima un messaggio “waiting” finché la connessione al server SNTP non è stabilita. Poi stampe dell’ora ogni 10 secondi, seguite da una notifica “synchronized” ogni 30 secondi.
Nella prossima sezione useremo essenzialmente lo stesso codice per costruire un orologio sincronizzato via internet che mostra ora e data su un LCD.
Mostrare l’ora di internet su un LCD
Collegare un LCD con interfaccia I2C all’ESP32 per mostrare l’ora è semplice. Prima collega 5V a VCC e G a Ground. Poi collega SDA al pin 8 (cavo giallo) e SCL al pin 9 (cavo arancione) sull’ESP32.

Se ti servono informazioni più dettagliate sui display LCD dai un’occhiata ai nostri tutorial su How to use a 16×2 character LCD with Arduino e Character I2C LCD with Arduino Tutorial (8 Examples).
Assicurati di collegare correttamente SDA e SCL. Se usi una scheda diversa potresti dover cambiare i pin che supportano l’interfaccia I2C sulla tua scheda. Cerca i pin contrassegnati SDA e SCL nel pinout. Ecco il pinout per l’ESP32-C3 Supermini che uso in questo progetto:
Anche se i pin GPIO8 e GPIO9 dovrebbero essere i pin nativi per l’interfaccia I2C, ho dovuto impostarli esplicitamente nel codice (Wire.begin(sda, scl)) per farlo funzionare. Per maggiori informazioni sulla scheda SuperMini dai un’occhiata al tutorial ESP32-C3 SuperMini Board.
La foto seguente mostra il cablaggio effettivo completato e l’orologio in funzione sul LCD. Come vedi ci sono solo pochi fili e l’ESP32-C3 Supermini è davvero “mini” rispetto all’LCD.

Codice per l’orologio sincronizzato via internet
Ecco il codice completo per sincronizzare l’orologio interno di un ESP32 con un server SNTP e mostrare ora e data correnti su un LCD.
// makerguides.com: synchronizing ESP32 time with SNTP server
#include "WiFi.h"
#include "esp_sntp.h"
#include "LiquidCrystal_I2C.h"
const int sdaPin = 8;
const int sclPin = 9;
const char* ssid = "SSID";
const char* password = "PASSWORD";
// https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
const char *timezone = "AEST-10AEDT,M10.1.0,M4.1.0/3"
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x3F, 16, 2);
void initWiFi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
}
}
void initSNTP() {
sntp_set_sync_interval(60 * 60 * 1000UL); // 1 h
esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL);
esp_sntp_setservername(0, "pool.ntp.org");
esp_sntp_init();
setenv("TZ", timezone, 1);
tzset();
}
void wait4SNTP() {
lcd.setCursor(0, 0);
lcd.print("synchronizing...");
while (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) {
delay(100);
}
lcd.clear();
}
void initLCD() {
Wire.begin(sdaPin, sclPin); // I2C for ESP32-C3 Supermini
lcd.init();
lcd.backlight();
lcd.clear();
}
void lcdPrintTime() {
// https://cplusplus.com/reference/ctime/tm/
char buff[24];
struct tm t;
getLocalTime(&t);
sprintf(buff, "%02d:%02d:%02d ", t.tm_hour, t.tm_min, t.tm_sec);
lcd.setCursor(4, 0);
lcd.print(buff);
sprintf(buff, "%02d-%02d-%04d ", t.tm_mday, t.tm_mon+1, t.tm_year+1900);
lcd.setCursor(3, 1);
lcd.print(buff);
}
void setup() {
initLCD();
initWiFi();
initSNTP();
wait4SNTP();
}
void loop() {
lcdPrintTime();
delay(1000);
}
Come vedi, è essenzialmente lo stesso codice di prima con l’aggiunta della funzione initLCD() che prepara l’LCD e della funzione lcdPrintTime() che mostra ora e data sull’LCD.
void lcdPrintTime() {
char buff[24];
struct tm t;
getLocalTime(&t);
sprintf(buff, "%02d:%02d:%02d ", t.tm_hour, t.tm_min, t.tm_sec);
lcd.setCursor(4, 0);
lcd.print(buff);
sprintf(buff, "%02d-%02d-%04d ", t.tm_mday, t.tm_mon, t.tm_year+1900);
lcd.setCursor(3, 1);
lcd.print(buff);
}
La funzione lcdPrintTime() chiama prima getLocalTime() per ottenere l’ora locale dell’orologio interno dell’ESP32, che abbiamo sincronizzato con il server SNTP. Da struct tm estraiamo poi le informazioni di tempo e data necessarie (ore, minuti, …, anno) e le scriviamo formattate nel buffer stringa buff. Infine impostiamo il cursore per l’LCD e mostriamo il contenuto del buffer sull’LCD. L’output sul tuo LCD dovrebbe apparire così:

E questo è tutto!
Ora hai un orologio sempre preciso su un ESP32 che sincronizza l’ora con un server SNTP.
Conclusioni
In questo tutorial hai imparato come sincronizzare l’orologio interno di un ESP32 con il segnale orario di un server SNTP. Questo garantisce che l’orologio dell’ESP32 sia sempre preciso e ha il vantaggio aggiuntivo che non dovrai mai più impostare l’orologio.
Abbiamo usato funzioni specifiche dell’ESP32 come sntp_set_sync_interval, esp_sntp_setoperatingmode e esp_sntp_setservername per configurare la comunicazione con un server SNTP.
Vale la pena sapere, però, che esistono connessioni Arduino core also provides two functions to set up an SNTP server. Quelle sono configTime e configTzTime. Sono wrapper più astratti attorno alle stesse funzioni che abbiamo usato in initSNTP ma funzionano senza modifiche per ESP32 o ESP8266, per esempio. Quindi potresti sostituire initSNTP chiamando configTzTime(timezone, "pool.ntp.org"). Ma initSNTP ti dà un po’ più di controllo, come impostare l’intervallo di sincronizzazione.
Infine, se vuoi far funzionare l’ESP32 in modalità deep sleep e sincronizzare l’orologio al risveglio, o hai bisogno di informazioni temporali aggiuntive, potresti preferire usare un provider di tempo internet come WorldTimeAPI. Per maggiori informazioni, dai un’occhiata al Automatic Daylight Savings Time Clock, al Digital Clock with CrowPanel 3.5″ ESP32 Display o al tutorial LED Ring Clock with WS2812.
Buon divertimento con il tinkering ; )


