feat(health_monitor): 移除电压监控功能并优化OTA健康检查
- 移除ESP32电压监控相关代码,因为ESP32-C3无内置电压传感器 - 更新健康监控模块,从"温度+电压+WiFi RSSI监控"改为"温度+WiFi RSSI监控" - 修改OTA健康检查频率,从每次包处理改为每32个包检查一次 - 添加OTA期间WiFi RSSI采样年龄限制和更宽松的阈值 docs: 更新文档中的系统监控描述 - 从README.md、开发指南和设计文档中移除电压监控相关内容 - 更新WebUI接口文档中的状态查询描述 refactor(wifi_ota): 增加系统状态响应中的运行时间信息 - 在/system/status接口响应中添加uptime_sec字段 - 前端工具页面显示系统运行时间 feat(wifi_ota): 添加ESP32重启接口和前端控制 - 新增/restart HTTP POST接口用于重启ESP32 - 在前端界面添加重启按钮替代健康检查按钮 - 移除前端健康检查功能 perf(ota_relay): 调整OTA块大小提升传输性能 - 将OTA块大小从4096字节增加到8192字节 - 更新固件上传和存储相关的缓冲区大小配置
This commit is contained in:
+1
-1
@@ -55,7 +55,7 @@ extern "C" void app_main(void)
|
||||
DEBUG_INFO("MAIN", "WiFi: %s", wifi_sta_ok ? "STA 连接中" : "将使用 AP 模式");
|
||||
DEBUG_INFO("MAIN", "========================================");
|
||||
|
||||
// 健康监控任务(温度 + 电压 + WiFi RSSI)
|
||||
// 健康监控任务(温度 + WiFi RSSI)
|
||||
BaseType_t xRet;
|
||||
xRet = xTaskCreatePinnedToCore(
|
||||
health_monitor_task,
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
// ============================================================
|
||||
// 固件版本
|
||||
// ============================================================
|
||||
inline constexpr const char *FW_VERSION = "0.0.6";
|
||||
inline constexpr const char *FW_VERSION = "0.1.4";
|
||||
|
||||
// ============================================================
|
||||
// WiFi 工作模式选择
|
||||
@@ -53,7 +53,7 @@ inline constexpr uint32_t HTTP_SERVER_STACK_SIZE = 8192;
|
||||
// ============================================================
|
||||
// OTA 配置
|
||||
// ============================================================
|
||||
inline constexpr size_t OTA_BLOCK_SIZE = 4096;
|
||||
inline constexpr size_t OTA_BLOCK_SIZE = 8192;
|
||||
inline constexpr size_t OTA_MAX_FIRMWARE_SIZE = 2 * 1024 * 1024; // 2MB
|
||||
|
||||
// ============================================================
|
||||
@@ -61,6 +61,9 @@ inline constexpr size_t OTA_MAX_FIRMWARE_SIZE = 2 * 1024 * 1024; // 2MB
|
||||
// ============================================================
|
||||
inline constexpr int32_t WIFI_RSSI_CRITICAL = -85;
|
||||
inline constexpr int32_t WIFI_RSSI_WARNING = -75;
|
||||
inline constexpr int32_t OTA_WIFI_RSSI_CRITICAL = -88; // OTA 期间更宽松,避免瞬时波动误判
|
||||
inline constexpr uint32_t OTA_WIFI_RSSI_SAMPLE_MAX_AGE_MS = 15000; // OTA 最多接受 15 秒内的 RSSI 样本
|
||||
inline constexpr uint32_t OTA_PACKET_HEALTH_CHECK_INTERVAL = 32; // 每 32 个 48B 包做一次 OTA 健康检查
|
||||
inline constexpr int32_t TEMP_WARNING = 80;
|
||||
inline constexpr int32_t TEMP_CRITICAL = 100;
|
||||
inline constexpr size_t HEAP_WARNING_MIN = 10240;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* health_monitor.cpp - 系统健康监控
|
||||
* 包含:健康状态管理 + WiFi RSSI 监控 + 温度监控 + 电压监控
|
||||
* 包含:健康状态管理 + WiFi RSSI 监控 + 温度监控
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "health_monitor.h"
|
||||
#include "debug_log.h"
|
||||
#include "server/wifi_ota.h"
|
||||
|
||||
/** @brief 全局健康状态,受互斥锁保护。 */
|
||||
health_state_t g_health_state = SYS_NORMAL;
|
||||
@@ -24,8 +25,10 @@ static SemaphoreHandle_t g_health_mutex = NULL;
|
||||
static temperature_sensor_handle_t s_temp_handle = NULL;
|
||||
/** @brief 最近一次采样的芯片温度(摄氏度)。 */
|
||||
static float s_last_temperature = 0.0f;
|
||||
/** @brief 最近一次估算的系统电压(伏特)。 */
|
||||
static float s_last_voltage = 3.30f;
|
||||
/** @brief 最近一次健康监控采样得到的 WiFi RSSI。 */
|
||||
static int8_t s_last_wifi_rssi = -127;
|
||||
/** @brief 最近一次 WiFi RSSI 采样时间(毫秒)。 */
|
||||
static int64_t s_last_wifi_rssi_ms = 0;
|
||||
|
||||
// ----------------------------------------
|
||||
// 健康状态管理
|
||||
@@ -68,14 +71,18 @@ float health_get_last_temperature(void)
|
||||
return s_last_temperature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 返回最近一次记录的系统电压估算值。
|
||||
* @return 最近一次电压估算值(伏特)。
|
||||
*/
|
||||
float health_get_last_voltage(void)
|
||||
bool health_get_last_wifi_rssi(int8_t *out_rssi, uint32_t *out_age_ms)
|
||||
{
|
||||
/** @brief 最近一次估算的系统电压(伏特)。 */
|
||||
return s_last_voltage;
|
||||
if (s_last_wifi_rssi_ms <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t now_ms = esp_timer_get_time() / 1000;
|
||||
uint32_t age_ms = (now_ms > s_last_wifi_rssi_ms) ? (uint32_t)(now_ms - s_last_wifi_rssi_ms) : 0;
|
||||
|
||||
if (out_rssi) *out_rssi = s_last_wifi_rssi;
|
||||
if (out_age_ms) *out_age_ms = age_ms;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
@@ -112,36 +119,9 @@ static void temp_monitor_callback(void *arg)
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// 电压监控(启动时调用一次)
|
||||
// 温度监控任务入口
|
||||
/**
|
||||
* @brief 电压定时回调,采样估算电压并根据阈值更新健康状态。
|
||||
* @param arg
|
||||
*/
|
||||
static void voltage_monitor_callback(void *arg)
|
||||
{
|
||||
float voltage = 3.30f; // ESP32-C3 无内置电压传感器,使用估算
|
||||
/** @brief 最近一次估算的系统电压(伏特)。 */
|
||||
s_last_voltage = voltage;
|
||||
|
||||
if (voltage < 2.3f) {
|
||||
/** @brief 全局健康状态,受互斥锁保护。 */
|
||||
g_health_state = SYS_SAFE_SHUTDOWN;
|
||||
DEBUG_ERR("VOLT", "!!! 危险 !!! 电压 %.2fV < 2.3V,触发安全关机", voltage);
|
||||
DEBUG_ERR("VOLT", "健康状态变更为: SYS_SAFE_SHUTDOWN");
|
||||
} else if (voltage < 2.5f) {
|
||||
if (g_health_state < SYS_WARNING) {
|
||||
/** @brief 全局健康状态,受互斥锁保护。 */
|
||||
g_health_state = SYS_WARNING;
|
||||
DEBUG_WARN("VOLT", "警告: 电压 %.2fV < 2.5V", voltage);
|
||||
DEBUG_WARN("VOLT", "健康状态变更为: SYS_WARNING");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// 温度电压监控任务入口
|
||||
/**
|
||||
* @brief 启动温度和电压监控定时器,周期性巡检系统健康。
|
||||
* @brief 启动温度监控定时器,周期性巡检系统健康。
|
||||
* @param pvParameters FreeRTOS 任务参数,未使用。
|
||||
*/
|
||||
void health_monitor_task(void *pvParameters)
|
||||
@@ -170,23 +150,15 @@ void health_monitor_task(void *pvParameters)
|
||||
|
||||
DEBUG_INFO("MON", "温度监控定时器已启动 (间隔 1 秒)");
|
||||
|
||||
voltage_monitor_callback(NULL);
|
||||
|
||||
// 每1秒刷新一次估算电压
|
||||
esp_timer_handle_t volt_timer;
|
||||
esp_timer_create_args_t volt_timer_args = {};
|
||||
volt_timer_args.callback = &voltage_monitor_callback;
|
||||
volt_timer_args.arg = nullptr;
|
||||
volt_timer_args.name = "volt_monitor";
|
||||
volt_timer_args.dispatch_method = ESP_TIMER_TASK;
|
||||
volt_timer_args.skip_unhandled_events = false;
|
||||
esp_timer_create(&volt_timer_args, &volt_timer);
|
||||
esp_timer_start_periodic(volt_timer, 1 * 1000000); // 1秒
|
||||
DEBUG_INFO("MON", "电压监控定时器已启动 (间隔 1 秒)");
|
||||
|
||||
// 保活循环:定时器由 esp_timer 任务驱动,此任务只需保持存活
|
||||
// 状态汇报已由 status_monitor_task 负责,此处只在异常时告警
|
||||
while (1) {
|
||||
int8_t rssi = wifi_get_rssi();
|
||||
if (wifi_is_connected() && rssi != -127) {
|
||||
s_last_wifi_rssi = rssi;
|
||||
s_last_wifi_rssi_ms = esp_timer_get_time() / 1000;
|
||||
}
|
||||
|
||||
uint32_t free_heap = esp_get_free_heap_size();
|
||||
if (free_heap < HEAP_WARNING_MIN) {
|
||||
DEBUG_WARN("MON", "空闲堆内存不足: %lu < %d", (unsigned long)free_heap, HEAP_WARNING_MIN);
|
||||
@@ -231,20 +203,26 @@ health_status_t health_check_before_ota(void)
|
||||
DEBUG_WARN("HEALTH", "OTA 前检查: SYS_WARNING,继续传输但需注意");
|
||||
}
|
||||
|
||||
wifi_ap_record_t ap_info;
|
||||
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
|
||||
if (ap_info.rssi < WIFI_RSSI_CRITICAL) {
|
||||
/** @brief 全局健康状态,受互斥锁保护。 */
|
||||
g_health_state = SYS_CRITICAL;
|
||||
DEBUG_WARN("HEALTH", "OTA 前检查: WiFi RSSI %d < %d (危险阈值)",
|
||||
ap_info.rssi, WIFI_RSSI_CRITICAL);
|
||||
return HEALTH_MON_CRITICAL;
|
||||
} else if (ap_info.rssi < WIFI_RSSI_NORMAL) {
|
||||
if (status < HEALTH_MON_WARNING) {
|
||||
status = HEALTH_MON_WARNING;
|
||||
if (!wifi_is_connected()) {
|
||||
DEBUG_WARN("HEALTH", "OTA 前检查: WiFi 未连接");
|
||||
return HEALTH_MON_CRITICAL;
|
||||
}
|
||||
|
||||
int8_t last_rssi = -127;
|
||||
uint32_t age_ms = 0;
|
||||
if (health_get_last_wifi_rssi(&last_rssi, &age_ms)) {
|
||||
if (age_ms <= OTA_WIFI_RSSI_SAMPLE_MAX_AGE_MS) {
|
||||
if (last_rssi < OTA_WIFI_RSSI_CRITICAL) {
|
||||
DEBUG_WARN("HEALTH", "OTA 前检查: 最近 RSSI %d < %d (样本年龄 %lu ms)",
|
||||
last_rssi, OTA_WIFI_RSSI_CRITICAL, (unsigned long)age_ms);
|
||||
return HEALTH_MON_CRITICAL;
|
||||
} else if (last_rssi < WIFI_RSSI_NORMAL) {
|
||||
if (status < HEALTH_MON_WARNING) {
|
||||
status = HEALTH_MON_WARNING;
|
||||
}
|
||||
DEBUG_WARN("HEALTH", "OTA 前检查: 最近 RSSI %d < %d (样本年龄 %lu ms)",
|
||||
last_rssi, WIFI_RSSI_NORMAL, (unsigned long)age_ms);
|
||||
}
|
||||
DEBUG_WARN("HEALTH", "OTA 前检查: WiFi RSSI %d < %d (警告阈值)",
|
||||
ap_info.rssi, WIFI_RSSI_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ extern health_state_t g_health_state;
|
||||
void health_monitor_init(void);
|
||||
|
||||
/**
|
||||
* @brief 健康监控任务入口,负责启动温度/电压定时检测并周期性巡检系统状态。
|
||||
* @brief 健康监控任务入口,负责启动温度定时检测并周期性巡检系统状态。
|
||||
* @param pvParameters FreeRTOS 任务参数,未使用。
|
||||
*/
|
||||
void health_monitor_task(void *pvParameters);
|
||||
@@ -72,7 +72,9 @@ health_status_t health_update_from_wifi_rssi(int8_t rssi);
|
||||
float health_get_last_temperature(void);
|
||||
|
||||
/**
|
||||
* @brief 获取最近一次记录的系统电压估算值(伏特)。
|
||||
* @return 最近一次电压估算值(伏特)。
|
||||
* @brief 获取最近一次健康监控采样得到的 WiFi RSSI。
|
||||
* @param out_rssi 输出 RSSI。
|
||||
* @param out_age_ms 输出样本距今时长(毫秒)。
|
||||
* @return true 有有效样本,false 当前无有效样本。
|
||||
*/
|
||||
float health_get_last_voltage(void);
|
||||
bool health_get_last_wifi_rssi(int8_t *out_rssi, uint32_t *out_age_ms);
|
||||
|
||||
@@ -376,10 +376,12 @@ static ota_result_t relay_packets_from_flash(void)
|
||||
g_relay_state = RELAY_RELAY_ACTIVE;
|
||||
|
||||
for (uint32_t seq = 0; seq < total_packets; seq++) {
|
||||
health_status_t health = health_check_before_ota();
|
||||
if (health == HEALTH_MON_SHUTDOWN || health == HEALTH_MON_CRITICAL) {
|
||||
DEBUG_ERR("OTA", "健康检查危险,中止 OTA");
|
||||
return OTA_RESULT_ERR_HEALTH;
|
||||
if ((seq % OTA_PACKET_HEALTH_CHECK_INTERVAL) == 0 || seq == 0 || seq == total_packets - 1) {
|
||||
health_status_t health = health_check_before_ota();
|
||||
if (health == HEALTH_MON_SHUTDOWN || health == HEALTH_MON_CRITICAL) {
|
||||
DEBUG_ERR("OTA", "健康检查危险,中止 OTA");
|
||||
return OTA_RESULT_ERR_HEALTH;
|
||||
}
|
||||
}
|
||||
|
||||
size_t chunk_size = 48;
|
||||
|
||||
@@ -445,6 +445,27 @@ static esp_err_t esp32_ota_update_handler(httpd_req_t *req)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HTTP POST /restart - 立即重启 ESP32
|
||||
* @param req
|
||||
* @return
|
||||
*/
|
||||
static esp_err_t restart_handler(httpd_req_t *req)
|
||||
{
|
||||
(void)req;
|
||||
http_set_cors_headers(req);
|
||||
|
||||
DEBUG_INFO("OTA", "收到重启请求,500ms 后重启...");
|
||||
|
||||
const char resp[] = "{\"status\": \"ok\", \"message\": \"rebooting\"}";
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_send(req, resp, strlen(resp));
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
esp_restart();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HTTP GET /ota/update - 查询 ESP32 OTA 状态
|
||||
* @param req
|
||||
@@ -558,7 +579,7 @@ static esp_err_t ota_upload_handler(httpd_req_t *req)
|
||||
}
|
||||
|
||||
// 堆分配缓冲区,避免栈溢出(HTTP server 栈仅 8KB)
|
||||
uint8_t *fw_buf = (uint8_t *)malloc(1024);
|
||||
uint8_t *fw_buf = (uint8_t *)malloc(OTA_BLOCK_SIZE);
|
||||
if (!fw_buf) {
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "内存分配失败");
|
||||
return ESP_FAIL;
|
||||
@@ -570,7 +591,7 @@ static esp_err_t ota_upload_handler(httpd_req_t *req)
|
||||
|
||||
while (total_received < req->content_len) {
|
||||
size_t to_read = req->content_len - total_received;
|
||||
if (to_read > 1024) to_read = 1024;
|
||||
if (to_read > OTA_BLOCK_SIZE) to_read = OTA_BLOCK_SIZE;
|
||||
|
||||
int ret = httpd_req_recv(req, (char *)fw_buf, to_read);
|
||||
if (ret <= 0) break;
|
||||
@@ -628,7 +649,7 @@ static esp_err_t ota_upload_handler(httpd_req_t *req)
|
||||
uint32_t fw_size = gd32_header[2] | (gd32_header[3] << 8) | (gd32_header[4] << 16) | (gd32_header[5] << 24);
|
||||
if (seq == 0) {
|
||||
ota_meta_t meta = { .total_size = fw_size, .stored_size = 0, .last_seq = 0,
|
||||
.chunk_count = (fw_size + 4095) / 4096, .checksum = {0} };
|
||||
.chunk_count = (fw_size + OTA_BLOCK_SIZE - 1) / OTA_BLOCK_SIZE, .checksum = {0} };
|
||||
meta_save(&meta);
|
||||
DEBUG_INFO("WIFI", "开始 GD32 固件存储: %u 字节", fw_size);
|
||||
}
|
||||
@@ -943,14 +964,14 @@ static esp_err_t system_status_handler(httpd_req_t *req)
|
||||
memset(&meta, 0, sizeof(meta));
|
||||
meta_load(&meta);
|
||||
|
||||
char resp[640];
|
||||
char resp[700];
|
||||
int len = snprintf(resp, sizeof(resp),
|
||||
"{\"version\": \"%s\", \"health\": %d, \"free_heap\": %lu, \"min_free_heap\": %lu, \"temperature\": %.1f, \"voltage\": %.2f, \"wifi\": {\"mode\": \"%s\", \"ip\": \"%s\", \"rssi\": %d, \"connected\": %s}, \"ota\": {\"total_size\": %lu, \"stored_size\": %lu, \"storage_done\": %s, \"relay_state\": %d}}",
|
||||
"{\"version\": \"%s\", \"health\": %d, \"free_heap\": %lu, \"min_free_heap\": %lu, \"temperature\": %.1f, \"uptime_sec\": %lu, \"wifi\": {\"mode\": \"%s\", \"ip\": \"%s\", \"rssi\": %d, \"connected\": %s}, \"ota\": {\"total_size\": %lu, \"stored_size\": %lu, \"storage_done\": %s, \"relay_state\": %d}}",
|
||||
FW_VERSION,health_get_current(),
|
||||
(unsigned long)esp_get_free_heap_size(),
|
||||
(unsigned long)esp_get_minimum_free_heap_size(),
|
||||
(double)health_get_last_temperature(),
|
||||
(double)health_get_last_voltage(),
|
||||
(unsigned long)(esp_timer_get_time() / 1000000),
|
||||
s_wifi_mode == OTA_WIFI_MODE_AP ? "AP" : "STA",
|
||||
ip_str,
|
||||
rssi,
|
||||
@@ -1047,6 +1068,21 @@ static esp_err_t http_ota_server_start(void)
|
||||
};
|
||||
httpd_register_uri_handler(s_http_server, &esp32_update_options_uri);
|
||||
|
||||
httpd_uri_t restart_uri = {
|
||||
.uri = "/restart",
|
||||
.method = HTTP_POST,
|
||||
.handler = restart_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
httpd_register_uri_handler(s_http_server, &restart_uri);
|
||||
httpd_uri_t restart_options_uri = {
|
||||
.uri = "/restart",
|
||||
.method = HTTP_OPTIONS,
|
||||
.handler = http_options_handler,
|
||||
.user_ctx = NULL
|
||||
};
|
||||
httpd_register_uri_handler(s_http_server, &restart_options_uri);
|
||||
|
||||
httpd_uri_t esp32_status_uri = {
|
||||
.uri = "/ota/esp32_status",
|
||||
.method = HTTP_GET,
|
||||
|
||||
Reference in New Issue
Block a user