文章目录
- 前言
- 一、TCP协议
- 1.什么是TCP协议
- 2.TCP客户端工作流程
- 二、ESP32C3创建TCP客户端
- 1.引入头文件
- 2.定义相关变量
- 3.TCP客户端任务函数实现
- 4.WiFi模式的初始化
- 5.main函数
- 6.工程源码
- 三、工程调试
- 总结
前言
本次实现使用ESP32C3创建AP和TCP客户端,电脑连接ESP32C3的AP并使用网络调试助手创建一个TCP服务器,ESP32C3客户端自动连接电脑的服务器并发送数据到电脑服务器。
一、TCP协议
1.什么是TCP协议
TCP(Transmission Control Protocol,传输控制协议),TCP 是重要的传输层协议,它和 UDP 不同,传输层软件 TCP 的目的是允许数据同网络上的另外站点进行可靠的交换。它能提供端口编号的译码,以识别主机的应用程序,而且完成数据的可靠传输。
TCP与IP用来指示一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇,TCP/IP是一个网络协议族的名字。
2.TCP客户端工作流程
一个正常的TCP客户端建立连接都有这三个步骤:
1)TCP客户端与服务器的三次握手;
2)数据发送和接收;
3)四次挥手关闭连接。
相关socket流程图如下:
TCP/IP的socket相关知识各位朋友请自己查阅,这部分细节还是很多的,这里只做简单介绍一下,主要还是ESP32C3的应用。
二、ESP32C3创建TCP客户端
ESP32C3集成了TCP/IP协议栈,并通过lwip库来实现相关网络功能,下面介绍如何简单实现一个TCP客户端的工程。
1.引入头文件
2.定义相关变量
定义WiFi的名称、密码、使用的通道和可接入AP的设备数;还有服务器地址和端口等;
部分参数需要打开menuconfig来配置:
3.TCP客户端任务函数实现
4.WiFi模式的初始化
设置WiFi为AP模式:
WiFi事件回调函数:
5.main函数
6.工程源码
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_mac.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/sockets.h"
#define EXAMPLE_ESP_WIFI_SSID "myssid"
#define EXAMPLE_ESP_WIFI_PASS "12345678"
#define EXAMPLE_ESP_WIFI_CHANNEL 1
#define EXAMPLE_MAX_STA_CONN 4
#if defined(CONFIG_EXAMPLE_IPV4)
#define HOST_IP_ADDR "192.168.4.2"
#elif defined(CONFIG_EXAMPLE_IPV6)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
#else
#define HOST_IP_ADDR ""
#endif
#define PORT 8080
static const char *TAG = "tcp_client";
static const char *payload = "Message from ESP32C3 ";
static void tcp_client_task(void *pvParameters)
{
char rx_buffer[128];
char host_ip[] = HOST_IP_ADDR;
int addr_family = 0;
int ip_protocol = 0;
#if defined(CONFIG_EXAMPLE_IPV4)
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = inet_addr(host_ip);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
#elif defined(CONFIG_EXAMPLE_IPV6)
struct sockaddr_in6 dest_addr = { 0 };
inet6_aton(host_ip, &dest_addr.sin6_addr);
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(PORT);
dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
struct sockaddr_storage dest_addr = { 0 };
ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr));
#endif
while (1) {
int sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (sock < 0) {
vTaskDelay(2000 / portTICK_PERIOD_MS);
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
continue;
}
ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
if (err != 0) {
vTaskDelay(2000 / portTICK_PERIOD_MS);
ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
continue;
}
ESP_LOGI(TAG, "Successfully connected");
while (1) {
int err = send(sock, payload, strlen(payload), 0);
if (err < 0) {
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
break;
}
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
// Error occurred during receiving
if (len < 0) {
ESP_LOGE(TAG, "recv failed: errno %d", errno);
break;
}
// Data received
else {
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
ESP_LOGI(TAG, "%s", rx_buffer);
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
if (sock != -1) {
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
}
vTaskDelete(NULL);
}
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_id == WIFI_EVENT_AP_START){
ESP_LOGI(TAG, "WIFI_EVENT_AP_START");
xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
} else if (event_id == WIFI_EVENT_AP_STACONNECTED) {
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
MAC2STR(event->mac), event->aid);
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
MAC2STR(event->mac), event->aid);
}
}
void wifi_init_softap(void)
{
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
wifi_config_t wifi_config = {
.ap = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
.password = EXAMPLE_ESP_WIFI_PASS,
.max_connection = EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK,
.pmf_cfg = {
.required = false,
},
},
};
if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}
void app_main(void)
{
//Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
wifi_init_softap();
}
三、工程调试
使用电脑连接ESP32C3的AP热点,并用网络调试助手开启一个TCP服务器,调试结果如下图:
打开服务器后客户端会自己连接上服务器并发送数据,服务器也可以发送数据给客户端。
关闭服务器后的现象如图:
总结
本次主要是实现ESP32C3的TCP客户端与服务器的通信,在实际的开发中也是客户端的使用场景较多。ESP32C3集成了强大的网络功能,我这目前也只是简单的应用。