实现效果:客户端收到什么就原封不动返回给服务端
网路调试助手下载:
一、ESP32 实现TCP Client 期准备
- NVS 初始化:为 WiFi 模块提供配置存储。
- 网络接口初始化:调用
esp_netif_init()
和esp_event_loop_create_default()
,为后续网络连接奠定基础。 - 创建默认网络接口:根据 STA 或 AP 模式创建对应的网络接口实例。
- WiFi 驱动初始化:调用
esp_wifi_init()
并配置 WiFi 模式及参数。 - 启动 WiFi:通过
esp_wifi_start()
启动 WiFi 模块,并利用事件回调获取 IP 地址。 - TCP/IP 协议栈启动:lwIP 初始化完成后,可以使用 socket API 实现 TCP 功能。
完成上述准备后,ESP32 就能正常使用 TCP 协议进行数据传输了。确保每个步骤正确完成,是实现稳定网络通信的关键。
本文重点是实现TCP 客户端驱动代码,所以对于上面的WiFi和网络接口的初始化请参考我的其他2片博客:
ap模式:
sta模式:
二、TCP Client 驱动代码编写(AP模式为例)
1.创建初始化AP模式的线程
初始化AP模式,需要注意的点就是:一定要确保AP模式初始化完全成功后,才可以去创建TCP 客户端相关的任务!如下所示:
xTaskCreate(wifi_ap_test, "wifi_sta_test", 4096, &apInitFlag, 4, NULL);
while(!apInitFlag) //确保AP已经初始化成功,
{
delay_ms(10);
}
程序会一直等待AP模式初始化,直到AP模式完全初始化成功!(apInitFlag标志是以形参方式传进去的)
2.初始化并创建TCP 客户端
void lwip_tcp_client_init(const uint8_t *serverAddr,in_port_t serverPort)
{
esp_err_t ret = 0;
int32_t recv_data_len = 0;
ESP_LOGI(TAG_TCP,"lwip_tcp_client_init: serverAddr=%s port=%d",serverAddr,serverPort);
start_tcpclientsocket:
//创建socket套接字
tcp_client_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
//设置服务器端的参数
target_server_addrInfo.sin_family = AF_INET;
target_server_addrInfo.sin_addr.s_addr = inet_addr(serverAddr);
target_server_addrInfo.sin_port = htons(serverPort); //将16位主机字节序的端口号转换为网络字节序(32位就用htonl)
//连接服务器
ret = connect(tcp_client_sock_fd,(struct sockaddr *)&target_server_addrInfo,sizeof(struct sockaddr ));
if(ret < 0)
{
ESP_LOGE(TAG_TCP,"lwip_tcp_client_init: disconnected server! serverAddr=%s port=%d ret=%d",serverAddr,serverPort,ret);
closesocket(tcp_client_sock_fd);
delay_ms(1000);
goto start_tcpclientsocket;
}
else if(ret == 0)
{
clientTcpState |= 0x80;
ESP_LOGI(TAG_TCP,"TCP Client connected Server ok ! serverAddr=%s port=%d",serverAddr,serverPort);
}
while(1)
{
if(clientTcpState & 0x80)
{
//memset(tcp_recvbuf,'\0',sizeof(tcp_recvbuf));
recv_data_len = recv(tcp_client_sock_fd,(void *)tcp_recvbuf,sizeof(tcp_recvbuf),0);
if(recv_data_len <= 0)
{
if(recv_data_len ==0)
ESP_LOGE(TAG_TCP,"Server closed the connection");
else
ESP_LOGE(TAG_TCP,"socket error");
closesocket(tcp_client_sock_fd);
clientTcpState &= ~0x80;
ESP_LOGE(TAG_TCP,"lwip_tcpClientRecvDataThread:Client disconnected server!(recv_data_len=%d) clientTcpState=0x%X",recv_data_len,clientTcpState);
}
else if(recv_data_len > 0)
{
tcp_recvbuf[recv_data_len] = '\0';
memcpy(tcp_sendbuf,tcp_recvbuf,recv_data_len);
tcp_sendbuf[recv_data_len] = '\0';
clientTcpState |= 0x40; //收到数据
xSemaphoreGive(recvDataSem);
//ESP_LOGI(TAG_TCP,"recvData=%s len=%d",tcp_recvbuf,recv_data_len);
}
}
else if(!(clientTcpState & 0x80))
{
closesocket(tcp_client_sock_fd);
goto start_tcpclientsocket;
}
else
{
delay_ms(1);
}
}
closesocket(tcp_client_sock_fd);
}
3.创建TCP发送数据处理任务线程
xTaskCreate(lwip_tcpClientSendDataThread, "lwip_tcpClientSendDataThread", 4096, NULL, LWIP_TCP_SEND_THREAD_PRIO, NULL);
发送数据处理线程服务函数:
void lwip_tcpClientSendDataThread(void *pvParameters)
{
esp_err_t ret = 0;
while(1)
{
ret = xSemaphoreTake(recvDataSem,portMAX_DELAY);
if(ret == pdPASS)
{
if(clientTcpState & 0x80) //
{
if(clientTcpState & 0x40) //
{
clientTcpState &= ~0x40;
ret = sendto(tcp_client_sock_fd,tcp_sendbuf,strlen(tcp_sendbuf),0,(struct sockaddr *)&target_server_addrInfo,sizeof(target_server_addrInfo)); //发送接收的数据
if(ret < 0)
{
ESP_LOGE(TAG_TCP,"lwip_tcpClientSendDataThread:Client send data to server error! ret=%d",ret);
closesocket(tcp_client_sock_fd);
clientTcpState &= ~0x80;
}
else
{
memset(tcp_sendbuf,'\0',sizeof(tcp_sendbuf));
clientTcpState |= 0x20;
}
}
}
}
}
closesocket(tcp_client_sock_fd);
}
4.TCP Client 测试
电脑连接上ESP32 创建的AP热点,确保电脑已经获取到IP
然后使用网络调试助手,创建一个TCP Server 服务端,等待ESP32自动连接
随后便可以进行收发数据测试:
附录:完整的实现 ESP32 TCP Client 代码
bsp_tcpClient.h
#ifndef __BSP_TCP_CLIENT_H
#define __BSP_TCP_CLIENT_H
#include <string.h>
#include <sys/socket.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
void tcp_client_test();
#endif
bsp_tcpClent.c
#include "bsp_tcpClient.h"
#include "bsp_delay.h"
#include "bsp_wifi_ap.h"
const char *TAG_TCP = "BSP_TCP";
#define TCP_RX_BUFSIZE 1024*10 /* 最大接收数据长度 */
#define LWIP_TCP_SEND_THREAD_PRIO ( tskIDLE_PRIORITY + 3 ) /* 发送数据线程优先级 */
#define LWIP_TCP_RECV_THREAD_PRIO ( tskIDLE_PRIORITY + 2 ) /* 发送数据线程优先级 */
SemaphoreHandle_t recvDataSem;
socklen_t tcp_client_sock_fd;
static struct sockaddr_in local_server_addrInfo = {0};
static struct sockaddr_in target_server_addrInfo = {0};
/* 接收数据缓冲区 */
uint8_t tcp_recvbuf[TCP_RX_BUFSIZE] = {0};
uint8_t tcp_sendbuf[TCP_RX_BUFSIZE] = {0};
//ESP32 tcp状态:bit7:是否成功连接服务器 bit6:是否收到服务端的数据 bit5:是否发送完数据到服务器 bit4: bit3: bit2: bit1: bit0:
uint8_t clientTcpState = 0;
void lwip_tcp_client_init(const uint8_t *serverAddr,in_port_t serverPort)
{
esp_err_t ret = 0;
int32_t recv_data_len = 0;
ESP_LOGI(TAG_TCP,"lwip_tcp_client_init: serverAddr=%s port=%d",serverAddr,serverPort);
start_tcpclientsocket:
//创建socket套接字
tcp_client_sock_fd = socket(AF_INET, SOCK_STREAM, 0);
//设置服务器端的参数
target_server_addrInfo.sin_family = AF_INET;
target_server_addrInfo.sin_addr.s_addr = inet_addr(serverAddr);
target_server_addrInfo.sin_port = htons(serverPort); //将16位主机字节序的端口号转换为网络字节序(32位就用htonl)
//连接服务器
ret = connect(tcp_client_sock_fd,(struct sockaddr *)&target_server_addrInfo,sizeof(struct sockaddr ));
if(ret < 0)
{
ESP_LOGE(TAG_TCP,"lwip_tcp_client_init: disconnected server! serverAddr=%s port=%d ret=%d",serverAddr,serverPort,ret);
closesocket(tcp_client_sock_fd);
delay_ms(1000);
goto start_tcpclientsocket;
}
else if(ret == 0)
{
clientTcpState |= 0x80;
ESP_LOGI(TAG_TCP,"TCP Client connected Server ok ! serverAddr=%s port=%d",serverAddr,serverPort);
}
while(1)
{
if(clientTcpState & 0x80)
{
//memset(tcp_recvbuf,'\0',sizeof(tcp_recvbuf));
recv_data_len = recv(tcp_client_sock_fd,(void *)tcp_recvbuf,sizeof(tcp_recvbuf),0);
if(recv_data_len <= 0)
{
if(recv_data_len ==0)
ESP_LOGE(TAG_TCP,"Server closed the connection");
else
ESP_LOGE(TAG_TCP,"socket error");
closesocket(tcp_client_sock_fd);
clientTcpState &= ~0x80;
ESP_LOGE(TAG_TCP,"lwip_tcpClientRecvDataThread:Client disconnected server!(recv_data_len=%d) clientTcpState=0x%X",recv_data_len,clientTcpState);
}
else if(recv_data_len > 0)
{
tcp_recvbuf[recv_data_len] = '\0';
memcpy(tcp_sendbuf,tcp_recvbuf,recv_data_len);
tcp_sendbuf[recv_data_len] = '\0';
clientTcpState |= 0x40; //收到数据
xSemaphoreGive(recvDataSem);
//ESP_LOGI(TAG_TCP,"recvData=%s len=%d",tcp_recvbuf,recv_data_len);
}
}
else if(!(clientTcpState & 0x80))
{
closesocket(tcp_client_sock_fd);
goto start_tcpclientsocket;
}
else
{
delay_ms(1);
}
}
closesocket(tcp_client_sock_fd);
}
void lwip_tcpClientSendDataThread(void *pvParameters)
{
esp_err_t ret = 0;
while(1)
{
ret = xSemaphoreTake(recvDataSem,portMAX_DELAY);
if(ret == pdPASS)
{
if(clientTcpState & 0x80) //
{
if(clientTcpState & 0x40) //
{
clientTcpState &= ~0x40;
ret = sendto(tcp_client_sock_fd,tcp_sendbuf,strlen(tcp_sendbuf),0,(struct sockaddr *)&target_server_addrInfo,sizeof(target_server_addrInfo)); //发送接收的数据
if(ret < 0)
{
ESP_LOGE(TAG_TCP,"lwip_tcpClientSendDataThread:Client send data to server error! ret=%d",ret);
closesocket(tcp_client_sock_fd);
clientTcpState &= ~0x80;
}
else
{
memset(tcp_sendbuf,'\0',sizeof(tcp_sendbuf));
clientTcpState |= 0x20;
}
}
}
}
}
closesocket(tcp_client_sock_fd);
}
void tcp_client_test()
{
uint8_t apInitFlag = 0;
ESP_LOGI(TAG_TCP,"tcp_client_test");
recvDataSem = xSemaphoreCreateBinary();
xTaskCreate(wifi_ap_test, "wifi_sta_test", 4096, &apInitFlag, 4, NULL);
while(!apInitFlag) //确保AP已经初始化成功,
{
delay_ms(10);
}
xTaskCreate(lwip_tcpClientSendDataThread, "lwip_tcpClientSendDataThread", 4096, NULL, LWIP_TCP_SEND_THREAD_PRIO, NULL);
lwip_tcp_client_init((uint8_t *)"192.168.4.2",8080);
while (1)
{
delay_ms(1000);
}
}