docs(readme): 更新调试日志与上位机通信说明

更新了 README.md 中关于调试日志与上位机通信的部分,详细说明了当
前通信架构分为两类:
1. 业务通信:HTTP 接口用于 WebUI 与 ESP32 通信
2. 调试镜像:UART0 + UDP 用于独立调试接收器

明确区分了 WebUI 使用的 HTTP 接口功能,并移除了不再需要的心跳包
说明。

refactor(health): 移除健康监控模块的备份文件

删除了 _assistant_backups 目录中的 health_monitor.cpp 和
health_monitor.h 备份文件,清理项目结构。
This commit is contained in:
小龙虾
2026-05-05 04:48:31 +08:00
parent 478e5e5a31
commit f598b9acb3
20 changed files with 955 additions and 1420 deletions
+61 -17
View File
@@ -42,12 +42,19 @@
static bool s_ble_ready = false; // NimBLE 同步完成
static bool s_ble_connected = false; // GATT 客户端已连接
/** @brief 当前 BLE 连接句柄。 */
static uint16_t s_conn_handle = BLE_HS_CONN_HANDLE_NONE;
static uint16_t s_data_out_val_handle = 0; // DATA_OUT 值句柄 (供通知用)
// ============================================================
// NimBLE 工具: 从 mbuf 读取原始数据
// ============================================================
/**
* @brief 将 NimBLE mbuf 中的数据拷贝到普通字节缓冲区。
* @param om NimBLE mbuf 数据指针。
* @param buf 输出缓冲区。
* @param buf_len 缓冲区最大长度。
* @return 拷贝的字节数,失败返回 -1。
*/
static int ble_ota_mbuf_to_buf(struct os_mbuf *om, uint8_t *buf, size_t buf_len)
{
if (!om || !buf) return -1;
@@ -184,7 +191,6 @@ static int ble_ota_data_in_access(uint16_t conn_handle, uint16_t attr_handle,
return BLE_ATT_ERR_UNLIKELY;
}
// --- 更新元数据 ---
err = meta_update(flash_offset + data_len, seq);
if (err != ESP_OK) {
DEBUG_WARN(TAG, "元数据更新失败 (seq=%u)", seq);
@@ -241,11 +247,16 @@ static int ble_ota_data_out_access(uint16_t conn_handle, uint16_t attr_handle,
// ============================================================
// BLE_UUID16_DECLARE 在 C++ 中产生 "taking address of rvalue" 编译错误。
// ESP-IDF 6.0 NimBLE 的 C++ 兼容写法: 先定义 static ble_uuid16_t 变量, 再取 .u 地址。
// 改用 static ble_uuid16_t 变量,再取 .u 地址。
/** @brief OTA GATT 服务 UUID */
static const ble_uuid16_t GATT_SVC_UUID = BLE_UUID16_INIT(BLE_OTA_SERVICE_UUID);
/** @brief CTRL 特征 UUID,写入 0xF7 触发中继 */
static const ble_uuid16_t GATT_CTRL_UUID = BLE_UUID16_INIT(BLE_OTA_CTRL_UUID);
/** @brief DATA_IN 特征 UUID,接收固件数据块 */
static const ble_uuid16_t GATT_DATA_IN_UUID = BLE_UUID16_INIT(BLE_OTA_DATA_IN_UUID);
/** @brief DATA_OUT 特征 UUID,Notify 推送 GD32 响应 */
static const ble_uuid16_t GATT_DATA_OUT_UUID = BLE_UUID16_INIT(BLE_OTA_DATA_OUT_UUID);
/** @brief VERSION 特征 UUID,返回固件版本字符串 */
static const ble_uuid16_t GATT_VERSION_UUID = BLE_UUID16_INIT(BLE_OTA_VERSION_UUID);
// NimBLE C 结构体在 C++ designated initializer 不能显式初始化全部字段。
@@ -296,7 +307,12 @@ static const struct ble_gatt_svc_def gatt_ota_svcs[] = {
// ============================================================
// GAP 事件回调
// ============================================================
/**
* @brief BLE GAP 事件回调,处理连接和断开事件。
* @param event
* @param arg
* @return 0 成功。
*/
static int ble_ota_gap_event(struct ble_gap_event *event, void *arg)
{
(void)arg;
@@ -307,10 +323,12 @@ static int ble_ota_gap_event(struct ble_gap_event *event, void *arg)
if (event->connect.status == 0) {
// 连接成功
s_ble_connected = true;
/** @brief 当前 BLE 连接句柄。 */
s_conn_handle = event->connect.conn_handle;
DEBUG_INFO(TAG, "BLE 客户端已连接: handle=%d", s_conn_handle);
} else {
DEBUG_WARN(TAG, "BLE 连接失败: status=%d", event->connect.status);
/** @brief BLE GATT 客户端连接状态。 */
s_ble_connected = false;
}
return 0;
@@ -318,7 +336,9 @@ static int ble_ota_gap_event(struct ble_gap_event *event, void *arg)
case BLE_GAP_EVENT_DISCONNECT:
DEBUG_INFO(TAG, "BLE 客户端已断开: reason=%d",
event->disconnect.reason);
/** @brief BLE GATT 客户端连接状态。 */
s_ble_connected = false;
/** @brief 当前 BLE 连接句柄。 */
s_conn_handle = BLE_HS_CONN_HANDLE_NONE;
// 恢复广播以接受新连接
ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER,
@@ -352,7 +372,9 @@ static int ble_ota_gap_event(struct ble_gap_event *event, void *arg)
// ============================================================
// BLE 广播
// ============================================================
/**
* @brief 启动 BLE 广播,使设备可被发现和连接。
*/
static void ble_ota_start_advertising(void)
{
uint8_t adv_data[31] = {0};
@@ -420,7 +442,9 @@ static void ble_ota_start_advertising(void)
// ============================================================
// NimBLE 同步/重置回调
// ============================================================
/**
* @brief NimBLE 协议栈同步完成回调,初始化广播。
*/
static void ble_ota_on_sync(void)
{
DEBUG_INFO(TAG, "NimBLE 协议栈同步完成");
@@ -432,21 +456,28 @@ static void ble_ota_on_sync(void)
ble_ota_start_advertising();
}
/**
* @brief NimBLE 协议栈重置回调。
* @param reason
*/
static void ble_ota_on_reset(int reason)
{
DEBUG_WARN(TAG, "NimBLE 协议栈重置: reason=%d", reason);
/** @brief NimBLE 协议栈同步完成标志。 */
s_ble_ready = false;
}
// ============================================================
// NimBLE Host 任务(独立 FreeRTOS 任务)
// ============================================================
/**
* @brief NimBLE 主机任务入口,处理协议栈事件循环。
* @param param
*/
static void ble_ota_nimble_host_task(void *param)
{
(void)param;
DEBUG_INFO(TAG, "NimBLE Host 任务启动");
// nimble_port_run() 阻塞运行 NimBLE 事件循环
nimble_port_run();
// 正常情况下不会到达这里
DEBUG_INFO(TAG, "NimBLE Host 任务退出");
@@ -455,7 +486,10 @@ static void ble_ota_nimble_host_task(void *param)
// ============================================================
// 公开 API
// ============================================================
/**
* @brief 启动 BLE GATT 服务器并注册 OTA 服务。
* @return ESP_OK 成功,其他失败。
*/
esp_err_t ble_ota_server_start(void)
{
int rc;
@@ -465,7 +499,6 @@ esp_err_t ble_ota_server_start(void)
DEBUG_INFO(TAG, " MTU: %" PRIu16, BLE_MTU_SIZE);
DEBUG_INFO(TAG, " Svc: 0x%04X", BLE_OTA_SERVICE_UUID);
// 1. 初始化 NimBLE 底层
esp_err_t err = nimble_port_init();
if (err != ESP_OK) {
DEBUG_ERR(TAG, "nimble_port_init 失败: %s", esp_err_to_name(err));
@@ -473,11 +506,9 @@ esp_err_t ble_ota_server_start(void)
}
DEBUG_INFO(TAG, "nimble_port_init 完成");
// 2. 初始化 GAP / GATT 服务(无需 BLE 存储回调 — 不做 bonding)
ble_svc_gap_init();
ble_svc_gatt_init();
// 4. 设置设备名称
rc = ble_svc_gap_device_name_set(BLE_DEVICE_NAME);
if (rc != 0) {
DEBUG_WARN(TAG, "设置设备名称失败: %d", rc);
@@ -490,7 +521,6 @@ esp_err_t ble_ota_server_start(void)
ble_hs_cfg.reset_cb = ble_ota_on_reset;
// 不做 bonding,不需要 store 回调
// 6. 预注册 GATT 服务(在 sync 回调中也会再注册一次确保)
rc = ble_gatts_count_cfg(gatt_ota_svcs);
if (rc != 0) {
DEBUG_ERR(TAG, "GATT 服务计数失败: %d", rc);
@@ -503,13 +533,18 @@ esp_err_t ble_ota_server_start(void)
return ESP_FAIL;
}
// 7. 启动 NimBLE Host 任务(内部创建 FreeRTOS 任务跑 nimble_port_run)
nimble_port_freertos_init(ble_ota_nimble_host_task);
DEBUG_INFO(TAG, "BLE OTA 服务器初始化完成");
return ESP_OK;
}
/**
* @brief 通过 BLE Notify 将 GD32 响应推送给上位机。
* @param data 待推送的数据指针。
* @param len 数据长度(字节)。
* @return ESP_OK 成功,其他失败。
*/
esp_err_t ble_ota_notify_response(const uint8_t *data, size_t len)
{
if (!s_ble_connected) {
@@ -522,7 +557,6 @@ esp_err_t ble_ota_notify_response(const uint8_t *data, size_t len)
if (len == 0) return ESP_OK;
// 通过 NimBLE mbuf 发送通知
struct os_mbuf *om = ble_hs_mbuf_from_flat(data, len);
if (om == NULL) {
DEBUG_ERR(TAG, "分配 mbuf 失败 (%zu 字节)", len);
@@ -539,11 +573,19 @@ esp_err_t ble_ota_notify_response(const uint8_t *data, size_t len)
return ESP_OK;
}
/**
* @brief 检查当前是否有 BLE 客户端连接。
* @return true 已连接,false 未连接。
*/
bool ble_ota_is_connected(void)
{
/** @brief BLE GATT 客户端连接状态。 */
return s_ble_connected;
}
/**
* @brief 通知中继模块开始向 GD32 转发固件。
*/
void ble_ota_signal_relay(void)
{
if (g_ota_dispatch_queue == NULL) {
@@ -568,13 +610,15 @@ void ble_ota_signal_relay(void)
// 实际的 BLE 事件循环在独立的 NimBLE Host 任务中运行。
// 为避免与 ota_relay_task 竞争,此任务不从 g_ota_dispatch_queue 消费。
// ============================================================
/**
* @brief BLE OTA FreeRTOS 任务,初始化 NimBLE 并运行 GATT 服务。
* @param pvParameters FreeRTOS 任务参数,未使用。
*/
void ble_ota_task(void *pvParameters)
{
(void)pvParameters;
DEBUG_INFO(TAG, "=== BLE OTA 任务启动 ===");
// 初始化并启动 BLE 服务器(同步操作,完成后广播开始)
esp_err_t err = ble_ota_server_start();
if (err != ESP_OK) {
DEBUG_ERR(TAG, "BLE 服务器启动失败: %s → BLE OTA 不可用",
+21 -5
View File
@@ -7,19 +7,35 @@
#include "ota_relay.h"
// BLE OTA 任务入口
/**
* @brief BLE OTA 任务入口,负责初始化 NimBLE 并运行 GATT OTA 服务。
* @param pvParameters FreeRTOS 任务参数,未使用。
*/
void ble_ota_task(void *pvParameters);
// 启动 BLE GATT 服务器
/**
* @brief 启动 BLE GATT 服务器并注册 OTA 相关服务与特征。
* @return ESP_OK 成功,其他失败。
*/
esp_err_t ble_ota_server_start(void);
// 通过 BLE 通知 APP GD32 的响应
/**
* @brief 通过 BLE Notify 将 GD32 的响应数据发送给上位机 APP。
* @param data 待推送的数据指针。
* @param len 数据长度(字节)。
* @return ESP_OK 成功,其他失败。
*/
esp_err_t ble_ota_notify_response(const uint8_t *data, size_t len);
// 检查 BLE 是否已连接
/**
* @brief 检查当前是否存在已连接的 BLE 客户端。
* @return true 已连接,false 未连接。
*/
bool ble_ota_is_connected(void);
// 信号通知中继开始(存储完成后调用)
/**
* @brief 在固件存储完成后通知中继模块开始向 GD32 转发。
*/
void ble_ota_signal_relay(void);
#endif // BLE_OTA_H
+160 -39
View File
@@ -28,26 +28,42 @@
#include "spi_master.h"
#include "cJSON.h"
/** @brief 当前 WiFi 工作模式(AP 或 STA)。 */
static ota_wifi_mode_t s_wifi_mode = OTA_WIFI_MODE_AP;
/** @brief OTA 中继是否已触发标志。 */
static bool s_relay_triggered = false;
/** @brief STA 模式连接状态。 */
static bool s_sta_connected = false;
/** @brief STA 连接重试次数。 */
static int s_sta_retry_count = 0;
/** @brief STA 连接成功信号量。 */
static SemaphoreHandle_t s_sta_connected_sem = NULL;
/** @brief STA 获取 IP 信号量。 */
static SemaphoreHandle_t s_sta_got_ip_sem = NULL;
// ESP32 OTA 状态
static bool s_esp32_ota_active = false;
/** @brief ESP32 OTA 写入句柄。 */
static esp_ota_handle_t s_esp32_ota_handle = 0;
static const esp_partition_t *s_esp32_update_partition = NULL;
/** @brief ESP32 OTA 已接收字节数。 */
static size_t s_esp32_received = 0;
// GD32 OTA 存储状态
static bool s_gd32_storage_done = false;
/** @brief HTTP 服务器句柄。 */
static httpd_handle_t s_http_server = NULL;
/**
* @brief 预先声明:为 STA 模式配置静态 IP、网关和子网掩码。
*/
static void wifi_set_static_ip(void);
/**
* @brief 为 HTTP 响应添加跨域访问控制头。
* @param req HTTP 请求对象。
*/
static void http_set_cors_headers(httpd_req_t *req)
{
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
@@ -56,6 +72,11 @@ static void http_set_cors_headers(httpd_req_t *req)
httpd_resp_set_hdr(req, "Access-Control-Max-Age", "600");
}
/**
* @brief 处理 HTTP OPTIONS 预检请求,返回 204。
* @param req HTTP 请求对象。
* @return ESP_OK。
*/
static esp_err_t http_options_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -79,7 +100,9 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base,
case WIFI_EVENT_STA_CONNECTED:
DEBUG_INFO("WIFI", "STA 已关联到 AP");
/** @brief STA 模式连接状态。 */
s_sta_connected = true;
/** @brief STA 连接重试次数。 */
s_sta_retry_count = 0;
if (s_sta_connected_sem) {
xSemaphoreGive(s_sta_connected_sem);
@@ -89,6 +112,7 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base,
case WIFI_EVENT_STA_DISCONNECTED: {
wifi_event_sta_disconnected_t* disconn = (wifi_event_sta_disconnected_t*)event_data;
DEBUG_WARN("WIFI", "STA 断开, reason=%d, retry=%d", disconn->reason, s_sta_retry_count);
/** @brief STA 模式连接状态。 */
s_sta_connected = false;
if (s_sta_retry_count < WIFI_STA_MAX_RETRY) {
@@ -136,32 +160,32 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base,
// ----------------------------------------
// WiFi AP 模式初始化
// ----------------------------------------
/**
* @brief 初始化 SoftAP 模式,供局域网 OTA 使用。
* @return ESP_OK 成功,其他失败。
*/
static esp_err_t wifi_init_ap(void)
{
DEBUG_INFO("WIFI", "=== 初始化 WiFi AP 模式 ===");
/** @brief 当前 WiFi 工作模式(AP 或 STA)。 */
s_wifi_mode = OTA_WIFI_MODE_AP;
// 创建 AP netif 并设置自定义 IP
esp_netif_t *ap_netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");
if (ap_netif == NULL) {
ap_netif = esp_netif_create_default_wifi_ap();
}
if (ap_netif != NULL) {
// 停止 DHCP 服务器(必须先停才能改 IP)
esp_netif_dhcps_stop(ap_netif);
esp_netif_ip_info_t ip_info;
memset(&ip_info, 0, sizeof(ip_info));
// 方案 A:SoftAP 使用 192.168.31.0/24 网段
// ESP32 自己作为 AP 网关,电脑连入后拿到 192.168.31.x
inet_pton(AF_INET, "192.168.31.1", &ip_info.ip);
inet_pton(AF_INET, "192.168.31.1", &ip_info.gw);
inet_pton(AF_INET, "255.255.255.0", &ip_info.netmask);
ESP_ERROR_CHECK(esp_netif_set_ip_info(ap_netif, &ip_info));
// 重新启动 DHCP 服务器,给连接的客户端分配 IP
esp_netif_dhcps_start(ap_netif);
DEBUG_INFO("WIFI", "AP IP 已设置: 192.168.31.1");
}
@@ -190,12 +214,17 @@ static esp_err_t wifi_init_ap(void)
// ----------------------------------------
// WiFi STA 模式初始化
// ----------------------------------------
/**
* @brief 初始化 STA 模式并连接预配置的路由器。
* @return ESP_OK 成功,其他失败。
*/
static esp_err_t wifi_init_sta(void)
{
DEBUG_INFO("WIFI", "=== 初始化 WiFi STA 模式 ===");
/** @brief 当前 WiFi 工作模式(AP 或 STA)。 */
s_wifi_mode = OTA_WIFI_MODE_STA;
/** @brief STA 连接重试次数。 */
s_sta_retry_count = 0;
// ★ 设置国家代码:中国,允许信道 1-13
@@ -222,7 +251,6 @@ static esp_err_t wifi_init_sta(void)
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
// 静态 IP 必须在 start/connect 之前配置,避免与 STA_START 自动连接发生竞态
wifi_set_static_ip();
ESP_ERROR_CHECK(esp_wifi_start());
@@ -233,7 +261,12 @@ static esp_err_t wifi_init_sta(void)
return ESP_OK;
}
// 等待 STA 连接成功并获取 IP(静态 IP 等 CONNECTED,DHCP 等 GOT_IP)
/**
* @brief 等待 STA 连接成功并获取 IP(静态 IP 等 CONNECTED,DHCP 等 GOT_IP)
* @param ip_str 输出缓冲区,存放获取到的 IP 字符串。
* @param len 缓冲区长度。
* @return ESP_OK 连接成功,其他失败。
*/
static esp_err_t wifi_wait_sta_connected(char *ip_str, size_t len)
{
SemaphoreHandle_t wait_sem = WIFI_STA_USE_STATIC_IP ? s_sta_connected_sem : s_sta_got_ip_sem;
@@ -254,6 +287,7 @@ static esp_err_t wifi_wait_sta_connected(char *ip_str, size_t len)
}
DEBUG_INFO("WIFI", "%s 等待成功, 当前 IP: " IPSTR, wait_name, IP2STR(&ip_info.ip));
}
/** @brief STA 模式连接状态。 */
s_sta_connected = true;
return ESP_OK;
}
@@ -262,7 +296,9 @@ static esp_err_t wifi_wait_sta_connected(char *ip_str, size_t len)
return ESP_ERR_TIMEOUT;
}
// 设置 STA 静态 IP
/**
* @brief 设置 STA 静态 IP
*/
static void wifi_set_static_ip(void)
{
if (WIFI_STA_USE_STATIC_IP) {
@@ -293,7 +329,11 @@ static void wifi_set_static_ip(void)
// ----------------------------------------
// ESP32 OTA 内部函数
// ----------------------------------------
/**
* @brief 初始化 ESP32 自身的 OTA 写入流程。
* @param total_size
* @return ESP_OK 成功,其他失败。
*/
static esp_err_t esp32_ota_begin(size_t total_size)
{
const esp_partition_t *boot_partition = esp_ota_get_running_partition();
@@ -327,16 +367,25 @@ static esp_err_t esp32_ota_begin(size_t total_size)
esp_err_t err = esp_ota_begin(s_esp32_update_partition, total_size, &s_esp32_ota_handle);
if (err != ESP_OK) {
DEBUG_ERR("OTA", "esp_ota_begin 失败: %s", esp_err_to_name(err));
/** @brief ESP32 OTA 目标分区。 */
s_esp32_update_partition = NULL;
return err;
}
/** @brief ESP32 OTA 已接收字节数。 */
s_esp32_received = 0;
/** @brief ESP32 自身 OTA 写入进行中标志。 */
s_esp32_ota_active = true;
DEBUG_INFO("OTA", "ESP32 OTA 开始,目标分区: %s", s_esp32_update_partition->label);
return ESP_OK;
}
/**
* @brief 将接收到的数据块写入 ESP32 OTA 分区。
* @param data
* @param len
* @return ESP_OK 成功,其他失败。
*/
static esp_err_t esp32_ota_write(const uint8_t *data, size_t len)
{
if (!s_esp32_ota_active || s_esp32_update_partition == NULL) {
@@ -349,7 +398,11 @@ static esp_err_t esp32_ota_write(const uint8_t *data, size_t len)
return err;
}
// HTTP POST /ota/update - 完成 ESP32 OTA 并重启
/**
* @brief HTTP POST /ota/update - 完成 ESP32 OTA 并重启
* @param req
* @return
*/
static esp_err_t esp32_ota_update_handler(httpd_req_t *req)
{
(void)req;
@@ -364,6 +417,7 @@ static esp_err_t esp32_ota_update_handler(httpd_req_t *req)
esp_err_t err = esp_ota_end(s_esp32_ota_handle);
if (err != ESP_OK) {
DEBUG_ERR("OTA", "esp_ota_end 失败: %s", esp_err_to_name(err));
/** @brief ESP32 自身 OTA 写入进行中标志。 */
s_esp32_ota_active = false;
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, esp_err_to_name(err));
return ESP_FAIL;
@@ -372,11 +426,13 @@ static esp_err_t esp32_ota_update_handler(httpd_req_t *req)
err = esp_ota_set_boot_partition(s_esp32_update_partition);
if (err != ESP_OK) {
DEBUG_ERR("OTA", "esp_ota_set_boot_partition 失败: %s", esp_err_to_name(err));
/** @brief ESP32 自身 OTA 写入进行中标志。 */
s_esp32_ota_active = false;
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, esp_err_to_name(err));
return ESP_FAIL;
}
/** @brief ESP32 自身 OTA 写入进行中标志。 */
s_esp32_ota_active = false;
DEBUG_INFO("OTA", "ESP32 OTA 完成,重启中...");
@@ -389,7 +445,11 @@ static esp_err_t esp32_ota_update_handler(httpd_req_t *req)
return ESP_OK;
}
// HTTP GET /ota/update - 查询 ESP32 OTA 状态
/**
* @brief HTTP GET /ota/update - 查询 ESP32 OTA 状态
* @param req
* @return
*/
static esp_err_t esp32_ota_status_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -408,7 +468,13 @@ static esp_err_t esp32_ota_status_handler(httpd_req_t *req)
}
// 从 HTTP 流中读取完整的 JSON 头(以 } 结尾)
// 返回 JSON 字符串长度,失败返回 0
/**
* @brief 返回 JSON 字符串长度,失败返回 0
* @param req
* @param buf
* @param buf_size
* @return
*/
static int recv_json_header(httpd_req_t *req, char *buf, int buf_size)
{
int total = 0;
@@ -429,7 +495,11 @@ static int recv_json_header(httpd_req_t *req, char *buf, int buf_size)
// ----------------------------------------
// 统一 /ota/upload 处理器(自动识别目标)
// ----------------------------------------
/**
* @brief HTTP 文件上传处理器,接收固件并写入 Flash。
* @param req
* @return ESP_OK 成功,其他失败。
*/
static esp_err_t ota_upload_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -592,6 +662,7 @@ static esp_err_t ota_upload_handler(httpd_req_t *req)
ota_meta_t meta;
meta_load(&meta);
if (meta.stored_size >= meta.total_size) {
/** @brief GD32 固件存储完成标志。 */
s_gd32_storage_done = true;
DEBUG_INFO("WIFI", "GD32 固件已存储,触发转发...");
wifi_ota_signal_relay();
@@ -603,7 +674,11 @@ static esp_err_t ota_upload_handler(httpd_req_t *req)
return ESP_OK;
}
// HTTP POST /ota/relay 处理器
/**
* @brief HTTP POST /ota/relay 处理器
* @param req
* @return
*/
static esp_err_t ota_relay_trigger_handler(httpd_req_t *req)
{
(void)req;
@@ -619,7 +694,11 @@ static esp_err_t ota_relay_trigger_handler(httpd_req_t *req)
return ESP_OK;
}
// HTTP GET /ota/status 处理器
/**
* @brief HTTP GET /ota/status 处理器
* @param req
* @return
*/
static esp_err_t ota_status_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -647,7 +726,11 @@ static esp_err_t ota_status_handler(httpd_req_t *req)
return ESP_OK;
}
// HTTP GET /health 处理器
/**
* @brief HTTP GET /health 处理器
* @param req
* @return
*/
static esp_err_t health_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -669,7 +752,11 @@ static esp_err_t health_handler(httpd_req_t *req)
return ESP_OK;
}
// HTTP GET /logs - 拉取并清空日志缓冲区
/**
* @brief HTTP GET /logs - 拉取并清空日志缓冲区
* @param req
* @return
*/
static esp_err_t logs_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -691,7 +778,11 @@ static esp_err_t logs_handler(httpd_req_t *req)
// HTTP POST /can/send 处理器
// 上位机发送 CAN 帧,经 ESP32 转发到 GD32
// 请求体: {"ch": 0, "id": 123456, "data": "0102030405060708", "dlc": 8}
// data 为 hex 字符串(可不填,自动用 dlc 决定实际发送长度)
/**
* @brief data 为 hex 字符串(可不填,自动用 dlc 决定实际发送长度)
* @param req
* @return
*/
static esp_err_t can_send_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -768,7 +859,11 @@ static esp_err_t can_send_handler(httpd_req_t *req)
return ESP_OK;
}
// HTTP GET /can/status 处理器
/**
* @brief HTTP GET /can/status 处理器
* @param req
* @return
*/
static esp_err_t can_status_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -782,7 +877,11 @@ static esp_err_t can_status_handler(httpd_req_t *req)
return ESP_OK;
}
// HTTP GET /wifi/status 处理器
/**
* @brief HTTP GET /wifi/status 处理器
* @param req
* @return
*/
static esp_err_t wifi_status_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -814,7 +913,11 @@ static esp_err_t wifi_status_handler(httpd_req_t *req)
return ESP_OK;
}
// HTTP GET /system/status 处理器
/**
* @brief HTTP GET /system/status 处理器
* @param req
* @return
*/
static esp_err_t system_status_handler(httpd_req_t *req)
{
http_set_cors_headers(req);
@@ -864,7 +967,10 @@ static esp_err_t system_status_handler(httpd_req_t *req)
// ----------------------------------------
// 启动 HTTP OTA 服务器
// ----------------------------------------
/**
* @brief 启动 HTTP OTA 服务器,注册各接口路由。
* @return ESP_OK 成功,其他失败。
*/
static esp_err_t http_ota_server_start(void)
{
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
@@ -1055,7 +1161,9 @@ static esp_err_t http_ota_server_start(void)
// ----------------------------------------
// 公开 API
// ----------------------------------------
/**
* @brief 通知 OTA 中继开始向 GD32 转发固件。
*/
void wifi_ota_signal_relay(void)
{
if (s_relay_triggered) {
@@ -1063,6 +1171,7 @@ void wifi_ota_signal_relay(void)
return;
}
/** @brief OTA 中继是否已触发标志。 */
s_relay_triggered = true;
if (g_ota_dispatch_queue) {
@@ -1072,11 +1181,20 @@ void wifi_ota_signal_relay(void)
}
}
/**
* @brief 获取当前 WiFi 工作模式(AP 或 STA)。
* @return 当前 WiFi 模式枚举值。
*/
ota_wifi_mode_t wifi_get_mode(void)
{
/** @brief 当前 WiFi 工作模式(AP 或 STA)。 */
return s_wifi_mode;
}
/**
* @brief 获取当前 WiFi 信号强度。
* @return 信号强度(dBm),未连接时返回无效值。
*/
int8_t wifi_get_rssi(void)
{
if (s_wifi_mode != OTA_WIFI_MODE_STA || !s_sta_connected) {
@@ -1089,8 +1207,13 @@ int8_t wifi_get_rssi(void)
return -127;
}
/**
* @brief 判断 STA 是否已连接到上级 WiFi。
* @return true 已连接,false 未连接。
*/
bool wifi_is_connected(void)
{
/** @brief STA 模式连接状态。 */
return s_sta_connected;
}
@@ -1098,12 +1221,14 @@ bool wifi_is_connected(void)
// ★ WiFi 早期初始化(在 app_main 调度器启动前调用)
// 包括:NVS → netif → event handler → WiFi driver → 配置 → connect
// 返回:true = STA 模式已启动(正在连接),false = 需要切 AP
// ----------------------------------------
/**
* @brief 在调度器启动前初始化 WiFi 并尝试 STA 连接。
* @return true STA 初始化中,false 需回退 AP。
*/
bool wifi_early_init(void)
{
DEBUG_INFO("WIFI", "=== WiFi 早期初始化 (调度器启动前) ===");
// NVS(WiFi 校准数据存储依赖 NVS)
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS 分区损坏或版本不匹配,擦除后重试
@@ -1117,8 +1242,9 @@ bool wifi_early_init(void)
DEBUG_INFO("WIFI", "NVS 初始化成功");
}
// 创建信号量
/** @brief STA 连接成功信号量。 */
s_sta_connected_sem = xSemaphoreCreateBinary();
/** @brief STA 获取 IP 信号量。 */
s_sta_got_ip_sem = xSemaphoreCreateBinary();
if (!s_sta_connected_sem || !s_sta_got_ip_sem) {
DEBUG_ERR("WIFI", "创建信号量失败");
@@ -1129,7 +1255,6 @@ bool wifi_early_init(void)
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
DEBUG_INFO("WIFI", "WiFi/IP 事件回调已注册");
// 创建 STA netif(必须在 esp_wifi_init 之前)
esp_netif_t *sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
if (sta_netif == NULL) {
sta_netif = esp_netif_create_default_wifi_sta();
@@ -1142,7 +1267,6 @@ bool wifi_early_init(void)
DEBUG_INFO("WIFI", "STA netif 已存在,跳过创建");
}
// WiFi driver 初始化(必须在 netif 创建之后)
wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&wifi_cfg));
DEBUG_INFO("WIFI", "WiFi driver 初始化成功");
@@ -1169,7 +1293,10 @@ bool wifi_early_init(void)
// WiFi OTA FreeRTOS 任务
// WiFi 初始化已在 app_main 中通过 wifi_early_init() 完成
// 此任务负责:等待连接结果 → 启动 HTTP 服务器 → 状态监控
// ----------------------------------------
/**
* @brief WiFi OTA FreeRTOS 任务,提供 HTTP 上传和中继触发。
* @param pvParameters FreeRTOS 任务参数,未使用。
*/
void wifi_ota_task(void *pvParameters)
{
(void)pvParameters;
@@ -1185,13 +1312,13 @@ void wifi_ota_task(void *pvParameters)
} else {
DEBUG_WARN("WIFI", "STA 连接超时,切换到 AP 模式...");
esp_wifi_stop(); // 先停 STA,再切 AP
/** @brief 当前 WiFi 工作模式(AP 或 STA)。 */
s_wifi_mode = OTA_WIFI_MODE_AP;
wifi_init_ap();
}
}
// AP 模式:wifi_early_init 已完成,无需额外操作
// 启动 HTTP 服务器
esp_err_t err = http_ota_server_start();
if (err != ESP_OK) {
DEBUG_ERR("WIFI", "HTTP 服务器启动失败");
@@ -1217,18 +1344,12 @@ void wifi_ota_task(void *pvParameters)
if (i == 0) vTaskDelay(pdMS_TO_TICKS(200));
}
// 心跳 + WiFi 状态监控
// 每秒一次循环:每 3 秒发心跳包,每 10 秒检查 RSSI
// WiFi 状态监控
// 每秒一次循环:每 10 秒检查 RSSI
uint32_t tick = 0;
while (1) {
tick++;
// 轻量心跳:每 3 秒发一次,上位机据此判断 ESP32 是否在线
if (tick % 3 == 0 && s_sta_connected) {
const char hb[] = "{\"t\":\"hb\"}\n";
debug_log_send((const uint8_t *)hb, sizeof(hb) - 1);
}
// 每 10 秒检查一次 RSSI
if (tick % 10 == 0) {
int8_t rssi = wifi_get_rssi();
+24 -8
View File
@@ -13,24 +13,40 @@ typedef enum {
OTA_WIFI_MODE_STA // STA 模式(连接路由器)
} ota_wifi_mode_t;
// WiFi OTA 任务入口
/**
* @brief WiFi OTA 任务入口,负责 HTTP 服务、上传处理和 OTA 触发流程。
* @param pvParameters FreeRTOS 任务参数,未使用。
*/
void wifi_ota_task(void *pvParameters);
// ★ WiFi 早期初始化(app_main 调度前调用)
// 包括 NVS、WiFi driver、netif、event handler、配置、连接尝试
// 返回 true = STA 模式启动中,false = 需要在 task 中切换 AP
// 在 app_main 早期执行 WiFi 初始化并尝试以 STA 模式联网。
/**
* @brief 返回 true 表示 STA 初始化已开始,false 表示后续应回退到 AP 模式。
* @return true STA 初始化中,false 需回退 AP。
*/
bool wifi_early_init(void);
// 信号通知 OTA 中继开始(存储完成后调用)
/**
* @brief 在固件存储完成后通知 OTA 中继开始向 GD32 转发。
*/
void wifi_ota_signal_relay(void);
// 获取当前 WiFi 模式
/**
* @brief 获取当前 WiFi 工作模式(AP 或 STA)。
* @return 当前 WiFi 模式枚举值。
*/
ota_wifi_mode_t wifi_get_mode(void);
// 获取 WiFi RSSI
/**
* @brief 获取当前 WiFi RSSI,未连接时通常返回无效/默认值。
* @return 信号强度(dBm),未连接时返回无效值。
*/
int8_t wifi_get_rssi(void);
// STA 是否已连接
/**
* @brief 判断当前 STA 模式下是否已经连接到上级 WiFi。
* @return true 已连接,false 未连接。
*/
bool wifi_is_connected(void);
#endif // WIFI_OTA_H