Quando si crea un dispositivo, probabilmente è necessario conoscere l’ora corretta, su un dispositivo wifi la scelta ragionevole è utilizzare Internet per ottenere l’ora tramite NTP.
Il Network Time Protocol (NTP) è un protocollo di rete per la sincronizzazione dell’orologio tra sistemi informatici su reti di dati a commutazione di pacchetto, a latenza variabile. In funzione da prima del 1985, NTP è uno dei più antichi protocolli Internet attualmente in uso. NTP è stato progettato da David L. Mills dell’Università del Delaware. (cit. wiki)
Libreria NTPClient
L’IDE Arduino mette a disposizione una libreria NTPClient molto semplice da usare.
Puoi scaricarla direttamente dall’IDE Arduino IDE --> Manage Libraries
.
Vado a mostrare subito lo sketch di esempio, in quest’articolo mostro direttamente il codice e vado a spiegare direttamente con i commenti il funzionamento vista la semplicità.
/*
* Simple NTP client
* https://mischianti.org/
*
* The MIT License (MIT)
* written by Renzo Mischianti <www.mischianti.org>
*/
#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>
const char *ssid = "<SSID>";
const char *password = "<PASSWORD>";
WiFiUDP ntpUDP;
// By default 'pool.ntp.org' is used with 60 seconds update interval and
// no offset
// NTPClient timeClient(ntpUDP);
// You can specify the time server pool and the offset, (in seconds)
// additionaly you can specify the update interval (in milliseconds).
int GTMOffset = 1;
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", GTMOffset*60*60, 60000);
void setup(){
Serial.begin(115200);
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED ) {
delay ( 500 );
Serial.print ( "." );
}
timeClient.begin();
}
void loop() {
// Update NTP client
timeClient.update();
// Print current date
Serial.println(timeClient.getFormattedTime());
delay(1000);
}
Ora il tuo arduino o esp è diventato un orologio.
Time library
Questo esempio presenta alcune limitazioni, per aggiornare la data che è necessario chiamare il servizio, un modo semplice per aggiornare localmente la data è utilizzare la libreria Time. Questa libreria di Arduino utilizza un orologio interno come un normale orologio che dopo averlo regolato tramite NTP è un normale orologio.
Puoi trovare la libreria qui GitHub.
Ecco un semplice esempio.
/*
* Simple NTP client
* https://mischianti.org/
*
* The MIT License (MIT)
* written by Renzo Mischianti <www.mischianti.org>
*/
const char *ssid = "<SSID>";
const char *password = "<PASSWORD>";
#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <time.h>
/**
* Input time in epoch format and return tm time format
* by Renzo Mischianti <www.mischianti.org>
*/
static tm getDateTimeByParams(long time){
struct tm *newtime;
const time_t tim = time;
newtime = localtime(&tim);
return *newtime;
}
/**
* Input tm time format and return String with format pattern
* by Renzo Mischianti <www.mischianti.org>
*/
static String getDateTimeStringByParams(tm *newtime, char* pattern = (char *)"%d/%m/%Y %H:%M:%S"){
char buffer[30];
strftime(buffer, 30, pattern, newtime);
return buffer;
}
/**
* Input time in epoch format format and return String with format pattern
* by Renzo Mischianti <www.mischianti.org>
*/
static String getEpochStringByParams(long time, char* pattern = (char *)"%d/%m/%Y %H:%M:%S"){
// struct tm *newtime;
tm newtime;
newtime = getDateTimeByParams(time);
return getDateTimeStringByParams(&newtime, pattern);
}
WiFiUDP ntpUDP;
// By default 'pool.ntp.org' is used with 60 seconds update interval and
// no offset
// NTPClient timeClient(ntpUDP);
// You can specify the time server pool and the offset, (in seconds)
// additionaly you can specify the update interval (in milliseconds).
int GTMOffset = 1;
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", GTMOffset*60*60, 60*60*1000);
void setup(){
Serial.begin(115200);
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED ) {
delay ( 500 );
Serial.print ( "." );
}
timeClient.begin();
delay ( 1000 );
if (timeClient.update()){
Serial.print ( "Adjust local clock" );
unsigned long epoch = timeClient.getEpochTime();
// HERE I UPDATE LOCAL CLOCK
setTime(epoch);
}else{
Serial.print ( "NTP Update not WORK!!" );
}
}
void loop() {
// Now use local clock to show the time
Serial.println(getEpochStringByParams(now()));
delay(1000);
}
Come puoi vedere, creo alcune semplici funzioni per analizzare e formattare la data.
/**
* Input time in epoch format and return tm time format
* by Renzo Mischianti <www.mischianti.org>
*/
static tm getDateTimeByParams(long time){
struct tm *newtime;
const time_t tim = time;
newtime = localtime(&tim);
return *newtime;
}
/**
* Input tm time format and return String with format pattern
* by Renzo Mischianti <www.mischianti.org>
*/
static String getDateTimeStringByParams(tm *newtime, char* pattern = (char *)"%d/%m/%Y %H:%M:%S"){
char buffer[30];
strftime(buffer, 30, pattern, newtime);
return buffer;
}
/**
* Input time in epoch format format and return String with format pattern
* by Renzo Mischianti <www.mischianti.org>
*/
static String getEpochStringByParams(long time, char* pattern = (char *)"%d/%m/%Y %H:%M:%S"){
// struct tm *newtime;
tm newtime;
newtime = getDateTimeByParams(time);
return getDateTimeStringByParams(&newtime, pattern);
}
Devi prestare attenzione alla linea 20, lì puoi scegliere se vuoi l’ora UTC, impostando l’offset GMT su 0 o se vuoi impostare l’GTMoffset
dell’area.
Vivo in Italia, quindi ho intenzione di impostare GTM + 1 e ho impostato l’ultimo parametro su 60000, l’intervallo di aggiornamento in millisecondi, questo protocollo usa UDP come puoi vedere e devi scegliere un server NTP.
Ma questo non è sufficiente, alcune zone hanno il DST (ora legale), quindi è necessario calcolare la variazione dell’offset. In estate l’ora italiana imposta un’altra ora all’offset (quindi stiamo andando formalmente in GTM + 2), ma molti paese per il risparmio energetico utilizzano l’ora legale.
Ma calcolare che l’offset dinamico è molto noioso, per fortuna esiste una libreria che lo fa per te.
Libreria Timezone
Per gestire l’ora legale è possibile utilizzare la libreria Timezone.
Puoi scaricarla direttamente dall’IDE Arduino IDE --> Manage Libraries
.
L’uso non è così semplice, ma con l’esempio puoi capirlo.
Per prima cosa devi configurare il tuo DST, c’è un esempio con alcune configurazioni che puoi usare come punto di partenza.
// Australia Eastern Time Zone (Sydney, Melbourne)
TimeChangeRule aEDT = {"AEDT", First, Sun, Oct, 2, 660}; // UTC + 11 hours
TimeChangeRule aEST = {"AEST", First, Sun, Apr, 3, 600}; // UTC + 10 hours
Timezone ausET(aEDT, aEST);
// Moscow Standard Time (MSK, does not observe DST)
TimeChangeRule msk = {"MSK", Last, Sun, Mar, 1, 180};
Timezone tzMSK(msk);
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
Timezone CE(CEST, CET);
// United Kingdom (London, Belfast)
TimeChangeRule BST = {"BST", Last, Sun, Mar, 1, 60}; // British Summer Time
TimeChangeRule GMT = {"GMT", Last, Sun, Oct, 2, 0}; // Standard Time
Timezone UK(BST, GMT);
// UTC
TimeChangeRule utcRule = {"UTC", Last, Sun, Mar, 1, 0}; // UTC
Timezone UTC(utcRule);
// US Eastern Time Zone (New York, Detroit)
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240}; // Eastern Daylight Time = UTC - 4 hours
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300}; // Eastern Standard Time = UTC - 5 hours
Timezone usET(usEDT, usEST);
// US Central Time Zone (Chicago, Houston)
TimeChangeRule usCDT = {"CDT", Second, Sun, Mar, 2, -300};
TimeChangeRule usCST = {"CST", First, Sun, Nov, 2, -360};
Timezone usCT(usCDT, usCST);
// US Mountain Time Zone (Denver, Salt Lake City)
TimeChangeRule usMDT = {"MDT", Second, Sun, Mar, 2, -360};
TimeChangeRule usMST = {"MST", First, Sun, Nov, 2, -420};
Timezone usMT(usMDT, usMST);
// Arizona is US Mountain Time Zone but does not use DST
Timezone usAZ(usMST);
// US Pacific Time Zone (Las Vegas, Los Angeles)
TimeChangeRule usPDT = {"PDT", Second, Sun, Mar, 2, -420};
TimeChangeRule usPST = {"PST", First, Sun, Nov, 2, -480};
Timezone usPT(usPDT, usPST);
Nel mio caso devo impostare i parametri CET e CEST (riga 10) e vado a modificare l’esempio NTP originale.
/*
* Simple NTP client
* https://mischianti.org/
*
* The MIT License (MIT)
* written by Renzo Mischianti <www.mischianti.org>
*/
const char *ssid = "<YOURSSID>";
const char *password = "<YOURPASSWD>";
#include <NTPClient.h>
// change next line to use with another board/shield
#include <ESP8266WiFi.h>
//#include <WiFi.h> // for WiFi shield
//#include <WiFi101.h> // for WiFi 101 shield or MKR1000
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <time.h>
#include <Timezone.h> // https://github.com/JChristensen/Timezone
/**
* Input time in epoch format and return tm time format
* by Renzo Mischianti <www.mischianti.org>
*/
static tm getDateTimeByParams(long time){
struct tm *newtime;
const time_t tim = time;
newtime = localtime(&tim);
return *newtime;
}
/**
* Input tm time format and return String with format pattern
* by Renzo Mischianti <www.mischianti.org>
*/
static String getDateTimeStringByParams(tm *newtime, char* pattern = (char *)"%d/%m/%Y %H:%M:%S"){
char buffer[30];
strftime(buffer, 30, pattern, newtime);
return buffer;
}
/**
* Input time in epoch format format and return String with format pattern
* by Renzo Mischianti <www.mischianti.org>
*/
static String getEpochStringByParams(long time, char* pattern = (char *)"%d/%m/%Y %H:%M:%S"){
// struct tm *newtime;
tm newtime;
newtime = getDateTimeByParams(time);
return getDateTimeStringByParams(&newtime, pattern);
}
WiFiUDP ntpUDP;
// By default 'pool.ntp.org' is used with 60 seconds update interval and
// no offset
// NTPClient timeClient(ntpUDP);
// You can specify the time server pool and the offset, (in seconds)
// additionaly you can specify the update interval (in milliseconds).
int GTMOffset = 0; // SET TO UTC TIME
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", GTMOffset*60*60, 60*60*1000);
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
Timezone CE(CEST, CET);
void setup(){
Serial.begin(115200);
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED ) {
delay ( 500 );
Serial.print ( "." );
}
timeClient.begin();
delay ( 1000 );
if (timeClient.update()){
Serial.print ( "Adjust local clock" );
unsigned long epoch = timeClient.getEpochTime();
setTime(epoch);
}else{
Serial.print ( "NTP Update not WORK!!" );
}
}
void loop() {
Serial.println(getEpochStringByParams(CE.toLocal(now())));
delay(1000);
}
Ora hai il tuo tempo con l’ora legale impostata correttamente.