Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 38 commits into from
Sep 29, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b8448e7
time: import IANA timezone definitions
d-a-v Aug 1, 2019
802d228
fix former configTime non-matching signature
d-a-v Aug 1, 2019
8ee720b
+include
d-a-v Aug 1, 2019
452706c
Merge branch 'master' into ntpexamplev2
d-a-v Sep 2, 2019
f0ebbe6
example: add scheduled function in callback
d-a-v Sep 2, 2019
d023a0c
Merge branch 'master' into ntpexamplev2
d-a-v Sep 6, 2019
60e565e
crlf fix
d-a-v Sep 6, 2019
b825a36
+missing license for napt
d-a-v Sep 6, 2019
d141e61
SNTP: expose configuration helpers
d-a-v Sep 6, 2019
b9a8eaa
update submodule
d-a-v Sep 6, 2019
c665bb2
update precompiled libraries
d-a-v Sep 6, 2019
e1ec8cc
optional: change SNTP startup delay
d-a-v Sep 6, 2019
c2c48fc
makes SNTP_UPDATE_DELAY a weak function
d-a-v Sep 8, 2019
ac5948f
on the proper use of polledTimeout api... thanks @mcspr :]
d-a-v Sep 9, 2019
857f67b
improve update script (per review)
d-a-v Sep 9, 2019
b96ce9a
Merge branch 'master' into ntpexamplev2
d-a-v Sep 9, 2019
ddca580
update lwIP submodule
d-a-v Sep 9, 2019
a109804
update submodule
d-a-v Sep 9, 2019
d10da64
Merge branch 'ntpexamplev2' of github.com:d-a-v/Arduino into ntpexamp…
d-a-v Sep 9, 2019
fb38186
hide harmless shell message
d-a-v Sep 10, 2019
25d5b32
Merge branch 'master' into ntpexamplev2
d-a-v Sep 11, 2019
a82ddcd
Merge branch 'master' into ntpexamplev2
earlephilhower Sep 13, 2019
233583a
Merge branch 'master' into ntpexamplev2
d-a-v Sep 21, 2019
91e3fef
update the release process by asking first to update TZ.h
d-a-v Sep 21, 2019
c2ad512
Merge branch 'master' into ntpexamplev2
d-a-v Sep 23, 2019
1b2fe6d
minor update in release documentation
d-a-v Sep 24, 2019
293760b
update in release documentation
d-a-v Sep 24, 2019
52c6012
update in release documentation
d-a-v Sep 24, 2019
2914603
clarify release documentation
d-a-v Sep 24, 2019
b634672
fix release documentation - sorry for the noise :(
d-a-v Sep 24, 2019
76b6a97
Merge branch 'master' into ntpexamplev2
earlephilhower Sep 26, 2019
e070886
Merge branch 'master' into ntpexamplev2
d-a-v Sep 28, 2019
714c294
fixes per review
d-a-v Sep 29, 2019
b49cdfc
example style
d-a-v Sep 29, 2019
f884a48
Merge branch 'master' into ntpexamplev2
d-a-v Sep 29, 2019
27b0af3
useless variable in example
d-a-v Sep 29, 2019
eaef5e1
update lwip2 submodule reference, to include espressif missing declar…
d-a-v Sep 29, 2019
58c61c2
Merge branch 'ntpexamplev2' of github.com:d-a-v/Arduino into ntpexamp…
d-a-v Sep 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions cores/esp8266/Arduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,13 @@ long secureRandom(long);
long secureRandom(long, long);
long map(long, long, long, long, long);

extern "C" void configTime(long timezone, int daylightOffset_sec,
const char* server1, const char* server2 = nullptr, const char* server3 = nullptr);
void configTime(int timezone, int daylightOffset_sec, const char* server1,
devyte marked this conversation as resolved.
Show resolved Hide resolved
const char* server2 = nullptr, const char* server3 = nullptr);

#endif
void configTime(const char* tz, const char* server1,
const char* server2 = nullptr, const char* server3 = nullptr);

#endif // __cplusplus

#include "pins_arduino.h"

Expand Down
475 changes: 475 additions & 0 deletions cores/esp8266/TZ.h

Large diffs are not rendered by default.

20 changes: 19 additions & 1 deletion cores/esp8266/time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*
*/

#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <sys/reent.h>
Expand Down Expand Up @@ -60,7 +61,7 @@ static void setServer(int id, const char* name_or_ip)
}
}


extern "C++"
d-a-v marked this conversation as resolved.
Show resolved Hide resolved
void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, const char* server2, const char* server3)
{
sntp_stop();
Expand All @@ -74,6 +75,23 @@ void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, c
sntp_init();
}

extern "C++"
void configTime(const char* tz, const char* server1, const char* server2, const char* server3)
{
sntp_stop();

setServer(0, server1);
d-a-v marked this conversation as resolved.
Show resolved Hide resolved
setServer(1, server2);
setServer(2, server3);

char tzram[strlen_P(tz) + 1];
memcpy_P(tzram, tz, sizeof(tzram));
setenv("TZ", tzram, 1/*overwrite*/);
tzset();

sntp_init();
}

int clock_gettime(clockid_t unused, struct timespec *tp)
{
(void) unused;
Expand Down
267 changes: 173 additions & 94 deletions libraries/esp8266/examples/NTP-TZ-DST/NTP-TZ-DST.ino
Original file line number Diff line number Diff line change
@@ -1,80 +1,148 @@
/*
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

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

// select 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 to watch automatic local time time adjustment on Summer/Winter change
// (exactly 182 days (~6 month) are regularly added or substracted in the example)
//#define MYTZ TZ_Europe_Paris
d-a-v marked this conversation as resolved.
Show resolved Hide resolved

////////////////////////////////////////////////////////

#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>

#include <time.h> // time() ctime()
#include <sys/time.h> // struct timeval

timeval cbtime; // time set in callback
bool cbtime_set = false;
#if LWIP_VERSION_MAJOR == 1
#include <lwip/sntp.h> // sntp_servermode_dhcp()
#else
#include <lwip/apps/sntp.h> // sntp_servermode_dhcp()
#endif

void time_is_set(void) {
gettimeofday(&cbtime, NULL);
cbtime_set = true;
Serial.println("------------------ settimeofday() was called ------------------");
// for testing purpose:
extern "C" int clock_gettime(clockid_t unused, struct timespec *tp);

////////////////////////////////////////////////////////

timeval tv;
timespec tp;
time_t now;
uint32_t now_ms, now_us;

bool time_is_set = false;
esp8266::polledTimeout::periodicMs showTime(3200);
bool next_change_is_future = true;
esp8266::polledTimeout::periodicMs add_6months_now(1 << 30);
d-a-v marked this conversation as resolved.
Show resolved Hide resolved

unsigned long testcbms;

void time_is_set_scheduled() {
// everything is allowed in this function
Serial.printf("\n----------> scheduled CB: settimeofday() has been called %ldms ago <----------\n"
"sleeping 2s\n\n",
millis() - testcbms);
delay(2000);
}

void time_is_set_callback() {
// like in an ISR
// it is not allowed to call "heavy" core API
// like yield()/delay()/print()/network/...

time_is_set = true;

testcbms = millis();
d-a-v marked this conversation as resolved.
Show resolved Hide resolved
// call time_is_set_scheduled() at next loop():
schedule_function(time_is_set_scheduled);
}

// 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 ()
//{
// //::printf("(debug: sntp_startup_delay_MS_rfc_not_less_than_60000 is called)\n");a
// 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 ()
//{
// ::printf("(debug: sntp_update_delay_MS_rfc_not_less_than_15000 is called)\n");
// return 15000; // 15s
//}

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

#if NTP0_OR_LOCAL1
// local
Serial.println("\nStarting...\n");

ESP.eraseConfig();
time_t rtc = RTC_TEST;
// 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 = { TZ_MN + DST_MN, 0 };
timezone tz = { 0, 0 };
settimeofday(&tv, &tz);

#else // ntp
// 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);

configTime(TZ_SEC, DST_SEC, "pool.ntp.org");
// 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(SSID, SSIDPWD);
// don't wait, observe time changing when ntp timestamp is received
WiFi.begin(STASSID, STAPSK);

#endif // ntp
// don't wait for network, observe time changing
// when NTP timestamp is received
}

// for testing purpose:
extern "C" int clock_gettime(clockid_t unused, struct timespec *tp);

#define PTM(w) \
Serial.print(":" #w "="); \
Serial.print(" " #w "="); \
Serial.print(tm->tm_##w);

void printTm(const char* what, const tm* tm) {
Expand All @@ -84,61 +152,72 @@ 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 loop() {

gettimeofday(&tv, nullptr);
clock_gettime(0, &tp);
now = time(nullptr);
now_ms = millis();
now_us = micros();
if (time_is_set) {
Serial.printf("\n----------> loop: settimeofday() has been called %ldms ago ! <----------\n\n", millis() - testcbms);
time_is_set = false;
add_6months_now.reset(20000);
}

// 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));
if (showTime) {
gettimeofday(&tv, nullptr);
clock_gettime(0, &tp);
now = time(nullptr);
now_ms = millis();
now_us = micros();

// for demo purpose, switch 6 months back and forth
if (add_6months_now) {
tv.tv_sec += (next_change_is_future ? -1 : 1) * (60 * 60 * 24 * 364 / 2);
settimeofday(&tv, nullptr);
Serial.printf("\n"
"-- time machine: artificially moving 6 months %s\n"
"-- observe local time change according to selected TZ's DST rules\n"
"-- (and without assistance from sketch)\n"
"\n",
next_change_is_future ? "forward (future)" : "backward (to now)");
next_change_is_future = !next_change_is_future;
return;
}

printTm("localtime:", localtime(&now));
Serial.println();
printTm("gmtime ", gmtime(&now));
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);
Serial.printf("in future: %s\n", next_change_is_future ? "no" : "6 months");

// human readable
Serial.print("ctime: ");
Serial.print(ctime(&now));

Serial.println();
}

// 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);
}
Loading