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:
+61
-17
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user