ESP32-C2基于ESP-IDF的网页及AT指令配置,多连接串口透传

ESP32-C3基于ESP-IDF的多功能程序开发文档


目录

  1. 概述
  2. 环境准备
  3. 功能说明
  4. 完整代码
  5. 代码解析
  6. 使用方法
  7. 注意事项
  8. 扩展功能
  9. 总结

概述

本项目旨在基于ESP32-C3开发一个功能丰富的程序,使用ESP-IDF框架,实现以下功能:

  • STA模式连接Wi-Fi网络:设备工作在STA模式,连接到指定的Wi-Fi网络。
  • TCP服务器:建立一个支持5个TCP客户端的TCP服务器,监听指定端口,进行数据通信。
  • AT指令配置Wi-Fi:通过UART串口接收AT指令,设置Wi-Fi的SSID和密码等参数。
  • HTTP网页配置Wi-Fi:当设备未配置Wi-Fi时,启动AP模式,提供HTTP服务器,通过网页配置Wi-Fi。
  • 恢复出厂设置功能:支持通过AT指令或HTTP网页恢复设备出厂设置,清除所有已保存的配置。

环境准备

硬件要求

  • ESP32-C3开发板
  • USB数据线
  • 电脑(Windows、Linux或macOS)

软件要求

  • ESP-IDF开发环境

    • 安装ESP-IDF(建议使用v4.4或以上版本)
    • 配置ESP32-C3的开发环境
  • 开发工具

    • 串口调试工具(如SecureCRT、PuTTY、XShell等)
    • TCP客户端工具(如Telnet、netcat、SocketTest等)
    • 浏览器(用于访问配置网页)

功能说明

3.1 AT指令配置Wi-Fi

通过UART串口,设备可以接收AT指令,用户可以:

  • 设置Wi-Fi SSID

    AT+SSID=Your_SSID
    
  • 设置Wi-Fi密码

    AT+PASS=Your_PASSWORD
    
  • 获取当前SSID

    AT+GETSSID
    
  • 获取当前密码

    AT+GETPASS
    
  • 重启设备

    AT+RESTART
    
  • 恢复出厂设置

    AT+RESTORE
    

3.2 HTTP网页配置Wi-Fi

当设备未配置Wi-Fi时,会启动AP模式,创建一个热点供用户连接。用户可以通过浏览器访问配置网页,设置Wi-Fi的SSID和密码。

  • 连接设备热点

    • 热点名称:ESP32_Config
    • 无密码,默认开放
  • 访问配置页面

    • 在浏览器中输入http://192.168.4.1/
  • 设置Wi-Fi参数

    • 输入SSID和密码,点击“连接”按钮提交
  • 恢复出厂设置

    • 在配置页面点击“恢复出厂设置”按钮,确认后设备将清除配置并重启

3.3 TCP服务器功能

设备在成功连接到Wi-Fi网络后,会启动一个TCP服务器:

  • 监听端口12345
  • 最大客户端数量:5
  • 功能:客户端可以连接到服务器,发送数据,服务器会回显并处理数据

3.4 恢复出厂设置功能

设备提供了恢复出厂设置的功能,可以通过以下方式触发:

  • AT指令

    AT+RESTORE
    
  • HTTP网页

    • 在配置页面点击“恢复出厂设置”按钮

恢复出厂设置将清除所有已保存的配置(如Wi-Fi参数),设备将重启并进入初始状态。


完整代码

请将以下代码保存为main.c文件,放置在ESP-IDF工程的main目录下。

// main.c
#include <stdio.h>
#include <string.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_netif.h>
#include <nvs_flash.h>
#include <nvs.h>
#include <esp_log.h>
#include <esp_http_server.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>

#include "driver/uart.h"
#include "driver/gpio.h"

#include "lwip/sockets.h"
#include "lwip/netdb.h"

#define UART_NUM            UART_NUM_0
#define UART_TX_PIN         GPIO_NUM_1
#define UART_RX_PIN         GPIO_NUM_3
#define UART_BUF_SIZE       1024

#define WIFI_SSID_MAX_LEN   32
#define WIFI_PASS_MAX_LEN   64

#define MAX_CLIENTS         5
#define TCP_PORT            12345               // 监听端口
#define LISTEN_QUEUE        5

static const char *TAG = "MAIN";

static char g_wifi_ssid[WIFI_SSID_MAX_LEN + 1] = {0};
static char g_wifi_pass[WIFI_PASS_MAX_LEN + 1] = {0};

static EventGroupHandle_t s_wifi_event_group;
static const int WIFI_CONNECTED_BIT = BIT0;

static int client_sockets[MAX_CLIENTS] = {0};

static void tcp_server_task(void *pvParameters);
static void tcp_client_handler(void *pvParameters);
static void uart_event_task(void *pvParameters);

static void start_wifi_ap();
static void start_wifi_sta();

static void start_http_server();
static esp_err_t root_get_handler(httpd_req_t *req);
static esp_err_t wifi_post_handler(httpd_req_t *req);
static esp_err_t reset_post_handler(httpd_req_t *req);

static void load_wifi_config();
static void save_wifi_config(const char *ssid, const char *pass);
static void restore_factory_settings();

static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT) {
        if (event_id == WIFI_EVENT_AP_START) {
            ESP_LOGI(TAG, "AP模式启动,等待客户端连接...");
        } else if (event_id == WIFI_EVENT_STA_START) {
            ESP_LOGI(TAG, "STA模式启动,开始连接Wi-Fi...");
            esp_wifi_connect();
        } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
            ESP_LOGI(TAG, "Wi-Fi连接失败,重新连接...");
            esp_wifi_connect();
        }
    } else if (event_base == IP_EVENT) {
        if (event_id == IP_EVENT_STA_GOT_IP) {
            ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
            ESP_LOGI(TAG, "获得IP地址: " IPSTR, IP2STR(&event->ip_info.ip));
            xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
        }
    }
}

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);

    // 加载Wi-Fi配置
    load_wifi_config();

    // 初始化网络接口
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    s_wifi_event_group = xEventGroupCreate();

    // 注册Wi-Fi事件处理程序
    esp_event_handler_instance_t instance_any_id;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        &instance_any_id));
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    // 初始化UART
    const 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
    };
    uart_param_config(UART_NUM, &uart_config);
    uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    uart_driver_install(UART_NUM, UART_BUF_SIZE * 2, 0, 0, NULL, 0);

    // 创建UART事件处理任务
    xTaskCreate(uart_event_task, "uart_event_task", 4096, NULL, 5, NULL);

    // 检查是否有已保存的Wi-Fi配置
    if (strlen(g_wifi_ssid) == 0) {
        // 无配置,启动AP模式,提供配置网页
        ESP_LOGI(TAG, "无Wi-Fi配置,启动AP模式");
        start_wifi_ap();
        start_http_server();
    } else {
        // 有配置,启动STA模式,连接Wi-Fi
        ESP_LOGI(TAG, "已有Wi-Fi配置,启动STA模式");
        start_wifi_sta();

        // 等待Wi-Fi连接成功
        EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                               WIFI_CONNECTED_BIT,
                                               pdFALSE,
                                               pdTRUE,
                                               pdMS_TO_TICKS(10000));
        if (bits & WIFI_CONNECTED_BIT) {
            // 启动TCP服务器
            xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL);
        } else {
            ESP_LOGE(TAG, "Wi-Fi连接超时,启动AP模式");
            esp_wifi_stop();
            start_wifi_ap();
            start_http_server();
        }
    }
}

// UART事件处理任务
static void uart_event_task(void *pvParameters)
{
    uint8_t *data = (uint8_t *) malloc(UART_BUF_SIZE);
    while (1) {
        int len = uart_read_bytes(UART_NUM, data, UART_BUF_SIZE - 1, pdMS_TO_TICKS(100));
        if (len > 0) {
            data[len] = '\0';
            ESP_LOGI(TAG, "接收到AT指令: %s", (char *)data);
            // 解析AT指令
            if (strncmp((char *)data, "AT+SSID=", 8) == 0) {
                // 设置SSID
                char *ssid = (char *)data + 8;
                ssid[strcspn(ssid, "\r\n")] = '\0'; // 去除换行符
                if (strlen(ssid) > 0 && strlen(ssid) <= WIFI_SSID_MAX_LEN) {
                    strncpy(g_wifi_ssid, ssid, WIFI_SSID_MAX_LEN);
                    save_wifi_config(g_wifi_ssid, g_wifi_pass);
                    uart_write_bytes(UART_NUM, "OK\r\n", 4);
                    ESP_LOGI(TAG, "SSID已设置为: %s", g_wifi_ssid);
                } else {
                    uart_write_bytes(UART_NUM, "ERROR\r\n", 7);
                }
            } else if (strncmp((char *)data, "AT+PASS=", 8) == 0) {
                // 设置密码
                char *pass = (char *)data + 8;
                pass[strcspn(pass, "\r\n")] = '\0'; // 去除换行符
                if (strlen(pass) >= 0 && strlen(pass) <= WIFI_PASS_MAX_LEN) {
                    strncpy(g_wifi_pass, pass, WIFI_PASS_MAX_LEN);
                    save_wifi_config(g_wifi_ssid, g_wifi_pass);
                    uart_write_bytes(UART_NUM, "OK\r\n", 4);
                    ESP_LOGI(TAG, "密码已设置为: %s", g_wifi_pass);
                } else {
                    uart_write_bytes(UART_NUM, "ERROR\r\n", 7);
                }
            } else if (strncmp((char *)data, "AT+RESTART", 10) == 0) {
                // 重启设备
                uart_write_bytes(UART_NUM, "RESTARTING\r\n", 12);
                ESP_LOGI(TAG, "设备即将重启...");
                vTaskDelay(pdMS_TO_TICKS(1000));
                esp_restart();
            } else if (strncmp((char *)data, "AT+GETSSID", 10) == 0) {
                // 获取当前SSID
                char response[64];
                snprintf(response, sizeof(response), "+SSID:%s\r\n", g_wifi_ssid);
                uart_write_bytes(UART_NUM, response, strlen(response));
            } else if (strncmp((char *)data, "AT+GETPASS", 10) == 0) {
                // 获取当前密码
                char response[80];
                snprintf(response, sizeof(response), "+PASS:%s\r\n", g_wifi_pass);
                uart_write_bytes(UART_NUM, response, strlen(response));
            } else if (strncmp((char *)data, "AT+RESTORE", 10) == 0) {
                // 恢复出厂设置
                uart_write_bytes(UART_NUM, "RESTORING\r\n", 11);
                ESP_LOGI(TAG, "设备正在恢复出厂设置...");
                // 清除Wi-Fi配置
                restore_factory_settings();
                vTaskDelay(pdMS_TO_TICKS(1000));
                esp_restart();
            } else {
                uart_write_bytes(UART_NUM, "UNKNOWN COMMAND\r\n", 17);
            }
        }
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    free(data);
    vTaskDelete(NULL);
}

// 启动AP模式
static void start_wifi_ap()
{
    esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
    assert(ap_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 配置AP参数
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = "ESP32_Config",   // AP的SSID,可自行修改
            .ssid_len = strlen("ESP32_Config"),
            .channel = 1,
            .password = "",
            .max_connection = 4,
            .authmode = WIFI_AUTH_OPEN
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
}

// 启动STA模式
static void start_wifi_sta()
{
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    wifi_config_t wifi_config = { 0 };
    strncpy((char *)wifi_config.sta.ssid, g_wifi_ssid, sizeof(wifi_config.sta.ssid));
    strncpy((char *)wifi_config.sta.password, g_wifi_pass, sizeof(wifi_config.sta.password));
    wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
}

// HTTP服务器URI处理程序
static esp_err_t root_get_handler(httpd_req_t *req)
{
    const char *resp =
        "<!DOCTYPE html>"
        "<html>"
        "<head>"
        "<title>Wi-Fi配置</title>"
        "<meta charset=\"UTF-8\">"
        "</head>"
        "<body>"
        "<h1>设置Wi-Fi</h1>"
        "<form action=\"/wifi\" method=\"post\">"
        "SSID: <input type=\"text\" name=\"ssid\"><br>"
        "密码: <input type=\"password\" name=\"password\"><br>"
        "<input type=\"submit\" value=\"连接\">"
        "</form>"
        "<br>"
        "<form action=\"/reset\" method=\"post\">"
        "<input type=\"submit\" value=\"恢复出厂设置\" οnclick=\"return confirm('确定要恢复出厂设置吗?')\">"
        "</form>"
        "</body>"
        "</html>";
    httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
    return ESP_OK;
}

static esp_err_t wifi_post_handler(httpd_req_t *req)
{
    char buf[128];
    int ret, remaining = req->content_len;

    char ssid[WIFI_SSID_MAX_LEN + 1] = {0};
    char password[WIFI_PASS_MAX_LEN + 1] = {0};

    while (remaining > 0) {
        if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) {
            if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
                continue;
            }
            return ESP_FAIL;
        }
        remaining -= ret;
        buf[ret] = '\0';

        // 解析表单数据
        char *ssid_param = strstr(buf, "ssid=");
        char *pass_param = strstr(buf, "password=");
        if (ssid_param) {
            ssid_param += 5;
            char *ssid_end = strchr(ssid_param, '&');
            if (ssid_end) *ssid_end = '\0';
            url_decode(ssid_param, ssid);
        }
        if (pass_param) {
            pass_param += 9;
            char *pass_end = strchr(pass_param, '&');
            if (pass_end) *pass_end = '\0';
            url_decode(pass_param, password);
        }
    }

    ESP_LOGI(TAG, "接收到Wi-Fi配置: SSID=%s, PASSWORD=%s", ssid, password);

    // 保存配置
    save_wifi_config(ssid, password);

    // 返回响应
    const char *resp =
        "<!DOCTYPE html>"
        "<html>"
        "<head>"
        "<title>配置成功</title>"
        "<meta charset=\"UTF-8\">"
        "</head>"
        "<body>"
        "<h1>配置成功!设备正在重启...</h1>"
        "</body>"
        "</html>";
    httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);

    // 延时一会儿,确保响应发送完毕
    vTaskDelay(pdMS_TO_TICKS(2000));

    // 重启设备
    esp_restart();

    return ESP_OK;
}

static esp_err_t reset_post_handler(httpd_req_t *req)
{
    ESP_LOGI(TAG, "接收到恢复出厂设置请求");
    // 清除Wi-Fi配置
    restore_factory_settings();

    // 返回响应
    const char *resp =
        "<!DOCTYPE html>"
        "<html>"
        "<head>"
        "<title>已恢复出厂设置</title>"
        "<meta charset=\"UTF-8\">"
        "</head>"
        "<body>"
        "<h1>已恢复出厂设置!设备正在重启...</h1>"
        "</body>"
        "</html>";
    httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);

    // 延时一会儿,确保响应发送完毕
    vTaskDelay(pdMS_TO_TICKS(2000));

    // 重启设备
    esp_restart();

    return ESP_OK;
}

static void start_http_server()
{
    httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.server_port = 80;

    if (httpd_start(&server, &config) == ESP_OK) {
        // 注册URI处理程序
        httpd_uri_t root_get_uri = {
            .uri = "/",
            .method = HTTP_GET,
            .handler = root_get_handler,
            .user_ctx = NULL
        };
        httpd_register_uri_handler(server, &root_get_uri);

        httpd_uri_t wifi_post_uri = {
            .uri = "/wifi",
            .method = HTTP_POST,
            .handler = wifi_post_handler,
            .user_ctx = NULL
        };
        httpd_register_uri_handler(server, &wifi_post_uri);

        httpd_uri_t reset_post_uri = {
            .uri = "/reset",
            .method = HTTP_POST,
            .handler = reset_post_handler,
            .user_ctx = NULL
        };
        httpd_register_uri_handler(server, &reset_post_uri);

        ESP_LOGI(TAG, "HTTP服务器已启动");
    } else {
        ESP_LOGE(TAG, "启动HTTP服务器失败");
    }
}

// URL解码函数
void url_decode(char *src, char *dest)
{
    char a, b;
    while (*src) {
        if ((*src == '%') &&
            ((a = src[1]) && (b = src[2])) &&
            (isxdigit(a) && isxdigit(b))) {
            if (a >= 'a') a -= 'a' - 'A';
            if (a >= 'A') a -= ('A' - 10);
            else a -= '0';
            if (b >= 'a') b -= 'a' - 'A';
            if (b >= 'A') b -= ('A' - 10);
            else b -= '0';
            *dest++ = 16 * a + b;
            src += 3;
        } else if (*src == '+') {
            *dest++ = ' ';
            src++;
        } else {
            *dest++ = *src++;
        }
    }
    *dest = '\0';
}

// 加载Wi-Fi配置
static void load_wifi_config()
{
    nvs_handle_t nvs_handle;
    esp_err_t err = nvs_open("wifi_config", NVS_READONLY, &nvs_handle);
    if (err == ESP_OK) {
        size_t ssid_len = WIFI_SSID_MAX_LEN;
        size_t pass_len = WIFI_PASS_MAX_LEN;
        nvs_get_str(nvs_handle, "ssid", g_wifi_ssid, &ssid_len);
        nvs_get_str(nvs_handle, "pass", g_wifi_pass, &pass_len);
        nvs_close(nvs_handle);
        ESP_LOGI(TAG, "Wi-Fi配置已加载: SSID=%s", g_wifi_ssid);
    } else {
        ESP_LOGI(TAG, "未找到Wi-Fi配置");
    }
}

// 保存Wi-Fi配置
static void save_wifi_config(const char *ssid, const char *pass)
{
    nvs_handle_t nvs_handle;
    esp_err_t err = nvs_open("wifi_config", NVS_READWRITE, &nvs_handle);
    if (err == ESP_OK) {
        nvs_set_str(nvs_handle, "ssid", ssid);
        nvs_set_str(nvs_handle, "pass", pass);
        nvs_commit(nvs_handle);
        nvs_close(nvs_handle);
        ESP_LOGI(TAG, "Wi-Fi配置已保存");
    } else {
        ESP_LOGE(TAG, "保存Wi-Fi配置失败");
    }
}

// 恢复出厂设置,清除NVS中的Wi-Fi配置
static void restore_factory_settings()
{
    nvs_handle_t nvs_handle;
    esp_err_t err = nvs_open("wifi_config", NVS_READWRITE, &nvs_handle);
    if (err == ESP_OK) {
        nvs_erase_all(nvs_handle);
        nvs_commit(nvs_handle);
        nvs_close(nvs_handle);
        ESP_LOGI(TAG, "已清除Wi-Fi配置");
    } else {
        ESP_LOGE(TAG, "清除Wi-Fi配置失败");
    }
}

// TCP服务器任务
static void tcp_server_task(void *pvParameters)
{
    char addr_str[128];
    int addr_family;
    int ip_protocol;

    while (1) {

        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(TCP_PORT);
        addr_family = AF_INET;
        ip_protocol = IPPROTO_IP;

        int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
        if (listen_sock < 0) {
            ESP_LOGE(TAG, "无法创建套接字: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "套接字已创建");

        int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if (err != 0) {
            ESP_LOGE(TAG, "套接字绑定失败: errno %d", errno);
            close(listen_sock);
            break;
        }
        ESP_LOGI(TAG, "套接字绑定成功");

        err = listen(listen_sock, LISTEN_QUEUE);
        if (err != 0) {
            ESP_LOGE(TAG, "监听失败: errno %d", errno);
            close(listen_sock);
            break;
        }
        ESP_LOGI(TAG, "监听端口 %d", TCP_PORT);

        while (1) {
            struct sockaddr_in6 source_addr;
            uint addr_len = sizeof(source_addr);
            int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
            if (sock < 0) {
                ESP_LOGE(TAG, "接受连接失败: errno %d", errno);
                break;
            }
            // 打印客户端地址
            if (source_addr.sin6_family == PF_INET) {
                inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
            } else if (source_addr.sin6_family == PF_INET6) {
                inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
            }
            ESP_LOGI(TAG, "新客户端连接,地址: %s", addr_str);

            // 添加新客户端到客户端数组
            bool added = false;
            for (int i = 0; i < MAX_CLIENTS; i++) {
                if (client_sockets[i] == 0) {
                    client_sockets[i] = sock;
                    ESP_LOGI(TAG, "客户端添加到位置 %d", i);
                    added = true;
                    // 创建一个任务处理该客户端
                    xTaskCreate(tcp_client_handler, "tcp_client_handler", 4096, (void *)(intptr_t)sock, 5, NULL);
                    break;
                }
            }
            if (!added) {
                ESP_LOGW(TAG, "客户端数量已达上限,拒绝新连接");
                close(sock);
            }
        }

        if (listen_sock != -1) {
            ESP_LOGE(TAG, "关闭监听套接字并重新启动...");
            close(listen_sock);
        }
    }
    vTaskDelete(NULL);
}

// TCP客户端处理任务
static void tcp_client_handler(void *pvParameters)
{
    int sock = (int)(intptr_t)pvParameters;
    char rx_buffer[128];
    char tx_buffer[128];

    while (1) {
        int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0) {
            ESP_LOGE(TAG, "接收数据失败: errno %d", errno);
            break;
        } else if (len == 0) {
            ESP_LOGI(TAG, "连接关闭");
            break;
        } else {
            rx_buffer[len] = 0; // Null-terminate
            ESP_LOGI(TAG, "收到 %d 字节: %s", len, rx_buffer);

            // 发送回应
            snprintf(tx_buffer, sizeof(tx_buffer), "服务器已收到您的消息:%s", rx_buffer);
            int to_write = strlen(tx_buffer);
            int written = send(sock, tx_buffer, to_write, 0);
            if (written < 0) {
                ESP_LOGE(TAG, "发送数据失败: errno %d", errno);
                break;
            }
        }
    }
    // 关闭套接字并从客户端数组中移除
    close(sock);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (client_sockets[i] == sock) {
            client_sockets[i] = 0;
            ESP_LOGI(TAG, "客户端从位置 %d 移除", i);
            break;
        }
    }
    vTaskDelete(NULL);
}

代码解析

由于篇幅限制,此处不再逐行解析代码。如需了解每个函数和模块的详细功能,请参考上文的功能说明和注释。


使用方法

6.1 编译和烧录程序

  1. 创建ESP-IDF工程

    • 将上述代码保存为main.c,放置在main目录下。
    • 确保CMakeLists.txt文件正确配置,包含main.c
  2. 配置工程

    • 在终端中,进入工程目录,执行:

      idf.py set-target esp32c3
      
    • 执行:

      idf.py menuconfig
      

      在菜单中进行必要的配置,确保UART、Wi-Fi和网络组件已启用。

  3. 编译工程

    • 执行:

      idf.py build
      
  4. 烧录程序

    • 将ESP32-C3开发板连接到电脑。

    • 执行:

      idf.py -p [PORT] flash
      

      其中[PORT]为串口端口。

6.2 使用AT指令配置Wi-Fi

  1. 连接串口工具

    • 使用串口工具连接设备的UART接口,波特率为115200。
  2. 设置SSID和密码

    AT+SSID=Your_SSID
    AT+PASS=Your_PASSWORD
    
  3. 重启设备

    AT+RESTART
    
  4. 恢复出厂设置

    AT+RESTORE
    

6.3 使用HTTP网页配置Wi-Fi

  1. 连接设备热点

    • 在Wi-Fi列表中选择ESP32_Config,无需密码。
  2. 访问配置页面

    • 在浏览器中输入:

      http://192.168.4.1/
      
  3. 设置Wi-Fi参数

    • 输入SSID和密码,点击“连接”按钮。
  4. 恢复出厂设置

    • 点击“恢复出厂设置”按钮,确认后设备将清除配置并重启。

6.4 连接TCP服务器

  • 使用TCP客户端工具,连接到设备的IP地址和端口12345
  • 可以同时连接最多5个客户端,进行数据通信。

注意事项

  • UART冲突

    • UART0默认用于日志输出和AT指令通信,可能会有冲突。
    • 可以考虑使用其他UART接口,需调整引脚和UART编号。
  • Wi-Fi安全性

    • AP模式下的热点未设置密码,建议在实际应用中设置密码,防止未授权的访问。
  • 配置保存

    • 确保NVS的读写操作正确,避免因存储失败导致配置无法保存。
  • 设备重启

    • 每次更改配置或恢复出厂设置后,设备会自动重启,使新的配置生效。

扩展功能

  • 增加参数配置

    • 可在配置网页或AT指令中添加更多参数,如设备名称、工作模式等。
  • OTA升级

    • 在HTTP服务器中添加固件升级功能,支持OTA更新。
  • 安全验证

    • 在恢复出厂设置或其他敏感操作前,增加密码验证,提升安全性。
  • 日志记录

    • 记录设备的操作日志,便于维护和调试。

总结

通过本项目的实现,我们在ESP32-C3上构建了一个功能全面的程序,支持Wi-Fi配置、TCP通信、AT指令和HTTP配置等功能。该程序具有良好的扩展性和实用性,可根据实际需求进行进一步的开发和优化。

如果您在开发过程中遇到任何问题,或者需要更多的帮助,请随时与我联系!

在这里插入图片描述
ESP32-C2模组


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值