From 01e8128953a9766608be0a9254afb7900107e222 Mon Sep 17 00:00:00 2001 From: graham sanderson Date: Wed, 29 Jun 2022 22:53:50 -0500 Subject: [PATCH] Add Pico W examples --- CMakeLists.txt | 1 + README.md | 26 ++ pico_w/CMakeLists.txt | 26 ++ pico_w/access_point/CMakeLists.txt | 33 ++ pico_w/access_point/dhcpserver/LICENSE | 21 ++ pico_w/access_point/dhcpserver/dhcpserver.c | 300 ++++++++++++++++++ pico_w/access_point/dhcpserver/dhcpserver.h | 49 +++ pico_w/access_point/lwipopts.h | 10 + pico_w/access_point/picow_access_point.c | 180 +++++++++++ pico_w/blink/CMakeLists.txt | 14 + pico_w/blink/picow_blink.c | 22 ++ pico_w/freertos/CMakeLists.txt | 8 + pico_w/freertos/FreeRTOS_Kernel_import.cmake | 62 ++++ pico_w/freertos/iperf/CMakeLists.txt | 38 +++ pico_w/freertos/iperf/FreeRTOSConfig.h | 143 +++++++++ pico_w/freertos/iperf/lwipopts.h | 21 ++ pico_w/freertos/iperf/picow_freertos_iperf.c | 132 ++++++++ pico_w/freertos/ping/CMakeLists.txt | 46 +++ pico_w/freertos/ping/FreeRTOSConfig.h | 143 +++++++++ pico_w/freertos/ping/lwipopts.h | 21 ++ pico_w/freertos/ping/picow_freertos_ping.c | 90 ++++++ pico_w/iperf/CMakeLists.txt | 37 +++ pico_w/iperf/lwipopts.h | 10 + pico_w/iperf/picow_iperf.c | 99 ++++++ pico_w/lwipopts_examples_common.h | 90 ++++++ pico_w/ntp_client/CMakeLists.txt | 35 ++ pico_w/ntp_client/lwipopts.h | 10 + pico_w/ntp_client/picow_ntp_client.c | 185 +++++++++++ .../python_test_tcp/python_test_tcp_client.py | 71 +++++ .../python_test_tcp/python_test_tcp_server.py | 85 +++++ pico_w/tcp_client/CMakeLists.txt | 40 +++ pico_w/tcp_client/lwipopts.h | 10 + pico_w/tcp_client/picow_tcp_client.c | 254 +++++++++++++++ pico_w/tcp_server/CMakeLists.txt | 34 ++ pico_w/tcp_server/lwipopts.h | 10 + pico_w/tcp_server/picow_tcp_server.c | 266 ++++++++++++++++ pico_w/wifi_scan/CMakeLists.txt | 27 ++ pico_w/wifi_scan/lwipopts.h | 10 + pico_w/wifi_scan/picow_wifi_scan.c | 71 +++++ usb/CMakeLists.txt | 4 +- 40 files changed, 2732 insertions(+), 2 deletions(-) create mode 100644 pico_w/CMakeLists.txt create mode 100644 pico_w/access_point/CMakeLists.txt create mode 100644 pico_w/access_point/dhcpserver/LICENSE create mode 100644 pico_w/access_point/dhcpserver/dhcpserver.c create mode 100644 pico_w/access_point/dhcpserver/dhcpserver.h create mode 100644 pico_w/access_point/lwipopts.h create mode 100644 pico_w/access_point/picow_access_point.c create mode 100644 pico_w/blink/CMakeLists.txt create mode 100644 pico_w/blink/picow_blink.c create mode 100644 pico_w/freertos/CMakeLists.txt create mode 100644 pico_w/freertos/FreeRTOS_Kernel_import.cmake create mode 100644 pico_w/freertos/iperf/CMakeLists.txt create mode 100644 pico_w/freertos/iperf/FreeRTOSConfig.h create mode 100644 pico_w/freertos/iperf/lwipopts.h create mode 100644 pico_w/freertos/iperf/picow_freertos_iperf.c create mode 100644 pico_w/freertos/ping/CMakeLists.txt create mode 100644 pico_w/freertos/ping/FreeRTOSConfig.h create mode 100644 pico_w/freertos/ping/lwipopts.h create mode 100644 pico_w/freertos/ping/picow_freertos_ping.c create mode 100644 pico_w/iperf/CMakeLists.txt create mode 100644 pico_w/iperf/lwipopts.h create mode 100644 pico_w/iperf/picow_iperf.c create mode 100644 pico_w/lwipopts_examples_common.h create mode 100644 pico_w/ntp_client/CMakeLists.txt create mode 100644 pico_w/ntp_client/lwipopts.h create mode 100644 pico_w/ntp_client/picow_ntp_client.c create mode 100644 pico_w/python_test_tcp/python_test_tcp_client.py create mode 100644 pico_w/python_test_tcp/python_test_tcp_server.py create mode 100644 pico_w/tcp_client/CMakeLists.txt create mode 100644 pico_w/tcp_client/lwipopts.h create mode 100644 pico_w/tcp_client/picow_tcp_client.c create mode 100644 pico_w/tcp_server/CMakeLists.txt create mode 100644 pico_w/tcp_server/lwipopts.h create mode 100644 pico_w/tcp_server/picow_tcp_server.c create mode 100644 pico_w/wifi_scan/CMakeLists.txt create mode 100644 pico_w/wifi_scan/lwipopts.h create mode 100644 pico_w/wifi_scan/picow_wifi_scan.c diff --git a/CMakeLists.txt b/CMakeLists.txt index fe1c225..4e69739 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ add_subdirectory(i2c) add_subdirectory(interp) add_subdirectory(multicore) add_subdirectory(picoboard) +add_subdirectory(pico_w) add_subdirectory(pio) add_subdirectory(pwm) add_subdirectory(reset) diff --git a/README.md b/README.md index 28cc661..e50b111 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,32 @@ App|Description [blinky](picoboard/blinky)| Blink "hello, world" in Morse code on Pico's LED [button](picoboard/button)| Use Pico's BOOTSEL button as a regular button input, by temporarily suspending flash access. +### Pico W Networking + +These eaxmples are for the Pico W, and are only available for `PICO_BOARD=pico_w` + +App|Description +---|--- +[picow_access_point](pico_w/picow_access_point)| Starts a WiFi access point, and fields DHCP requests. +[picow_blink](pico_w/picow_blink)| Blinks the on-board LED (which is connected via the WiFi chip). +[picow_iperf_server](pico_w/picow_iperf)| Runs an "iperf" server for WiFi speed testing. +[picow_ntp_client](pico_w/picow_ntp_client)| Connects to an NTP server to fetch and display the current time. +[picow_tcp_client](pico_w/picow_tcp_client)| A simple TCP client. You can run [python_test_tcp_server.py](pico_w/python_test_tcp/python_test_tcp_server.py) for it to connect to. +[picow_tcp_server](pico_w/picow_tcp_server)| A simple TCP server. You can use [python_test_tcp_client.py](pico_w/python_test_tcp/python_test_tcp_client.py) to connect to it. +[picow_wifi_scan](pico_w/picow_wifi_scan)| Scans for WiFi networks and prints the results. + +#### FreeRTOS examples + +These are examples of integrating Pico W networking under FreeRTOS, and require you to set the `FREERTOS_KERNEL_PATH` +to point to the FreeRTOS Kernel. + +App|Description +---|--- +[picow_freertos_iperf_server_nosys](pico_w/freertos/picow_iperf)| Runs an "iperf" server for WiFi speed testing under FreeRTOS in NO_SYS=1 mode. The LED is blinked in another task +[picow_freertos_iperf_server_sys](pico_w/freertos/picow_iperf)| Runs an "iperf" server for WiFi speed testing under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. The LED is blinked in another task +[picow_freertos_ping_nosys](pico_w/freertos/ping)| Runs the lwip-contrib/apps/ping test app under FreeRTOS in NO_SYS=1 mode. +[picow_freertos_iperf_server_sys](pico_w/freertos/picow_iperf)| Runs the lwip-contrib/apps/ping test app under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. The test app uses the lwIP \em socket API in this case. + ### PIO App|Description diff --git a/pico_w/CMakeLists.txt b/pico_w/CMakeLists.txt new file mode 100644 index 0000000..d3abf8a --- /dev/null +++ b/pico_w/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.12) + +if (PICO_CYW43_SUPPORTED) # set by BOARD=pico-w + if (NOT TARGET pico_cyw43_arch) + message("Skipping Pico W examples as support is not available") + else() + set(WIFI_SSID "${WIFI_SSID}" CACHE INTERNAL "WiFi SSID for examples") + set(WIFI_PASSWORD "${WIFI_PASSWORD}" CACHE INTERNAL "WiFi password for examples") + + add_subdirectory(blink) + add_subdirectory(wifi_scan) + add_subdirectory(access_point) + + if ("${WIFI_SSID}" STREQUAL "") + message("Skipping some Pico W examples as WIFI_SSID is not defined") + elseif ("${WIFI_PASSWORD}" STREQUAL "") + message("Skipping some Pico W examples as WIFI_PASSWORD is not defined") + else() + add_subdirectory(iperf) + add_subdirectory(ntp_client) + add_subdirectory(tcp_client) + add_subdirectory(tcp_server) + add_subdirectory(freertos) + endif() + endif() +endif() diff --git a/pico_w/access_point/CMakeLists.txt b/pico_w/access_point/CMakeLists.txt new file mode 100644 index 0000000..7ee8112 --- /dev/null +++ b/pico_w/access_point/CMakeLists.txt @@ -0,0 +1,33 @@ +add_executable(picow_access_point_background + picow_access_point.c + dhcpserver/dhcpserver.c + ) + +target_include_directories(picow_access_point_background PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ${CMAKE_CURRENT_LIST_DIR}/dhcpserver + ) + +target_link_libraries(picow_access_point_background + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + ) + +pico_add_extra_outputs(picow_access_point_background) + +add_executable(picow_access_point_poll + picow_access_point.c + dhcpserver/dhcpserver.c + ) +target_include_directories(picow_access_point_poll PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ${CMAKE_CURRENT_LIST_DIR}/dhcpserver + ) +target_link_libraries(picow_access_point_poll + pico_cyw43_arch_lwip_poll + pico_stdlib + ) +pico_add_extra_outputs(picow_access_point_poll) + diff --git a/pico_w/access_point/dhcpserver/LICENSE b/pico_w/access_point/dhcpserver/LICENSE new file mode 100644 index 0000000..8f9b52c --- /dev/null +++ b/pico_w/access_point/dhcpserver/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2022 Damien P. George + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pico_w/access_point/dhcpserver/dhcpserver.c b/pico_w/access_point/dhcpserver/dhcpserver.c new file mode 100644 index 0000000..ad5d397 --- /dev/null +++ b/pico_w/access_point/dhcpserver/dhcpserver.c @@ -0,0 +1,300 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// For DHCP specs see: +// https://www.ietf.org/rfc/rfc2131.txt +// https://tools.ietf.org/html/rfc2132 -- DHCP Options and BOOTP Vendor Extensions + +#include +#include +#include + +#include "cyw43_config.h" +#include "dhcpserver.h" +#include "lwip/udp.h" + +#define DHCPDISCOVER (1) +#define DHCPOFFER (2) +#define DHCPREQUEST (3) +#define DHCPDECLINE (4) +#define DHCPACK (5) +#define DHCPNACK (6) +#define DHCPRELEASE (7) +#define DHCPINFORM (8) + +#define DHCP_OPT_PAD (0) +#define DHCP_OPT_SUBNET_MASK (1) +#define DHCP_OPT_ROUTER (3) +#define DHCP_OPT_DNS (6) +#define DHCP_OPT_HOST_NAME (12) +#define DHCP_OPT_REQUESTED_IP (50) +#define DHCP_OPT_IP_LEASE_TIME (51) +#define DHCP_OPT_MSG_TYPE (53) +#define DHCP_OPT_SERVER_ID (54) +#define DHCP_OPT_PARAM_REQUEST_LIST (55) +#define DHCP_OPT_MAX_MSG_SIZE (57) +#define DHCP_OPT_VENDOR_CLASS_ID (60) +#define DHCP_OPT_CLIENT_ID (61) +#define DHCP_OPT_END (255) + +#define PORT_DHCP_SERVER (67) +#define PORT_DHCP_CLIENT (68) + +#define DEFAULT_DNS MAKE_IP4(8, 8, 8, 8) +#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds + +#define MAC_LEN (6) +#define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) + +typedef struct { + uint8_t op; // message opcode + uint8_t htype; // hardware address type + uint8_t hlen; // hardware address length + uint8_t hops; + uint32_t xid; // transaction id, chosen by client + uint16_t secs; // client seconds elapsed + uint16_t flags; + uint8_t ciaddr[4]; // client IP address + uint8_t yiaddr[4]; // your IP address + uint8_t siaddr[4]; // next server IP address + uint8_t giaddr[4]; // relay agent IP address + uint8_t chaddr[16]; // client hardware address + uint8_t sname[64]; // server host name + uint8_t file[128]; // boot file name + uint8_t options[312]; // optional parameters, variable, starts with magic +} dhcp_msg_t; + +static int dhcp_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) { + // family is AF_INET + // type is SOCK_DGRAM + + *udp = udp_new(); + if (*udp == NULL) { + return -ENOMEM; + } + + // Register callback + udp_recv(*udp, cb_udp_recv, (void *)cb_data); + + return 0; // success +} + +static void dhcp_socket_free(struct udp_pcb **udp) { + if (*udp != NULL) { + udp_remove(*udp); + *udp = NULL; + } +} + +static int dhcp_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) { + ip_addr_t addr; + IP4_ADDR(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); + // TODO convert lwIP errors to errno + return udp_bind(*udp, &addr, port); +} + +static int dhcp_socket_sendto(struct udp_pcb **udp, const void *buf, size_t len, uint32_t ip, uint16_t port) { + if (len > 0xffff) { + len = 0xffff; + } + + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (p == NULL) { + return -ENOMEM; + } + + memcpy(p->payload, buf, len); + + ip_addr_t dest; + IP4_ADDR(&dest, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); + err_t err = udp_sendto(*udp, p, &dest, port); + + pbuf_free(p); + + if (err != ERR_OK) { + return err; + } + + return len; +} + +static uint8_t *opt_find(uint8_t *opt, uint8_t cmd) { + for (int i = 0; i < 308 && opt[i] != DHCP_OPT_END;) { + if (opt[i] == cmd) { + return &opt[i]; + } + i += 2 + opt[i + 1]; + } + return NULL; +} + +static void opt_write_n(uint8_t **opt, uint8_t cmd, size_t n, void *data) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = n; + memcpy(o, data, n); + *opt = o + n; +} + +static void opt_write_u8(uint8_t **opt, uint8_t cmd, uint8_t val) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = 1; + *o++ = val; + *opt = o; +} + +static void opt_write_u32(uint8_t **opt, uint8_t cmd, uint32_t val) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = 4; + *o++ = val >> 24; + *o++ = val >> 16; + *o++ = val >> 8; + *o++ = val; + *opt = o; +} + +static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) { + dhcp_server_t *d = arg; + (void)upcb; + (void)src_addr; + (void)src_port; + + // This is around 548 bytes + dhcp_msg_t dhcp_msg; + + #define DHCP_MIN_SIZE (240 + 3) + if (p->tot_len < DHCP_MIN_SIZE) { + goto ignore_request; + } + + size_t len = pbuf_copy_partial(p, &dhcp_msg, sizeof(dhcp_msg), 0); + if (len < DHCP_MIN_SIZE) { + goto ignore_request; + } + + dhcp_msg.op = DHCPOFFER; + memcpy(&dhcp_msg.yiaddr, &d->ip.addr, 4); + + uint8_t *opt = (uint8_t *)&dhcp_msg.options; + opt += 4; // assume magic cookie: 99, 130, 83, 99 + + switch (opt[2]) { + case DHCPDISCOVER: { + int yi = DHCPS_MAX_IP; + for (int i = 0; i < DHCPS_MAX_IP; ++i) { + if (memcmp(d->lease[i].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { + // MAC match, use this IP address + yi = i; + break; + } + if (yi == DHCPS_MAX_IP) { + // Look for a free IP address + if (memcmp(d->lease[i].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { + // IP available + yi = i; + } + uint32_t expiry = d->lease[i].expiry << 16 | 0xffff; + if ((int32_t)(expiry - cyw43_hal_ticks_ms()) < 0) { + // IP expired, reuse it + memset(d->lease[i].mac, 0, MAC_LEN); + yi = i; + } + } + } + if (yi == DHCPS_MAX_IP) { + // No more IP addresses left + goto ignore_request; + } + dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; + opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPOFFER); + break; + } + + case DHCPREQUEST: { + uint8_t *o = opt_find(opt, DHCP_OPT_REQUESTED_IP); + if (o == NULL) { + // Should be NACK + goto ignore_request; + } + if (memcmp(o + 2, &d->ip.addr, 3) != 0) { + // Should be NACK + goto ignore_request; + } + uint8_t yi = o[5] - DHCPS_BASE_IP; + if (yi >= DHCPS_MAX_IP) { + // Should be NACK + goto ignore_request; + } + if (memcmp(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { + // MAC match, ok to use this IP address + } else if (memcmp(d->lease[yi].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { + // IP unused, ok to use this IP address + memcpy(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN); + } else { + // IP already in use + // Should be NACK + goto ignore_request; + } + d->lease[yi].expiry = (cyw43_hal_ticks_ms() + DEFAULT_LEASE_TIME_S * 1000) >> 16; + dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; + opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPACK); + printf("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x IP=%u.%u.%u.%u\n", + dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5], + dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], dhcp_msg.yiaddr[3]); + break; + } + + default: + goto ignore_request; + } + + opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &d->ip.addr); + opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &d->nm.addr); + opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &d->ip.addr); // aka gateway; can have mulitple addresses + opt_write_u32(&opt, DHCP_OPT_DNS, DEFAULT_DNS); // can have mulitple addresses + opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S); + *opt++ = DHCP_OPT_END; + dhcp_socket_sendto(&d->udp, &dhcp_msg, opt - (uint8_t *)&dhcp_msg, 0xffffffff, PORT_DHCP_CLIENT); + +ignore_request: + pbuf_free(p); +} + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) { + ip_addr_copy(d->ip, *ip); + ip_addr_copy(d->nm, *nm); + memset(d->lease, 0, sizeof(d->lease)); + if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) { + return; + } + dhcp_socket_bind(&d->udp, 0, PORT_DHCP_SERVER); +} + +void dhcp_server_deinit(dhcp_server_t *d) { + dhcp_socket_free(&d->udp); +} diff --git a/pico_w/access_point/dhcpserver/dhcpserver.h b/pico_w/access_point/dhcpserver/dhcpserver.h new file mode 100644 index 0000000..2349d2e --- /dev/null +++ b/pico_w/access_point/dhcpserver/dhcpserver.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H +#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H + +#include "lwip/ip_addr.h" + +#define DHCPS_BASE_IP (16) +#define DHCPS_MAX_IP (8) + +typedef struct _dhcp_server_lease_t { + uint8_t mac[6]; + uint16_t expiry; +} dhcp_server_lease_t; + +typedef struct _dhcp_server_t { + ip_addr_t ip; + ip_addr_t nm; + dhcp_server_lease_t lease[DHCPS_MAX_IP]; + struct udp_pcb *udp; +} dhcp_server_t; + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm); +void dhcp_server_deinit(dhcp_server_t *d); + +#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H diff --git a/pico_w/access_point/lwipopts.h b/pico_w/access_point/lwipopts.h new file mode 100644 index 0000000..8571ed5 --- /dev/null +++ b/pico_w/access_point/lwipopts.h @@ -0,0 +1,10 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#endif diff --git a/pico_w/access_point/picow_access_point.c b/pico_w/access_point/picow_access_point.c new file mode 100644 index 0000000..6e54709 --- /dev/null +++ b/pico_w/access_point/picow_access_point.c @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" + +#include "lwip/pbuf.h" +#include "lwip/tcp.h" + +#include "dhcpserver.h" + +#ifndef USE_LED +#define USE_LED 1 +#endif + +#define TCP_PORT 80 +#define DEBUG_printf printf + +typedef struct TCP_ASERVER_T_ { + struct tcp_pcb *server_pcb; + struct tcp_pcb *client_pcb; + bool complete; +} TCP_SERVER_T; + +static err_t tcp_server_close(void *arg) { + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + err_t err = ERR_OK; + if (state->client_pcb != NULL) { + tcp_arg(state->client_pcb, NULL); + tcp_poll(state->client_pcb, NULL, 0); + tcp_sent(state->client_pcb, NULL); + tcp_recv(state->client_pcb, NULL); + tcp_err(state->client_pcb, NULL); + err = tcp_close(state->client_pcb); + if (err != ERR_OK) { + DEBUG_printf("close failed %d, calling abort\n", err); + tcp_abort(state->client_pcb); + err = ERR_ABRT; + } + state->client_pcb = NULL; + } + if (state->server_pcb) { + tcp_arg(state->server_pcb, NULL); + tcp_close(state->server_pcb); + state->server_pcb = NULL; + } + return err; +} + +static err_t tcp_ap_result(void *arg, int status) { + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + if (status == 0) { + DEBUG_printf("test success\n"); + } else { + DEBUG_printf("test failed %d\n", status); + } + state->complete = true; + return tcp_server_close(arg); +} + +static err_t tcp_server_accept(void *arg, struct tcp_pcb *client_pcb, err_t err) { +// TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + if (err != ERR_OK || client_pcb == NULL) { + DEBUG_printf("Failure in accept\n"); + tcp_ap_result(arg, err); + return ERR_VAL; + } + DEBUG_printf("Client connected\n"); + + /*state->client_pcb = client_pcb; + tcp_arg(client_pcb, state); + tcp_sent(client_pcb, tcp_server_sent); + tcp_recv(client_pcb, tcp_server_recv); + tcp_poll(client_pcb, tcp_server_poll, POLL_TIME_S * 2); + tcp_err(client_pcb, tcp_server_err); + + return tcp_server_send_data(arg, state->client_pcb);*/ + return ERR_OK; +} + +static bool tcp_server_open(void *arg) { + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + DEBUG_printf("Starting server on port %u\n", TCP_PORT); + + struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_ANY); + if (!pcb) { + DEBUG_printf("failed to create pcb\n"); + return false; + } + + err_t err = tcp_bind(pcb, NULL, TCP_PORT); + if (err) { + DEBUG_printf("failed to bind to port %d\n"); + return false; + } + + state->server_pcb = tcp_listen_with_backlog(pcb, 1); + if (!state->server_pcb) { + DEBUG_printf("failed to listen\n"); + if (pcb) { + tcp_close(pcb); + } + return false; + } + + tcp_arg(state->server_pcb, state); + tcp_accept(state->server_pcb, tcp_server_accept); + + return true; +} + +int main() { + stdio_init_all(); + + TCP_SERVER_T *state = calloc(1, sizeof(TCP_SERVER_T)); + if (!state) { + DEBUG_printf("failed to allocate state\n"); + return 1; + } + + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + const char *ap_name = "picow_test"; +#if 1 + const char *password = "password"; +#else + const char *password = NULL; +#endif + + cyw43_arch_enable_ap_mode(ap_name, password, CYW43_AUTH_WPA2_AES_PSK); + + ip4_addr_t gw, mask; + IP4_ADDR(&gw, 192, 168, 4, 1); + IP4_ADDR(&mask, 255, 255, 255, 0); + + // Start the dhcp server + dhcp_server_t dhcp_server; + dhcp_server_init(&dhcp_server, &gw, &mask); + + if (!tcp_server_open(state)) { + tcp_ap_result(state, -1); + } + + while(!state->complete) { + #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_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, led_on); + led_time = make_timeout_time_ms(1000); + } + #endif + // 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 + // if you are using pico_cyw43_arch_poll, then you must poll periodically from your + // main loop (not from a timer) to check for WiFi driver or lwIP work that needs to be done. + cyw43_arch_poll(); + sleep_ms(1); +#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) + // work you might be doing. + sleep_ms(1000); +#endif + } + dhcp_server_deinit(&dhcp_server); + cyw43_arch_deinit(); + return 0; +} diff --git a/pico_w/blink/CMakeLists.txt b/pico_w/blink/CMakeLists.txt new file mode 100644 index 0000000..255de3c --- /dev/null +++ b/pico_w/blink/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable(picow_blink + picow_blink.c + ) + +target_link_libraries(picow_blink + pico_stdlib # for core functionality + pico_cyw43_arch_none # we need Wifi to access the GPIO, but we don't need anything else + ) + +# create map/bin/hex file etc. +pico_add_extra_outputs(picow_blink) + +# add url via pico_set_program_url +example_auto_set_url(picow_blink) diff --git a/pico_w/blink/picow_blink.c b/pico_w/blink/picow_blink.c new file mode 100644 index 0000000..d5d7ac4 --- /dev/null +++ b/pico_w/blink/picow_blink.c @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" + +int main() { + stdio_init_all(); + if (cyw43_arch_init()) { + printf("WiFi init failed"); + return -1; + } + while (true) { + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); + sleep_ms(250); + cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); + sleep_ms(250); + } +} diff --git a/pico_w/freertos/CMakeLists.txt b/pico_w/freertos/CMakeLists.txt new file mode 100644 index 0000000..4769aee --- /dev/null +++ b/pico_w/freertos/CMakeLists.txt @@ -0,0 +1,8 @@ +if (NOT FREERTOS_KERNEL_PATH AND NOT DEFINED ENV{FREERTOS_KERNEL_PATH}) + message("Skipping Pico W FreeRTOS examples as FREERTOS_KERNEL_PATH not defined") +else() + include(FreeRTOS_Kernel_import.cmake) + + add_subdirectory(iperf) + add_subdirectory(ping) +endif() \ No newline at end of file diff --git a/pico_w/freertos/FreeRTOS_Kernel_import.cmake b/pico_w/freertos/FreeRTOS_Kernel_import.cmake new file mode 100644 index 0000000..dc68ed0 --- /dev/null +++ b/pico_w/freertos/FreeRTOS_Kernel_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /portable/ThirdParty/GCC/RP2040/FREERTOS_KERNEL_import.cmake + +# This can be dropped into an external project to help locate the FreeRTOS kernel +# It should be include()ed prior to project(). Alternatively this file may +# or the CMakeLists.txt in this directory may be included or added via add_subdirectory +# respectively. + +if (DEFINED ENV{FREERTOS_KERNEL_PATH} AND (NOT FREERTOS_KERNEL_PATH)) + set(FREERTOS_KERNEL_PATH $ENV{FREERTOS_KERNEL_PATH}) + message("Using FREERTOS_KERNEL_PATH from environment ('${FREERTOS_KERNEL_PATH}')") +endif () + +set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/GCC/RP2040") +# undo the above +set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../..") + +if (NOT FREERTOS_KERNEL_PATH) + # check if we are inside the FreeRTOS kernel tree (i.e. this file has been included directly) + get_filename_component(_ACTUAL_PATH ${CMAKE_CURRENT_LIST_DIR} REALPATH) + get_filename_component(_POSSIBLE_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} REALPATH) + if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) + get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) + endif() + if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) + get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) + message("Setting FREERTOS_KERNEL_PATH to ${FREERTOS_KERNEL_PATH} based on location of FreeRTOS-Kernel-import.cmake") + elseif (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../FreeRTOS-Kernel") + set(FREERTOS_KERNEL_PATH ${PICO_SDK_PATH}/../FreeRTOS-Kernel) + message("Defaulting FREERTOS_KERNEL_PATH as sibling of PICO_SDK_PATH: ${FREERTOS_KERNEL_PATH}") + endif() +endif () + +if (NOT FREERTOS_KERNEL_PATH) + foreach(POSSIBLE_SUFFIX Source FreeRTOS-Kernel FreeRTOS/Source) + # check if FreeRTOS-Kernel exists under directory that included us + set(SEARCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}}) + set(SEARCH_ROOT ../../../..) + get_filename_component(_POSSIBLE_PATH ${SEARCH_ROOT}/${POSSIBLE_SUFFIX} REALPATH) + if (EXISTS ${_POSSIBLE_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) + get_filename_component(FREERTOS_KERNEL_PATH ${_POSSIBLE_PATH} REALPATH) + message("Setting FREERTOS_KERNEL_PATH to '${FREERTOS_KERNEL_PATH}' found relative to enclosing project") + break() + endif() + endforeach() +endif() + +if (NOT FREERTOS_KERNEL_PATH) + message(FATAL_ERROR "FreeRTOS location was not specified. Please set FREERTOS_KERNEL_PATH.") +endif() + +set(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" CACHE PATH "Path to the FreeRTOS Kernel") + +get_filename_component(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${FREERTOS_KERNEL_PATH}) + message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' not found") +endif() +if (NOT EXISTS ${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) + message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' does not contain an RP2040 port here: ${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}") +endif() +set(FREERTOS_KERNEL_PATH ${FREERTOS_KERNEL_PATH} CACHE PATH "Path to the FreeRTOS_KERNEL" FORCE) + +add_subdirectory(${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} FREERTOS_KERNEL) \ No newline at end of file diff --git a/pico_w/freertos/iperf/CMakeLists.txt b/pico_w/freertos/iperf/CMakeLists.txt new file mode 100644 index 0000000..4ed0652 --- /dev/null +++ b/pico_w/freertos/iperf/CMakeLists.txt @@ -0,0 +1,38 @@ +add_executable(picow_freertos_iperf_server_nosys + picow_freertos_iperf.c + ) +target_compile_definitions(picow_freertos_iperf_server_nosys PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) +target_include_directories(picow_freertos_iperf_server_nosys PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../.. # for our common lwipopts + ) +target_link_libraries(picow_freertos_iperf_server_nosys + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + pico_lwip_iperf + FreeRTOS-Kernel-Heap4 # FreeRTOS kernel and dynamic heap + ) +pico_add_extra_outputs(picow_freertos_iperf_server_nosys) + +add_executable(picow_freertos_iperf_server_sys + picow_freertos_iperf.c + ) +target_compile_definitions(picow_freertos_iperf_server_sys PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + NO_SYS=0 # don't want NO_SYS (generally this would be in your lwipopts.h) + ) +target_include_directories(picow_freertos_iperf_server_sys PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../.. # for our common lwipopts + ) +target_link_libraries(picow_freertos_iperf_server_sys + pico_cyw43_arch_lwip_sys_freertos + pico_stdlib + pico_lwip_iperf + FreeRTOS-Kernel-Heap4 # FreeRTOS kernel and dynamic heap + ) +pico_add_extra_outputs(picow_freertos_iperf_server_sys) diff --git a/pico_w/freertos/iperf/FreeRTOSConfig.h b/pico_w/freertos/iperf/FreeRTOSConfig.h new file mode 100644 index 0000000..f9f075d --- /dev/null +++ b/pico_w/freertos/iperf/FreeRTOSConfig.h @@ -0,0 +1,143 @@ +/* + * FreeRTOS V202111.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html + *----------------------------------------------------------*/ + +/* Scheduler Related */ +#define configUSE_PREEMPTION 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) +#define configMAX_PRIORITIES 32 +#define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 256 +#define configUSE_16_BIT_TICKS 0 + +#define configIDLE_SHOULD_YIELD 1 + +/* Synchronization Related */ +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_QUEUE_SETS 1 +#define configUSE_TIME_SLICING 1 +#define configUSE_NEWLIB_REENTRANT 0 +// todo need this for lwip FreeRTOS sys_arch to compile +#define configENABLE_BACKWARD_COMPATIBILITY 1 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 + +/* System */ +#define configSTACK_DEPTH_TYPE uint32_t +#define configMESSAGE_BUFFER_LENGTH_TYPE size_t + +/* Memory allocation related definitions. */ +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configTOTAL_HEAP_SIZE (128*1024) +#define configAPPLICATION_ALLOCATED_HEAP 0 + +/* Hook function related definitions. */ +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 + +/* Run time and task stats gathering related definitions. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 + +/* Co-routine related definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES 1 + +/* Software timer related definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 1024 + +/* Interrupt nesting behaviour configuration. */ +/* +#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] +#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] +#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] +*/ + +#if FREE_RTOS_KERNEL_SMP // set by the RP2040 SMP port of FreeRTOS +/* SMP port only */ +#define configNUM_CORES 2 +#define configTICK_CORE 0 +#define configRUN_MULTIPLE_PRIORITIES 1 +#define configUSE_CORE_AFFINITY 1 +#endif + +/* RP2040 specific */ +#define configSUPPORT_PICO_SYNC_INTEROP 1 +#define configSUPPORT_PICO_TIME_INTEROP 1 + +#include +/* Define to trap errors during development. */ +#define configASSERT(x) assert(x) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xQueueGetMutexHolder 1 + +/* A header file that defines trace macro can be included here. */ + +#endif /* FREERTOS_CONFIG_H */ + diff --git a/pico_w/freertos/iperf/lwipopts.h b/pico_w/freertos/iperf/lwipopts.h new file mode 100644 index 0000000..b8983d7 --- /dev/null +++ b/pico_w/freertos/iperf/lwipopts.h @@ -0,0 +1,21 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#if !NO_SYS +#define TCPIP_THREAD_STACKSIZE 1024 +#define DEFAULT_THREAD_STACKSIZE 1024 +#define DEFAULT_RAW_RECVMBOX_SIZE 8 +#define TCPIP_MBOX_SIZE 8 +#define LWIP_TIMEVAL_PRIVATE 0 + +// not necessary, can be done either way +#define LWIP_TCPIP_CORE_LOCKING_INPUT 1 +#endif + +#endif diff --git a/pico_w/freertos/iperf/picow_freertos_iperf.c b/pico_w/freertos/iperf/picow_freertos_iperf.c new file mode 100644 index 0000000..d6d6472 --- /dev/null +++ b/pico_w/freertos/iperf/picow_freertos_iperf.c @@ -0,0 +1,132 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/cyw43_arch.h" +#include "pico/stdlib.h" + +#include "lwip/netif.h" +#include "lwip/ip4_addr.h" +#include "lwip/apps/lwiperf.h" + +#include "FreeRTOS.h" +#include "task.h" + +#ifndef RUN_FREERTOS_ON_CORE +#define RUN_FREERTOS_ON_CORE 0 +#endif + +#define TEST_TASK_PRIORITY ( tskIDLE_PRIORITY + 2UL ) +#define BLINK_TASK_PRIORITY ( tskIDLE_PRIORITY + 1UL ) + +#if CLIENT_TEST && !defined(IPERF_SERVER_IP) +#error IPERF_SERVER_IP not defined +#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, + u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec) { + static uint32_t total_iperf_megabytes = 0; + uint32_t mbytes = bytes_transferred / 1024 / 1024; + float mbits = bandwidth_kbitpsec / 1000.0; + + total_iperf_megabytes += mbytes; + + printf("Completed iperf transfer of %d MBytes @ %.1f Mbits/sec\n", mbytes, mbits); + printf("Total iperf megabytes since start %d Mbytes\n", total_iperf_megabytes); +} + +void blink_task(__unused void *params) { + bool on = false; + printf("blink_task starts\n"); + while (true) { +#if 0 && configNUM_CORES > 1 + static int last_core_id; + if (portGET_CORE_ID() != last_core_id) { + last_core_id = portGET_CORE_ID(); + printf("blinking now from core %d\n", last_core_id); + } +#endif + cyw43_arch_gpio_put(0, on); + on = !on; + vTaskDelay(200); + } +} + +void main_task(__unused void *params) { + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return; + } + cyw43_arch_enable_sta_mode(); + printf("Connecting to WiFi...\n"); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("failed to connect.\n"); + exit(1); + } else { + printf("Connected.\n"); + } + + xTaskCreate(blink_task, "BlinkThread", configMINIMAL_STACK_SIZE, NULL, BLINK_TASK_PRIORITY, NULL); + +#if CLIENT_TEST + printf("\nReady, running iperf client\n"); + ip_addr_t clientaddr; + ip4_addr_set_u32(&clientaddr, ipaddr_addr(xstr(IPERF_SERVER_IP))); + assert(lwiperf_start_tcp_client_default(&clientaddr, &iperf_report, NULL) != NULL); +#else + printf("\nReady, running iperf server at %s\n", ip4addr_ntoa(netif_ip4_addr(netif_list))); + lwiperf_start_tcp_server_default(&iperf_report, NULL); +#endif + + while(true) { + // not much to do as LED is in another task, and we're using RAW (callback) lwIP API + vTaskDelay(100); + } + + cyw43_arch_deinit(); +} + +void vLaunch( void) { + TaskHandle_t task; + xTaskCreate(main_task, "TestMainThread", configMINIMAL_STACK_SIZE, NULL, TEST_TASK_PRIORITY, &task); + +#if NO_SYS && configUSE_CORE_AFFINITY && configNUM_CORES > 1 + // we must bind the main task to one core (well at least while the init is called) + // (note we only do this in NO_SYS mode, because cyw43_arch_freertos + // takes care of it otherwise) + vTaskCoreAffinitySet(task, 1); +#endif + + /* Start the tasks and timer running. */ + vTaskStartScheduler(); +} + +int main( void ) +{ + stdio_init_all(); + + /* Configure the hardware ready to run the demo. */ + const char *rtos_name; +#if ( portSUPPORT_SMP == 1 ) + rtos_name = "FreeRTOS SMP"; +#else + rtos_name = "FreeRTOS"; +#endif + +#if ( portSUPPORT_SMP == 1 ) && ( configNUM_CORES == 2 ) + printf("Starting %s on both cores:\n", rtos_name); + vLaunch(); +#elif ( RUN_FREE_RTOS_ON_CORE == 1 ) + printf("Starting %s on core 1:\n", rtos_name); + multicore_launch_core1(vLaunch); + while (true); +#else + printf("Starting %s on core 0:\n", rtos_name); + vLaunch(); +#endif + return 0; +} diff --git a/pico_w/freertos/ping/CMakeLists.txt b/pico_w/freertos/ping/CMakeLists.txt new file mode 100644 index 0000000..a76991c --- /dev/null +++ b/pico_w/freertos/ping/CMakeLists.txt @@ -0,0 +1,46 @@ +if (EXISTS ${PICO_LWIP_CONTRIB_PATH}/apps/ping/ping.c) + add_executable(picow_freertos_ping_nosys + picow_freertos_ping.c + ${PICO_LWIP_CONTRIB_PATH}/apps/ping/ping.c + ) + target_compile_definitions(picow_freertos_ping_nosys PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) + target_include_directories(picow_freertos_ping_nosys PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../.. # for our common lwipopts + ${PICO_LWIP_CONTRIB_PATH}/apps/ping + ) + target_link_libraries(picow_freertos_ping_nosys + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + pico_lwip_iperf + FreeRTOS-Kernel-Heap4 # FreeRTOS kernel and dynamic heap + ) + pico_add_extra_outputs(picow_freertos_ping_nosys) + + add_executable(picow_freertos_ping_sys + picow_freertos_ping.c + ${PICO_LWIP_CONTRIB_PATH}/apps/ping/ping.c + ) + target_compile_definitions(picow_freertos_ping_sys PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + NO_SYS=0 # don't want NO_SYS (generally this would be in your lwipopts.h) + LWIP_SOCKET=1 # we need the socket API (generally this would be in your lwipopts.h) + PING_USE_SOCKETS=1 + ) + target_include_directories(picow_freertos_ping_sys PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../.. # for our common lwipopts + ${PICO_LWIP_CONTRIB_PATH}/apps/ping + ) + target_link_libraries(picow_freertos_ping_sys + pico_cyw43_arch_lwip_sys_freertos + pico_stdlib + pico_lwip_iperf + FreeRTOS-Kernel-Heap4 # FreeRTOS kernel and dynamic heap + ) + pico_add_extra_outputs(picow_freertos_ping_sys) +endif() \ No newline at end of file diff --git a/pico_w/freertos/ping/FreeRTOSConfig.h b/pico_w/freertos/ping/FreeRTOSConfig.h new file mode 100644 index 0000000..f9f075d --- /dev/null +++ b/pico_w/freertos/ping/FreeRTOSConfig.h @@ -0,0 +1,143 @@ +/* + * FreeRTOS V202111.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html + *----------------------------------------------------------*/ + +/* Scheduler Related */ +#define configUSE_PREEMPTION 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) +#define configMAX_PRIORITIES 32 +#define configMINIMAL_STACK_SIZE ( configSTACK_DEPTH_TYPE ) 256 +#define configUSE_16_BIT_TICKS 0 + +#define configIDLE_SHOULD_YIELD 1 + +/* Synchronization Related */ +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_QUEUE_SETS 1 +#define configUSE_TIME_SLICING 1 +#define configUSE_NEWLIB_REENTRANT 0 +// todo need this for lwip FreeRTOS sys_arch to compile +#define configENABLE_BACKWARD_COMPATIBILITY 1 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 + +/* System */ +#define configSTACK_DEPTH_TYPE uint32_t +#define configMESSAGE_BUFFER_LENGTH_TYPE size_t + +/* Memory allocation related definitions. */ +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configTOTAL_HEAP_SIZE (128*1024) +#define configAPPLICATION_ALLOCATED_HEAP 0 + +/* Hook function related definitions. */ +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 + +/* Run time and task stats gathering related definitions. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 + +/* Co-routine related definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES 1 + +/* Software timer related definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 1024 + +/* Interrupt nesting behaviour configuration. */ +/* +#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] +#define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] +#define configMAX_API_CALL_INTERRUPT_PRIORITY [dependent on processor and application] +*/ + +#if FREE_RTOS_KERNEL_SMP // set by the RP2040 SMP port of FreeRTOS +/* SMP port only */ +#define configNUM_CORES 2 +#define configTICK_CORE 0 +#define configRUN_MULTIPLE_PRIORITIES 1 +#define configUSE_CORE_AFFINITY 1 +#endif + +/* RP2040 specific */ +#define configSUPPORT_PICO_SYNC_INTEROP 1 +#define configSUPPORT_PICO_TIME_INTEROP 1 + +#include +/* Define to trap errors during development. */ +#define configASSERT(x) assert(x) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xQueueGetMutexHolder 1 + +/* A header file that defines trace macro can be included here. */ + +#endif /* FREERTOS_CONFIG_H */ + diff --git a/pico_w/freertos/ping/lwipopts.h b/pico_w/freertos/ping/lwipopts.h new file mode 100644 index 0000000..b8983d7 --- /dev/null +++ b/pico_w/freertos/ping/lwipopts.h @@ -0,0 +1,21 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#if !NO_SYS +#define TCPIP_THREAD_STACKSIZE 1024 +#define DEFAULT_THREAD_STACKSIZE 1024 +#define DEFAULT_RAW_RECVMBOX_SIZE 8 +#define TCPIP_MBOX_SIZE 8 +#define LWIP_TIMEVAL_PRIVATE 0 + +// not necessary, can be done either way +#define LWIP_TCPIP_CORE_LOCKING_INPUT 1 +#endif + +#endif diff --git a/pico_w/freertos/ping/picow_freertos_ping.c b/pico_w/freertos/ping/picow_freertos_ping.c new file mode 100644 index 0000000..e2e38a1 --- /dev/null +++ b/pico_w/freertos/ping/picow_freertos_ping.c @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/cyw43_arch.h" +#include "pico/stdlib.h" + +#include "lwip/ip4_addr.h" + +#include "FreeRTOS.h" +#include "task.h" +#include "ping.h" + +#ifndef PING_ADDR +#define PING_ADDR "142.251.35.196" +#endif +#ifndef RUN_FREERTOS_ON_CORE +#define RUN_FREERTOS_ON_CORE 0 +#endif + +#define TEST_TASK_PRIORITY ( tskIDLE_PRIORITY + 1UL ) + +void main_task(__unused void *params) { + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return; + } + cyw43_arch_enable_sta_mode(); + printf("Connecting to WiFi...\n"); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("failed to connect.\n"); + exit(1); + } else { + printf("Connected.\n"); + } + + ip_addr_t ping_addr; + ip4_addr_set_u32(&ping_addr, ipaddr_addr(PING_ADDR)); + ping_init(&ping_addr); + + while(true) { + // not much to do as LED is in another task, and we're using RAW (callback) lwIP API + vTaskDelay(100); + } + + cyw43_arch_deinit(); +} + +void vLaunch( void) { + TaskHandle_t task; + xTaskCreate(main_task, "TestMainThread", configMINIMAL_STACK_SIZE, NULL, TEST_TASK_PRIORITY, &task); + +#if NO_SYS && configUSE_CORE_AFFINITY && configNUM_CORES > 1 + // we must bind the main task to one core (well at least while the init is called) + // (note we only do this in NO_SYS mode, because cyw43_arch_freertos + // takes care of it otherwise) + vTaskCoreAffinitySet(task, 1); +#endif + + /* Start the tasks and timer running. */ + vTaskStartScheduler(); +} + +int main( void ) +{ + stdio_init_all(); + + /* Configure the hardware ready to run the demo. */ + const char *rtos_name; +#if ( portSUPPORT_SMP == 1 ) + rtos_name = "FreeRTOS SMP"; +#else + rtos_name = "FreeRTOS"; +#endif + +#if ( portSUPPORT_SMP == 1 ) && ( configNUM_CORES == 2 ) + printf("Starting %s on both cores:\n", rtos_name); + vLaunch(); +#elif ( RUN_FREE_RTOS_ON_CORE == 1 ) + printf("Starting %s on core 1:\n", rtos_name); + multicore_launch_core1(vLaunch); + while (true); +#else + printf("Starting %s on core 0:\n", rtos_name); + vLaunch(); +#endif + return 0; +} diff --git a/pico_w/iperf/CMakeLists.txt b/pico_w/iperf/CMakeLists.txt new file mode 100644 index 0000000..d9c43fe --- /dev/null +++ b/pico_w/iperf/CMakeLists.txt @@ -0,0 +1,37 @@ +add_executable(picow_iperf_server_background + picow_iperf.c + ) +target_compile_definitions(picow_iperf_server_background PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) +target_include_directories(picow_iperf_server_background PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) +target_link_libraries(picow_iperf_server_background + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + pico_lwip_iperf + ) + +pico_add_extra_outputs(picow_iperf_server_background) + +add_executable(picow_iperf_server_poll + picow_iperf.c + ) +target_compile_definitions(picow_iperf_server_poll PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) +target_include_directories(picow_iperf_server_poll PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) +target_link_libraries(picow_iperf_server_poll + pico_cyw43_arch_lwip_poll + pico_stdlib + pico_lwip_iperf + ) +pico_add_extra_outputs(picow_iperf_server_poll) + diff --git a/pico_w/iperf/lwipopts.h b/pico_w/iperf/lwipopts.h new file mode 100644 index 0000000..8571ed5 --- /dev/null +++ b/pico_w/iperf/lwipopts.h @@ -0,0 +1,10 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#endif diff --git a/pico_w/iperf/picow_iperf.c b/pico_w/iperf/picow_iperf.c new file mode 100644 index 0000000..cd479cc --- /dev/null +++ b/pico_w/iperf/picow_iperf.c @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/cyw43_arch.h" +#include "pico/stdlib.h" + +#include "lwip/netif.h" +#include "lwip/ip4_addr.h" +#include "lwip/apps/lwiperf.h" + +#ifndef USE_LED +#define USE_LED 1 +#endif + +#if CLIENT_TEST && !defined(IPERF_SERVER_IP) +#error IPERF_SERVER_IP not defined +#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, + u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec) { + static uint32_t total_iperf_megabytes = 0; + uint32_t mbytes = bytes_transferred / 1024 / 1024; + float mbits = bandwidth_kbitpsec / 1000.0; + + total_iperf_megabytes += mbytes; + + printf("Completed iperf transfer of %d MBytes @ %.1f Mbits/sec\n", mbytes, mbits); + printf("Total iperf megabytes since start %d Mbytes\n", total_iperf_megabytes); +#if CYW43_USE_STATS + printf("packets in %u packets out %u\n", CYW43_STAT_GET(PACKET_IN_COUNT), CYW43_STAT_GET(PACKET_OUT_COUNT)); +#endif +} + +int main() { + stdio_init_all(); + + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + cyw43_arch_enable_sta_mode(); + printf("Connecting to WiFi...\n"); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("failed to connect.\n"); + return 1; + } else { + printf("Connected.\n"); + } + +#if CLIENT_TEST + printf("\nReady, running iperf client\n"); + ip_addr_t clientaddr; + ip4_addr_set_u32(&clientaddr, ipaddr_addr(xstr(IPERF_SERVER_IP))); + assert(lwiperf_start_tcp_client_default(&clientaddr, &iperf_report, NULL) != NULL); +#else + printf("\nReady, running iperf server at %s\n", ip4addr_ntoa(netif_ip4_addr(netif_list))); + lwiperf_start_tcp_server_default(&iperf_report, NULL); +#endif + + while(true) { +#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); + } +#endif + // 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 + // if you are using pico_cyw43_arch_poll, then you must poll periodically from your + // main loop (not from a timer) to check for WiFi driver or lwIP work that needs to be done. + cyw43_arch_poll(); + sleep_ms(1); +#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) + // work you might be doing. + sleep_ms(1000); +#endif + } + + cyw43_arch_deinit(); + return 0; +} diff --git a/pico_w/lwipopts_examples_common.h b/pico_w/lwipopts_examples_common.h new file mode 100644 index 0000000..217cb13 --- /dev/null +++ b/pico_w/lwipopts_examples_common.h @@ -0,0 +1,90 @@ +#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H +#define _LWIPOPTS_EXAMPLE_COMMONH_H + + +// Common settings used in most of the pico_w examples +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details) + +// allow override in some examples +#ifndef NO_SYS +#define NO_SYS 1 +#endif +// allow override in some examples +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 0 +#endif +#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__ */ diff --git a/pico_w/ntp_client/CMakeLists.txt b/pico_w/ntp_client/CMakeLists.txt new file mode 100644 index 0000000..22a2e63 --- /dev/null +++ b/pico_w/ntp_client/CMakeLists.txt @@ -0,0 +1,35 @@ +add_executable(picow_ntp_client_background + picow_ntp_client.c + ) +target_compile_definitions(picow_ntp_client_background PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) +target_include_directories(picow_ntp_client_background PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts +) +target_link_libraries(picow_ntp_client_background + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + ) + +pico_add_extra_outputs(picow_ntp_client_background) + +add_executable(picow_ntp_client_poll + picow_ntp_client.c + ) +target_compile_definitions(picow_ntp_client_poll PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) +target_include_directories(picow_ntp_client_poll PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) +target_link_libraries(picow_ntp_client_poll + pico_cyw43_arch_lwip_poll + pico_stdlib + ) +pico_add_extra_outputs(picow_ntp_client_poll) + diff --git a/pico_w/ntp_client/lwipopts.h b/pico_w/ntp_client/lwipopts.h new file mode 100644 index 0000000..8571ed5 --- /dev/null +++ b/pico_w/ntp_client/lwipopts.h @@ -0,0 +1,10 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#endif diff --git a/pico_w/ntp_client/picow_ntp_client.c b/pico_w/ntp_client/picow_ntp_client.c new file mode 100644 index 0000000..5e16091 --- /dev/null +++ b/pico_w/ntp_client/picow_ntp_client.c @@ -0,0 +1,185 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" + +#include "lwip/dns.h" +#include "lwip/pbuf.h" +#include "lwip/udp.h" + +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; +} 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) + +// Called with results of operation +static void ntp_result(NTP_T* state, int status, time_t *result) { + if (status == 0 && result) { + struct tm *utc = gmtime(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; +} + +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. + // 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(); + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, NTP_MSG_LEN, PBUF_RAM); + uint8_t *req = (uint8_t *) p->payload; + memset(req, 0, NTP_MSG_LEN); + req[0] = 0x1b; + udp_sendto(state->ntp_pcb, p, &state->ntp_server_address, NTP_PORT); + pbuf_free(p); + 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; + if (ipaddr) { + state->ntp_server_address = *ipaddr; + printf("ntp address %s\n", ip4addr_ntoa(ipaddr)); + ntp_request(state); + } else { + printf("ntp dns request failed\n"); + ntp_result(state, -1, NULL); + } +} + +// NTP data received +static void ntp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { + NTP_T *state = (NTP_T*)arg; + uint8_t mode = pbuf_get_at(p, 0) & 0x7; + uint8_t stratum = pbuf_get_at(p, 1); + + // Check the result + if (ip_addr_cmp(addr, &state->ntp_server_address) && port == NTP_PORT && p->tot_len == NTP_MSG_LEN && + mode == 0x4 && stratum != 0) { + uint8_t seconds_buf[4] = {0}; + pbuf_copy_partial(p, seconds_buf, sizeof(seconds_buf), 40); + uint32_t seconds_since_1900 = seconds_buf[0] << 24 | seconds_buf[1] << 16 | seconds_buf[2] << 8 | seconds_buf[3]; + uint32_t seconds_since_1970 = seconds_since_1900 - NTP_DELTA; + time_t epoch = seconds_since_1970; + ntp_result(state, 0, &epoch); + } else { + printf("invalid ntp response\n"); + ntp_result(state, -1, NULL); + } + pbuf_free(p); +} + +// Perform initialisation +static NTP_T* ntp_init(void) { + NTP_T *state = calloc(1, sizeof(NTP_T)); + if (!state) { + printf("failed to allocate state\n"); + return NULL; + } + state->ntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY); + if (!state->ntp_pcb) { + printf("failed to create pcb\n"); + free(state); + return NULL; + } + udp_recv(state->ntp_pcb, ntp_recv, state); + return state; +} + +// Runs ntp test forever +void run_ntp_test(void) { + NTP_T *state = ntp_init(); + if (!state) + return; + 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); + } + } +#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) to check for WiFi driver or lwIP work that needs to be done. + cyw43_arch_poll(); + sleep_ms(1); +#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) + // work you might be doing. + sleep_ms(1000); +#endif + } + free(state); +} + +int main() { + stdio_init_all(); + + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + + cyw43_arch_enable_sta_mode(); + + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 10000)) { + printf("failed to connect\n"); + return 1; + } + run_ntp_test(); + cyw43_arch_deinit(); + return 0; +} \ No newline at end of file diff --git a/pico_w/python_test_tcp/python_test_tcp_client.py b/pico_w/python_test_tcp/python_test_tcp_client.py new file mode 100644 index 0000000..6db1a7b --- /dev/null +++ b/pico_w/python_test_tcp/python_test_tcp_client.py @@ -0,0 +1,71 @@ +import network +import utime as time +import usocket as socket + +# Set your wifi ssid and password here +WIFI_SSID = const('') +WIFI_PASSWORD = const('') + +# Set the server address here like 1.2.3.4 +SERVER_ADDR = const('') + +# These constants should match the server +BUF_SIZE = const(2048) +SERVER_PORT = const(4242) +TEST_ITERATIONS = const(10) + +# Check if wifi details have been set +if len(WIFI_SSID) == 0 or len(WIFI_PASSWORD) == 0: + raise RuntimeError('set wifi ssid and password in this script') + +# Check server ip address set +if len(SERVER_ADDR) == 0: + raise RuntimeError('set the IP address of the server') + +# Start connection +wlan = network.WLAN(network.STA_IF) +wlan.active(True) +wlan.connect(WIFI_SSID, WIFI_PASSWORD) + +# Wait for connect success or failure +max_wait = 20 +while max_wait > 0: + if wlan.status() < 0 or wlan.status() >= 3: + break + max_wait -= 1 + print('waiting for connection...') + time.sleep(1) + +# Handle connection error +if wlan.status() != 3: + raise RuntimeError('wifi connection failed %d' % wlan.status()) +else: + print('connected') + status = wlan.ifconfig() + print( 'ip = ' + status[0] ) + +# Open socket to the server +sock = socket.socket() +addr = (SERVER_ADDR, SERVER_PORT) +sock.connect(addr) + +# repeat test for a number of iterations +for test_iteration in range(TEST_ITERATIONS): + + # Read BUF_SIZE bytes from the server + read_buf = sock.read(BUF_SIZE) + print('read %d bytes from server' % len(read_buf)) + + # Check size of data received + if len(read_buf) != BUF_SIZE: + raise RuntimeError('wrong amount of data read') + + # Send the data back to the server + write_len = sock.write(read_buf) + print('written %d bytes to server' % write_len) + if write_len != BUF_SIZE: + raise RuntimeError('wrong amount of data written') + +# All done +sock.close() +print("test completed") diff --git a/pico_w/python_test_tcp/python_test_tcp_server.py b/pico_w/python_test_tcp/python_test_tcp_server.py new file mode 100644 index 0000000..971160d --- /dev/null +++ b/pico_w/python_test_tcp/python_test_tcp_server.py @@ -0,0 +1,85 @@ +import network +import utime as time +import usocket as socket +import random + +# Set your wifi ssid and password here +WIFI_SSID = const('') +WIFI_PASSWORD = const('') + +# These constants should match the client +BUF_SIZE = const(2048) +SERVER_PORT = const(4242) +TEST_ITERATIONS = const(10) + +# Check if wifi details have been set +if len(WIFI_SSID) == 0 or len(WIFI_PASSWORD) == 0: + raise RuntimeError('Please set wifi ssid and password in this script') + +# Start connection +wlan = network.WLAN(network.STA_IF) +wlan.active(True) +wlan.connect(WIFI_SSID, WIFI_PASSWORD) + +# Wait for connect success or failure +max_wait = 20 +while max_wait > 0: + if wlan.status() < 0 or wlan.status() >= 3: + break + max_wait -= 1 + print('waiting for connection...') + time.sleep(1) + +# Handle connection error +if wlan.status() != 3: + raise RuntimeError('wifi connection failed %d' % wlan.status()) +else: + print('connected') + status = wlan.ifconfig() + print( 'ip = ' + status[0] ) + +# Open socket to the server +sock = socket.socket() +addr = ('0.0.0.0', SERVER_PORT) +sock.bind(addr) +sock.listen(1) +print('server listening on', addr) + +# Wait for the client +con = None +con, addr = sock.accept() +print('client connected from', addr) + +# repeat test for a number of iterations +for test_iteration in range(TEST_ITERATIONS): + + # Generate a buffer of random data + random_list = [] + for n in range(BUF_SIZE): + random_list.append(random.randint(0, 255)) + write_buf = bytearray(random_list) + + # write BUF_SIZE bytes to the client + write_len = con.send(bytearray(write_buf)) + print('Written %d bytes to client' % write_len) + + # Check size of data written + if write_len != BUF_SIZE: + raise RuntimeError('wrong amount of data written') + + # Read the data back from the client + read_buf = con.read(BUF_SIZE) + print('read %d bytes from client' % len(read_buf)) + + # Check size of data received + if len(read_buf) != BUF_SIZE: + raise RuntimeError('wrong amount of data read') + + # Check the data sent and received + if read_buf != write_buf: + raise RuntimeError('buffer mismatch') + +# All done +con.close() +sock.close() +print("test completed") diff --git a/pico_w/tcp_client/CMakeLists.txt b/pico_w/tcp_client/CMakeLists.txt new file mode 100644 index 0000000..0bb498a --- /dev/null +++ b/pico_w/tcp_client/CMakeLists.txt @@ -0,0 +1,40 @@ +if (NOT TEST_TCP_SERVER_IP) + message("Skipping tcp_client example as TEST_TCP_SERVER_IP is not defined") +else() + add_executable(picow_tcpip_client_background + picow_tcp_client.c + ) + target_compile_definitions(picow_tcpip_client_background PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + TEST_TCP_SERVER_IP=\"${TEST_TCP_SERVER_IP}\" + ) + target_include_directories(picow_tcpip_client_background PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) + target_link_libraries(picow_tcpip_client_background + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + ) + + pico_add_extra_outputs(picow_tcpip_client_background) + + add_executable(picow_tcpip_client_poll + picow_tcp_client.c + ) + target_compile_definitions(picow_tcpip_client_poll PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + TEST_TCP_SERVER_IP=\"${TEST_TCP_SERVER_IP}\" + ) + target_include_directories(picow_tcpip_client_poll PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) + target_link_libraries(picow_tcpip_client_poll + pico_cyw43_arch_lwip_poll + pico_stdlib + ) + pico_add_extra_outputs(picow_tcpip_client_poll) +endif() diff --git a/pico_w/tcp_client/lwipopts.h b/pico_w/tcp_client/lwipopts.h new file mode 100644 index 0000000..8571ed5 --- /dev/null +++ b/pico_w/tcp_client/lwipopts.h @@ -0,0 +1,10 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#endif diff --git a/pico_w/tcp_client/picow_tcp_client.c b/pico_w/tcp_client/picow_tcp_client.c new file mode 100644 index 0000000..9d7ba3f --- /dev/null +++ b/pico_w/tcp_client/picow_tcp_client.c @@ -0,0 +1,254 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" + +#include "lwip/pbuf.h" +#include "lwip/tcp.h" + +#if !defined(TEST_TCP_SERVER_IP) +#error TEST_TCP_SERVER_IP not defined +#endif + +#define TCP_PORT 4242 +#define DEBUG_printf printf +#define BUF_SIZE 2048 + +#define TEST_ITERATIONS 10 +#define POLL_TIME_S 5 + +#if 0 +static void dump_bytes(const uint8_t *bptr, uint32_t len) { + unsigned int i = 0; + + printf("dump_bytes %d", len); + for (i = 0; i < len;) { + if ((i & 0x0f) == 0) { + printf("\n"); + } else if ((i & 0x07) == 0) { + printf(" "); + } + printf("%02x ", bptr[i++]); + } + printf("\n"); +} +#define DUMP_BYTES dump_bytes +#else +#define DUMP_BYTES(A,B) +#endif + +typedef struct TCP_CLIENT_T_ { + struct tcp_pcb *tcp_pcb; + ip_addr_t remote_addr; + uint8_t buffer[BUF_SIZE]; + int buffer_len; + int sent_len; + bool complete; + int run_count; + bool connected; +} TCP_CLIENT_T; + +static err_t tcp_client_close(void *arg) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + err_t err = ERR_OK; + if (state->tcp_pcb != NULL) { + tcp_arg(state->tcp_pcb, NULL); + tcp_poll(state->tcp_pcb, NULL, 0); + tcp_sent(state->tcp_pcb, NULL); + tcp_recv(state->tcp_pcb, NULL); + tcp_err(state->tcp_pcb, NULL); + err = tcp_close(state->tcp_pcb); + if (err != ERR_OK) { + DEBUG_printf("close failed %d, calling abort\n", err); + tcp_abort(state->tcp_pcb); + err = ERR_ABRT; + } + state->tcp_pcb = NULL; + } + return err; +} + +// Called with results of operation +static err_t tcp_result(void *arg, int status) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + if (status == 0) { + DEBUG_printf("test success\n"); + } else { + DEBUG_printf("test failed %d\n", status); + } + state->complete = true; + return tcp_client_close(arg); +} + +static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + DEBUG_printf("tcp_client_sent %u\n", len); + state->sent_len += len; + + if (state->sent_len >= BUF_SIZE) { + + state->run_count++; + if (state->run_count >= TEST_ITERATIONS) { + tcp_result(arg, 0); + return ERR_OK; + } + + // We should receive a new buffer from the server + state->buffer_len = 0; + state->sent_len = 0; + DEBUG_printf("Waiting for buffer from server\n"); + } + + return ERR_OK; +} + +static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + if (err != ERR_OK) { + printf("connect failed %d\n", err); + return tcp_result(arg, err); + } + state->connected = true; + DEBUG_printf("Waiting for buffer from server\n"); + return ERR_OK; +} + +static err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb) { + DEBUG_printf("tcp_client_poll\n"); + return tcp_result(arg, -1); // no response is an error? +} + +static void tcp_client_err(void *arg, err_t err) { + if (err != ERR_ABRT) { + DEBUG_printf("tcp_client_err %d\n", err); + tcp_result(arg, err); + } +} + +err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + if (!p) { + return tcp_result(arg, -1); + } + // this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you + // can use this method to cause an assertion in debug mode, if this method is called when + // cyw43_arch_lwip_begin IS needed + cyw43_arch_lwip_check(); + if (p->tot_len > 0) { + DEBUG_printf("recv %d err %d\n", p->tot_len, err); + for (struct pbuf *q = p; q != NULL; q = q->next) { + DUMP_BYTES(q->payload, q->len); + } + // Receive the buffer + const uint16_t buffer_left = BUF_SIZE - state->buffer_len; + state->buffer_len += pbuf_copy_partial(p, state->buffer + state->buffer_len, + p->tot_len > buffer_left ? buffer_left : p->tot_len, 0); + tcp_recved(tpcb, p->tot_len); + } + pbuf_free(p); + + // If we have received the whole buffer, send it back to the server + if (state->buffer_len == BUF_SIZE) { + DEBUG_printf("Writing %d bytes to server\n", state->buffer_len); + err_t err = tcp_write(tpcb, state->buffer, state->buffer_len, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + DEBUG_printf("Failed to write data %d\n", err); + return tcp_result(arg, -1); + } + } + return ERR_OK; +} + +static bool tcp_client_open(void *arg) { + TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg; + DEBUG_printf("Connecting to %s port %u\n", ip4addr_ntoa(&state->remote_addr), TCP_PORT); + state->tcp_pcb = tcp_new_ip_type(IP_GET_TYPE(&state->remote_addr)); + if (!state->tcp_pcb) { + DEBUG_printf("failed to create pcb\n"); + return false; + } + + tcp_arg(state->tcp_pcb, state); + tcp_poll(state->tcp_pcb, tcp_client_poll, POLL_TIME_S * 2); + tcp_sent(state->tcp_pcb, tcp_client_sent); + tcp_recv(state->tcp_pcb, tcp_client_recv); + tcp_err(state->tcp_pcb, tcp_client_err); + + state->buffer_len = 0; + + // 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(); + err_t err = tcp_connect(state->tcp_pcb, &state->remote_addr, TCP_PORT, tcp_client_connected); + cyw43_arch_lwip_end(); + + return err == ERR_OK; +} + +// Perform initialisation +static TCP_CLIENT_T* tcp_client_init(void) { + TCP_CLIENT_T *state = calloc(1, sizeof(TCP_CLIENT_T)); + if (!state) { + DEBUG_printf("failed to allocate state\n"); + return NULL; + } + ip4addr_aton(TEST_TCP_SERVER_IP, &state->remote_addr); + return state; +} + +void run_tcp_client_test(void) { + TCP_CLIENT_T *state = tcp_client_init(); + if (!state) { + return; + } + if (!tcp_client_open(state)) { + tcp_result(state, -1); + return; + } + while(!state->complete) { + // 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 + // if you are using pico_cyw43_arch_poll, then you must poll periodically from your + // main loop (not from a timer) to check for WiFi driver or lwIP work that needs to be done. + cyw43_arch_poll(); + sleep_ms(1); +#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) + // work you might be doing. + sleep_ms(1000); +#endif + } + free(state); +} + +int main() { + stdio_init_all(); + + if (cyw43_arch_init()) { + DEBUG_printf("failed to initialise\n"); + return 1; + } + cyw43_arch_enable_sta_mode(); + + printf("Connecting to WiFi...\n"); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("failed to connect.\n"); + return 1; + } else { + printf("Connected.\n"); + } + run_tcp_client_test(); + cyw43_arch_deinit(); + return 0; +} \ No newline at end of file diff --git a/pico_w/tcp_server/CMakeLists.txt b/pico_w/tcp_server/CMakeLists.txt new file mode 100644 index 0000000..3e8a484 --- /dev/null +++ b/pico_w/tcp_server/CMakeLists.txt @@ -0,0 +1,34 @@ +add_executable(picow_tcpip_server_background + picow_tcp_server.c + ) +target_compile_definitions(picow_tcpip_server_background PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) +target_include_directories(picow_tcpip_server_background PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) +target_link_libraries(picow_tcpip_server_background + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + ) + +pico_add_extra_outputs(picow_tcpip_server_background) + +add_executable(picow_tcpip_server_poll + picow_tcp_server.c + ) +target_compile_definitions(picow_tcpip_server_poll PRIVATE + WIFI_SSID=\"${WIFI_SSID}\" + WIFI_PASSWORD=\"${WIFI_PASSWORD}\" + ) +target_include_directories(picow_tcpip_server_poll PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) +target_link_libraries(picow_tcpip_server_poll + pico_cyw43_arch_lwip_poll + pico_stdlib + ) +pico_add_extra_outputs(picow_tcpip_server_poll) diff --git a/pico_w/tcp_server/lwipopts.h b/pico_w/tcp_server/lwipopts.h new file mode 100644 index 0000000..8571ed5 --- /dev/null +++ b/pico_w/tcp_server/lwipopts.h @@ -0,0 +1,10 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#endif diff --git a/pico_w/tcp_server/picow_tcp_server.c b/pico_w/tcp_server/picow_tcp_server.c new file mode 100644 index 0000000..30eb952 --- /dev/null +++ b/pico_w/tcp_server/picow_tcp_server.c @@ -0,0 +1,266 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" + +#include "lwip/pbuf.h" +#include "lwip/tcp.h" + +#define TCP_PORT 4242 +#define DEBUG_printf printf +#define BUF_SIZE 2048 +#define TEST_ITERATIONS 10 +#define POLL_TIME_S 5 + +typedef struct TCP_SERVER_T_ { + struct tcp_pcb *server_pcb; + struct tcp_pcb *client_pcb; + bool complete; + uint8_t buffer_sent[BUF_SIZE]; + uint8_t buffer_recv[BUF_SIZE]; + int sent_len; + int recv_len; + int run_count; +} TCP_SERVER_T; + +static TCP_SERVER_T* tcp_server_init(void) { + TCP_SERVER_T *state = calloc(1, sizeof(TCP_SERVER_T)); + if (!state) { + DEBUG_printf("failed to allocate state\n"); + return NULL; + } + return state; +} + +static err_t tcp_server_close(void *arg) { + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + err_t err = ERR_OK; + if (state->client_pcb != NULL) { + tcp_arg(state->client_pcb, NULL); + tcp_poll(state->client_pcb, NULL, 0); + tcp_sent(state->client_pcb, NULL); + tcp_recv(state->client_pcb, NULL); + tcp_err(state->client_pcb, NULL); + err = tcp_close(state->client_pcb); + if (err != ERR_OK) { + DEBUG_printf("close failed %d, calling abort\n", err); + tcp_abort(state->client_pcb); + err = ERR_ABRT; + } + state->client_pcb = NULL; + } + if (state->server_pcb) { + tcp_arg(state->server_pcb, NULL); + tcp_close(state->server_pcb); + state->server_pcb = NULL; + } + return err; +} + +static err_t tcp_server_result(void *arg, int status) { + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + if (status == 0) { + DEBUG_printf("test success\n"); + } else { + DEBUG_printf("test failed %d\n", status); + } + state->complete = true; + return tcp_server_close(arg); +} + +static err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) { + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + DEBUG_printf("tcp_server_sent %u\n", len); + state->sent_len += len; + + if (state->sent_len >= BUF_SIZE) { + + // We should get the data back from the client + state->recv_len = 0; + DEBUG_printf("Waiting for buffer from client\n"); + } + + return ERR_OK; +} + +err_t tcp_server_send_data(void *arg, struct tcp_pcb *tpcb) +{ + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + for(int i=0; i< BUF_SIZE; i++) { + state->buffer_sent[i] = rand(); + } + + state->sent_len = 0; + DEBUG_printf("Writing %ld bytes to client\n", BUF_SIZE); + // this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you + // can use this method to cause an assertion in debug mode, if this method is called when + // cyw43_arch_lwip_begin IS needed + cyw43_arch_lwip_check(); + err_t err = tcp_write(tpcb, state->buffer_sent, BUF_SIZE, TCP_WRITE_FLAG_COPY); + if (err != ERR_OK) { + DEBUG_printf("Failed to write data %d\n", err); + return tcp_server_result(arg, -1); + } + return ERR_OK; +} + +err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + if (!p) { + return tcp_server_result(arg, -1); + } + // this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you + // can use this method to cause an assertion in debug mode, if this method is called when + // cyw43_arch_lwip_begin IS needed + cyw43_arch_lwip_check(); + if (p->tot_len > 0) { + DEBUG_printf("tcp_server_recv %d/%d err %d\n", p->tot_len, state->recv_len, err); + + // Receive the buffer + const uint16_t buffer_left = BUF_SIZE - state->recv_len; + state->recv_len += pbuf_copy_partial(p, state->buffer_recv + state->recv_len, + p->tot_len > buffer_left ? buffer_left : p->tot_len, 0); + tcp_recved(tpcb, p->tot_len); + } + pbuf_free(p); + + // Have we have received the whole buffer + if (state->recv_len == BUF_SIZE) { + + // check it matches + if (memcmp(state->buffer_sent, state->buffer_recv, BUF_SIZE) != 0) { + DEBUG_printf("buffer mismatch\n"); + return tcp_server_result(arg, -1); + } + DEBUG_printf("tcp_server_recv buffer ok\n"); + + // Test complete? + state->run_count++; + if (state->run_count >= TEST_ITERATIONS) { + tcp_server_result(arg, 0); + return ERR_OK; + } + + // Send another buffer + return tcp_server_send_data(arg, state->client_pcb); + } + return ERR_OK; +} + +static err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb) { + DEBUG_printf("tcp_server_poll_fn\n"); + return tcp_server_result(arg, -1); // no response is an error? +} + +static void tcp_server_err(void *arg, err_t err) { + if (err != ERR_ABRT) { + DEBUG_printf("tcp_client_err_fn %d\n", err); + tcp_server_result(arg, err); + } +} + +static err_t tcp_server_accept(void *arg, struct tcp_pcb *client_pcb, err_t err) { + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + if (err != ERR_OK || client_pcb == NULL) { + DEBUG_printf("Failure in accept\n"); + tcp_server_result(arg, err); + return ERR_VAL; + } + DEBUG_printf("Client connected\n"); + + state->client_pcb = client_pcb; + tcp_arg(client_pcb, state); + tcp_sent(client_pcb, tcp_server_sent); + tcp_recv(client_pcb, tcp_server_recv); + tcp_poll(client_pcb, tcp_server_poll, POLL_TIME_S * 2); + tcp_err(client_pcb, tcp_server_err); + + return tcp_server_send_data(arg, state->client_pcb); +} + +static bool tcp_server_open(void *arg) { + TCP_SERVER_T *state = (TCP_SERVER_T*)arg; + DEBUG_printf("Starting server at %s on port %u\n", ip4addr_ntoa(netif_ip4_addr(netif_list)), TCP_PORT); + + struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_ANY); + if (!pcb) { + DEBUG_printf("failed to create pcb\n"); + return false; + } + + err_t err = tcp_bind(pcb, NULL, TCP_PORT); + if (err) { + DEBUG_printf("failed to bind to port %d\n"); + return false; + } + + state->server_pcb = tcp_listen_with_backlog(pcb, 1); + if (!state->server_pcb) { + DEBUG_printf("failed to listen\n"); + if (pcb) { + tcp_close(pcb); + } + return false; + } + + tcp_arg(state->server_pcb, state); + tcp_accept(state->server_pcb, tcp_server_accept); + + return true; +} + +void run_tcp_server_test(void) { + TCP_SERVER_T *state = tcp_server_init(); + if (!state) { + return; + } + if (!tcp_server_open(state)) { + tcp_server_result(state, -1); + return; + } + while(!state->complete) { + // 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 + // if you are using pico_cyw43_arch_poll, then you must poll periodically from your + // main loop (not from a timer) to check for WiFi driver or lwIP work that needs to be done. + cyw43_arch_poll(); + sleep_ms(1); +#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) + // work you might be doing. + sleep_ms(1000); +#endif + } + free(state); +} + +int main() { + stdio_init_all(); + + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + + cyw43_arch_enable_sta_mode(); + + printf("Connecting to WiFi...\n"); + if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) { + printf("failed to connect.\n"); + return 1; + } else { + printf("Connected.\n"); + } + run_tcp_server_test(); + cyw43_arch_deinit(); + return 0; +} \ No newline at end of file diff --git a/pico_w/wifi_scan/CMakeLists.txt b/pico_w/wifi_scan/CMakeLists.txt new file mode 100644 index 0000000..ba0cd4f --- /dev/null +++ b/pico_w/wifi_scan/CMakeLists.txt @@ -0,0 +1,27 @@ +add_executable(picow_wifi_scan_background + picow_wifi_scan.c + ) +target_include_directories(picow_wifi_scan_background PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) +target_link_libraries(picow_wifi_scan_background + pico_cyw43_arch_lwip_threadsafe_background + pico_stdlib + ) + +pico_add_extra_outputs(picow_wifi_scan_background) + +add_executable(picow_wifi_scan_poll + picow_wifi_scan.c + ) +target_include_directories(picow_wifi_scan_poll PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts + ) +target_link_libraries(picow_wifi_scan_poll + pico_cyw43_arch_lwip_poll + pico_stdlib + ) +pico_add_extra_outputs(picow_wifi_scan_poll) + diff --git a/pico_w/wifi_scan/lwipopts.h b/pico_w/wifi_scan/lwipopts.h new file mode 100644 index 0000000..8571ed5 --- /dev/null +++ b/pico_w/wifi_scan/lwipopts.h @@ -0,0 +1,10 @@ +#ifndef _LWIPOPTS_H +#define _LWIPOPTS_H + +// Generally you would define your own explicit list of lwIP options +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html) +// +// This example uses a common include to avoid repetition +#include "lwipopts_examples_common.h" + +#endif diff --git a/pico_w/wifi_scan/picow_wifi_scan.c b/pico_w/wifi_scan/picow_wifi_scan.c new file mode 100644 index 0000000..082b23e --- /dev/null +++ b/pico_w/wifi_scan/picow_wifi_scan.c @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2022 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" + +static int scan_result(void *env, const cyw43_ev_scan_result_t *result) { + if (result) { + printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %u\n", + result->ssid, result->rssi, result->channel, + result->bssid[0], result->bssid[1], result->bssid[2], result->bssid[3], result->bssid[4], result->bssid[5], + result->auth_mode); + } + return 0; +} + +#include "hardware/vreg.h" +#include "hardware/clocks.h" + +int main() { + stdio_init_all(); + + if (cyw43_arch_init()) { + printf("failed to initialise\n"); + return 1; + } + + cyw43_arch_enable_sta_mode(); + + absolute_time_t scan_test = nil_time; + bool scan_in_progress = false; + while(true) { + if (absolute_time_diff_us(get_absolute_time(), scan_test) < 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_test = make_timeout_time_ms(10000); // wait 10s and scan again + } + } else if (!cyw43_wifi_scan_active(&cyw43_state)) { + scan_test = make_timeout_time_ms(10000); // wait 10s and scan again + scan_in_progress = false; + } + } + // 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 + // if you are using pico_cyw43_arch_poll, then you must poll periodically from your + // main loop (not from a timer) to check for WiFi driver or lwIP work that needs to be done. + cyw43_arch_poll(); + sleep_ms(1); +#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) + // work you might be doing. + sleep_ms(1000); +#endif + } + + cyw43_arch_deinit(); + return 0; +} diff --git a/usb/CMakeLists.txt b/usb/CMakeLists.txt index 2d79b6b..89e0b98 100644 --- a/usb/CMakeLists.txt +++ b/usb/CMakeLists.txt @@ -1,10 +1,10 @@ if (TARGET tinyusb_device) add_subdirectory(device) else () - message("Skipping TinyUSB device examples, as TinyUSB unavailable") + message("Skipping TinyUSB device examples as TinyUSB is unavailable") endif () if (TARGET tinyusb_host) add_subdirectory(host) else () - message("Skipping TinyUSB host examples, as TinyUSB unavailable") + message("Skipping TinyUSB host examples as TinyUSB is unavailable") endif () \ No newline at end of file