Skip to content

Commit b80f975

Browse files
committed
Merge pull request #865 from pguyot/w40/picow-sta-sntp
Add SNTP support on Pico-W in STA mode These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 5c66e69 + 178984d commit b80f975

File tree

4 files changed

+88
-3
lines changed

4 files changed

+88
-3
lines changed

examples/erlang/rp2040/picow_wifi_sta.erl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ start() ->
3030
{connected, fun connected/0},
3131
{got_ip, fun got_ip/1},
3232
{disconnected, fun disconnected/0}
33+
]},
34+
{sntp, [
35+
{host, "pool.ntp.org"},
36+
{synchronized, fun sntp_synchronized/1}
3337
]}
3438
],
3539
case network:start(Config) of
@@ -47,3 +51,6 @@ got_ip({IPv4, Netmask, Gateway}) ->
4751

4852
disconnected() ->
4953
io:format("Disconnected from access point.\n").
54+
55+
sntp_synchronized({TVSec, TVUsec}) ->
56+
io:format("Synchronized time with SNTP server. TVSec=~p TVUsec=~p\n", [TVSec, TVUsec]).

src/platforms/rp2040/src/lib/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ if (PICO_CYW43_SUPPORTED)
7676
target_include_directories(pan_lwip_dhserver INTERFACE
7777
${BTSTACK_3RD_PARTY_PATH}/lwip/dhcp-server
7878
)
79-
target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC pico_cyw43_arch_lwip_threadsafe_background INTERFACE pan_lwip_dhserver)
79+
target_link_libraries(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC pico_cyw43_arch_lwip_threadsafe_background pico_lwip_sntp INTERFACE pan_lwip_dhserver)
8080
target_link_options(libAtomVM${PLATFORM_LIB_SUFFIX} PUBLIC "SHELL:-Wl,-u -Wl,networkregister_port_driver")
8181
endif()
8282

src/platforms/rp2040/src/lib/lwipopts.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,9 @@
100100
#define SLIP_DEBUG LWIP_DBG_OFF
101101
#define DHCP_DEBUG LWIP_DBG_OFF
102102

103+
#define SNTP_GET_SERVERS_FROM_DHCP 1
104+
#define SNTP_SERVER_DNS 1
105+
void sntp_set_system_time_us(unsigned long sec, unsigned long usec);
106+
#define SNTP_SET_SYSTEM_TIME_US(sec, usec) sntp_set_system_time_us(sec, usec)
107+
103108
#endif /* __LWIPOPTS_H__ */

src/platforms/rp2040/src/lib/networkdriver.c

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
#include <cyw43.h>
3434
#include <dhserver.h>
35+
#include <hardware/rtc.h>
36+
#include <lwip/apps/sntp.h>
3537
#include <pico/cyw43_arch.h>
3638

3739
#pragma GCC diagnostic pop
@@ -42,7 +44,10 @@ static const char *const ap_atom = ATOM_STR("\x2", "ap");
4244
static const char *const ap_sta_connected_atom = ATOM_STR("\x10", "ap_sta_connected");
4345
static const char *const ap_sta_disconnected_atom = ATOM_STR("\x13", "ap_sta_disconnected");
4446
static const char *const ap_started_atom = ATOM_STR("\xA", "ap_started");
47+
static const char *const host_atom = ATOM_STR("\x4", "host");
4548
static const char *const psk_atom = ATOM_STR("\x3", "psk");
49+
static const char *const sntp_atom = ATOM_STR("\x4", "sntp");
50+
static const char *const sntp_sync_atom = ATOM_STR("\x9", "sntp_sync");
4651
static const char *const ssid_atom = ATOM_STR("\x4", "ssid");
4752
static const char *const sta_atom = ATOM_STR("\x3", "sta");
4853
static const char *const sta_connected_atom = ATOM_STR("\xD", "sta_connected");
@@ -67,6 +72,7 @@ struct NetworkDriverData
6772
uint32_t owner_process_id;
6873
uint64_t ref_ticks;
6974
int link_status;
75+
char *sntp_hostname;
7076
int stas_count;
7177
uint8_t *stas_mac;
7278
struct dhcp_config *dhcp_config;
@@ -169,6 +175,18 @@ static void send_ap_sta_disconnected(uint8_t *mac)
169175
send_atom_mac(globalcontext_make_atom(driver_data->global, ap_sta_disconnected_atom), mac);
170176
}
171177

178+
static void send_sntp_sync(struct timeval *tv)
179+
{
180+
// {Ref, {sntp_sync, {TVSec, TVUsec}}}
181+
BEGIN_WITH_STACK_HEAP(PORT_REPLY_SIZE + TUPLE_SIZE(2) * 2 + BOXED_INT64_SIZE * 2, heap);
182+
{
183+
term tv_tuple = port_heap_create_tuple2(&heap, term_make_maybe_boxed_int64(tv->tv_sec, &heap), term_make_maybe_boxed_int64(tv->tv_usec, &heap));
184+
term reply = port_heap_create_tuple2(&heap, globalcontext_make_atom(driver_data->global, sntp_sync_atom), tv_tuple);
185+
send_term(&heap, reply);
186+
}
187+
END_WITH_STACK_HEAP(heap, driver_data->global);
188+
}
189+
172190
static term start_sta(term sta_config, GlobalContext *global)
173191
{
174192
term ssid_term = interop_kv_get_value(sta_config, ssid_atom, global);
@@ -374,6 +392,52 @@ static term start_ap(term ap_config, GlobalContext *global)
374392
return setup_dhcp_server();
375393
}
376394

395+
void sntp_set_system_time_us(unsigned long sec, unsigned long usec)
396+
{
397+
struct timeval tv;
398+
tv.tv_sec = sec;
399+
tv.tv_usec = usec;
400+
settimeofday(&tv, NULL);
401+
402+
send_sntp_sync(&tv);
403+
404+
// We also set RTC time.
405+
if (UNLIKELY(!rtc_running())) {
406+
rtc_init();
407+
}
408+
gettimeofday(&tv, NULL);
409+
struct tm utc;
410+
gmtime_r(&tv.tv_sec, &utc);
411+
datetime_t pico_datetime;
412+
pico_datetime.year = utc.tm_year + 1900;
413+
pico_datetime.month = utc.tm_mon + 1;
414+
pico_datetime.day = utc.tm_mday;
415+
pico_datetime.dotw = 0;
416+
pico_datetime.hour = utc.tm_hour;
417+
pico_datetime.min = utc.tm_min;
418+
pico_datetime.sec = utc.tm_sec;
419+
rtc_set_datetime(&pico_datetime);
420+
}
421+
422+
static void setup_sntp(term sntp_config, GlobalContext *global)
423+
{
424+
if (!term_is_invalid_term(interop_kv_get_value(sntp_config, host_atom, global))) {
425+
int ok;
426+
driver_data->sntp_hostname = interop_term_to_string(interop_kv_get_value(sntp_config, host_atom, global), &ok);
427+
if (LIKELY(ok)) {
428+
sntp_setoperatingmode(SNTP_OPMODE_POLL);
429+
sntp_setservername(0, driver_data->sntp_hostname);
430+
sntp_init();
431+
} else {
432+
free(driver_data->sntp_hostname);
433+
}
434+
} else {
435+
sntp_setoperatingmode(SNTP_OPMODE_POLL);
436+
sntp_servermode_dhcp(1);
437+
sntp_init();
438+
}
439+
}
440+
377441
static void network_driver_netif_status_cb(struct netif *netif)
378442
{
379443
// We don't really need to lock to call cyw43_tcpip_link_status
@@ -404,13 +468,16 @@ static void start_network(Context *ctx, term pid, term ref, term config)
404468

405469
if (driver_data == NULL) {
406470
driver_data = malloc(sizeof(struct NetworkDriverData));
471+
driver_data->sntp_hostname = NULL;
407472
driver_data->stas_mac = NULL;
408473
driver_data->dhcp_config = NULL;
409474
}
410475
driver_data->global = ctx->global;
411476
driver_data->owner_process_id = term_to_local_process_id(pid);
412477
driver_data->ref_ticks = term_to_ref_ticks(ref);
413478
driver_data->link_status = CYW43_LINK_DOWN;
479+
free(driver_data->sntp_hostname);
480+
driver_data->sntp_hostname = NULL;
414481
free(driver_data->stas_mac);
415482
free(driver_data->dhcp_config);
416483
driver_data->stas_count = 0;
@@ -427,7 +494,7 @@ static void start_network(Context *ctx, term pid, term ref, term config)
427494
return;
428495
}
429496

430-
if (sta_config) {
497+
if (!term_is_invalid_term(sta_config)) {
431498
term result_atom = start_sta(sta_config, ctx->global);
432499
if (result_atom != OK_ATOM) {
433500
term error = port_create_error_tuple(ctx, result_atom);
@@ -440,7 +507,7 @@ static void start_network(Context *ctx, term pid, term ref, term config)
440507
cyw43_arch_enable_sta_mode();
441508
}
442509

443-
if (ap_config) {
510+
if (!term_is_invalid_term(ap_config)) {
444511
term result_atom = start_ap(ap_config, ctx->global);
445512
if (result_atom != OK_ATOM) {
446513
term error = port_create_error_tuple(ctx, result_atom);
@@ -455,6 +522,11 @@ static void start_network(Context *ctx, term pid, term ref, term config)
455522
cyw43_arch_disable_ap_mode();
456523
}
457524

525+
term sntp_config = interop_kv_get_value_default(config, sntp_atom, term_invalid_term(), ctx->global);
526+
if (!term_is_invalid_term(sntp_config)) {
527+
setup_sntp(sntp_config, ctx->global);
528+
}
529+
458530
//
459531
// Done -- send an ok so the FSM can proceed
460532
//
@@ -524,6 +596,7 @@ void network_driver_destroy(GlobalContext *global)
524596
UNUSED(global);
525597

526598
if (driver_data) {
599+
free(driver_data->sntp_hostname);
527600
free(driver_data->stas_mac);
528601
if (driver_data->dhcp_config) {
529602
dhserv_free();

0 commit comments

Comments
 (0)