feat(config): 优化NimBLE配置以节省内存并提升OTA性能

- 启用BLE连接事件长度优化,允许单CE内传输多包,提升OTA吞吐
- 缩减NimBLE mbuf池配置,单连接OTA场景节省约7KB内存
- 禁用未使用的标准GATT服务,仅保留自定义OTA service,节省8-15KB
- 缩减NimBLE传输缓冲区配置以优化内存使用

refactor(debug_log): 重构环形日志缓冲区API以支持逐条弹出

- 新增ringPopOldest方法,支持弹出最旧日志并从缓冲区移除
- 重构ringDrainJson实现,基于新的逐条弹出机制
- 改进日志转义处理逻辑,支持更多特殊字符转义

perf(tasks): 优化各任务栈大小配置以节省内存

- 将ota_relay任务栈从12288字节缩减至6144字节
- 将ble_ota任务栈从8192字节缩减至4096字节
- 将ble_ota数据工作线程栈从8192字节缩减至4096字节
- 通过栈高水位监控验证实际使用情况,确保安全余量

feat(monitor): 添加任务句柄获取接口以支持系统状态监控

- 为HealthMonitor、OtaRelay、BleOta和WifiOta类添加任务句柄获取方法
- 在任务入口处记录当前任务句柄
- 便于系统状态监控和调试信息收集

feat(status): 扩展系统状态API以提供更详细的内存和任务信息

- 增加内部堆和默认堆的详细统计信息
- 添加各关键任务的栈使用情况监控
- 扩展/system/status端点返回更全面的系统资源使用情况
- 包含最大空闲块、最小空闲内存等关键指标
This commit is contained in:
小龙虾
2026-06-20 00:46:04 +08:00
parent 59c729d77a
commit 3fff959a2b
18 changed files with 701 additions and 103 deletions
+90 -4
View File
@@ -31,6 +31,7 @@
#include "health_monitor.h"
#include "monitor/debug_log.h"
#include "spi_master.h"
#include "ble_ota.h"
#include "cJSON.h"
@@ -1166,6 +1167,46 @@ static esp_err_t spi_send_handler(httpd_req_t *req)
return ESP_OK;
}
static constexpr uint32_t INTERNAL_HEAP_CAPS = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
struct TaskStackMetric {
const char* name;
TaskHandle_t handle;
size_t stack_size_bytes;
};
static size_t get_task_stack_high_watermark_bytes(TaskHandle_t handle)
{
if (handle == NULL) return 0;
UBaseType_t highWaterMark = uxTaskGetStackHighWaterMark(handle);
return (size_t)highWaterMark;
}
static size_t get_task_stack_peak_used_bytes(TaskHandle_t handle, size_t stack_size_bytes)
{
size_t high_watermark = get_task_stack_high_watermark_bytes(handle);
return (stack_size_bytes >= high_watermark) ? (stack_size_bytes - high_watermark) : 0;
}
/**
* @brief 追加 JSON 内容到响应缓冲区,自动处理 snprintf 截断。
*/
static int append_jsonf(char* buf, size_t cap, int offset, const char* fmt, ...)
{
if (!buf || cap == 0 || offset < 0 || (size_t)offset >= cap) return offset;
va_list args;
va_start(args, fmt);
int n = vsnprintf(buf + offset, cap - (size_t)offset, fmt, args);
va_end(args);
if (n < 0) return offset;
size_t written = (size_t)n;
if (offset + (int)written >= (int)cap) {
buf[cap - 1] = '\0';
return (int)(cap - 1);
}
return offset + (int)written;
}
/**
* @brief HTTP GET /system/status 处理器
* @param req
@@ -1236,12 +1277,45 @@ static esp_err_t system_status_handler(httpd_req_t *req)
gd32_version_t snapVer2 = {};
OtaRelay::getGd32Version(&snapVer2);
ota_system_state_t sysState = OtaCoordinator::instance().getSystemState();
char resp[1280];
size_t free_heap = esp_get_free_heap_size();
size_t min_free_heap = esp_get_minimum_free_heap_size();
size_t largest_free_block = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
size_t internal_free_heap = heap_caps_get_free_size(INTERNAL_HEAP_CAPS);
size_t internal_min_free_heap = heap_caps_get_minimum_free_size(INTERNAL_HEAP_CAPS);
size_t internal_largest_free_block = heap_caps_get_largest_free_block(INTERNAL_HEAP_CAPS);
size_t internal_heap_total = heap_caps_get_total_size(INTERNAL_HEAP_CAPS);
size_t default_heap_total = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
TaskStackMetric task_metrics[] = {
{ "http_server", xTaskGetCurrentTaskHandle(), HTTP_SERVER_STACK_SIZE },
{ "health_mon", HealthMonitor::instance().getTaskHandle(), 4096 },
{ "ota_relay", OtaRelay::getTaskHandle(), 6144 },
{ "wifi_ota", WifiOta::instance().getTaskHandle(), 8192 },
{ "wifi_reconnect", WifiOta::instance().m_reconnectTask, 4096 },
{ "ble_ota", BleOta::instance().getTaskHandle(), 4096 },
{ "ble_ota_worker", BleOta::instance().getWorkerTaskHandle(), 4096 }
};
char resp[4096];
int len = snprintf(resp, sizeof(resp),
"{\"version\": \"%s\", \"build_time\": \"%s\", \"git_hash\": \"%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\": {\"sys_state\": \"%s\", \"sys_state_code\": %d, \"total_size\": %lu, \"stored_size\": %lu, \"storage_done\": %s, \"relay_state\": %d, \"relay_seq\": %lu, \"relay_total_packets\": %lu, \"gd32_app_alive\": %s, \"gd32_app\": \"v%d.%d.%d.%d\", \"gd32_boot\": \"v%d.%d.%d.%d\"}}",
"{\"version\": \"%s\", \"build_time\": \"%s\", \"git_hash\": \"%s\", \"health\": %d, "
"\"free_heap\": %lu, \"min_free_heap\": %lu, \"largest_free_block\": %lu, "
"\"internal_free_heap\": %lu, \"internal_min_free_heap\": %lu, \"internal_largest_free_block\": %lu, "
"\"internal_heap_total\": %lu, \"default_heap_total\": %lu, "
"\"temperature\": %.1f, \"uptime_sec\": %lu, "
"\"wifi\": {\"mode\": \"%s\", \"ip\": \"%s\", \"rssi\": %d, \"connected\": %s}, "
"\"ota\": {\"sys_state\": \"%s\", \"sys_state_code\": %d, \"total_size\": %lu, \"stored_size\": %lu, \"storage_done\": %s, "
"\"relay_state\": %d, \"relay_seq\": %lu, \"relay_total_packets\": %lu, \"gd32_app_alive\": %s, "
"\"gd32_app\": \"v%d.%d.%d.%d\", \"gd32_boot\": \"v%d.%d.%d.%d\"}, "
"\"task_stack\": {",
FW_VERSION, FW_BUILD_TIME, FW_GIT_HASH, HealthMonitor::instance().getCurrent(),
(unsigned long)esp_get_free_heap_size(),
(unsigned long)esp_get_minimum_free_heap_size(),
(unsigned long)free_heap,
(unsigned long)min_free_heap,
(unsigned long)largest_free_block,
(unsigned long)internal_free_heap,
(unsigned long)internal_min_free_heap,
(unsigned long)internal_largest_free_block,
(unsigned long)internal_heap_total,
(unsigned long)default_heap_total,
(double)HealthMonitor::instance().getLastTemperature(),
(unsigned long)(esp_timer_get_time() / 1000000),
WifiOta::instance().m_wifiMode == OTA_WIFI_MODE_AP ? "AP" : "STA",
@@ -1262,6 +1336,17 @@ static esp_err_t system_status_handler(httpd_req_t *req)
snapVer2.boot_major, snapVer2.boot_minor,
snapVer2.boot_patch, snapVer2.boot_build);
for (size_t i = 0; i < sizeof(task_metrics) / sizeof(task_metrics[0]); ++i) {
len = append_jsonf(resp, sizeof(resp), len,
"%s\"%s\":{\"stack_size_bytes\":%lu,\"high_watermark_bytes\":%lu,\"peak_used_bytes\":%lu}",
i == 0 ? "" : ",",
task_metrics[i].name,
(unsigned long)task_metrics[i].stack_size_bytes,
(unsigned long)get_task_stack_high_watermark_bytes(task_metrics[i].handle),
(unsigned long)get_task_stack_peak_used_bytes(task_metrics[i].handle, task_metrics[i].stack_size_bytes));
}
len = append_jsonf(resp, sizeof(resp), len, "}}");
httpd_resp_set_type(req, "application/json");
httpd_resp_send(req, resp, len);
return ESP_OK;
@@ -1648,6 +1733,7 @@ bool WifiOta::earlyInit()
void WifiOta::taskEntry(void *pvParameters)
{
(void)pvParameters;
WifiOta::instance().m_taskHandle = xTaskGetCurrentTaskHandle();
DEBUG_INFO("WIFI", "=== WiFi OTA 任务启动 ===");