关于PicoW的WiFi功能程序的编写,官方有介绍文件《connecting-to-the-internet-with-pico-w》,帖子最后有下载,在官方例程中也有WiFi TCP通信的相关例程,路径为Pico_W/wifi/tcp_client和tcp_server,WiFi例程默认是不会编译的,需要提前进行配置,这个在PicoW学习(C语言环境搭建、新建工程、在线调试)有介绍。这两个例程需要一起使用,也就是两个PicoW中分别烧录tcp_client和tcp_server,然后连在同一个热点中,会完成一次数据的交互。但是例程需要两个PicoW太麻烦,而且不直观,而且都只有单一的首或者发的功能。通信例程通常是“echo”例程,也就是PicoW将收到的信息发送发送方。
目标
新建一个PicoW模块基于TCP协议、STA模式作为Server的“echo”例程工程,可以通过电脑创建TCP协议的Client与PicoW进行通信,并通过串口打印出相关信息。
工程创建
参考前面帖子的工程创建方法,使用“pico project generato”工具,工程名为“Test_IwIP_TCP”,勾选“Console over UART”和“Background IwIP”,该库表示中断模式接收,这里还有一个“Polled IwIP”可选,这个是轮寻方式接收。“PicoW onboard LED”库这个表示这个库仅仅只能控制LED灯,后面两个库是包含了LED灯的控制的。
代码编写
前面的工作已经将工程框架搭建完成,下面贴出主要的代码。
添加头文件
#include "lwip/tcp.h"
添加定义与全局变量
主要是WIFI用户名和密码,这里XX需要替换为自己的。然后是TCP相关的结构体。
#define WIFI_SSID "XXXXXXXXXXX"
#define WIFI_PASSWORD "XXXXXXXXX"
#define TCP_PORT 4242
#define DEBUG_printf printf
#define BUF_SIZE 2048
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;
main函数编写
主要是芯片的初始化,连接WiFi路由器,运行TCP任务,进行LED灯闪烁。这里要注意如果长时间没有连上路由器,会报错。
int main()
{
static bool LED_State=0;
stdio_init_all();
puts("Test IwIP TCP");
if (cyw43_arch_init()) {
printf("Wi-Fi init failed");
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();
while (true) {
sleep_ms(100);
if(LED_State){
LED_State=false;
}else{
LED_State=true;
}
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, LED_State);
}
return 0;
}
TCP Server任务函数编写
主要是TCP Server初始化,连接回调函数的编写,在连接回调函数中注册接收、发送和错误等回调函数,并给Client发送连接成功字符。
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;
}
}
接收发送回调函数编写
发送回调函数,在发送成功后进入,并通过串口发送发送字节数给PC。接收回调函数,在收到数据后,将数据发回给Client,并通过串口发送接收到的数据给PC。
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_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);
}
cyw43_arch_lwip_check();
if (p->tot_len > 0) {
DEBUG_printf("tcp_server_recv %s", p->payload, err);
err_t err = tcp_write(tpcb,p->payload, p->tot_len, TCP_WRITE_FLAG_COPY);
if (err != ERR_OK) {
DEBUG_printf("Failed to write data %d\n", err);
return tcp_server_result(arg, -1);
}
}
pbuf_free(p);
return ERR_OK;
}
运行效果
将PicoW上电,会自动连接上路由器,在路由器上查看PicoW的IP地址,可以看到PicoW的IP地址为:192.168.31.136。
在PC打开网络调试助手,选择TCP Clinet,设置好IP地址,远端主机端口“4242”,点击连接,编辑发送数据,点击发送,就会看到接收到相同的数据。
连接串口调试助手,在串口调试助手上,会有WiFi连接整个过程的信息,例如,接收和发送回调函数打印的信息。