1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-07-02 14:22:55 +03:00

time: import IANA timezone definitions, expose SNTP API (#6373)

* time: import IANA timezone definitions
- `configTime("timezone", "ntp servers...")` added
- timezone definitions by country/cities (TZ.h)
- script to update timezone definitions
- updated example

* fix former configTime non-matching signature

* +include

* example: add scheduled function in callback

* crlf fix

* +missing license for napt

* SNTP: expose configuration helpers

* update submodule

* update precompiled libraries

* optional: change SNTP startup delay

* makes SNTP_UPDATE_DELAY a weak function
update example
fix for lwip1.4

* on the proper use of polledTimeout api... thanks @mcspr :]

* improve update script (per review)

* update lwIP submodule

* update submodule

* hide harmless shell message

* update the release process by asking first to update TZ.h
[ci skip]

* minor update in release documentation

* update in release documentation

* update in release documentation

* clarify release documentation

* fix release documentation - sorry for the noise :(

* fixes per review

* example style

* useless variable in example

* update lwip2 submodule reference, to include espressif missing declaration fixes
This commit is contained in:
david gauchard
2019-09-29 05:25:01 +02:00
committed by Develo
parent 4489e239bb
commit ffe5476fc4
18 changed files with 980 additions and 270 deletions

View File

@ -1,80 +1,145 @@
/*
NTP-TZ-DST
NTP-TZ-DST (v2)
NetWork Time Protocol - Time Zone - Daylight Saving Time
This example shows how to read and set time,
and how to use NTP (set NTP0_OR_LOCAL1 to 0 below)
or an external RTC (set NTP0_OR_LOCAL1 to 1 below)
TZ and DST below have to be manually set
according to your local settings.
This example shows:
- how to read and set time
- how to set timezone per country/city
- how is local time automatically handled per official timezone definitions
- how to change internal sntp start and update delay
- how to use callbacks when time is updated
This example code is in the public domain.
*/
#include <ESP8266WiFi.h>
#include <time.h> // time() ctime()
#include <sys/time.h> // struct timeval
#include <coredecls.h> // settimeofday_cb()
////////////////////////////////////////////////////////
#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK "your-password"
#endif
#define SSID STASSID
#define SSIDPWD STAPSK
#define TZ 1 // (utc+) TZ in hours
#define DST_MN 60 // use 60mn for summer time in some countries
// initial time (possibly given by an external RTC)
#define RTC_UTC_TEST 1510592825 // 1510592825 = Monday 13 November 2017 17:07:05 UTC
#define NTP0_OR_LOCAL1 1 // 0:use NTP 1:fake external RTC
#define RTC_TEST 1510592825 // 1510592825 = Monday 13 November 2017 17:07:05 UTC
// This database is autogenerated from IANA timezone database
// https://www.iana.org/time-zones
// and can be updated on demand in this repository
#include <TZ.h>
// "TZ_" macros follow DST change across seasons without source code change
// check for your nearest city in TZ.h
// espressif headquarter TZ
//#define MYTZ TZ_Asia_Shanghai
// example for "Not Only Whole Hours" timezones:
// Kolkata/Calcutta is shifted by 30mn
//#define MYTZ TZ_Asia_Kolkata
// example of a timezone with a variable Daylight-Saving-Time:
// demo: watch automatic time adjustment on Summer/Winter change (DST)
#define MYTZ TZ_Europe_London
////////////////////////////////////////////////////////
#define TZ_MN ((TZ)*60)
#define TZ_SEC ((TZ)*3600)
#define DST_SEC ((DST_MN)*60)
#include <ESP8266WiFi.h>
#include <coredecls.h> // settimeofday_cb()
#include <Schedule.h>
#include <PolledTimeout.h>
timeval cbtime; // time set in callback
bool cbtime_set = false;
#include <time.h> // time() ctime()
#include <sys/time.h> // struct timeval
void time_is_set(void) {
gettimeofday(&cbtime, NULL);
cbtime_set = true;
Serial.println("------------------ settimeofday() was called ------------------");
}
void setup() {
Serial.begin(115200);
settimeofday_cb(time_is_set);
#if NTP0_OR_LOCAL1
// local
ESP.eraseConfig();
time_t rtc = RTC_TEST;
timeval tv = { rtc, 0 };
timezone tz = { TZ_MN + DST_MN, 0 };
settimeofday(&tv, &tz);
#else // ntp
configTime(TZ_SEC, DST_SEC, "pool.ntp.org");
WiFi.mode(WIFI_STA);
WiFi.begin(SSID, SSIDPWD);
// don't wait, observe time changing when ntp timestamp is received
#endif // ntp
}
#if LWIP_VERSION_MAJOR == 1
#include <lwip/sntp.h> // sntp_servermode_dhcp()
#else
#include <lwip/apps/sntp.h> // sntp_servermode_dhcp()
#endif
// for testing purpose:
extern "C" int clock_gettime(clockid_t unused, struct timespec *tp);
////////////////////////////////////////////////////////
static timeval tv;
static timespec tp;
static time_t now;
static uint32_t now_ms, now_us;
static esp8266::polledTimeout::periodicMs showTimeNow(60000);
static int time_machine_days = 0; // 0 = now
static bool time_machine_running = false;
// OPTIONAL: change SNTP startup delay
// a weak function is already defined and returns 0 (RFC violation)
// it can be redefined:
//uint32_t sntp_startup_delay_MS_rfc_not_less_than_60000 ()
//{
// //info_sntp_startup_delay_MS_rfc_not_less_than_60000_has_been_called = true;
// return 60000; // 60s (or lwIP's original default: (random() % 5000))
//}
// OPTIONAL: change SNTP update delay
// a weak function is already defined and returns 1 hour
// it can be redefined:
//uint32_t sntp_update_delay_MS_rfc_not_less_than_15000 ()
//{
// //info_sntp_update_delay_MS_rfc_not_less_than_15000_has_been_called = true;
// return 15000; // 15s
//}
void showTime() {
gettimeofday(&tv, nullptr);
clock_gettime(0, &tp);
now = time(nullptr);
now_ms = millis();
now_us = micros();
Serial.println();
printTm("localtime:", localtime(&now));
Serial.println();
printTm("gmtime: ", gmtime(&now));
Serial.println();
// time from boot
Serial.print("clock: ");
Serial.print((uint32_t)tp.tv_sec);
Serial.print("s / ");
Serial.print((uint32_t)tp.tv_nsec);
Serial.println("ns");
// time from boot
Serial.print("millis: ");
Serial.println(now_ms);
Serial.print("micros: ");
Serial.println(now_us);
// EPOCH+tz+dst
Serial.print("gtod: ");
Serial.print((uint32_t)tv.tv_sec);
Serial.print("s / ");
Serial.print((uint32_t)tv.tv_usec);
Serial.println("us");
// EPOCH+tz+dst
Serial.print("time: ");
Serial.println((uint32_t)now);
// timezone and demo in the future
Serial.printf("timezone: %s\n", MYTZ);
// human readable
Serial.print("ctime: ");
Serial.print(ctime(&now));
Serial.println();
}
#define PTM(w) \
Serial.print(":" #w "="); \
Serial.print(" " #w "="); \
Serial.print(tm->tm_##w);
void printTm(const char* what, const tm* tm) {
@ -84,61 +149,83 @@ void printTm(const char* what, const tm* tm) {
PTM(hour); PTM(min); PTM(sec);
}
timeval tv;
timespec tp;
time_t now;
uint32_t now_ms, now_us;
void time_is_set_scheduled() {
// everything is allowed in this function
// time machine demo
if (time_machine_running) {
if (time_machine_days == 0)
Serial.printf("---- settimeofday() has been called - possibly from SNTP\n"
" (starting time machine demo to show libc's automatic DST handling)\n\n");
now = time(nullptr);
const tm* tm = localtime(&now);
Serial.printf("future=%3ddays: DST=%s - ",
time_machine_days,
tm->tm_isdst ? "true " : "false");
Serial.print(ctime(&now));
gettimeofday(&tv, nullptr);
constexpr int days = 30;
time_machine_days += days;
if (time_machine_days > 360) {
tv.tv_sec -= (time_machine_days - days) * 60 * 60 * 24;
time_machine_days = 0;
} else {
tv.tv_sec += days * 60 * 60 * 24;
}
settimeofday(&tv, nullptr);
} else {
showTime();
}
}
void time_is_set_callback() {
// As in an ISR,
// it is not allowed to call "heavy" core API
// like yield()/delay()/print()/network/...
// If this is needed, use a scheduled function.
// This scheduled function is used for the demo, it is normaly unneeded
schedule_function(time_is_set_scheduled);
if (time_machine_days == 0) {
time_machine_running = !time_machine_running;
}
}
void setup() {
Serial.begin(115200);
Serial.println("\nStarting...\n");
// setup RTC time
// it will be used until NTP server will send us real current time
time_t rtc = RTC_UTC_TEST;
timeval tv = { rtc, 0 };
timezone tz = { 0, 0 };
settimeofday(&tv, &tz);
// install callback - called when settimeofday is called (by SNTP or us)
// once enabled (by DHCP), SNTP is updated every hour
settimeofday_cb(time_is_set_callback);
// NTP servers may be overriden by your DHCP server for a more local one
// (see below)
configTime(MYTZ, "pool.ntp.org");
// OPTIONAL: disable obtaining SNTP servers from DHCP
//sntp_servermode_dhcp(0); // 0: disable obtaining SNTP servers from DHCP (enabled by default)
// start network
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
WiFi.begin(STASSID, STAPSK);
// don't wait for network, observe time changing
// when NTP timestamp is received
Serial.printf("Time is currently set by a constant:\n");
showTime();
}
void loop() {
gettimeofday(&tv, nullptr);
clock_gettime(0, &tp);
now = time(nullptr);
now_ms = millis();
now_us = micros();
// localtime / gmtime every second change
static time_t lastv = 0;
if (lastv != tv.tv_sec) {
lastv = tv.tv_sec;
Serial.println();
printTm("localtime", localtime(&now));
Serial.println();
printTm("gmtime ", gmtime(&now));
Serial.println();
Serial.println();
if (showTimeNow) {
showTime();
}
// time from boot
Serial.print("clock:");
Serial.print((uint32_t)tp.tv_sec);
Serial.print("/");
Serial.print((uint32_t)tp.tv_nsec);
Serial.print("ns");
// time from boot
Serial.print(" millis:");
Serial.print(now_ms);
Serial.print(" micros:");
Serial.print(now_us);
// EPOCH+tz+dst
Serial.print(" gtod:");
Serial.print((uint32_t)tv.tv_sec);
Serial.print("/");
Serial.print((uint32_t)tv.tv_usec);
Serial.print("us");
// EPOCH+tz+dst
Serial.print(" time:");
Serial.print((uint32_t)now);
// human readable
Serial.print(" ctime:(UTC+");
Serial.print((uint32_t)(TZ * 60 + DST_MN));
Serial.print("mn)");
Serial.print(ctime(&now));
// simple drifting loop
delay(100);
}