文章目录

  • 前言
  • 一、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流程图如下:

ESP32 UDP连接_TCP


TCP/IP的socket相关知识各位朋友请自己查阅,这部分细节还是很多的,这里只做简单介绍一下,主要还是ESP32C3的应用。

二、ESP32C3创建TCP客户端

ESP32C3集成了TCP/IP协议栈,并通过lwip库来实现相关网络功能,下面介绍如何简单实现一个TCP客户端的工程。

1.引入头文件

ESP32 UDP连接_网络协议_02

2.定义相关变量

ESP32 UDP连接_iot_03


定义WiFi的名称、密码、使用的通道和可接入AP的设备数;还有服务器地址和端口等;

部分参数需要打开menuconfig来配置:

ESP32 UDP连接_TCP_04

3.TCP客户端任务函数实现

ESP32 UDP连接_iot_05

4.WiFi模式的初始化

设置WiFi为AP模式:

ESP32 UDP连接_网络协议_06


WiFi事件回调函数:

ESP32 UDP连接_网络协议_07

5.main函数

ESP32 UDP连接_物联网_08

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服务器,调试结果如下图:

ESP32 UDP连接_ESP32 UDP连接_09


打开服务器后客户端会自己连接上服务器并发送数据,服务器也可以发送数据给客户端。

关闭服务器后的现象如图:

ESP32 UDP连接_TCP_10

总结

本次主要是实现ESP32C3的TCP客户端与服务器的通信,在实际的开发中也是客户端的使用场景较多。ESP32C3集成了强大的网络功能,我这目前也只是简单的应用。