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:
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user