-
Notifications
You must be signed in to change notification settings - Fork 0
SNTP, Time, TZ, libc
With the Arduino ESP8266 Core version number 2.4.0, the newlib C Library (libc) was added. And thus support for a familiar collection of time functions has been added. A manual for the newlib C Library can be found here
For libc to handle timezone and daylight savings time for a local, the TZ
environment variable must be set.
A description of the format can be found here on page 289 and
here. After setting TZ
, a call to tzset()
will parse the TZ environment variable and store the results for later use by localtime() and other time functions. After the call to tzset()
, we no longer need to tie up memory with the TZ
environment variable; however, unsetenv()
does not work as you would expect. unsetenv()
will remove the variable from the environment table; however, its memory allocation is not free-ed.
Due to the legacy nature of libc, the way the environment table works is less than optimal for an embedded environment. When environment variables are unset with unsetenv()
or updated using set()
the old memory allocations are never released. This was done this way because there was no way to notify that a change had been made and the old value was invalid. Thus small leaks were allowed. This was tolerable with a transient application, where the leaks would be free-ed at termination. This behavior is not so nice in a memory tight embedded environment, where there is no application to terminate.
To work around this I use the following function to handle processing a tzset()
call. I set up a temporary environment table for tzset()
to reference, then restore the old table pointer afterward. tzset()
parses and stores all the information it needs. While tzset()
does save a copy of the pointer to the environment variable, it parsed. It is only used to compare against a future TZ
environment variable for change detection. The only places I see in the libc code that call tzset()
are: 1) setenv()
will check to see if you are setting TZ
and call tzset()
and 2) When strftime.c
is compiled for regression testing, a call to tzset() is made; however, compiled as a library function, no calls are made.
CAVEAT: To avoid confusion, if you use this function
setTimeTZ()
, then only usesetTimeTZ()
to update time zone information. Never calltzset()
directly or callsetenv()
to setTZ
. Also, a call totzset()
with out an environment variableTZ
present, will reset time zone information back to GMT.
#include <time.h>
extern char **environ;
void setTimeTZ(const char *tz) {
char **old_environ = environ;
char * tzEnv[2] = { (char *)tz, NULL };
environ = &tzEnv[0];
tzset();
environ = old_environ;
}
void setup() {
...
setTimeTZ("PST+08PDT+07,M3.2.0M11.1.0");
configTime(0, 0, "pool.ntp.org");
// or configTime(0, 0, "0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org");
...
}
...
Also in the call to configTime()
, use 0 for timezone and daylight options. This sets up the lwIP sntp code to return GMT zone time. You want GMT zone time to pass to the libc time function calls.
- The character arrays used to identify the NTP Servers in the call to
configTime()
must be gobal, static or permanently allocated memory. These pointers may be referenced long after the calling function has exited. - When the DHCP server has been configured to give out NTP Server Names,
calling
configTime()
with one to three NTP Server Name arguments has no effect. You just as well be calling with nullptr,configTime(0, 0, nullptr, nullptr, nullptr)
. - When the DHCP server has not been configured to give out NTP Server Name, then the NTP Server Names provided by configTime() are used.
- The above summary is my interpretation of the code at
tools\sdk\lwip2\builder\lwip2-src\src\apps\sntp\sntp.c
.- A call to
dhcp_set_ntp_servers()
will overwrite any NTP Server Names set by a previous call toconfigTime()
orsntp_setservername()
. - When
dhcp_set_ntp_servers()
is called with a list of NTP Server Addresses, any remaining possitions, not set from the list, are cleared. -
dhcp_set_ntp_servers()
is called as part of the DHCP process. WhenlwIP
is built with the defines:LWIP_DHCP
andSNTP_GET_SERVERS_FROM_DHCP
set. - Calls to
sntp_setserver()
, when built withSNTP_SERVER_DNS
defined, will set toNULL
the NTP Server Name set by a previous call tosntp_setservername()
. Note, this is howdhcp_set_ntp_servers()
is clearing previously set NTP Server Names.
- A call to