Skip to content
6 changes: 3 additions & 3 deletions pico_w/wifi/ntp_system_time/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ The example uses:

1. the [SNTP application](https://www.nongnu.org/lwip/2_0_x/group__sntp.html) provided by the lwIP network stack
2. the Pico SDK high level "always on timer" abstraction: [pico_aon_timer](https://www.raspberrypi.com/documentation/pico-sdk/high_level.html#group_pico_aon_timer)
3. a [POSIX timezone](https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_node/libc_431.html) to convert UTC to local time
3. an optional [POSIX timezone](https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_node/libc_431.html) to convert UTC to local time

### lwIP SNTP

The lwIP SNTP app provides a straightforward way to obtain and process timestamps from a pool of NTP servers without the complexity of a full NTP implementation. The lwIP documentation covers the [configuration options](https://www.nongnu.org/lwip/2_0_x/group__sntp.html) but the comments in the [source code](https://github.com/lwip-tcpip/lwip/blob/master/src/apps/sntp/sntp.c) are also very helpful.

SNTP uses the **macros** `SNTP_GET_SYSTEM_TIME(sec, us)` and `SNTP_SET_SYSTEM_TIME(sec, us)` to call user-provided functions for accessing the system clock. The example defines the macros in `lwipopts.h` and the callbacks themselves are near the top of `ntp_system_time.c`.
SNTP uses the **macros** `SNTP_GET_SYSTEM_TIME(sec, us)` and `SNTP_SET_SYSTEM_TIME_US(sec, us)` to call user-provided functions for accessing the system clock. The example defines the macros in `lwipopts.h` and the callbacks themselves are near the top of `ntp_system_time.c`.

Note that the example runs lwIP/SNTP from `pico_cyw43_arch` in _threadsafe background mode_ as described in [SDK Networking](https://www.raspberrypi.com/documentation/pico-sdk/networking.html#group_pico_cyw43_arch).
If you reconfigure it to use _polling mode_ then your user code should periodically call `cyw43_arch_poll()`.
Expand Down Expand Up @@ -87,6 +87,6 @@ which means
Normal time ("GMT") is UTC +0. Daylight-saving time ("BST") runs from 1am on the last Sunday in March to 2am on the last Sunday in October.
```

The format to define your own POSIX timezone is pretty straightforward and can be found [here](https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_node/libc_431.html).
The format to define your own POSIX timezone is pretty straightforward and can be found [here](https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_node/libc_431.html); or you can simply choose a pre-defined one from an online resource such as [this](https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv).

_Note that it is entirely optional to create a local timezone: without one the Pico SDK time-conversion functions will simply use UTC._
6 changes: 4 additions & 2 deletions pico_w/wifi/ntp_system_time/lwipopts.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@
#define SNTP_RETRY_TIMEOUT_EXP 1
#define SNTP_MONITOR_SERVER_REACHABILITY 1

// configure SNTP to use our callback to set the system time
//* configure SNTP to use our callback functions for reading and setting the system time
#define SNTP_GET_SYSTEM_TIME(sec, us) sntp_get_system_time_us(&(sec), &(us))
#define SNTP_SET_SYSTEM_TIME_US(sec, us) sntp_set_system_time_us(sec, us)

// declare our callback functions (they are defined in ntp_system_time.c)
//* declare our callback functions (the implementations are in ntp_system_time.c)
#include "stdint.h"
void sntp_set_system_time_us(uint32_t sec, uint32_t us);
void sntp_get_system_time_us(uint32_t *sec_ptr, uint32_t *us_ptr);


#endif /* __LWIPOPTS_H__ */
33 changes: 18 additions & 15 deletions pico_w/wifi/ntp_system_time/ntp_system_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
auto_init_mutex(aon_timer_mutex);
static bool aon_timer_is_initialised = false;

// callback for lwIP/SNTP to set the aon_timer to UTC (see lwipopts.h)
// this is called every time the application receives a valid NTP server response
// callback for lwIP/SNTP to set the aon_timer to UTC
// we configure SNTP to call this function when it receives a valid NTP timestamp
// (see lwipopts.h)
void sntp_set_system_time_us(uint32_t sec, uint32_t us) {
static struct timespec ntp_ts;
ntp_ts.tv_sec = sec;
Expand All @@ -40,7 +41,8 @@ void sntp_set_system_time_us(uint32_t sec, uint32_t us) {
}

// callback for lwIP/SNTP to read system time (UTC) from the aon_timer
// when it needs to (eg) calculate the roundtrip transmission delay
// we configure SNTP to call this function to read the current UTC system time,
// eg to calculate the roundtrip transmission delay (see lwipopts.h)
void sntp_get_system_time_us(uint32_t *sec_ptr, uint32_t * us_ptr) {
static struct timespec sys_ts;
// we don't need exclusive access because we are on the background thread
Expand Down Expand Up @@ -91,8 +93,9 @@ int main() {
struct timespec ts;
struct tm tm;

// OPTIONAL: set the 'TZ' env variable to the local POSIX timezone (in this case Europe/London,
// to create your own see https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_node/libc_431.html)
// OPTIONAL: set the 'TZ' env variable to the local POSIX timezone (in this case Europe/London)
// For the format see: https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_node/libc_431.html
// or just copy one from (eg): https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
setenv("TZ", "BST0GMT,M3.5.0/1,M10.5.0/2", 1);

// If the environment contains a valid 'TZ' definition then functions like ctime(), localtime()
Expand All @@ -108,15 +111,14 @@ int main() {

// if you just want a string representation of the current time and you're not interested
// in the individual date/time fields, then here you can simply call:
// printf("%s", ctime(&(ts.tv_sec)));
// The string produced is the same as `asctime()` (see below). Note that if you set 'TZ'
// then the output will be in local time, otherwise UTC.

// you can unpack the raw UTC seconds count into individual date/time fields like this.
// Again, if you set 'TZ' then the values will be in local time, otherwise UTC. Note
// that by default `pico_localtime_r()` just calls `localtime_r()` from the standard 'C'
// library, but the declaration is 'weak' so that a user can override it with their own
// implementation if desired.

// if you don't need the date/time fields, you can call `ctime()` or one of its variants
// here to convert the raw timer value into a string like "Mon Oct 27 22:06:08 2025\n".
// If you have defined a valid 'TZ' the string will be in local time, otherwise UTC.
//printf("%s", ctime(&(ts.tv_sec)));

// you can extract the date/time fields use `localtime()` or one of its variants. If you
// have defined a valid 'TZ' then the field values will be in local time, otherwise UTC.
pico_localtime_r(&(ts.tv_sec), &tm);

// display the name of the currently active local timeszone, if defined
Expand All @@ -127,7 +129,8 @@ int main() {
printf("UTC: ");
}

// display individual date/time fields in the form "Mon Oct 27 22:06:08 2025\n"
// you can use `asctime()` and its variants to convert the date/time fields into a string
// like: "Mon Oct 27 22:06:08 2025\n". If you need more flexibility consider `strftime()`
printf("%s", asctime(&tm));

} else {
Expand Down