AP模式/ESP32作为TCP服务端,转发串口接收的数据给网络调试助手

此代码为接收STM32的数据然后直接转发到网络调试助手,当有设备连接到esp32软件热点时会通过串口发送字符'a'给STM32,当有设备断开连接时会通过串口发送字符'b',

ESP32的TX:GPIO4, RX:GPIO5

ESP32作为TCP服务器地址为192.168.4.1 监听端口为3333

cpp 复制代码
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_mac.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h" //lwip错误代码
#include "lwip/sys.h"
#include "sdkconfig.h"
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>
#include <arpa/inet.h>
#include "esp_netif.h"
#include <sys/param.h>
#include "esp_system.h"
#include "lwip/sockets.h"
#include <lwip/netdb.h>
#include "driver/uart.h"
#include "driver/gpio.h"

#define EXAMPLE_ESP_WIFI_SSID "laotie666"                // 热点名
#define EXAMPLE_ESP_WIFI_PASS "12345678"                 // 热点密码
#define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_CHANNEL // 信道(来自menuconfig)
#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN     // 最大连接数(来自menuconfig)
#define CONFIG_EXAMPLE_IPV4 1                            // 启用IPv4
#define PORT 3333                                        // 服务器监听的端口号
#define KEEPALIVE_IDLE 5                                 // TCP Keepalive空闲时间(秒)
#define KEEPALIVE_INTERVAL 5                             // Keepalive探测间隔(秒)
#define KEEPALIVE_COUNT 3                                // 超时前的探测次数
#define EX_UART_NUM UART_NUM_1                           // 使用UART1端口
#define BUF_SIZE (1024)                                  // UART缓冲区大小
#define RD_BUF_SIZE (BUF_SIZE)                           // 读取缓冲区大小

/*函数声明*/

uint8_t mac[6];
// 日志标签
static const char *TAG = "wifi softAP";
static const char *TAG_ZH = "Zhanghui:";

static uint8_t uart_buffer1[1024];
uint8_t uart_data;

static QueueHandle_t uart0_queue;

// 当建立tcp连接之后才会进行串口接收震动数据
uint8_t uart_re_en_flag = 0;
// TCP发送错误标志
int tcp_send_error_flag;

// WiFi事件处理函数
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_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);
        // 当有设备连接到esp32软件热点时会通过串口发送字符'a'
        uart_data = 'a';
        uart_write_bytes(EX_UART_NUM, &uart_data, 1);
    }
    else if (event_id == WIFI_EVENT_AP_STADISCONNECTED)
    {
        // 断开连接时
        wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data;
        // 打印断开设备的信息,如ip
        ESP_LOGI(TAG, "station " MACSTR " leave, AID=%d, reason=%d",
                 MAC2STR(event->mac), event->aid, event->reason);

        // 当有设备断开连接时会通过串口发送字符'b'
        uart_data = 'b';
        uart_write_bytes(EX_UART_NUM, &uart_data, 1);
    }
}

// 初始化SoftAP模式
void wifi_init_softap(void)
{
    // 通过调用esp_netif_init()来启动LWIPtask
    ESP_ERROR_CHECK(esp_netif_init()); // 初始化底层TCP/IP栈

    // 通过esp_event_loop_create_default()来创建启动事件task--eventtask
    ESP_ERROR_CHECK(esp_event_loop_create_default()); // 创建默认事件循环
    esp_netif_create_default_wifi_ap();               // 创建默认WIFI AP网络接口

    // 初始化WIFI驱动配置为默认值
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    // esp_wifi_init() 初始化WIFI driver
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 4.对我们所需处理的一些事件注册了一个回调函数wifi_event_handler
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));
    // 配置driver
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,             // 热点的名字
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID), // SSID长度
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,       // 无线信道 WiFi信号工作在2.4GHz或5GHz频段(取决于设备支持),这些频段被划分为多个子频段,每个子频段称为一个信道。
            .password = EXAMPLE_ESP_WIFI_PASS,         // 热点的密码
            .max_connection = EXAMPLE_MAX_STA_CONN,    // 热点的最大连接数目
#ifdef CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT              // 用于根据系统配置决定使用哪种 Wi-Fi 安全协议
            .authmode = WIFI_AUTH_WPA3_PSK,            // 授权的模式,登录的加密模式 // WPA3加密模式
            .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,          // SAE密码交换方法
#else                                                  /* CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT */
            .authmode = WIFI_AUTH_WPA2_PSK, // 默认WPA2加密
#endif
            .pmf_cfg = {
                // 受保护的管理帧配置
                .required = true, // 要求PMF
            },
        },
    };

    // 如果密码为空则使用开放模式
    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0)
    {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }
    // 使用esp_wifi_set_mode()对driver进行配置
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); // 设置WIFI为AP模式

    // 应用AP配置
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
    // 启动driver
    ESP_ERROR_CHECK(esp_wifi_start()); // 启动WIFI

    // 打印热点配置信息
    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);
}
/*
    wifi driver被启动之后, 会发送WIFI_EVENT_AP_START这个事件到event Task中,
    如果有处理这个事件的回调函数,就会再这个回调函数中进行处理;
    如果有设备进行连接,就会发送WIFI_EVENT_AP_STACONNECTED这个事件到event Task,
    之后再wifi_event_handler中进行处理(实例代码中只是打印了相应的信息)
    如果有断开连接的事件WIFI_EVENT_AP_STADISCONNECTED,也会发送到event Task,
    event Task调用wifi_event_handler进行处理
*/

// 数据转发函数
static void do_retransmit(const int sock)
{
    int len;
    uart_event_t event; // UART事件结构体
    uint8_t *dtmp = (uint8_t *)malloc(RD_BUF_SIZE);
    while (1)
    {
        //从名为`uart0_queue`的队列中接收数据,并将接收到的数据存储到`event`变量中,如果没有数据可接收,则无限期等待(阻塞)直到有数据到来。
        if (xQueueReceive(uart0_queue, (void *)&event, (TickType_t)portMAX_DELAY))
        {
            //清零内存区域,即将这段内存区域中的所有字节都设置为0,与memset()函数类似但功能受限。由于bzero()已被废弃,推荐使用memset()进行内存设置。
            bzero(dtmp, RD_BUF_SIZE);
            ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM);
            switch (event.type)
            {
                
                // 接收到的数据通常会在这里被处理 dtmp指向接收到的数据  event.size为接收到数据的大小
                case UART_DATA: 
                {

                    ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
                    uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);

                        int written = send(sock, dtmp, event.size, 0);
                        if (written < 0)
                        {
                            ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                            // Failed to retransmit, giving up
                            return;
                        }
                    
                    
                }

                break;
                // 硬件FIFO溢出事件
                case UART_FIFO_OVF:
                    ESP_LOGI(TAG, "hw fifo overflow");
                    // If fifo overflow happened, you should consider adding flow control for your application.
                    // The ISR has already reset the rx FIFO,
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(EX_UART_NUM);
                    xQueueReset(uart0_queue);
                    break;
                // 环形缓冲区满事件
                case UART_BUFFER_FULL:
                    ESP_LOGI(TAG, "ring buffer full");
                    // If buffer full happened, you should consider increasing your buffer size
                    // As an example, we directly flush the rx buffer here in order to read more data.
                    uart_flush_input(EX_UART_NUM);
                    xQueueReset(uart0_queue);
                    break;
                // 检测到UART线路断开
                case UART_BREAK:

                    ESP_LOGI(TAG, "uart rx break");

                    break;
                // 帧错误
                case UART_FRAME_ERR:
                    ESP_LOGI(TAG, "uart frame error");
                    break;
                default:
                    ESP_LOGI(TAG, "uart event type: %d", event.type);
                    break;
            }

            
        }
    }

    // do {
    //     len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
    //     if (len < 0) {
    //         ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
    //     } else if (len == 0) {
    //         ESP_LOGW(TAG, "Connection closed");
    //     } else {
    //         rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
    //         ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);

    //         // send() can return less bytes than supplied length.
    //         // Walk-around for robust implementation.
    //         int to_write = len;
    //         while (to_write > 0) {
    //             int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
    //             if (written < 0) {
    //                 ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
    //                 // Failed to retransmit, giving up
    //                 return;
    //             }
    //             to_write -= written;
    //         }
    //     }
    // } while (len > 0);
}
/* UART事件处理任务 */

// TCP服务器任务
static void tcp_server_task(void *pvParameters)
{
    char addr_str[128];                  // 存储客户端IP字符串
    int addr_family = (int)pvParameters; // 获取地址族参数
    int ip_protocol = 0;                 // IP协议类型
    int keepAlive = 1;                   // 启用Keepalive
    int keepIdle = 5;                    // Keepalive空闲时间
    int keepInterval = 5;                // 探测间隔
    int keepCount = 3;                   // 探测次数
    struct sockaddr_storage dest_addr;   // 服务器地址存储结构

    // 配置服务器地址
    if (addr_family == AF_INET)
    {
        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有接口
        dest_addr_ip4->sin_family = AF_INET;                // IPv4地址族
        dest_addr_ip4->sin_port = htons(PORT);              // 设置端口
        ip_protocol = IPPROTO_IP;                           // IP协议
    }

    // 创建监听套接字
    ESP_LOGE(TAG_ZH, "create socket");
    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0)
    {
        ESP_LOGE(TAG_ZH, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
    // 设置套接字选项(地址重用)
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
    // Note that by default IPV6 binds to both protocols, it is must be disabled
    // if both protocols used at the same time (used in CI)
    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endif

    ESP_LOGI(TAG, "套接字已创建");

    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0)
    {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); // 套接字绑定失败
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    err = listen(listen_sock, 1); // 最大挂起连接数为1
    if (err != 0)
    {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1)
    {
        struct sockaddr_storage source_addr; // 客户端地址存储
        socklen_t addr_len = sizeof(source_addr);
        // 接受客户端连接
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0)
        {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }

        // 设置TCP Keepalive选项
        setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
        // 将客户端IP地址转换为字符串
#ifdef CONFIG_EXAMPLE_IPV4
        if (source_addr.ss_family == PF_INET)
        {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
        }
#endif
#ifdef CONFIG_EXAMPLE_IPV6
        if (source_addr.ss_family == PF_INET6)
        {
            inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
        }
#endif
        ESP_LOGI(TAG_ZH, "Socket accepted ip address: %s", addr_str); // 接受的客户端IP:
        uart_re_en_flag = 1;                                          // 设置标志位,表示可以接收串口数据
                                                                      // do_retransmit(sock);
        do_retransmit(sock);

        shutdown(sock, 0);
        close(sock);
    }

CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}

void app_main(void)
{

    // 初始化存储空间
    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);

    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    // 安装UART驱动程序(创建事件队列)
    uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);
    // 应用参数配置
    uart_param_config(EX_UART_NUM, &uart_config);

    // 设置UART引脚(TX:GPIO4, RX:GPIO5)
    uart_set_pin(EX_UART_NUM, GPIO_NUM_4, GPIO_NUM_5, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
    ESP_ERROR_CHECK(esp_efuse_mac_get_default(mac));
    wifi_init_softap();
    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void *)AF_INET, 5, NULL);
}