From c44c298436cba9ea87c126d9fc072b7ed16d9459 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Mon, 14 Apr 2025 15:52:53 +0100 Subject: [PATCH 1/3] Use async context for led in iperf example Improve led handling by using the async context to turn the led on and off. Fix assert in poll mode when cyw43_arch_disable_sta_mode is called in interrupt context. --- pico_w/wifi/iperf/picow_iperf.c | 45 +++++++++++++++++---------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/pico_w/wifi/iperf/picow_iperf.c b/pico_w/wifi/iperf/picow_iperf.c index 927e5c7b2..f2ed4b34d 100644 --- a/pico_w/wifi/iperf/picow_iperf.c +++ b/pico_w/wifi/iperf/picow_iperf.c @@ -19,6 +19,17 @@ #error IPERF_SERVER_IP not defined #endif +#if USE_LED +// Invert led +static void led_worker_fn(async_context_t *context, async_at_time_worker_t *worker) { + // Invert the led + static int led_on = true; + led_on = !led_on; + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); + async_context_add_at_time_worker_in_ms(context, worker, 1000); +} +#endif + // Report IP results and exit static void iperf_report(void *arg, enum lwiperf_report_type report_type, const ip_addr_t *local_addr, u16_t local_port, const ip_addr_t *remote_addr, u16_t remote_port, @@ -36,12 +47,12 @@ static void iperf_report(void *arg, enum lwiperf_report_type report_type, #endif } +// Note: This is called from an interrupt handler void key_pressed_func(void *param) { int key = getchar_timeout_us(0); // get any pending key press but don't wait if (key == 'd' || key == 'D') { - cyw43_arch_lwip_begin(); - cyw43_arch_disable_sta_mode(); - cyw43_arch_lwip_end(); + bool *exit = (bool*)param; + *exit = true; } } @@ -53,9 +64,6 @@ int main() { return 1; } - // Get notified if the user presses a key - stdio_set_chars_available_callback(key_pressed_func, cyw43_arch_async_context()); - cyw43_arch_enable_sta_mode(); printf("Connecting to Wi-Fi... (press 'd' to disconnect)\n"); if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { @@ -77,23 +85,15 @@ int main() { #endif cyw43_arch_lwip_end(); - while(cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA) != CYW43_LINK_DOWN) { + bool exit = false; + stdio_set_chars_available_callback(key_pressed_func, &exit); + #if USE_LED - static absolute_time_t led_time; - static int led_on = true; - - // Invert the led - if (absolute_time_diff_us(get_absolute_time(), led_time) < 0) { - led_on = !led_on; - cyw43_gpio_set(&cyw43_state, 0, led_on); - led_time = make_timeout_time_ms(1000); - - // Check we can read back the led value - bool actual_led_val = !led_on; - cyw43_gpio_get(&cyw43_state, 0, &actual_led_val); - assert(led_on == actual_led_val); - } + // start the led flashing + async_at_time_worker_t led_worker = { .do_work = led_worker_fn }; + async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &led_worker, 0); #endif + while(!exit && cyw43_wifi_link_status(&cyw43_state, CYW43_ITF_STA) != CYW43_LINK_DOWN) { // the following #ifdef is only here so this same example can be used in multiple modes; // you do not need it in your code #if PICO_CYW43_ARCH_POLL @@ -102,7 +102,7 @@ int main() { cyw43_arch_poll(); // you can poll as often as you like, however if you have nothing else to do you can // choose to sleep until either a specified time, or cyw43_arch_poll() has work to do: - cyw43_arch_wait_for_work_until(led_time); + cyw43_arch_wait_for_work_until(at_the_end_of_time); #else // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work // is done via interrupt in the background. This sleep is just an example of some (blocking) @@ -110,6 +110,7 @@ int main() { sleep_ms(1000); #endif } + cyw43_arch_disable_sta_mode(); cyw43_arch_deinit(); printf("Test complete\n"); From a8a16966571577104aa55b6840e074becdd97dd8 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Mon, 14 Apr 2025 15:59:10 +0100 Subject: [PATCH 2/3] Improve wifi scan example Use async context to initiate a scan in future instead of keeping track of time in the main loop. Detect a key press to exit the example. --- pico_w/wifi/wifi_scan/picow_wifi_scan.c | 49 +++++++++++++++---------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/pico_w/wifi/wifi_scan/picow_wifi_scan.c b/pico_w/wifi/wifi_scan/picow_wifi_scan.c index 1e500f890..4ddd55129 100644 --- a/pico_w/wifi/wifi_scan/picow_wifi_scan.c +++ b/pico_w/wifi/wifi_scan/picow_wifi_scan.c @@ -19,6 +19,19 @@ static int scan_result(void *env, const cyw43_ev_scan_result_t *result) { return 0; } +// Start a wifi scan +static void scan_worker_fn(async_context_t *context, async_at_time_worker_t *worker) { + cyw43_wifi_scan_options_t scan_options = {0}; + int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result); + if (err == 0) { + bool *scan_started = (bool*)worker->user_data; + *scan_started = true; + printf("\nPerforming wifi scan\n"); + } else { + printf("Failed to start scan: %d\n", err); + } +} + #include "hardware/vreg.h" #include "hardware/clocks.h" @@ -30,26 +43,24 @@ int main() { return 1; } + printf("Press 'q' to quit\n"); cyw43_arch_enable_sta_mode(); - absolute_time_t scan_time = nil_time; - bool scan_in_progress = false; - while(true) { - if (absolute_time_diff_us(get_absolute_time(), scan_time) < 0) { - if (!scan_in_progress) { - cyw43_wifi_scan_options_t scan_options = {0}; - int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result); - if (err == 0) { - printf("\nPerforming wifi scan\n"); - scan_in_progress = true; - } else { - printf("Failed to start scan: %d\n", err); - scan_time = make_timeout_time_ms(10000); // wait 10s and scan again - } - } else if (!cyw43_wifi_scan_active(&cyw43_state)) { - scan_time = make_timeout_time_ms(10000); // wait 10s and scan again - scan_in_progress = false; - } + // Start a scan immediately + bool scan_started = false; + async_at_time_worker_t scan_worker = { .do_work = scan_worker_fn, .user_data = &scan_started }; + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &scan_worker, 0)); + + bool exit = false; + while(!exit) { + int key = getchar_timeout_us(0); + if (key == 'q' || key == 'Q') { + exit = true; + } + if (!cyw43_wifi_scan_active(&cyw43_state) && scan_started) { + // Start a scan in 10s + scan_started = false; + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &scan_worker, 10000)); } // the following #ifdef is only here so this same example can be used in multiple modes; // you do not need it in your code @@ -59,7 +70,7 @@ int main() { cyw43_arch_poll(); // you can poll as often as you like, however if you have nothing else to do you can // choose to sleep until either a specified time, or cyw43_arch_poll() has work to do: - cyw43_arch_wait_for_work_until(scan_time); + cyw43_arch_wait_for_work_until(at_the_end_of_time); #else // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work // is done via interrupt in the background. This sleep is just an example of some (blocking) From 9f2b0a6dd7a54f9c07553f7bacf8cfb4ade30690 Mon Sep 17 00:00:00 2001 From: Peter Harper Date: Mon, 14 Apr 2025 16:01:11 +0100 Subject: [PATCH 3/3] Improve NTP example Use async context NTP requests, rather than keeping track of time in the main loop. Detect a key press to exit the example. --- pico_w/wifi/ntp_client/picow_ntp_client.c | 81 +++++++++++------------ 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/pico_w/wifi/ntp_client/picow_ntp_client.c b/pico_w/wifi/ntp_client/picow_ntp_client.c index 0fe3f0b13..2f1d3a1e3 100644 --- a/pico_w/wifi/ntp_client/picow_ntp_client.c +++ b/pico_w/wifi/ntp_client/picow_ntp_client.c @@ -16,18 +16,17 @@ typedef struct NTP_T_ { ip_addr_t ntp_server_address; - bool dns_request_sent; struct udp_pcb *ntp_pcb; - absolute_time_t ntp_test_time; - alarm_id_t ntp_resend_alarm; + async_at_time_worker_t request_worker; + async_at_time_worker_t resend_worker; } NTP_T; #define NTP_SERVER "pool.ntp.org" #define NTP_MSG_LEN 48 #define NTP_PORT 123 #define NTP_DELTA 2208988800 // seconds between 1 Jan 1900 and 1 Jan 1970 -#define NTP_TEST_TIME (30 * 1000) -#define NTP_RESEND_TIME (10 * 1000) +#define NTP_TEST_TIME_MS (30 * 1000) +#define NTP_RESEND_TIME_MS (10 * 1000) // Called with results of operation static void ntp_result(NTP_T* state, int status, time_t *result) { @@ -36,17 +35,11 @@ static void ntp_result(NTP_T* state, int status, time_t *result) { printf("got ntp response: %02d/%02d/%04d %02d:%02d:%02d\n", utc->tm_mday, utc->tm_mon + 1, utc->tm_year + 1900, utc->tm_hour, utc->tm_min, utc->tm_sec); } - - if (state->ntp_resend_alarm > 0) { - cancel_alarm(state->ntp_resend_alarm); - state->ntp_resend_alarm = 0; - } - state->ntp_test_time = make_timeout_time_ms(NTP_TEST_TIME); - state->dns_request_sent = false; + async_context_remove_at_time_worker(cyw43_arch_async_context(), &state->resend_worker); + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &state->request_worker, NTP_TEST_TIME_MS)); // repeat the request in future + printf("Next request in %ds\n", NTP_TEST_TIME_MS / 1000); } -static int64_t ntp_failed_handler(alarm_id_t id, void *user_data); - // Make an NTP request static void ntp_request(NTP_T *state) { // cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking. @@ -63,14 +56,6 @@ static void ntp_request(NTP_T *state) { cyw43_arch_lwip_end(); } -static int64_t ntp_failed_handler(alarm_id_t id, void *user_data) -{ - NTP_T* state = (NTP_T*)user_data; - printf("ntp request failed\n"); - ntp_result(state, -1, NULL); - return 0; -} - // Call back with a DNS result static void ntp_dns_found(const char *hostname, const ip_addr_t *ipaddr, void *arg) { NTP_T *state = (NTP_T*)arg; @@ -106,6 +91,26 @@ static void ntp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_ad pbuf_free(p); } +// Called to make a NTP request +static void request_worker_fn(__unused async_context_t *context, async_at_time_worker_t *worker) { + NTP_T* state = (NTP_T*)worker->user_data; + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &state->resend_worker, NTP_RESEND_TIME_MS)); // in case UDP request is lost + int err = dns_gethostbyname(NTP_SERVER, &state->ntp_server_address, ntp_dns_found, state); + if (err == ERR_OK) { + ntp_request(state); // Cached DNS result, make NTP request + } else if (err != ERR_INPROGRESS) { // ERR_INPROGRESS means expect a callback + printf("dns request failed\n"); + ntp_result(state, -1, NULL); + } +} + +// Called to resend an NTP request if it appears to get lost +static void resend_worker_fn(__unused async_context_t *context, async_at_time_worker_t *worker) { + NTP_T* state = (NTP_T*)worker->user_data; + printf("ntp request failed\n"); + ntp_result(state, -1, NULL); +} + // Perform initialisation static NTP_T* ntp_init(void) { NTP_T *state = (NTP_T*)calloc(1, sizeof(NTP_T)); @@ -120,6 +125,10 @@ static NTP_T* ntp_init(void) { return NULL; } udp_recv(state->ntp_pcb, ntp_recv, state); + state->request_worker.do_work = request_worker_fn; + state->request_worker.user_data = state; + state->resend_worker.do_work = resend_worker_fn; + state->resend_worker.user_data = state; return state; } @@ -128,26 +137,12 @@ void run_ntp_test(void) { NTP_T *state = ntp_init(); if (!state) return; + printf("Press 'q' to quit\n"); + hard_assert(async_context_add_at_time_worker_in_ms(cyw43_arch_async_context(), &state->request_worker, 0)); // make the first request while(true) { - if (absolute_time_diff_us(get_absolute_time(), state->ntp_test_time) < 0 && !state->dns_request_sent) { - // Set alarm in case udp requests are lost - state->ntp_resend_alarm = add_alarm_in_ms(NTP_RESEND_TIME, ntp_failed_handler, state, true); - - // cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking. - // You can omit them if you are in a callback from lwIP. Note that when using pico_cyw_arch_poll - // these calls are a no-op and can be omitted, but it is a good practice to use them in - // case you switch the cyw43_arch type later. - cyw43_arch_lwip_begin(); - int err = dns_gethostbyname(NTP_SERVER, &state->ntp_server_address, ntp_dns_found, state); - cyw43_arch_lwip_end(); - - state->dns_request_sent = true; - if (err == ERR_OK) { - ntp_request(state); // Cached result - } else if (err != ERR_INPROGRESS) { // ERR_INPROGRESS means expect a callback - printf("dns request failed\n"); - ntp_result(state, -1, NULL); - } + int key = getchar_timeout_us(0); + if (key == 'q' || key == 'Q') { + break; } #if PICO_CYW43_ARCH_POLL // if you are using pico_cyw43_arch_poll, then you must poll periodically from your @@ -155,7 +150,7 @@ void run_ntp_test(void) { cyw43_arch_poll(); // you can poll as often as you like, however if you have nothing else to do you can // choose to sleep until either a specified time, or cyw43_arch_poll() has work to do: - cyw43_arch_wait_for_work_until(state->dns_request_sent ? at_the_end_of_time : state->ntp_test_time); + cyw43_arch_wait_for_work_until(at_the_end_of_time); #else // if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work // is done via interrupt in the background. This sleep is just an example of some (blocking) @@ -176,7 +171,7 @@ int main() { cyw43_arch_enable_sta_mode(); - if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 10000)) { + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { printf("failed to connect\n"); return 1; }