|
| 1 | +/** |
| 2 | + * Copyright (c) 2025 mjcross |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: BSD-3-Clause |
| 5 | + */ |
| 6 | + |
| 7 | +#include <stdio.h> |
| 8 | +#include "pico/stdlib.h" |
| 9 | +#include "pico/cyw43_arch.h" |
| 10 | +#include "lwip/apps/sntp.h" |
| 11 | +#include "pico/util/datetime.h" |
| 12 | +#include "pico/aon_timer.h" |
| 13 | +#include "pico/mutex.h" |
| 14 | + |
| 15 | +// create a mutex to avoid reading the aon_timer at the same time as lwIP/SNTP is updating it |
| 16 | +auto_init_mutex(aon_timer_mutex); |
| 17 | +static bool aon_timer_is_initialised = false; |
| 18 | + |
| 19 | +// callback for lwIP/SNTP to set the aon_timer to UTC (see lwipopts.h) |
| 20 | +// this is called every time the application receives a valid NTP server response |
| 21 | +void sntp_set_system_time_us(uint32_t sec, uint32_t us) { |
| 22 | + static struct timespec ntp_ts; |
| 23 | + ntp_ts.tv_sec = sec; |
| 24 | + ntp_ts.tv_nsec = us * 1000; |
| 25 | + |
| 26 | + if (aon_timer_is_initialised) { |
| 27 | + // wait up to 10ms to obtain exclusive access to the aon_timer |
| 28 | + if (mutex_enter_timeout_ms (&aon_timer_mutex, 10)) { |
| 29 | + aon_timer_set_time(&ntp_ts); |
| 30 | + mutex_exit(&aon_timer_mutex); // release the mutex as soon as possible |
| 31 | + puts("-> updated system time from NTP"); |
| 32 | + } else { |
| 33 | + puts("-> skipped NTP system time update (aon_timer was busy)"); |
| 34 | + } |
| 35 | + } else { |
| 36 | + // the aon_timer is uninitialised so we don't need exclusive access |
| 37 | + aon_timer_is_initialised = aon_timer_start(&ntp_ts); |
| 38 | + puts("-> initialised system time from NTP"); |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +// callback for lwIP/SNTP to read system time (UTC) from the aon_timer |
| 43 | +// when it needs to (eg) calculate the roundtrip transmission delay |
| 44 | +void sntp_get_system_time_us(uint32_t *sec_ptr, uint32_t * us_ptr) { |
| 45 | + static struct timespec sys_ts; |
| 46 | + // we don't need exclusive access because we are on the background thread |
| 47 | + aon_timer_get_time(&sys_ts); |
| 48 | + *sec_ptr = sys_ts.tv_sec; |
| 49 | + *us_ptr = sys_ts.tv_nsec / 1000; |
| 50 | +} |
| 51 | + |
| 52 | +// function for user code to safely read the system time (UTC) asynchronously |
| 53 | +int get_time_utc(struct timespec *ts_ptr) { |
| 54 | + int retval = 1; |
| 55 | + if (mutex_enter_timeout_ms(&aon_timer_mutex, 10)) { |
| 56 | + aon_timer_get_time(ts_ptr); |
| 57 | + mutex_exit(&aon_timer_mutex); |
| 58 | + retval = 0; |
| 59 | + } |
| 60 | + return retval; |
| 61 | +} |
| 62 | + |
| 63 | +int main() { |
| 64 | + stdio_init_all(); |
| 65 | + |
| 66 | + // Set local timezone for London |
| 67 | + |
| 68 | + // BST starts at 01:00 on the last Sunday in March and ends at 02:00 on the last Sunday in October |
| 69 | + |
| 70 | + |
| 71 | + // OPTIONAL: if you define a POSIX TZ here then the example will display local time instead of UTC. |
| 72 | + // For the format see https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_node/libc_431.html |
| 73 | + setenv("TZ", "BST0GMT,M3.5.0/1,M10.5.0/2", 1); // <-- this is the timezone spec for Europe/London |
| 74 | + // there is no need to call tzset() |
| 75 | + |
| 76 | + // Initialise the Wi-Fi chip |
| 77 | + if (cyw43_arch_init()) { |
| 78 | + printf("Wi-Fi init failed\n"); |
| 79 | + return -1; |
| 80 | + } |
| 81 | + |
| 82 | + // Enable wifi station mode |
| 83 | + cyw43_arch_enable_sta_mode(); |
| 84 | + printf("Connecting to Wi-Fi...\n"); |
| 85 | + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { |
| 86 | + printf("failed to connect\n"); |
| 87 | + return 1; |
| 88 | + } |
| 89 | + |
| 90 | + // display the ip address in human readable form |
| 91 | + uint8_t *ip_address = (uint8_t*)&(netif_default->ip_addr.addr); |
| 92 | + printf("IP address %d.%d.%d.%d\n", ip_address[0], ip_address[1], ip_address[2], ip_address[3]); |
| 93 | + |
| 94 | + // initialise the lwIP/SNTP application |
| 95 | + sntp_setoperatingmode(SNTP_OPMODE_POLL); // lwIP/SNTP also accepts SNTP_OPMODE_LISTENONLY |
| 96 | + sntp_init(); |
| 97 | + |
| 98 | + |
| 99 | + // ----- simple demonstration of how to read and display the system time ----- |
| 100 | + // |
| 101 | + struct timespec ts; |
| 102 | + struct tm tm; |
| 103 | + |
| 104 | + while (true) { |
| 105 | + |
| 106 | + if(aon_timer_is_initialised) { |
| 107 | + |
| 108 | + // read the current time as UTC seconds and ms since the epoch |
| 109 | + get_time_utc(&ts); |
| 110 | + |
| 111 | + // if you simply want to display the local time you could now do so with |
| 112 | + // puts(ctime(&(ts.tv_sec))); |
| 113 | + |
| 114 | + // to unpack the hours/mins/seconds etc for the local time zone (if defined, see above) |
| 115 | + pico_localtime_r(&(ts.tv_sec), &tm); // convert UTC linear time to broken-down local time |
| 116 | + printf("%s: %s", tm.tm_isdst ? "BST": "GMT", asctime(&tm)); // display as text |
| 117 | + |
| 118 | + } else { |
| 119 | + puts("system time not yet initialised"); |
| 120 | + } |
| 121 | + |
| 122 | + sleep_ms(5000); // do nothing for 5 seconds |
| 123 | + } |
| 124 | +} |
0 commit comments