Add Bluetooth examples
Co-authored-by: Peter Harper <77111776+peterharperuk@users.noreply.github.com>
This commit is contained in:
25
pico_w/wifi/CMakeLists.txt
Normal file
25
pico_w/wifi/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
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)
|
||||
add_subdirectory(udp_beacon)
|
||||
|
||||
if (NOT PICO_MBEDTLS_PATH)
|
||||
message("Skipping tls examples as PICO_MBEDTLS_PATH is not defined")
|
||||
else()
|
||||
add_subdirectory(tls_client)
|
||||
endif()
|
||||
endif()
|
||||
36
pico_w/wifi/access_point/CMakeLists.txt
Normal file
36
pico_w/wifi/access_point/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
add_executable(picow_access_point_background
|
||||
picow_access_point.c
|
||||
dhcpserver/dhcpserver.c
|
||||
dnsserver/dnsserver.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
|
||||
${CMAKE_CURRENT_LIST_DIR}/dnsserver
|
||||
)
|
||||
|
||||
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
|
||||
dnsserver/dnsserver.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
|
||||
${CMAKE_CURRENT_LIST_DIR}/dnsserver
|
||||
)
|
||||
target_link_libraries(picow_access_point_poll
|
||||
pico_cyw43_arch_lwip_poll
|
||||
pico_stdlib
|
||||
)
|
||||
pico_add_extra_outputs(picow_access_point_poll)
|
||||
21
pico_w/wifi/access_point/dhcpserver/LICENSE
Normal file
21
pico_w/wifi/access_point/dhcpserver/LICENSE
Normal file
@@ -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.
|
||||
303
pico_w/wifi/access_point/dhcpserver/dhcpserver.c
Normal file
303
pico_w/wifi/access_point/dhcpserver/dhcpserver.c
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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_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, uint16_t port) {
|
||||
// TODO convert lwIP errors to errno
|
||||
return udp_bind(*udp, IP_ANY_TYPE, 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(ip_2_ip4(&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, const 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, &ip4_addr_get_u32(ip_2_ip4(&d->ip)), 4);
|
||||
|
||||
uint8_t *opt = (uint8_t *)&dhcp_msg.options;
|
||||
opt += 4; // assume magic cookie: 99, 130, 83, 99
|
||||
|
||||
uint8_t *msgtype = opt_find(opt, DHCP_OPT_MSG_TYPE);
|
||||
if (msgtype == NULL) {
|
||||
// A DHCP package without MSG_TYPE?
|
||||
goto ignore_request;
|
||||
}
|
||||
|
||||
switch (msgtype[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, &ip4_addr_get_u32(ip_2_ip4(&d->ip)), 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, &ip4_addr_get_u32(ip_2_ip4(&d->ip)));
|
||||
opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &ip4_addr_get_u32(ip_2_ip4(&d->nm)));
|
||||
opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &ip4_addr_get_u32(ip_2_ip4(&d->ip))); // aka gateway; can have mulitple addresses
|
||||
opt_write_n(&opt, DHCP_OPT_DNS, 4, &ip4_addr_get_u32(ip_2_ip4(&d->ip))); // this server is the dns
|
||||
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, PORT_DHCP_SERVER);
|
||||
}
|
||||
|
||||
void dhcp_server_deinit(dhcp_server_t *d) {
|
||||
dhcp_socket_free(&d->udp);
|
||||
}
|
||||
49
pico_w/wifi/access_point/dhcpserver/dhcpserver.h
Normal file
49
pico_w/wifi/access_point/dhcpserver/dhcpserver.h
Normal file
@@ -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
|
||||
235
pico_w/wifi/access_point/dnsserver/dnsserver.c
Normal file
235
pico_w/wifi/access_point/dnsserver/dnsserver.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dnsserver.h"
|
||||
#include "lwip/udp.h"
|
||||
|
||||
#define PORT_DNS_SERVER 53
|
||||
#define DUMP_DATA 0
|
||||
|
||||
#define DEBUG_printf(...)
|
||||
#define ERROR_printf printf
|
||||
|
||||
typedef struct dns_header_t_ {
|
||||
uint16_t id;
|
||||
uint16_t flags;
|
||||
uint16_t question_count;
|
||||
uint16_t answer_record_count;
|
||||
uint16_t authority_record_count;
|
||||
uint16_t additional_record_count;
|
||||
} dns_header_t;
|
||||
|
||||
#define MAX_DNS_MSG_SIZE 300
|
||||
|
||||
static int dns_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) {
|
||||
*udp = udp_new();
|
||||
if (*udp == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
udp_recv(*udp, cb_udp_recv, (void *)cb_data);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void dns_socket_free(struct udp_pcb **udp) {
|
||||
if (*udp != NULL) {
|
||||
udp_remove(*udp);
|
||||
*udp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dns_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);
|
||||
err_t err = udp_bind(*udp, &addr, port);
|
||||
if (err != ERR_OK) {
|
||||
ERROR_printf("dns failed to bind to port %u: %d", port, err);
|
||||
assert(false);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
#if DUMP_DATA
|
||||
static void dump_bytes(const uint8_t *bptr, uint32_t len) {
|
||||
unsigned int i = 0;
|
||||
|
||||
for (i = 0; i < len;) {
|
||||
if ((i & 0x0f) == 0) {
|
||||
printf("\n");
|
||||
} else if ((i & 0x07) == 0) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%02x ", bptr[i++]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static int dns_socket_sendto(struct udp_pcb **udp, const void *buf, size_t len, const ip_addr_t *dest, uint16_t port) {
|
||||
if (len > 0xffff) {
|
||||
len = 0xffff;
|
||||
}
|
||||
|
||||
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
|
||||
if (p == NULL) {
|
||||
ERROR_printf("DNS: Failed to send message out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(p->payload, buf, len);
|
||||
err_t err = udp_sendto(*udp, p, dest, port);
|
||||
|
||||
pbuf_free(p);
|
||||
|
||||
if (err != ERR_OK) {
|
||||
ERROR_printf("DNS: Failed to send message %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if DUMP_DATA
|
||||
dump_bytes(buf, len);
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
static void dns_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) {
|
||||
dns_server_t *d = arg;
|
||||
DEBUG_printf("dns_server_process %u\n", p->tot_len);
|
||||
|
||||
uint8_t dns_msg[MAX_DNS_MSG_SIZE];
|
||||
dns_header_t *dns_hdr = (dns_header_t*)dns_msg;
|
||||
|
||||
size_t msg_len = pbuf_copy_partial(p, dns_msg, sizeof(dns_msg), 0);
|
||||
if (msg_len < sizeof(dns_header_t)) {
|
||||
goto ignore_request;
|
||||
}
|
||||
|
||||
#if DUMP_DATA
|
||||
dump_bytes(dns_msg, msg_len);
|
||||
#endif
|
||||
|
||||
uint16_t flags = lwip_ntohs(dns_hdr->flags);
|
||||
uint16_t question_count = lwip_ntohs(dns_hdr->question_count);
|
||||
|
||||
DEBUG_printf("len %d\n", msg_len);
|
||||
DEBUG_printf("dns flags 0x%x\n", flags);
|
||||
DEBUG_printf("dns question count 0x%x\n", question_count);
|
||||
|
||||
// flags from rfc1035
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|
||||
// Check QR indicates a query
|
||||
if (((flags >> 15) & 0x1) != 0) {
|
||||
DEBUG_printf("Ignoring non-query\n");
|
||||
goto ignore_request;
|
||||
}
|
||||
|
||||
// Check for standard query
|
||||
if (((flags >> 11) & 0xf) != 0) {
|
||||
DEBUG_printf("Ignoring non-standard query\n");
|
||||
goto ignore_request;
|
||||
}
|
||||
|
||||
// Check question count
|
||||
if (question_count < 1) {
|
||||
DEBUG_printf("Invalid question count\n");
|
||||
goto ignore_request;
|
||||
}
|
||||
|
||||
// Print the question
|
||||
DEBUG_printf("question: ");
|
||||
const uint8_t *question_ptr_start = dns_msg + sizeof(dns_header_t);
|
||||
const uint8_t *question_ptr_end = dns_msg + msg_len;
|
||||
const uint8_t *question_ptr = question_ptr_start;
|
||||
while(question_ptr < question_ptr_end) {
|
||||
if (*question_ptr == 0) {
|
||||
question_ptr++;
|
||||
break;
|
||||
} else {
|
||||
if (question_ptr > question_ptr_start) {
|
||||
DEBUG_printf(".");
|
||||
}
|
||||
int label_len = *question_ptr++;
|
||||
if (label_len > 63) {
|
||||
DEBUG_printf("Invalid label\n");
|
||||
goto ignore_request;
|
||||
}
|
||||
DEBUG_printf("%.*s", label_len, question_ptr);
|
||||
question_ptr += label_len;
|
||||
}
|
||||
}
|
||||
DEBUG_printf("\n");
|
||||
|
||||
// Check question length
|
||||
if (question_ptr - question_ptr_start > 255) {
|
||||
DEBUG_printf("Invalid question length\n");
|
||||
goto ignore_request;
|
||||
}
|
||||
|
||||
// Skip QNAME and QTYPE
|
||||
question_ptr += 4;
|
||||
|
||||
// Generate answer
|
||||
uint8_t *answer_ptr = dns_msg + (question_ptr - dns_msg);
|
||||
*answer_ptr++ = 0xc0; // pointer
|
||||
*answer_ptr++ = question_ptr_start - dns_msg; // pointer to question
|
||||
|
||||
*answer_ptr++ = 0;
|
||||
*answer_ptr++ = 1; // host address
|
||||
|
||||
*answer_ptr++ = 0;
|
||||
*answer_ptr++ = 1; // Internet class
|
||||
|
||||
*answer_ptr++ = 0;
|
||||
*answer_ptr++ = 0;
|
||||
*answer_ptr++ = 0;
|
||||
*answer_ptr++ = 60; // ttl 60s
|
||||
|
||||
*answer_ptr++ = 0;
|
||||
*answer_ptr++ = 4; // length
|
||||
memcpy(answer_ptr, &d->ip.addr, 4); // use our address
|
||||
answer_ptr += 4;
|
||||
|
||||
dns_hdr->flags = lwip_htons(
|
||||
0x1 << 15 | // QR = response
|
||||
0x1 << 10 | // AA = authoritive
|
||||
0x1 << 7); // RA = authenticated
|
||||
dns_hdr->question_count = lwip_htons(1);
|
||||
dns_hdr->answer_record_count = lwip_htons(1);
|
||||
dns_hdr->authority_record_count = 0;
|
||||
dns_hdr->additional_record_count = 0;
|
||||
|
||||
// Send the reply
|
||||
DEBUG_printf("Sending %d byte reply to %s:%d\n", answer_ptr - dns_msg, ipaddr_ntoa(src_addr), src_port);
|
||||
dns_socket_sendto(&d->udp, &dns_msg, answer_ptr - dns_msg, src_addr, src_port);
|
||||
|
||||
ignore_request:
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
void dns_server_init(dns_server_t *d, ip_addr_t *ip) {
|
||||
if (dns_socket_new_dgram(&d->udp, d, dns_server_process) != ERR_OK) {
|
||||
DEBUG_printf("dns server failed to start\n");
|
||||
return;
|
||||
}
|
||||
if (dns_socket_bind(&d->udp, 0, PORT_DNS_SERVER) != ERR_OK) {
|
||||
DEBUG_printf("dns server failed to bind\n");
|
||||
return;
|
||||
}
|
||||
ip_addr_copy(d->ip, *ip);
|
||||
DEBUG_printf("dns server listening on port %d\n", PORT_DNS_SERVER);
|
||||
}
|
||||
|
||||
void dns_server_deinit(dns_server_t *d) {
|
||||
dns_socket_free(&d->udp);
|
||||
}
|
||||
20
pico_w/wifi/access_point/dnsserver/dnsserver.h
Normal file
20
pico_w/wifi/access_point/dnsserver/dnsserver.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _DNSSERVER_H_
|
||||
#define _DNSSERVER_H_
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
|
||||
typedef struct dns_server_t_ {
|
||||
struct udp_pcb *udp;
|
||||
ip_addr_t ip;
|
||||
} dns_server_t;
|
||||
|
||||
void dns_server_init(dns_server_t *d, ip_addr_t *ip);
|
||||
void dns_server_deinit(dns_server_t *d);
|
||||
|
||||
#endif
|
||||
10
pico_w/wifi/access_point/lwipopts.h
Normal file
10
pico_w/wifi/access_point/lwipopts.h
Normal file
@@ -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
|
||||
330
pico_w/wifi/access_point/picow_access_point.c
Normal file
330
pico_w/wifi/access_point/picow_access_point.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/tcp.h"
|
||||
|
||||
#include "dhcpserver.h"
|
||||
#include "dnsserver.h"
|
||||
|
||||
#define TCP_PORT 80
|
||||
#define DEBUG_printf printf
|
||||
#define POLL_TIME_S 5
|
||||
#define HTTP_GET "GET"
|
||||
#define HTTP_RESPONSE_HEADERS "HTTP/1.1 %d OK\nContent-Length: %d\nContent-Type: text/html; charset=utf-8\nConnection: close\n\n"
|
||||
#define LED_TEST_BODY "<html><body><h1>Hello from Pico W.</h1><p>Led is %s</p><p><a href=\"?led=%d\">Turn led %s</a></body></html>"
|
||||
#define LED_PARAM "led=%d"
|
||||
#define LED_TEST "/ledtest"
|
||||
#define LED_GPIO 0
|
||||
#define HTTP_RESPONSE_REDIRECT "HTTP/1.1 302 Redirect\nLocation: http://%s" LED_TEST "\n\n"
|
||||
|
||||
typedef struct TCP_SERVER_T_ {
|
||||
struct tcp_pcb *server_pcb;
|
||||
bool complete;
|
||||
ip_addr_t gw;
|
||||
} TCP_SERVER_T;
|
||||
|
||||
typedef struct TCP_CONNECT_STATE_T_ {
|
||||
struct tcp_pcb *pcb;
|
||||
int sent_len;
|
||||
char headers[128];
|
||||
char result[256];
|
||||
int header_len;
|
||||
int result_len;
|
||||
ip_addr_t *gw;
|
||||
} TCP_CONNECT_STATE_T;
|
||||
|
||||
static err_t tcp_close_client_connection(TCP_CONNECT_STATE_T *con_state, struct tcp_pcb *client_pcb, err_t close_err) {
|
||||
if (client_pcb) {
|
||||
assert(con_state && con_state->pcb == client_pcb);
|
||||
tcp_arg(client_pcb, NULL);
|
||||
tcp_poll(client_pcb, NULL, 0);
|
||||
tcp_sent(client_pcb, NULL);
|
||||
tcp_recv(client_pcb, NULL);
|
||||
tcp_err(client_pcb, NULL);
|
||||
err_t err = tcp_close(client_pcb);
|
||||
if (err != ERR_OK) {
|
||||
DEBUG_printf("close failed %d, calling abort\n", err);
|
||||
tcp_abort(client_pcb);
|
||||
close_err = ERR_ABRT;
|
||||
}
|
||||
if (con_state) {
|
||||
free(con_state);
|
||||
}
|
||||
}
|
||||
return close_err;
|
||||
}
|
||||
|
||||
static void tcp_server_close(TCP_SERVER_T *state) {
|
||||
if (state->server_pcb) {
|
||||
tcp_arg(state->server_pcb, NULL);
|
||||
tcp_close(state->server_pcb);
|
||||
state->server_pcb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static err_t tcp_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len) {
|
||||
TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg;
|
||||
DEBUG_printf("tcp_server_sent %u\n", len);
|
||||
con_state->sent_len += len;
|
||||
if (con_state->sent_len >= con_state->header_len + con_state->result_len) {
|
||||
DEBUG_printf("all done\n");
|
||||
return tcp_close_client_connection(con_state, pcb, ERR_OK);
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static int test_server_content(const char *request, const char *params, char *result, size_t max_result_len) {
|
||||
int len = 0;
|
||||
if (strncmp(request, LED_TEST, sizeof(LED_TEST) - 1) == 0) {
|
||||
// Get the state of the led
|
||||
bool value;
|
||||
cyw43_gpio_get(&cyw43_state, LED_GPIO, &value);
|
||||
int led_state = value;
|
||||
|
||||
// See if the user changed it
|
||||
if (params) {
|
||||
int led_param = sscanf(params, LED_PARAM, &led_state);
|
||||
if (led_param == 1) {
|
||||
if (led_state) {
|
||||
// Turn led on
|
||||
cyw43_gpio_set(&cyw43_state, 0, true);
|
||||
} else {
|
||||
// Turn led off
|
||||
cyw43_gpio_set(&cyw43_state, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Generate result
|
||||
if (led_state) {
|
||||
len = snprintf(result, max_result_len, LED_TEST_BODY, "ON", 0, "OFF");
|
||||
} else {
|
||||
len = snprintf(result, max_result_len, LED_TEST_BODY, "OFF", 1, "ON");
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
err_t tcp_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
|
||||
TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg;
|
||||
if (!p) {
|
||||
DEBUG_printf("connection closed\n");
|
||||
return tcp_close_client_connection(con_state, pcb, ERR_OK);
|
||||
}
|
||||
assert(con_state && con_state->pcb == pcb);
|
||||
if (p->tot_len > 0) {
|
||||
DEBUG_printf("tcp_server_recv %d err %d\n", p->tot_len, err);
|
||||
#if 0
|
||||
for (struct pbuf *q = p; q != NULL; q = q->next) {
|
||||
DEBUG_printf("in: %.*s\n", q->len, q->payload);
|
||||
}
|
||||
#endif
|
||||
// Copy the request into the buffer
|
||||
pbuf_copy_partial(p, con_state->headers, p->tot_len > sizeof(con_state->headers) - 1 ? sizeof(con_state->headers) - 1 : p->tot_len, 0);
|
||||
|
||||
// Handle GET request
|
||||
if (strncmp(HTTP_GET, con_state->headers, sizeof(HTTP_GET) - 1) == 0) {
|
||||
char *request = con_state->headers + sizeof(HTTP_GET); // + space
|
||||
char *params = strchr(request, '?');
|
||||
if (params) {
|
||||
if (*params) {
|
||||
char *space = strchr(request, ' ');
|
||||
*params++ = 0;
|
||||
if (space) {
|
||||
*space = 0;
|
||||
}
|
||||
} else {
|
||||
params = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate content
|
||||
con_state->result_len = test_server_content(request, params, con_state->result, sizeof(con_state->result));
|
||||
DEBUG_printf("Request: %s?%s\n", request, params);
|
||||
DEBUG_printf("Result: %d\n", con_state->result_len);
|
||||
|
||||
// Check we had enough buffer space
|
||||
if (con_state->result_len > sizeof(con_state->result) - 1) {
|
||||
DEBUG_printf("Too much result data %d\n", con_state->result_len);
|
||||
return tcp_close_client_connection(con_state, pcb, ERR_CLSD);
|
||||
}
|
||||
|
||||
// Generate web page
|
||||
if (con_state->result_len > 0) {
|
||||
con_state->header_len = snprintf(con_state->headers, sizeof(con_state->headers), HTTP_RESPONSE_HEADERS,
|
||||
200, con_state->result_len);
|
||||
if (con_state->header_len > sizeof(con_state->headers) - 1) {
|
||||
DEBUG_printf("Too much header data %d\n", con_state->header_len);
|
||||
return tcp_close_client_connection(con_state, pcb, ERR_CLSD);
|
||||
}
|
||||
} else {
|
||||
// Send redirect
|
||||
con_state->header_len = snprintf(con_state->headers, sizeof(con_state->headers), HTTP_RESPONSE_REDIRECT,
|
||||
ipaddr_ntoa(con_state->gw));
|
||||
DEBUG_printf("Sending redirect %s", con_state->headers);
|
||||
}
|
||||
|
||||
// Send the headers to the client
|
||||
con_state->sent_len = 0;
|
||||
err_t err = tcp_write(pcb, con_state->headers, con_state->header_len, 0);
|
||||
if (err != ERR_OK) {
|
||||
DEBUG_printf("failed to write header data %d\n", err);
|
||||
return tcp_close_client_connection(con_state, pcb, err);
|
||||
}
|
||||
|
||||
// Send the body to the client
|
||||
if (con_state->result_len) {
|
||||
err = tcp_write(pcb, con_state->result, con_state->result_len, 0);
|
||||
if (err != ERR_OK) {
|
||||
DEBUG_printf("failed to write result data %d\n", err);
|
||||
return tcp_close_client_connection(con_state, pcb, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
tcp_recved(pcb, p->tot_len);
|
||||
}
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static err_t tcp_server_poll(void *arg, struct tcp_pcb *pcb) {
|
||||
TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg;
|
||||
DEBUG_printf("tcp_server_poll_fn\n");
|
||||
return tcp_close_client_connection(con_state, pcb, ERR_OK); // Just disconnect clent?
|
||||
}
|
||||
|
||||
static void tcp_server_err(void *arg, err_t err) {
|
||||
TCP_CONNECT_STATE_T *con_state = (TCP_CONNECT_STATE_T*)arg;
|
||||
if (err != ERR_ABRT) {
|
||||
DEBUG_printf("tcp_client_err_fn %d\n", err);
|
||||
tcp_close_client_connection(con_state, con_state->pcb, 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");
|
||||
return ERR_VAL;
|
||||
}
|
||||
DEBUG_printf("client connected\n");
|
||||
|
||||
// Create the state for the connection
|
||||
TCP_CONNECT_STATE_T *con_state = calloc(1, sizeof(TCP_CONNECT_STATE_T));
|
||||
if (!con_state) {
|
||||
DEBUG_printf("failed to allocate connect state\n");
|
||||
return ERR_MEM;
|
||||
}
|
||||
con_state->pcb = client_pcb; // for checking
|
||||
con_state->gw = &state->gw;
|
||||
|
||||
// setup connection to client
|
||||
tcp_arg(client_pcb, con_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 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, IP_ANY_TYPE, 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()) {
|
||||
DEBUG_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 mask;
|
||||
IP4_ADDR(ip_2_ip4(&state->gw), 192, 168, 4, 1);
|
||||
IP4_ADDR(ip_2_ip4(&mask), 255, 255, 255, 0);
|
||||
|
||||
// Start the dhcp server
|
||||
dhcp_server_t dhcp_server;
|
||||
dhcp_server_init(&dhcp_server, &state->gw, &mask);
|
||||
|
||||
// Start the dns server
|
||||
dns_server_t dns_server;
|
||||
dns_server_init(&dns_server, &state->gw);
|
||||
|
||||
if (!tcp_server_open(state)) {
|
||||
DEBUG_printf("failed to open server\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
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 interrupt) to check for Wi-Fi driver or lwIP work that needs to be done.
|
||||
cyw43_arch_poll();
|
||||
// you can poll as often as you like, however if you have nothing else to do you can
|
||||
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
|
||||
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
|
||||
#else
|
||||
// if you are not using pico_cyw43_arch_poll, then Wi-FI 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
|
||||
}
|
||||
dns_server_deinit(&dns_server);
|
||||
dhcp_server_deinit(&dhcp_server);
|
||||
cyw43_arch_deinit();
|
||||
return 0;
|
||||
}
|
||||
14
pico_w/wifi/blink/CMakeLists.txt
Normal file
14
pico_w/wifi/blink/CMakeLists.txt
Normal file
@@ -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)
|
||||
22
pico_w/wifi/blink/picow_blink.c
Normal file
22
pico_w/wifi/blink/picow_blink.c
Normal file
@@ -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("Wi-Fi 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);
|
||||
}
|
||||
}
|
||||
8
pico_w/wifi/freertos/CMakeLists.txt
Normal file
8
pico_w/wifi/freertos/CMakeLists.txt
Normal file
@@ -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()
|
||||
62
pico_w/wifi/freertos/FreeRTOS_Kernel_import.cmake
Normal file
62
pico_w/wifi/freertos/FreeRTOS_Kernel_import.cmake
Normal file
@@ -0,0 +1,62 @@
|
||||
# This is a copy of <FREERTOS_KERNEL_PATH>/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)
|
||||
38
pico_w/wifi/freertos/iperf/CMakeLists.txt
Normal file
38
pico_w/wifi/freertos/iperf/CMakeLists.txt
Normal file
@@ -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)
|
||||
143
pico_w/wifi/freertos/iperf/FreeRTOSConfig.h
Normal file
143
pico_w/wifi/freertos/iperf/FreeRTOSConfig.h
Normal file
@@ -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 <assert.h>
|
||||
/* 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 */
|
||||
|
||||
25
pico_w/wifi/freertos/iperf/lwipopts.h
Normal file
25
pico_w/wifi/freertos/iperf/lwipopts.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#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
|
||||
|
||||
// ping_thread sets socket receive timeout, so enable this feature
|
||||
#define LWIP_SO_RCVTIMEO 1
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
134
pico_w/wifi/freertos/iperf/picow_freertos_iperf.c
Normal file
134
pico_w/wifi/freertos/iperf/picow_freertos_iperf.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* 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 Wi-Fi...\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);
|
||||
|
||||
cyw43_arch_lwip_begin();
|
||||
#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
|
||||
cyw43_arch_lwip_end();
|
||||
|
||||
while(true) {
|
||||
// not much to do as LED is in another task, and we're using RAW (callback) lwIP API
|
||||
vTaskDelay(10000);
|
||||
}
|
||||
|
||||
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_FREERTOS_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;
|
||||
}
|
||||
46
pico_w/wifi/freertos/ping/CMakeLists.txt
Normal file
46
pico_w/wifi/freertos/ping/CMakeLists.txt
Normal file
@@ -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()
|
||||
143
pico_w/wifi/freertos/ping/FreeRTOSConfig.h
Normal file
143
pico_w/wifi/freertos/ping/FreeRTOSConfig.h
Normal file
@@ -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 <assert.h>
|
||||
/* 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 */
|
||||
|
||||
21
pico_w/wifi/freertos/ping/lwipopts.h
Normal file
21
pico_w/wifi/freertos/ping/lwipopts.h
Normal file
@@ -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
|
||||
90
pico_w/wifi/freertos/ping/picow_freertos_ping.c
Normal file
90
pico_w/wifi/freertos/ping/picow_freertos_ping.c
Normal file
@@ -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 Wi-Fi...\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;
|
||||
ipaddr_aton(PING_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_FREERTOS_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;
|
||||
}
|
||||
36
pico_w/wifi/iperf/CMakeLists.txt
Normal file
36
pico_w/wifi/iperf/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
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)
|
||||
|
||||
10
pico_w/wifi/iperf/lwipopts.h
Normal file
10
pico_w/wifi/iperf/lwipopts.h
Normal file
@@ -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
|
||||
103
pico_w/wifi/iperf/picow_iperf.c
Normal file
103
pico_w/wifi/iperf/picow_iperf.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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 Wi-Fi...\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");
|
||||
}
|
||||
|
||||
cyw43_arch_lwip_begin();
|
||||
#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
|
||||
cyw43_arch_lwip_end();
|
||||
|
||||
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 interrupt) to check for Wi-Fi driver or lwIP work that needs to be done.
|
||||
cyw43_arch_poll();
|
||||
// you can poll as often as you like, however if you have nothing else to do you can
|
||||
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
|
||||
cyw43_arch_wait_for_work_until(led_time);
|
||||
#else
|
||||
// if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work
|
||||
// is done via interrupt in the background. This sleep is just an example of some (blocking)
|
||||
// work you might be doing.
|
||||
sleep_ms(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
cyw43_arch_deinit();
|
||||
return 0;
|
||||
}
|
||||
90
pico_w/wifi/lwipopts_examples_common.h
Normal file
90
pico_w/wifi/lwipopts_examples_common.h
Normal file
@@ -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__ */
|
||||
35
pico_w/wifi/ntp_client/CMakeLists.txt
Normal file
35
pico_w/wifi/ntp_client/CMakeLists.txt
Normal file
@@ -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)
|
||||
|
||||
10
pico_w/wifi/ntp_client/lwipopts.h
Normal file
10
pico_w/wifi/ntp_client/lwipopts.h
Normal file
@@ -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
|
||||
186
pico_w/wifi/ntp_client/picow_ntp_client.c
Normal file
186
pico_w/wifi/ntp_client/picow_ntp_client.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#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", ipaddr_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 interrupt) to check for Wi-Fi driver or lwIP work that needs to be done.
|
||||
cyw43_arch_poll();
|
||||
// you can poll as often as you like, however if you have nothing else to do you can
|
||||
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
|
||||
cyw43_arch_wait_for_work_until(state->dns_request_sent ? at_the_end_of_time : state->ntp_test_time);
|
||||
#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;
|
||||
}
|
||||
71
pico_w/wifi/python_test_tcp/micropython_test_tcp_client.py
Normal file
71
pico_w/wifi/python_test_tcp/micropython_test_tcp_client.py
Normal file
@@ -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")
|
||||
85
pico_w/wifi/python_test_tcp/micropython_test_tcp_server.py
Normal file
85
pico_w/wifi/python_test_tcp/micropython_test_tcp_server.py
Normal file
@@ -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")
|
||||
47
pico_w/wifi/python_test_tcp/python_test_tcp_client.py
Normal file
47
pico_w/wifi/python_test_tcp/python_test_tcp_client.py
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import socket
|
||||
import sys
|
||||
|
||||
# Check server ip address set
|
||||
if len(sys.argv) < 2:
|
||||
raise RuntimeError('pass IP address of the server')
|
||||
|
||||
# Set the server address here like 1.2.3.4
|
||||
SERVER_ADDR = sys.argv[1]
|
||||
|
||||
# These constants should match the server
|
||||
BUF_SIZE = 2048
|
||||
SERVER_PORT = 4242
|
||||
TEST_ITERATIONS = 10
|
||||
|
||||
# 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
|
||||
total_size = BUF_SIZE
|
||||
read_buf = b''
|
||||
while total_size > 0:
|
||||
buf = sock.recv(BUF_SIZE)
|
||||
print('read %d bytes from server' % len(buf))
|
||||
total_size -= len(buf)
|
||||
read_buf += buf
|
||||
|
||||
# Check size of data received
|
||||
if len(read_buf) != BUF_SIZE:
|
||||
raise RuntimeError('wrong amount of data read %d', len(read_buf))
|
||||
|
||||
# Send the data back to the server
|
||||
write_len = sock.send(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")
|
||||
61
pico_w/wifi/python_test_tcp/python_test_tcp_server.py
Normal file
61
pico_w/wifi/python_test_tcp/python_test_tcp_server.py
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import random
|
||||
import socket
|
||||
|
||||
# Set server adress to machines IP
|
||||
SERVER_ADDR = "0.0.0.0"
|
||||
|
||||
# These constants should match the client
|
||||
BUF_SIZE = 2048
|
||||
TEST_ITERATIONS = 10
|
||||
SERVER_PORT = 4242
|
||||
|
||||
# Open socket to the server
|
||||
sock = socket.socket()
|
||||
sock.bind((SERVER_ADDR, SERVER_PORT))
|
||||
sock.listen(1)
|
||||
print("server listening on", SERVER_ADDR, SERVER_PORT)
|
||||
|
||||
# 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 %d' % write_len)
|
||||
|
||||
# Read the data back from the client
|
||||
total_size = BUF_SIZE
|
||||
read_buf = b''
|
||||
while total_size > 0:
|
||||
buf = con.recv(BUF_SIZE)
|
||||
print('read %d bytes from client' % len(buf))
|
||||
total_size -= len(buf)
|
||||
read_buf += 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")
|
||||
40
pico_w/wifi/tcp_client/CMakeLists.txt
Normal file
40
pico_w/wifi/tcp_client/CMakeLists.txt
Normal file
@@ -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()
|
||||
10
pico_w/wifi/tcp_client/lwipopts.h
Normal file
10
pico_w/wifi/tcp_client/lwipopts.h
Normal file
@@ -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
|
||||
256
pico_w/wifi/tcp_client/picow_tcp_client.c
Normal file
256
pico_w/wifi/tcp_client/picow_tcp_client.c
Normal file
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#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 Wi-Fi driver or lwIP work that needs to be done.
|
||||
cyw43_arch_poll();
|
||||
// you can poll as often as you like, however if you have nothing else to do you can
|
||||
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
|
||||
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
|
||||
#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 Wi-Fi...\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;
|
||||
}
|
||||
34
pico_w/wifi/tcp_server/CMakeLists.txt
Normal file
34
pico_w/wifi/tcp_server/CMakeLists.txt
Normal file
@@ -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)
|
||||
10
pico_w/wifi/tcp_server/lwipopts.h
Normal file
10
pico_w/wifi/tcp_server/lwipopts.h
Normal file
@@ -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
|
||||
268
pico_w/wifi/tcp_server/picow_tcp_server.c
Normal file
268
pico_w/wifi/tcp_server/picow_tcp_server.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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 %u\n", TCP_PORT);
|
||||
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 Wi-Fi driver or lwIP work that needs to be done.
|
||||
cyw43_arch_poll();
|
||||
// you can poll as often as you like, however if you have nothing else to do you can
|
||||
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
|
||||
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
|
||||
#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 Wi-Fi...\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;
|
||||
}
|
||||
44
pico_w/wifi/tls_client/CMakeLists.txt
Normal file
44
pico_w/wifi/tls_client/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
add_executable(picow_tls_client_background
|
||||
picow_tls_client.c
|
||||
)
|
||||
target_compile_definitions(picow_tls_client_background PRIVATE
|
||||
WIFI_SSID=\"${WIFI_SSID}\"
|
||||
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
|
||||
)
|
||||
target_include_directories(picow_tls_client_background PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
|
||||
)
|
||||
target_link_libraries(picow_tls_client_background
|
||||
pico_cyw43_arch_lwip_threadsafe_background
|
||||
pico_lwip_mbedtls
|
||||
pico_mbedtls
|
||||
pico_stdlib
|
||||
)
|
||||
pico_add_extra_outputs(picow_tls_client_background)
|
||||
|
||||
add_executable(picow_tls_client_poll
|
||||
picow_tls_client.c
|
||||
)
|
||||
target_compile_definitions(picow_tls_client_poll PRIVATE
|
||||
WIFI_SSID=\"${WIFI_SSID}\"
|
||||
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
|
||||
)
|
||||
target_include_directories(picow_tls_client_poll PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
|
||||
)
|
||||
target_link_libraries(picow_tls_client_poll
|
||||
pico_cyw43_arch_lwip_poll
|
||||
pico_lwip_mbedtls
|
||||
pico_mbedtls
|
||||
pico_stdlib
|
||||
)
|
||||
pico_add_extra_outputs(picow_tls_client_poll)
|
||||
|
||||
# Ignore warnings from lwip code
|
||||
set_source_files_properties(
|
||||
${PICO_LWIP_PATH}/src/apps/altcp_tls/altcp_tls_mbedtls.c
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wno-unused-result"
|
||||
)
|
||||
19
pico_w/wifi/tls_client/lwipopts.h
Normal file
19
pico_w/wifi/tls_client/lwipopts.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _LWIPOPTS_H
|
||||
#define _LWIPOPTS_H
|
||||
|
||||
#include "lwipopts_examples_common.h"
|
||||
|
||||
/* TCP WND must be at least 16 kb to match TLS record size
|
||||
or you will get a warning "altcp_tls: TCP_WND is smaller than the RX decrypion buffer, connection RX might stall!" */
|
||||
#undef TCP_WND
|
||||
#define TCP_WND 16384
|
||||
|
||||
#define LWIP_ALTCP 1
|
||||
#define LWIP_ALTCP_TLS 1
|
||||
#define LWIP_ALTCP_TLS_MBEDTLS 1
|
||||
|
||||
#define LWIP_DEBUG 1
|
||||
#define ALTCP_MBEDTLS_DEBUG LWIP_DBG_ON
|
||||
|
||||
#endif
|
||||
|
||||
63
pico_w/wifi/tls_client/mbedtls_config.h
Normal file
63
pico_w/wifi/tls_client/mbedtls_config.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/* Workaround for some mbedtls source files using INT_MAX without including limits.h */
|
||||
#include <limits.h>
|
||||
|
||||
#define MBEDTLS_NO_PLATFORM_ENTROPY
|
||||
#define MBEDTLS_ENTROPY_HARDWARE_ALT
|
||||
|
||||
#define MBEDTLS_SSL_OUT_CONTENT_LEN 2048
|
||||
|
||||
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
|
||||
#define MBEDTLS_HAVE_TIME
|
||||
|
||||
#define MBEDTLS_CIPHER_MODE_CBC
|
||||
#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_BP256R1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_BP384R1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_BP512R1_ENABLED
|
||||
#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
|
||||
#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
|
||||
#define MBEDTLS_PKCS1_V15
|
||||
#define MBEDTLS_SHA256_SMALLER
|
||||
#define MBEDTLS_SSL_SERVER_NAME_INDICATION
|
||||
#define MBEDTLS_AES_C
|
||||
#define MBEDTLS_ASN1_PARSE_C
|
||||
#define MBEDTLS_BIGNUM_C
|
||||
#define MBEDTLS_CIPHER_C
|
||||
#define MBEDTLS_CTR_DRBG_C
|
||||
#define MBEDTLS_ENTROPY_C
|
||||
#define MBEDTLS_ERROR_C
|
||||
#define MBEDTLS_MD_C
|
||||
#define MBEDTLS_MD5_C
|
||||
#define MBEDTLS_OID_C
|
||||
#define MBEDTLS_PKCS5_C
|
||||
#define MBEDTLS_PK_C
|
||||
#define MBEDTLS_PK_PARSE_C
|
||||
#define MBEDTLS_PLATFORM_C
|
||||
#define MBEDTLS_RSA_C
|
||||
#define MBEDTLS_SHA1_C
|
||||
#define MBEDTLS_SHA224_C
|
||||
#define MBEDTLS_SHA256_C
|
||||
#define MBEDTLS_SHA512_C
|
||||
#define MBEDTLS_SSL_CLI_C
|
||||
#define MBEDTLS_SSL_SRV_C
|
||||
#define MBEDTLS_SSL_TLS_C
|
||||
#define MBEDTLS_X509_CRT_PARSE_C
|
||||
#define MBEDTLS_X509_USE_C
|
||||
#define MBEDTLS_AES_FEWER_TABLES
|
||||
|
||||
/* TLS 1.2 */
|
||||
#define MBEDTLS_SSL_PROTO_TLS1_2
|
||||
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
|
||||
#define MBEDTLS_GCM_C
|
||||
#define MBEDTLS_ECDH_C
|
||||
#define MBEDTLS_ECP_C
|
||||
#define MBEDTLS_ECDSA_C
|
||||
#define MBEDTLS_ASN1_WRITE_C
|
||||
|
||||
244
pico_w/wifi/tls_client/picow_tls_client.c
Normal file
244
pico_w/wifi/tls_client/picow_tls_client.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/cyw43_arch.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/altcp_tcp.h"
|
||||
#include "lwip/altcp_tls.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
#define TLS_CLIENT_SERVER "worldtimeapi.org"
|
||||
#define TLS_CLIENT_HTTP_REQUEST "GET /api/ip HTTP/1.1\r\n" \
|
||||
"Host: " TLS_CLIENT_SERVER "\r\n" \
|
||||
"Connection: close\r\n" \
|
||||
"\r\n"
|
||||
#define TLS_CLIENT_TIMEOUT_SECS 15
|
||||
|
||||
|
||||
typedef struct TLS_CLIENT_T_ {
|
||||
struct altcp_pcb *pcb;
|
||||
bool complete;
|
||||
} TLS_CLIENT_T;
|
||||
|
||||
static struct altcp_tls_config *tls_config = NULL;
|
||||
|
||||
static err_t tls_client_close(void *arg) {
|
||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||
err_t err = ERR_OK;
|
||||
|
||||
state->complete = true;
|
||||
if (state->pcb != NULL) {
|
||||
altcp_arg(state->pcb, NULL);
|
||||
altcp_poll(state->pcb, NULL, 0);
|
||||
altcp_recv(state->pcb, NULL);
|
||||
altcp_err(state->pcb, NULL);
|
||||
err = altcp_close(state->pcb);
|
||||
if (err != ERR_OK) {
|
||||
printf("close failed %d, calling abort\n", err);
|
||||
altcp_abort(state->pcb);
|
||||
err = ERR_ABRT;
|
||||
}
|
||||
state->pcb = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static err_t tls_client_connected(void *arg, struct altcp_pcb *pcb, err_t err) {
|
||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||
if (err != ERR_OK) {
|
||||
printf("connect failed %d\n", err);
|
||||
return tls_client_close(state);
|
||||
}
|
||||
|
||||
printf("connected to server, sending request\n");
|
||||
err = altcp_write(state->pcb, TLS_CLIENT_HTTP_REQUEST, strlen(TLS_CLIENT_HTTP_REQUEST), TCP_WRITE_FLAG_COPY);
|
||||
if (err != ERR_OK) {
|
||||
printf("error writing data, err=%d", err);
|
||||
return tls_client_close(state);
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static err_t tls_client_poll(void *arg, struct altcp_pcb *pcb) {
|
||||
printf("timed out");
|
||||
return tls_client_close(arg);
|
||||
}
|
||||
|
||||
static void tls_client_err(void *arg, err_t err) {
|
||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||
printf("tls_client_err %d\n", err);
|
||||
state->pcb = NULL; /* pcb freed by lwip when _err function is called */
|
||||
}
|
||||
|
||||
static err_t tls_client_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) {
|
||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||
if (!p) {
|
||||
printf("connection closed\n");
|
||||
return tls_client_close(state);
|
||||
}
|
||||
|
||||
if (p->tot_len > 0) {
|
||||
/* For simplicity this examples creates a buffer on stack the size of the data pending here,
|
||||
and copies all the data to it in one go.
|
||||
Do be aware that the amount of data can potentially be a bit large (TLS record size can be 16 KB),
|
||||
so you may want to use a smaller fixed size buffer and copy the data to it using a loop, if memory is a concern */
|
||||
char buf[p->tot_len + 1];
|
||||
|
||||
pbuf_copy_partial(p, buf, p->tot_len, 0);
|
||||
buf[p->tot_len] = 0;
|
||||
|
||||
printf("***\nnew data received from server:\n***\n\n%s\n", buf);
|
||||
|
||||
altcp_recved(pcb, p->tot_len);
|
||||
}
|
||||
pbuf_free(p);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void tls_client_connect_to_server_ip(const ip_addr_t *ipaddr, TLS_CLIENT_T *state)
|
||||
{
|
||||
err_t err;
|
||||
u16_t port = 443;
|
||||
|
||||
printf("connecting to server IP %s port %d\n", ipaddr_ntoa(ipaddr), port);
|
||||
err = altcp_connect(state->pcb, ipaddr, port, tls_client_connected);
|
||||
if (err != ERR_OK)
|
||||
{
|
||||
fprintf(stderr, "error initiating connect, err=%d\n", err);
|
||||
tls_client_close(state);
|
||||
}
|
||||
}
|
||||
|
||||
static void tls_client_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
|
||||
{
|
||||
if (ipaddr)
|
||||
{
|
||||
printf("DNS resolving complete\n");
|
||||
tls_client_connect_to_server_ip(ipaddr, (TLS_CLIENT_T *) arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("error resolving hostname %s\n", hostname);
|
||||
tls_client_close(arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool tls_client_open(const char *hostname, void *arg) {
|
||||
err_t err;
|
||||
ip_addr_t server_ip;
|
||||
TLS_CLIENT_T *state = (TLS_CLIENT_T*)arg;
|
||||
|
||||
state->pcb = altcp_tls_new(tls_config, IPADDR_TYPE_ANY);
|
||||
if (!state->pcb) {
|
||||
printf("failed to create pcb\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
altcp_arg(state->pcb, state);
|
||||
altcp_poll(state->pcb, tls_client_poll, TLS_CLIENT_TIMEOUT_SECS * 2);
|
||||
altcp_recv(state->pcb, tls_client_recv);
|
||||
altcp_err(state->pcb, tls_client_err);
|
||||
|
||||
/* Set SNI */
|
||||
mbedtls_ssl_set_hostname(altcp_tls_context(state->pcb), hostname);
|
||||
|
||||
printf("resolving %s\n", hostname);
|
||||
|
||||
// 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 = dns_gethostbyname(hostname, &server_ip, tls_client_dns_found, state);
|
||||
if (err == ERR_OK)
|
||||
{
|
||||
/* host is in DNS cache */
|
||||
tls_client_connect_to_server_ip(&server_ip, state);
|
||||
}
|
||||
else if (err != ERR_INPROGRESS)
|
||||
{
|
||||
printf("error initiating DNS resolving, err=%d\n", err);
|
||||
tls_client_close(state->pcb);
|
||||
}
|
||||
|
||||
cyw43_arch_lwip_end();
|
||||
|
||||
return err == ERR_OK || err == ERR_INPROGRESS;
|
||||
}
|
||||
|
||||
// Perform initialisation
|
||||
static TLS_CLIENT_T* tls_client_init(void) {
|
||||
TLS_CLIENT_T *state = calloc(1, sizeof(TLS_CLIENT_T));
|
||||
if (!state) {
|
||||
printf("failed to allocate state\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
void run_tls_client_test(void) {
|
||||
/* No CA certificate checking */
|
||||
tls_config = altcp_tls_create_config_client(NULL, 0);
|
||||
|
||||
TLS_CLIENT_T *state = tls_client_init();
|
||||
if (!state) {
|
||||
return;
|
||||
}
|
||||
if (!tls_client_open(TLS_CLIENT_SERVER, state)) {
|
||||
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 Wi-Fi driver or lwIP work that needs to be done.
|
||||
cyw43_arch_poll();
|
||||
// you can poll as often as you like, however if you have nothing else to do you can
|
||||
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
|
||||
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
|
||||
#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);
|
||||
altcp_tls_free_config(tls_config);
|
||||
}
|
||||
|
||||
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, 30000)) {
|
||||
printf("failed to connect\n");
|
||||
return 1;
|
||||
}
|
||||
run_tls_client_test();
|
||||
|
||||
/* sleep a bit to let usb stdio write out any buffer to host */
|
||||
sleep_ms(100);
|
||||
|
||||
cyw43_arch_deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
34
pico_w/wifi/udp_beacon/CMakeLists.txt
Normal file
34
pico_w/wifi/udp_beacon/CMakeLists.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
add_executable(picow_udp_beacon_background
|
||||
picow_udp_beacon.c
|
||||
)
|
||||
target_compile_definitions(picow_udp_beacon_background PRIVATE
|
||||
WIFI_SSID=\"${WIFI_SSID}\"
|
||||
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
|
||||
)
|
||||
target_include_directories(picow_udp_beacon_background PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
|
||||
)
|
||||
target_link_libraries(picow_udp_beacon_background
|
||||
pico_cyw43_arch_lwip_threadsafe_background
|
||||
pico_stdlib
|
||||
)
|
||||
|
||||
pico_add_extra_outputs(picow_udp_beacon_background)
|
||||
|
||||
add_executable(picow_udp_beacon_poll
|
||||
picow_udp_beacon.c
|
||||
)
|
||||
target_compile_definitions(picow_udp_beacon_poll PRIVATE
|
||||
WIFI_SSID=\"${WIFI_SSID}\"
|
||||
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
|
||||
)
|
||||
target_include_directories(picow_udp_beacon_poll PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
|
||||
)
|
||||
target_link_libraries(picow_udp_beacon_poll
|
||||
pico_cyw43_arch_lwip_poll
|
||||
pico_stdlib
|
||||
)
|
||||
pico_add_extra_outputs(picow_udp_beacon_poll)
|
||||
10
pico_w/wifi/udp_beacon/lwipopts.h
Normal file
10
pico_w/wifi/udp_beacon/lwipopts.h
Normal file
@@ -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
|
||||
79
pico_w/wifi/udp_beacon/picow_udp_beacon.c
Normal file
79
pico_w/wifi/udp_beacon/picow_udp_beacon.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Andrew McDonnell
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/cyw43_arch.h"
|
||||
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/udp.h"
|
||||
|
||||
#define UDP_PORT 4444
|
||||
#define BEACON_MSG_LEN_MAX 127
|
||||
#define BEACON_TARGET "255.255.255.255"
|
||||
#define BEACON_INTERVAL_MS 1000
|
||||
|
||||
void run_udp_beacon() {
|
||||
struct udp_pcb* pcb = udp_new();
|
||||
|
||||
ip_addr_t addr;
|
||||
ipaddr_aton(BEACON_TARGET, &addr);
|
||||
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, BEACON_MSG_LEN_MAX+1, PBUF_RAM);
|
||||
char *req = (char *)p->payload;
|
||||
memset(req, 0, BEACON_MSG_LEN_MAX+1);
|
||||
snprintf(req, BEACON_MSG_LEN_MAX, "%d\n", counter);
|
||||
err_t er = udp_sendto(pcb, p, &addr, UDP_PORT);
|
||||
pbuf_free(p);
|
||||
if (er != ERR_OK) {
|
||||
printf("Failed to send UDP packet! error=%d", er);
|
||||
} else {
|
||||
printf("Sent packet %d\n", counter);
|
||||
counter++;
|
||||
}
|
||||
|
||||
// Note in practice for this simple UDP transmitter,
|
||||
// the end result for both background and poll is the same
|
||||
|
||||
#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 Wi-Fi driver or lwIP work that needs to be done.
|
||||
cyw43_arch_poll();
|
||||
sleep_ms(BEACON_INTERVAL_MS);
|
||||
#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(BEACON_INTERVAL_MS);
|
||||
#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 Wi-Fi...\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_udp_beacon();
|
||||
cyw43_arch_deinit();
|
||||
return 0;
|
||||
}
|
||||
27
pico_w/wifi/wifi_scan/CMakeLists.txt
Normal file
27
pico_w/wifi/wifi_scan/CMakeLists.txt
Normal file
@@ -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)
|
||||
|
||||
10
pico_w/wifi/wifi_scan/lwipopts.h
Normal file
10
pico_w/wifi/wifi_scan/lwipopts.h
Normal file
@@ -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
|
||||
73
pico_w/wifi/wifi_scan/picow_wifi_scan.c
Normal file
73
pico_w/wifi/wifi_scan/picow_wifi_scan.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#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_time = nil_time;
|
||||
bool scan_in_progress = false;
|
||||
while(true) {
|
||||
if (absolute_time_diff_us(get_absolute_time(), scan_time) < 0) {
|
||||
if (!scan_in_progress) {
|
||||
cyw43_wifi_scan_options_t scan_options = {0};
|
||||
int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result);
|
||||
if (err == 0) {
|
||||
printf("\nPerforming wifi scan\n");
|
||||
scan_in_progress = true;
|
||||
} else {
|
||||
printf("Failed to start scan: %d\n", err);
|
||||
scan_time = make_timeout_time_ms(10000); // wait 10s and scan again
|
||||
}
|
||||
} else if (!cyw43_wifi_scan_active(&cyw43_state)) {
|
||||
scan_time = make_timeout_time_ms(10000); // wait 10s and scan again
|
||||
scan_in_progress = false;
|
||||
}
|
||||
}
|
||||
// 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 Wi-Fi driver or lwIP work that needs to be done.
|
||||
cyw43_arch_poll();
|
||||
// you can poll as often as you like, however if you have nothing else to do you can
|
||||
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
|
||||
cyw43_arch_wait_for_work_until(scan_time);
|
||||
#else
|
||||
// if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work
|
||||
// is done via interrupt in the background. This sleep is just an example of some (blocking)
|
||||
// work you might be doing.
|
||||
sleep_ms(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
cyw43_arch_deinit();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user