diff --git a/CMakeLists.txt b/CMakeLists.txt index 06a6de0..efeef67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,3 +40,4 @@ add_subdirectory(reset) add_subdirectory(scanvideo) add_subdirectory(sleep) add_subdirectory(stdio) +add_subdirectory(wifi_settings_connect) diff --git a/README.md b/README.md index 466b3fb..9fd3cd8 100644 --- a/README.md +++ b/README.md @@ -68,3 +68,11 @@ even though it is in the Pico SDK Name|Description ---|--- [stdio_pio](stdio/pio)| Demonstrates adding a custom STDIO driver using a PIO UART + +## WiFi + +Example using the wifi\_settings\_connect library from pico\_extras. + +Name|Description +---|--- +[wifi\_settings\_connect\_example](wifi_settings_connect/example)| Demonstrates connecting to WiFi hotspots and sending UDP broadcasts diff --git a/wifi_settings_connect/CMakeLists.txt b/wifi_settings_connect/CMakeLists.txt new file mode 100644 index 0000000..a5c4dce --- /dev/null +++ b/wifi_settings_connect/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(example) diff --git a/wifi_settings_connect/example/CMakeLists.txt b/wifi_settings_connect/example/CMakeLists.txt new file mode 100644 index 0000000..8f35978 --- /dev/null +++ b/wifi_settings_connect/example/CMakeLists.txt @@ -0,0 +1,40 @@ +# +# Copyright (c) 2025 Jack Whitham +# +# SPDX-License-Identifier: BSD-3-Clause +# +# Example for wifi_settings_connect. +# +# This example connects to WiFi using wifi_settings_connect functions, +# and then broadcasts a message on UDP port 1234 every second. +# You can receive these with any tool that can receive +# UDP, e.g. tcpdump, Wireshark, or netcat: +# +# nc -l -u -p 1234 +# +# The WiFi connection details must be configured in Flash as described here: +# https://github.com/jwhitham/pico-wifi-settings/blob/master/doc/SETTINGS_FILE.md +# + +# only build wifi_settings_connect example if library is available +if (TARGET wifi_settings_connect) + # Build example with async_context tasks running in the background + # You can use pico_cyw43_arch_lwip_poll instead of + # pico_cyw43_arch_lwip_threadsafe_background to use polling, if you prefer. + add_executable(wifi_settings_connect_example + example.c + ) + target_include_directories(wifi_settings_connect_example PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ) + target_link_libraries(wifi_settings_connect_example + pico_cyw43_arch_lwip_threadsafe_background + wifi_settings_connect + pico_stdlib + ) + + pico_enable_stdio_usb(wifi_settings_connect_example 1) + pico_enable_stdio_uart(wifi_settings_connect_example 0) + + pico_add_extra_outputs(wifi_settings_connect_example) +endif () diff --git a/wifi_settings_connect/example/README.md b/wifi_settings_connect/example/README.md new file mode 100644 index 0000000..3a9ac2d --- /dev/null +++ b/wifi_settings_connect/example/README.md @@ -0,0 +1,44 @@ +# wifi\_settings\_connect example + +Example using the wifi\_settings\_connect library to connect to a WiFi hotspot. + +The Flash storage location for hotspot details is specified in +`include/wifi_settings/wifi_settings_configuration.h`. It is at +`0x101fc000` (for Pico W) and `0x103fc000` (for Pico 2 W). This +"wifi-settings file" is a text file which can be edited with any text editor. +Here is an example of typical contents: +``` + ssid1=MyHomeWiFi + pass1=mypassword1234 + ssid2=MyPhoneHotspot + pass2=secretpassword + country=GB +``` +To add your WiFi details at this location, please [see the setup instructions in the pico-extras +repo](https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/wifi_settings_connect/doc/SETTINGS_FILE.md). + +To build this example, first run `cmake` at the top level of the +`pico-playground` repository, specifying the locations for +the `pico-sdk` and `pico-extras` repositories, e.g.: +``` + cd /home/user/pico-playground + mkdir build + cd build + cmake -DPICO_SDK_PATH=/home/user/pico-sdk \ + -DPICO_EXTRAS_PATH=/home/user/pico-extras \ + -DPICO_BOARD=pico_w .. +``` +Then, run `make` within the `build/wifi_settings_connect/example` +directory, e.g. +``` + cd /home/user/pico-playground + make -C build/wifi_settings_connect/example +``` +This will create the UF2 file to be downloaded to the Pico +in `build/wifi_settings_connect/example`. + +The example program will search for WiFi hotspots. If it finds a hotspot which +matches those configured in Flash, then it will automatically +connect to it and begin a UDP broadcast. The example program prints +its status to the USB serial port; connect with a terminal program +to see what it is doing. diff --git a/wifi_settings_connect/example/example.c b/wifi_settings_connect/example/example.c new file mode 100644 index 0000000..bf325e0 --- /dev/null +++ b/wifi_settings_connect/example/example.c @@ -0,0 +1,157 @@ +/** + * Copyright (c) 2025 Jack Whitham + * + * SPDX-License-Identifier: BSD-3-Clause + * + * Example for wifi-settings. + * + * This example connects to WiFi using wifi_settings_connect functions, + * and then broadcasts a message on UDP port 1234 every second. + * You can receive these with any tool that can receive + * UDP, e.g. tcpdump, Wireshark, or netcat: + * + * nc -l -u -p 1234 + * + * The WiFi connection details must be configured in Flash as described here: + * https://github.com/jwhitham/pico-wifi-settings/blob/master/doc/SETTINGS_FILE.md + */ + + +#include +#include + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" +#include "pico/bootrom.h" + +#include "lwip/pbuf.h" +#include "lwip/tcp.h" + +#include "wifi_settings/wifi_settings_connect.h" +#include "wifi_settings/wifi_settings_hostname.h" + + +bool send_udp_packet(uint count) { + // Send a UDP broadcast to port 1234 + char text[256]; + bool ok = false; + + struct udp_pcb* udp_pcb = udp_new(); + if (!udp_pcb) { + printf("Failed to allocate space for UDP PCB!\n"); + } else { + snprintf(text, sizeof(text), "Hello World %u from %s\n", count, wifi_settings_get_hostname()); + const uint size = strlen(text); + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); + if (!p) { + printf("Failed to allocate space for UDP packet!\n"); + } else { + memcpy(p->payload, text, size); + ip_addr_t addr; + ipaddr_aton("255.255.255.255", &addr); + const err_t err = udp_sendto(udp_pcb, p, &addr, 1234); + if (err) { + printf("Failed to send UDP packet! error=%d\n", (int) err); + } else { + printf("UDP broadcast, port 1234: %s", text); + ok = true; + } + pbuf_free(p); + } + udp_remove(udp_pcb); + } + return ok; +} + +int main() { + stdio_init_all(); + + // Initialise pico-wifi-settings + int rc = wifi_settings_init(); + if (rc != 0) { + panic("wifi_settings_init() failed"); + return 1; + } + + // Begin connecting to WiFi (this function returns immediately) + wifi_settings_connect(); + + uint count = 0; + bool stay_in_loop = true; + while (stay_in_loop) { + // clear screen + printf("\x1b[2J\r"); + + // print host name and board ID + printf("Hostname = %s\n" + "Board ID = %s\n\n", + wifi_settings_get_hostname(), + wifi_settings_get_board_id_hex()); + fflush(stdout); + + // print connection status + char text[512]; + wifi_settings_get_connect_status_text(text, sizeof(text)); + printf("%s\n\n", text); + if (wifi_settings_has_no_wifi_details()) { + // Help the user if no SSIDs are configured + printf("You need to configure at least one hotspot! See\n" + "https://github.com/jwhitham/pico-wifi-settings/blob/master/doc/SETTINGS_FILE.md\n" + "for instructions.\n\n"); + } else { + wifi_settings_get_hw_status_text(text, sizeof(text)); + printf("%s\n", text); + wifi_settings_get_ip_status_text(text, sizeof(text)); + printf("%s\n", text); + } + fflush(stdout); + + // Send a UDP broadcast to port 1234 if connected + if (wifi_settings_is_connected()) { + if (send_udp_packet(count)) { + count++; + } + } + + printf("press 'c' to connect, 'd' to disconnect, 'r' to return to bootloader\n"); + fflush(stdout); + switch (getchar_timeout_us(1)) { + case 'c': + wifi_settings_connect(); + break; + case 'd': + wifi_settings_disconnect(); + break; + case 'r': + stay_in_loop = false; + break; + default: + break; + } +#if PICO_CYW43_ARCH_POLL + // if you are using pico_cyw43_arch_poll, then you must poll periodically from your + // main loop (not from a timer interrupt) to check for wifi_settings, WiFi driver + // or lwIP work that needs to be done. + cyw43_arch_poll(); + // you can poll as often as you like, however if you have nothing else to do you can + // choose to sleep: + sleep_ms(1000); +#else + // if you are not using pico_cyw43_arch_poll, then wifi_settings, WiFI driver and lwIP work + // is done via interrupt in the background. This sleep is just an example of some (blocking) + // work you might be doing. + sleep_ms(1000); +#endif + } + printf("That's all\n"); + + // Disconnection and de-initialisation are optional steps + // with wifi_settings, but you may wish to explicitly stop + // the WiFi connection for some reason: + wifi_settings_disconnect(); + wifi_settings_deinit(); + printf("Goodbye\n"); + // return to boot loader + reset_usb_boot(0, 0); + return 0; +} diff --git a/wifi_settings_connect/example/lwipopts.h b/wifi_settings_connect/example/lwipopts.h new file mode 100644 index 0000000..7c958cf --- /dev/null +++ b/wifi_settings_connect/example/lwipopts.h @@ -0,0 +1,85 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + + +// This example LWIP settings file has been adapted from the pico-examples repo. +// See https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details. +// Note: pico-wifi-settings has been tested with LWIP projects that use NO_SYS and +// don't use LWIP_SOCKET - there has not yet been any attempt to support FreeRTOS. +#define NO_SYS 1 +#define LWIP_SOCKET 0 +#if PICO_CYW43_ARCH_POLL +#define MEM_LIBC_MALLOC 1 +#else +// MEM_LIBC_MALLOC is incompatible with non polling versions +#define MEM_LIBC_MALLOC 0 +#endif +#define MEM_ALIGNMENT 4 +#define MEM_SIZE 4000 +#define MEMP_NUM_TCP_SEG 32 +#define MEMP_NUM_ARP_QUEUE 10 +#define PBUF_POOL_SIZE 24 +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_ICMP 1 +#define LWIP_RAW 1 +#define TCP_WND (8 * TCP_MSS) +#define TCP_MSS 1460 +#define TCP_SND_BUF (8 * TCP_MSS) +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETCONN 0 +#define MEM_STATS 0 +#define SYS_STATS 0 +#define MEMP_STATS 0 +#define LINK_STATS 0 +// #define ETH_PAD_SIZE 2 +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_DHCP 1 +#define LWIP_IPV4 1 +#define LWIP_TCP 1 +#define LWIP_UDP 1 +#define LWIP_DNS 1 +#define LWIP_TCP_KEEPALIVE 1 +#define LWIP_NETIF_TX_SINGLE_PBUF 1 +#define DHCP_DOES_ARP_CHECK 0 +#define LWIP_DHCP_DOES_ACD_CHECK 0 + +#ifndef NDEBUG +#define LWIP_DEBUG 1 +#define LWIP_STATS 1 +#define LWIP_STATS_DISPLAY 1 +#endif + +#define ETHARP_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_OFF +#define PBUF_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_OFF +#define API_MSG_DEBUG LWIP_DBG_OFF +#define SOCKETS_DEBUG LWIP_DBG_OFF +#define ICMP_DEBUG LWIP_DBG_OFF +#define INET_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_OFF +#define IP_REASS_DEBUG LWIP_DBG_OFF +#define RAW_DEBUG LWIP_DBG_OFF +#define MEM_DEBUG LWIP_DBG_OFF +#define MEMP_DEBUG LWIP_DBG_OFF +#define SYS_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_OFF +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#define TCP_WND_DEBUG LWIP_DBG_OFF +#define TCP_FR_DEBUG LWIP_DBG_OFF +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#define TCP_RST_DEBUG LWIP_DBG_OFF +#define UDP_DEBUG LWIP_DBG_OFF +#define TCPIP_DEBUG LWIP_DBG_OFF +#define PPP_DEBUG LWIP_DBG_OFF +#define SLIP_DEBUG LWIP_DBG_OFF +#define DHCP_DEBUG LWIP_DBG_OFF + +#endif /* __LWIPOPTS_H__ */