ESP32-C3基于ESP-IDF的多功能程序开发文档
目录
概述
本项目旨在基于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 编译和烧录程序
-
创建ESP-IDF工程
- 将上述代码保存为
main.c
,放置在main
目录下。 - 确保
CMakeLists.txt
文件正确配置,包含main.c
。
- 将上述代码保存为
-
配置工程
-
在终端中,进入工程目录,执行:
idf.py set-target esp32c3
-
执行:
idf.py menuconfig
在菜单中进行必要的配置,确保UART、Wi-Fi和网络组件已启用。
-
-
编译工程
-
执行:
idf.py build
-
-
烧录程序
-
将ESP32-C3开发板连接到电脑。
-
执行:
idf.py -p [PORT] flash
其中
[PORT]
为串口端口。
-
6.2 使用AT指令配置Wi-Fi
-
连接串口工具
- 使用串口工具连接设备的UART接口,波特率为115200。
-
设置SSID和密码
AT+SSID=Your_SSID AT+PASS=Your_PASSWORD
-
重启设备
AT+RESTART
-
恢复出厂设置
AT+RESTORE
6.3 使用HTTP网页配置Wi-Fi
-
连接设备热点
- 在Wi-Fi列表中选择
ESP32_Config
,无需密码。
- 在Wi-Fi列表中选择
-
访问配置页面
-
在浏览器中输入:
http://192.168.4.1/
-
-
设置Wi-Fi参数
- 输入SSID和密码,点击“连接”按钮。
-
恢复出厂设置
- 点击“恢复出厂设置”按钮,确认后设备将清除配置并重启。
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配置等功能。该程序具有良好的扩展性和实用性,可根据实际需求进行进一步的开发和优化。
如果您在开发过程中遇到任何问题,或者需要更多的帮助,请随时与我联系!