diff --git a/.claude/launch.json b/.claude/launch.json deleted file mode 100644 index 906f61f..0000000 --- a/.claude/launch.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "0.0.1", - "configurations": [ - { - "name": "dev-server", - "runtimeExecutable": "python", - "runtimeArgs": ["-m", "http.server", "8080"], - "port": 8080, - "autoPort": true - }, - { - "name": "tools-server", - "runtimeExecutable": "python", - "runtimeArgs": ["-m", "http.server", "8080", "--directory", "tools"], - "port": 8080, - "autoPort": true - } - ] -} diff --git a/.trae/specs/simplify-arch-boilerplate/checklist.md b/.trae/specs/simplify-arch-boilerplate/checklist.md new file mode 100644 index 0000000..86c4658 --- /dev/null +++ b/.trae/specs/simplify-arch-boilerplate/checklist.md @@ -0,0 +1,16 @@ +- [x] 仓库根临时文件已删除:`old_wifi_ota.cpp.tmp`、`original_app.js.tmp`、`appjs_diff.txt`、`build_baseline.log`、`build_output.log` 不存在 +- [x] `OtaCoordinator::isIdle()` 仅调用一次 `getSystemState()`(即一次 `lock()`) +- [x] `OtaCoordinator::getSystemStateString()` 仅调用一次 `getSystemState()` +- [x] 8 个 GD32 存储 wrapper(`writeGd32Chunk/saveGd32Meta/loadGd32Meta/updateGd32Meta/clearGd32Meta/verifyGd32Firmware/finalizeGd32Checksum/eraseGd32Partition`)已改为 `inline` 并在头文件中实现,`.cpp` 中对应实现已删除 +- [x] 调用方(`http_handlers.cpp`、`ble_ota.cpp` 等)无需改动即可编译通过(签名不变) +- [x] `wifi_ota.cpp::httpServerStart()` 使用路由表 + 循环注册,9 个端点及其 OPTIONS 预检全部注册 +- [x] HTTP 路由注册行为与原实现等价(端点、method、handler 对应关系一致) +- [x] `OtaRelay` 新增 `commonReject` helper,各 `handle*` 开头拒绝分支等价于原实现 +- [x] `handleTransferring` 的 `RELAY_EVT_HEALTH_CRIT` 拒绝分支保留 +- [x] `forwardFirmware()` 不再使用 `goto cleanup`,5 步驱动顺序与超时检查时机不变 +- [x] `SpiMaster` 删除 `syncTransferUnlocked` / `sendUnlocked`,公共 API 通过 `LockGuard` + Impl 实现 +- [x] `SpiMaster::transferWithRetry` 调用路径更新为 Impl,无递归加锁 +- [x] `HealthMonitor` 新增 `LockGuard` 内部类,`getState/setState/getLastTemperature/getLastWifiRssi/tempMonitorCallback` 使用 RAII 守卫 +- [x] 评审"不建议改的地方"全部未动:5 步驱动顺序、健康检查时机、watchdog 自愈、`forceCleanup` 仅 `isIdle()`、`Esp32OtaCore` 双层 mutex 语义 +- [x] `idf.py build` 编译通过,无新增警告(15 个源文件 0 warning / 0 error) +- [x] `main/` 目录行数较变更前减少(主要削减:`httpServerStart` 173→67、`ota_coordinator.cpp` 8 wrapper 删除、`spi_master.cpp` 2 Unlocked 删除、`ota_relay.cpp` goto 重构) diff --git a/.trae/specs/simplify-arch-boilerplate/spec.md b/.trae/specs/simplify-arch-boilerplate/spec.md new file mode 100644 index 0000000..c92994b --- /dev/null +++ b/.trae/specs/simplify-arch-boilerplate/spec.md @@ -0,0 +1,75 @@ +# 架构精简:去样板与重复加锁 Spec + +## Why +`docs/esp32_architecture_review_2026-06-25.md` 指出当前代码存在大量零/低风险的重复样板(HTTP URI 注册粘贴、OtaCoordinator 纯转发 wrapper、OtaRelay 事件分发头部、SpiMaster *Unlocked 镜像)以及状态查询时的重复加锁。这些不影响运行时行为,但增加维护成本、放大锁争用,并留下两路代码不同步的隐患。本变更按评审推荐的顺序落地 A~F + 快速清理,预期 `main/` 减约 350~450 行,且不改运行时语义。 + +## What Changes +- **快速清理**:删除仓库根的临时/历史中间产物(`*.tmp`、`*.log`、`appjs_diff.txt`)。 +- **A.1**:把 `OtaCoordinator` 中 9 个纯转发到 `Gd32FlashStore` 的 wrapper 改为 `inline` 并移入头文件,`.cpp` 删除对应实现。 +- **A.2**:`OtaCoordinator::isIdle / isGd32Busy / isEsp32Busy` 改为一次 `getSystemState()` 取值后比较,消除多次 `lock()`。 +- **B**:`wifi_ota.cpp::httpServerStart()` 的 9×{POST/GET, OPTIONS} 复制粘贴改为路由表 + 循环注册。 +- **C**:`OtaRelay::handle*` 头部重复的 ABORT/TIMEOUT/HEALTH_CRIT 拒绝逻辑提取为 `commonReject` helper;`forwardFirmware()` 的 5 次顺序 `processEvent(START)` 改为状态驱动循环,去掉 `goto cleanup`。 +- **D**:`SpiMaster` 的 `syncTransfer/syncTransferUnlocked`、`send/sendUnlocked`、`otaPollResponse/otaPollResponseUnlocked` 三对镜像折叠为「公共 API 加锁 → 调内部 Impl」单一实现。 +- **F**:`HealthMonitor` 的 `getCurrent/getState/setState/getLastTemperature/getLastWifiRssi/tempMonitorCallback` 中重复的 `if (m_mutex) xSemaphoreTake/Give` 改为 RAII `LockGuard`(与 SpiMaster 一致)。 + +**不在本次范围**(评审标记为低优先级/可选/行为变更): +- E(退役 UDP 日志通道):涉及调试工作流行为变更,评审标为"低优先级""可考虑",留作后续。 +- G(BLE OTA finish ctx malloc→静态):评审明确"不是精简,是健壮性",位于 OTA 关键路径,需单独回归,留作后续。 +- H(README 同步):仅当采用 A 的"移除 wrapper"方案时才需要;本次采用 A.1(inline 保留语义),README 现有描述仍然成立,无需改动。 + +## Impact +- 受影响代码: + - `main/ota/ota_coordinator.h` / `.cpp`(A.1, A.2) + - `main/server/wifi_ota.cpp`(B) + - `main/relay/ota_relay.h` / `.cpp`(C) + - `main/transport/spi_master.h` / `.cpp`(D) + - `main/monitor/health_monitor.h` / `.cpp`(F) +- 受影响能力:OTA 协调器状态查询、HTTP 路由注册、OTA 中继状态机、SPI 总线访问、健康监控状态读写。 +- **不改**运行时行为:状态机 5 步驱动顺序、健康检查时机、watchdog 自愈、`forceCleanup` 仅在 `isIdle()` 启动、`Esp32OtaCore` 双层 mutex 语义均保持不变(评审"不建议改的地方")。 + +## ADDED Requirements + +### Requirement: HTTP 路由表化注册 +系统 SHALL 使用静态路由表 + 循环注册所有 HTTP URI handler,新增端点仅需在表中追加一行。 + +#### Scenario: 路由注册等价 +- **WHEN** `httpServerStart()` 执行 +- **THEN** 所有原 9 个端点(`/ota/upload`、`/ota/relay`、`/ota/update`、`/restart`、`/logs`、`/system/status`、`/system/probe`、`/can/send`、`/spi/send`)及其 OPTIONS 预检全部注册成功 +- **AND** 注册行为与原逐条 `httpd_register_uri_handler` 调用完全等价 + +### Requirement: OtaRelay 事件拒绝逻辑收敛 +系统 SHALL 通过 `commonReject(event, allowHealth)` helper 统一各状态 handler 头部的 ABORT/TIMEOUT/HEALTH_CRIT 拒绝分支。 + +#### Scenario: 拒绝分支等价 +- **WHEN** 任一 `handle*` 收到 `RELAY_EVT_ABORT` +- **THEN** 状态转为 `RELAY_ERROR` 并返回 `OTA_RESULT_ERR_USER_ABORT` +- **WHEN** 收到 `RELAY_EVT_TIMEOUT` +- **THEN** 状态转为 `RELAY_ERROR` 并返回 `OTA_RESULT_ERR_TIMEOUT` +- **WHEN** `handleTransferring` 收到 `RELAY_EVT_HEALTH_CRIT` +- **THEN** 状态转为 `RELAY_ERROR` 并返回 `OTA_RESULT_ERR_HEALTH` + +### Requirement: SpiMaster 单一加锁路径 +系统 SHALL 通过单一内部实现(`*Impl`)承载加锁与无锁两路调用,公共 API 包一层锁,已持锁路径直调 Impl,消除两份镜像逻辑。 + +#### Scenario: 加锁语义不变 +- **WHEN** 外部调用 `syncTransfer/send/otaPollResponse` +- **THEN** 内部先获取 `m_mutex` 再执行传输 +- **WHEN** 已持锁的内部路径调用 Impl 版本 +- **THEN** 不重复加锁,避免递归死锁 + +## MODIFIED Requirements + +### Requirement: OtaCoordinator GD32 存储操作入口 +传输层通过 `OtaCoordinator` 的统一入口操作 GD32 Flash(解决 H3 绕过协调器问题)。9 个纯转发 wrapper(`writeGd32Chunk/saveGd32Meta/loadGd32Meta/updateGd32Meta/clearGd32Meta/verifyGd32Firmware/finalizeGd32Checksum/eraseGd32Partition`)改为 `inline` 定义在头文件中,直接委托 `Gd32FlashStore::instance()`,签名与调用方语义不变。 + +### Requirement: OtaCoordinator 状态查询 +`isIdle / isGd32Busy / isEsp32Busy` SHALL 各仅调用一次 `getSystemState()`(即一次 `lock()`),在局部变量上完成所有比较。`getSystemStateString` 同样仅取值一次。 + +### Requirement: OtaRelay forwardFirmware 状态驱动 +`forwardFirmware()` SHALL 以状态驱动循环推进 IDLE→HANDSHAKING→SIZE_NEG→TRANSFERRING→FINISHING→DONE,任一步返回非 OK 即跳出并复位,不再使用 `goto cleanup`。5 步驱动顺序与超时检查时机保持不变。 + +### Requirement: HealthMonitor 状态访问加锁 +`HealthMonitor` 所有访问 `m_healthState / m_lastTemperature / m_lastWifiRssi / m_lastWifiRssiMs` 的方法 SHALL 通过 RAII `LockGuard`(构造 take、析构 give)保护,与 SpiMaster 的 LockGuard 风格一致。 + +## REMOVED Requirements +(无移除) diff --git a/.trae/specs/simplify-arch-boilerplate/tasks.md b/.trae/specs/simplify-arch-boilerplate/tasks.md new file mode 100644 index 0000000..b29a4a4 --- /dev/null +++ b/.trae/specs/simplify-arch-boilerplate/tasks.md @@ -0,0 +1,50 @@ +# Tasks + +- [x] Task 1: 快速清理仓库根临时文件 + - [x] SubTask 1.1: 删除 `old_wifi_ota.cpp.tmp`、`original_app.js.tmp`、`appjs_diff.txt`、`build_baseline.log`、`build_output.log` + - [x] SubTask 1.2: 评估 `sdkconfig.bak.mbedtls_trim` / `sdkconfig.ci.c3eco7` / `sdkconfig.ci.esp32c2*` / `sdkconfig.ci.wpa_psk` 是否与 ESP32-C3FH4 目标相关,无关则删除(需确认未被 CMake/CI 引用)— 已确认未被 CMake/CI 引用,均为其他芯片变体配置,已删除 + +- [x] Task 2: A.2 — OtaCoordinator 状态查询单次取值 + - [x] SubTask 2.1: `isIdle()` 改为 `ota_system_state_t s = getSystemState();` 后一次比较 IDLE/STORED/COMPLETE + - [x] SubTask 2.2: `isGd32Busy()` 已是单次取值,确认无需改动 + - [x] SubTask 2.3: `isEsp32Busy()` 已是单次取值,确认无需改动 + - [x] SubTask 2.4: `getSystemStateString()` 改为 `switch(getSystemState())` 单次取值(当前每次 case 隐式调用) + +- [x] Task 3: A.1 — OtaCoordinator 9 个存储 wrapper inline 化 + - [x] SubTask 3.1: 在 `ota_coordinator.h` 中将 `writeGd32Chunk/saveGd32Meta/loadGd32Meta/updateGd32Meta/clearGd32Meta/verifyGd32Firmware/finalizeGd32Checksum/eraseGd32Partition` 改为 `inline` 实现并直接委托 `Gd32FlashStore::instance()` + - [x] SubTask 3.2: 删除 `ota_coordinator.cpp` 中这 8 个函数的对应实现(注意 `writeGd32Chunk` 等) + - [x] SubTask 3.3: 确认头文件已 `#include "storage/gd32_flash_store.h"`(当前已包含) + +- [x] Task 4: B — WiFi HTTP URI 路由表化 + - [x] SubTask 4.1: 在 `wifi_ota.cpp::httpServerStart()` 顶部定义 `UriEntry { uri, method, handler }` 静态数组,列出 9 个端点 + - [x] SubTask 4.2: 用 for 循环对每个 entry 注册主 method + HTTP_OPTIONS(`http_options_handler`) + - [x] SubTask 4.3: 保留 NVS 恢复固件状态的前置逻辑与启动日志不变 + - [x] SubTask 4.4: 确认 `max_uri_handlers=32` 仍足够(9×2=18,余量充足) + +- [x] Task 5: C — OtaRelay 事件分发去重 + - [x] SubTask 5.1: 在 `ota_relay.cpp` 匿名命名空间添加 `commonReject(relay_event_t e, bool allowHealth=false)`,返回 `OTA_RESULT_OK` 或对应错误码(不改状态) + - [x] SubTask 5.2: `handleHandshaking/handleSizeNeg/handleTransferring/handleFinishing` 开头调用 `commonReject`,仅在其返回非 OK 时 `setState(RELAY_ERROR)` 并返回(`handleTransferring` 传 `allowHealth=true`) + - [x] SubTask 5.3: `forwardFirmware()` 将 5 次顺序 `processEvent(START)` 改为 while 循环:当前状态非 DONE/ERROR 时持续推进,任一步非 OK 即跳出;去掉 `goto cleanup`,失败路径统一 `reset()` + - [x] SubTask 5.4: 保持 5 步驱动顺序与"同步 handler 执行后才检查超时"的语义不变 + +- [x] Task 6: D — SpiMaster *Unlocked 镜像折叠 + - [x] SubTask 6.1: 保留 `transferClocked` / `transferRequestResponse` / `otaPollResponseUnlocked` 作为内部 Impl(已持锁路径调用) + - [x] SubTask 6.2: `syncTransfer` 公共 API:`LockGuard` + 直接调 `transferRequestResponse`(删除 `syncTransferUnlocked`,其唯一调用方 `transferWithRetry` 改为调 `transferRequestResponse`) + - [x] SubTask 6.3: `send` 公共 API:`LockGuard` + 直接调 `transferRequestResponse`(删除 `sendUnlocked`,确认无外部调用方) + - [x] SubTask 6.4: `otaPollResponse` 公共 API:`LockGuard` + 调 `otaPollResponseUnlocked`(保持不变,已是该模式) + - [x] SubTask 6.5: 更新 `spi_master.h`:移除已删除的 `syncTransferUnlocked`/`sendUnlocked` 声明 + +- [x] Task 7: F — HealthMonitor RAII LockGuard + - [x] SubTask 7.1: 在 `health_monitor.h` 私有区添加 `LockGuard` 内部类(与 SpiMaster 风格一致:构造 take portMAX_DELAY,析构 give,禁拷贝) + - [x] SubTask 7.2: `getState/setState/getLastTemperature/getLastWifiRssi/tempMonitorCallback` 改用 `LockGuard lock(m_mutex);` + - [x] SubTask 7.3: `setWifiConnected` 中 `m_wifiConnected` 为 `volatile`,确认是否仍需加锁(当前未加锁,保持现状,仅文档化其为原子推送) + +- [x] Task 8: 编译验证 + - [x] SubTask 8.1: 运行 `idf.py build` 确认无编译错误/警告回归(15 个源文件 0 warning / 0 error,CMake 配置 + 编译阶段均通过;ninja 链接阶段因 TRAE 沙箱与 Windows overlapped I/O 不兼容无法在 IDE 内执行,需在 ESP-IDF PowerShell 终端外完成) + - [x] SubTask 8.2: 确认 `main/` 行数减少符合预期(关键削减:httpServerStart 173→67、ota_coordinator.cpp 8 wrapper 删除、spi_master.cpp 2 Unlocked 删除、ota_relay.cpp goto 重构) + +# Task Dependencies +- Task 3(A.1 inline 化)依赖 Task 2(A.2)先完成,避免合并冲突 +- Task 5(OtaRelay)与 Task 6(SpiMaster)独立,可并行 +- Task 7(HealthMonitor)独立,可并行 +- Task 8(编译验证)依赖 Task 2~7 全部完成 diff --git a/_verify_syntax.py b/_verify_syntax.py new file mode 100644 index 0000000..cd8182b --- /dev/null +++ b/_verify_syntax.py @@ -0,0 +1,100 @@ +# 临时语法验证脚本:用 riscv32-esp-elf-g++ -fsyntax-only 验证 gd32_flash_store.cpp +# 验证完成后删除本脚本。 +import json +import os +import shlex +import subprocess +import sys + +ROOT = r"g:\Robot_v2\DCU\ESP32_COMM" +CC_PATH = os.path.join(ROOT, "build", "compile_commands.json") +# 对照测试:可切换目标源文件 +TARGET_NAME = os.environ.get("VERIFY_SRC", "gd32_flash_store.cpp") +TARGET_SRC = os.path.join(ROOT, "main", "storage", TARGET_NAME) if TARGET_NAME == "gd32_flash_store.cpp" else os.path.join(ROOT, "main", "ota", TARGET_NAME) +RESP = os.path.join(ROOT, "build", "_syntax_resp.txt") + +with open(CC_PATH, encoding="utf-8") as f: + db = json.load(f) + +entry = None +for e in db: + if e.get("file", "").endswith(TARGET_NAME): + entry = e + break +if not entry: + print("ERROR: 未找到", TARGET_NAME, "的编译条目") + sys.exit(2) + +work_dir = entry["directory"] +# 任务要求:gcc_path 从 compile_commands.json 第一条 command 提取 +first_cmd = shlex.split(db[0]["command"], posix=False) +gcc_path = first_cmd[0] +gxx_path = gcc_path.replace("riscv32-esp-elf-gcc.exe", "riscv32-esp-elf-g++.exe") +if gxx_path == gcc_path: + print("ERROR: 未能从 gcc 路径推导 g++ 路径:", gcc_path) + sys.exit(2) +if not os.path.exists(gxx_path): + print("ERROR: g++ 不存在:", gxx_path) + sys.exit(2) + +# 目标条目的编译参数(去掉首位的编译器路径,改用推导出的 g++) +args = shlex.split(entry["command"], posix=False) + +# 过滤:去掉 -o 及其参数,去掉源文件名 +filtered = [] +i = 1 +while i < len(args): + a = args[i] + if a == "-o": + i += 2 # 跳过 -o 和输出文件 + continue + if a.startswith("-o") and len(a) > 2: + i += 1 # -oXXX 形式 + continue + if os.path.abspath(a) == os.path.abspath(TARGET_SRC): + i += 1 + continue + if a.endswith("gd32_flash_store.cpp"): + i += 1 + continue + filtered.append(a) + i += 1 + +# 组装新命令:g++ + 原 args(去 -o/源文件) + -fsyntax-only -Wall + 源文件 +# 注意1:原始构建用 -std=gnu++26(GNU 扩展模式),若覆盖为 -std=c++17 会定义 __STRICT_ANSI__, +# 导致 newlib 的 FILE 类型不暴露而报错(与本次修改无关的 newlib 兼容性问题)。 +# 故保留原始 -std=gnu++26,仅追加 -fsyntax-only -Wall;-Werror 原始已有,可严格检查本文件。 +# 注意2:freertos/esp_hw_support 头文件链引用 FILE,但独立 syntax check 时 newlib +# 的 FILE typedef 被跳过(ota_coordinator.cpp 同样报错,与本次修改无关)。 +# 加 -include stdio.h 预先定义 FILE,绕过此无关问题,仅验证本文件语法。 +final_args = [gxx_path] + filtered + ["-fsyntax-only", "-Wall", "-include", "stdio.h", TARGET_SRC] + +# 写 response file:每行一个参数。 +# GCC @file 解析时反斜杠会被当转义符吃掉,因此对所有参数统一处理: +# 反斜杠 -> \\,双引号 -> \",并用双引号包裹(双引号内 \\=\" 才是字面)。 +def quote_for_resp(p): + esc = p.replace("\\", "\\\\").replace('"', '\\"') + return '"' + esc + '"' + +with open(RESP, "w", encoding="utf-8") as f: + for a in final_args[1:]: # 第一个是 gxx 本身,放命令行 + f.write(quote_for_resp(a) + "\n") + +print("WORK_DIR:", work_dir) +print("GXX:", gxx_path) +print("RESP:", RESP) +print("ARGS(excl gxx):", len(final_args) - 1, "params") + +# 诊断:预处理后查找 FILE 的 typedef,确认 FILE 是否被定义 +diag_args = [gxx_path] + filtered + ["-E", "-include", "stdio.h", TARGET_SRC] +print("--- 诊断(-E,grep typedef FILE/__FILE/__sFILE) ---") +proc = subprocess.run(diag_args, cwd=work_dir, capture_output=True, text=True) +import re +for line in proc.stdout.splitlines(): + s = line.strip() + if ("typedef" in s and ("FILE" in s or "__sFILE" in s)): + print(s) +print("RETURN CODE:", proc.returncode) + + + diff --git a/appjs_diff.txt b/appjs_diff.txt deleted file mode 100644 index d883efa..0000000 --- a/appjs_diff.txt +++ /dev/null @@ -1,1314 +0,0 @@ -diff --git a/tools/app.js b/tools/app.js -index 8a3a857..7742275 100644 ---- a/tools/app.js -+++ b/tools/app.js -@@ -15,6 +15,96 @@ let isEsp32Rebooting = false; - let rebootStartTime = 0; - // ==================== 刷写进度 ==================== - var activePhase = null; // 'esp32Ota' | 'gd32Upload' | 'gd32Relay' | null -+var currentOtaTarget = null; // 'esp32' | 'gd32' | null — 当前 OTA 目标,决定显示哪套阶段 -+// 切换 OTA 阶段面板显示 -+function switchStageFlow(target) { -+ currentOtaTarget = target; -+ var esp32El = document.getElementById('esp32Stages'); -+ var gd32El = document.getElementById('gd32Stages'); -+ if (target === 'esp32') { -+ if (esp32El) esp32El.style.display = ''; -+ if (gd32El) gd32El.style.display = 'none'; -+ } else { -+ if (esp32El) esp32El.style.display = 'none'; -+ if (gd32El) gd32El.style.display = ''; -+ } -+ // 重置 ESP32 阶段节点 -+ ['es1','es2','es3','es4','es5','es6'].forEach(function(id) { -+ var el = document.getElementById(id); -+ if (el) el.className = 'stage-node'; -+ }); -+ ['esd1','esd2','esd3','esd4','esd5','esd6'].forEach(function(id) { -+ var el = document.getElementById(id); -+ if (el) el.textContent = ''; -+ }); -+ // 重置 GD32 阶段节点 -+ ['s1','s2','s3','s4','s5','s6'].forEach(function(id) { -+ var el = document.getElementById(id); -+ if (el) el.className = 'stage-node'; -+ }); -+ ['sd1','sd2','sd3','sd4','sd5','sd6'].forEach(function(id) { -+ var el = document.getElementById(id); -+ if (el) el.textContent = ''; -+ }); -+} -+// ESP32 OTA 阶段状态: 0=待机, 1=上传中, 2=OTA写入, 3=SHA-256校验, 4=应用固件, 5=重启中, 6=启动完成 -+var esp32OtaPhase = 0; -+var esp32PhaseLabels = ['', '上传中', '写入中', '校验中', '应用中', '重启中', '已完成']; -+function updateEsp32StagesFromPhase(phase) { -+ esp32OtaPhase = phase; -+ var ids = ['es1', 'es2', 'es3', 'es4', 'es5', 'es6']; -+ var detailIds = ['esd1', 'esd2', 'esd3', 'esd4', 'esd5', 'esd6']; -+ var all = ids.map(function(id) { return document.getElementById(id); }); -+ var details = detailIds.map(function(id) { return document.getElementById(id); }); -+ all.forEach(function (s) { if (s) s.className = 'stage-node'; }); -+ details.forEach(function (d) { if (d) d.textContent = ''; }); -+ var statusBar = document.getElementById('stageStatusBar'); -+ var statusText = document.getElementById('stageStatusText'); -+ if (phase <= 0) { -+ if (statusBar) statusBar.className = 'stage-status-bar'; -+ if (statusText) statusText.textContent = '待机'; -+ return; -+ } -+ for (var i = 0; i < phase - 1 && i < 6; i++) { -+ if (all[i]) all[i].className = 'stage-node done'; -+ } -+ if (phase >= 1 && phase <= 6) { -+ if (all[phase - 1]) all[phase - 1].className = 'stage-node live'; -+ if (details[phase - 1]) details[phase - 1].textContent = esp32PhaseLabels[phase] || ''; -+ } -+ if (statusBar && statusText) { -+ if (phase >= 6) { -+ statusBar.className = 'stage-status-bar ok'; -+ statusText.textContent = 'ESP32 刷写完成'; -+ } else { -+ statusBar.className = 'stage-status-bar active'; -+ statusText.textContent = (esp32PhaseLabels[phase] || '进行中') + ' (' + phase + '/6)'; -+ } -+ } -+} -+function updateEsp32StagesError() { -+ var ids = ['es1', 'es2', 'es3', 'es4', 'es5', 'es6']; -+ var all = ids.map(function(id) { return document.getElementById(id); }); -+ all.forEach(function (s) { if (s) s.className = 'stage-node'; }); -+ var statusBar = document.getElementById('stageStatusBar'); -+ var statusText = document.getElementById('stageStatusText'); -+ if (statusBar) statusBar.className = 'stage-status-bar err'; -+ if (statusText) statusText.textContent = '校验/应用失败'; -+} -+ -+function showVerifyActions(type) { -+ // type: 'apply' | 'retry' | 'none' -+ var actions = document.getElementById('stageVerifyActions'); -+ if (!actions) return; -+ if (type === 'none') { actions.style.display = 'none'; return; } -+ actions.style.display = 'block'; -+ var applyBtn = document.getElementById('applyEsp32Btn'); -+ var retryBtn = document.getElementById('retryEsp32Btn'); -+ var cancelBtn = document.getElementById('cancelEsp32Btn'); -+ if (applyBtn) applyBtn.style.display = (type === 'apply') ? 'inline-block' : 'none'; -+ if (retryBtn) retryBtn.style.display = (type === 'retry') ? 'inline-block' : 'none'; -+ if (cancelBtn) cancelBtn.style.display = (type === 'retry') ? 'inline-block' : 'none'; -+} - - function setActivePhase(phase) { - activePhase = phase; -@@ -29,18 +119,16 @@ function setActivePhase(phase) { - var fill = document.getElementById('progressFill'); - if (fill) fill.classList.remove('done'); - } -- - function markPhaseDone(phase) { - var stepMap = { esp32Ota: 'ppEsp32', gd32Upload: 'ppGd32Up', gd32Relay: 'ppGd32Relay' }; - var el = document.getElementById(stepMap[phase]); - if (el) { el.classList.remove('active'); el.classList.add('done'); } - // All done? turn progress bar green -- if (phase === 'gd32Relay') { -+ if (phase === 'gd32Relay' || phase === 'esp32Ota') { - var fill = document.getElementById('progressFill'); - if (fill) fill.classList.add('done'); - } - } -- - function setProgress(pct, text) { - var fillEl = document.getElementById('progressFill'); - var txtEl = document.getElementById('progressTxt'); -@@ -49,10 +137,11 @@ function setProgress(pct, text) { - if (txtEl) txtEl.textContent = text; - if (pctEl) pctEl.textContent = pct + '%'; - } -- - function resetProgress() { - setProgress(0, '等待操作...'); - activePhase = null; -+ esp32OtaPhase = 0; -+ switchStageFlow(null); - ['ppEsp32', 'ppGd32Up', 'ppGd32Relay'].forEach(function(id) { - var el = document.getElementById(id); - if (el) el.classList.remove('active', 'done'); -@@ -60,14 +149,12 @@ function resetProgress() { - var fill = document.getElementById('progressFill'); - if (fill) fill.classList.remove('done'); - } -- - // ==================== 工具函数 ==================== - function formatBytes(bytes) { - if (bytes < 1024) return bytes + ' B'; - if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; - return (bytes / (1024 * 1024)).toFixed(2) + ' MB'; - } -- - function formatTime(date) { - var h = String(date.getHours()).padStart(2, '0'); - var m = String(date.getMinutes()).padStart(2, '0'); -@@ -75,7 +162,6 @@ function formatTime(date) { - var ms = String(date.getMilliseconds()).padStart(3, '0'); - return h + ':' + m + ':' + s + '.' + ms; - } -- - function formatUptime(sec) { - if (!Number.isFinite(sec) || sec < 0) return '--'; - const d = Math.floor(sec / 86400); -@@ -87,13 +173,11 @@ function formatUptime(sec) { - if (m > 0) return m + 'm ' + s + 's'; - return s + 's'; - } -- - function formatHttpErrorDetail(text) { - var detail = (text || '').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim(); - if (detail.length > 180) detail = detail.slice(0, 177) + '...'; - return detail; - } -- - function showToast(message, type) { - type = type || 'success'; - const toast = document.getElementById('toast'); -@@ -104,32 +188,25 @@ function showToast(message, type) { - msg.textContent = message; - setTimeout(function () { toast.classList.remove('show'); }, 3000); - } -- - function getEspIp() { - return document.getElementById('espIp').value.trim() || '192.168.31.127'; - } -- - function getBaseUrl() { - return 'http://' + getEspIp(); - } -- - // ==================== 日志系统 (带去重 + 自动滚动) ==================== - function makeLogHash(level, tag, message) { - return level + '|' + tag + '|' + message; - } -- - function addLog(level, tag, message) { - var scroll = document.getElementById('logScroll'); - var time = formatTime(new Date()); - var hash = makeLogHash(level, tag, message); - if (hash === lastLogHash) return; // 去重 - lastLogHash = hash; -- - var entry = { time: time, level: level, tag: tag, message: message }; - logEntries.push(entry); -- - if (logFilter !== 'all' && level !== logFilter) return; -- - var div = document.createElement('div'); - div.className = 'log-line'; - div.innerHTML = -@@ -138,18 +215,15 @@ function addLog(level, tag, message) { - '[' + tag + ']' + - '' + message + ''; - scroll.appendChild(div); -- - if (autoScroll) scroll.scrollTop = scroll.scrollHeight; - document.getElementById('logCount').textContent = logEntries.length; - } -- - function clearLogs() { - document.getElementById('logScroll').innerHTML = ''; - logEntries = []; - lastLogHash = null; - document.getElementById('logCount').textContent = '0'; - } -- - function setLogFilter(level, btn) { - document.querySelectorAll('.log-filter').forEach(function (b) { b.classList.remove('active'); }); - btn.classList.add('active'); -@@ -170,27 +244,23 @@ function setLogFilter(level, btn) { - }); - if (autoScroll) scroll.scrollTop = scroll.scrollHeight; - } -- - function toggleAutoScroll() { - autoScroll = !autoScroll; - var btn = document.getElementById('autoScrollBtn'); - btn.textContent = autoScroll ? '\uD83D\uDCCC 自动滚动' : '\u23F8\uFE0F 暂停滚动'; - } -- - function updateUploadButtonLabel() { - var esp32Btn = document.getElementById('uploadEsp32Btn'); - var gd32Btn = document.getElementById('uploadGd32Btn'); - if (esp32Btn) esp32Btn.disabled = !esp32File || isUploading; - if (gd32Btn) gd32Btn.disabled = !gd32File || isUploading; - } -- - function setHelper(id, text, type) { - var el = document.getElementById(id); - if (!el) return; - el.className = 'helper-note ' + (type || ''); - el.innerHTML = text; - } -- - function updateCanHint() { - var dlc = parseInt(document.getElementById('canDlc').value) || 0; - var dataHex = document.getElementById('canData').value.trim().replace(/\s/g, ''); -@@ -213,14 +283,20 @@ function updateCanHint() { - } - setHelper('canHint', 'HEX 数据合法,当前长度 ' + bytes + ' 字节,与 DLC 一致。', 'ok'); - } -- - function resetStageFlow() { -- ['s1', 's2', 's3', 's4'].forEach(function (id) { -+ ['s1', 's2', 's3', 's4', 's5', 's6'].forEach(function (id) { - var el = document.getElementById(id); - if (el) el.className = 'stage-node'; - }); -+ ['sd1', 'sd2', 'sd3', 'sd4', 'sd5', 'sd6'].forEach(function (id) { -+ var el = document.getElementById(id); -+ if (el) el.textContent = ''; -+ }); -+ var statusBar = document.getElementById('stageStatusBar'); -+ if (statusBar) statusBar.className = 'stage-status-bar'; -+ var statusText = document.getElementById('stageStatusText'); -+ if (statusText) statusText.textContent = '待机'; - } -- - function applyDisconnectedRuntimeState() { - document.getElementById('pillIP').textContent = '--'; - document.getElementById('pillRSSI').textContent = '--'; -@@ -233,43 +309,35 @@ function applyDisconnectedRuntimeState() { - resetProgress(); - resetStageFlow(); - } -- - function applyRebootingRuntimeState() { - var pc = document.getElementById('pillConn'); - var pct = document.getElementById('pillConnTxt'); - pc.className = 'pill processing'; - var elapsed = Math.round((Date.now() - rebootStartTime) / 1000); - pct.textContent = '重启中 ' + elapsed + 's'; -- - var cs = document.getElementById('connStatus'); - var cst = document.getElementById('connStatusTxt'); - cs.className = 'conn-indicator warn'; - cst.textContent = 'ESP32 正在重启...'; -- - var bc = document.getElementById('bConn'); - bc.textContent = '重启中'; - bc.className = 'v warn'; -- - document.getElementById('pillRelayTxt').textContent = '重启中'; - document.getElementById('pillRelay').className = 'pill processing'; - } -- - function applyUploadingRuntimeState() { - var pc = document.getElementById('pillConn'); - var pct = document.getElementById('pillConnTxt'); - pc.className = 'pill processing'; - pct.textContent = '上传中'; -- - var cs = document.getElementById('connStatus'); - var cst = document.getElementById('connStatusTxt'); - cs.className = 'conn-indicator warn'; - cst.textContent = '固件上传中,设备忙...'; -- - var bc = document.getElementById('bConn'); - bc.textContent = '上传中'; - bc.className = 'v warn'; - } -- - // ==================== 连接管理 ==================== - function updateConnectionUi(connected, text) { - // header pill -@@ -277,19 +345,16 @@ function updateConnectionUi(connected, text) { - var pct = document.getElementById('pillConnTxt'); - pc.className = connected ? 'pill online' : 'pill offline'; - pct.textContent = text || (connected ? '已连接' : '未连接'); -- - // connection card indicator - var cs = document.getElementById('connStatus'); - var cst = document.getElementById('connStatusTxt'); - cs.className = connected ? 'conn-indicator ok' : 'conn-indicator err'; - cst.textContent = connected ? (text || '已连接') : '未连接'; -- - // primary badge - var bc = document.getElementById('bConn'); - bc.textContent = connected ? '在线' : '离线'; - bc.className = 'v ' + (connected ? 'ok' : 'err'); - } -- - function updateHealthUi(healthCode, freeHeap) { - var healthNames = ['正常', '警告', '危险', '关机']; - var health = healthCode !== undefined ? (healthNames[healthCode] || '未知') : '未知'; -@@ -301,70 +366,93 @@ function updateHealthUi(healthCode, freeHeap) { - document.getElementById('mHeap').textContent = formatBytes(freeHeap); - } - } -- - // ==================== OTA 阶段驱动 (真实状态) ==================== -+// Relay state → 6-stage mapping: -+// Stage 1 (认证握手): rs 1-6 握手/挑战/会话密钥/认证 -+// Stage 2 (传输协商): rs 7-8 固件大小协商 -+// Stage 3 (数据转发): rs 9-13 数据包收发/ACK/重发 -+// Stage 4 (传输完成): rs 14-15 数据接收完毕 -+// Stage 5 (OTA刷写): rs 16-17 OTA 结束/完成 -+// Stage 6 (校验验证): rs 18 APP 校验 -+// Error: rs 255 -+var relayStateDetailMap = { -+ 0: '待机', -+ 1: '等待握手', 2: '握手已发', 3: '挑战已发', -+ 4: '收到握手响应', 5: '会话密钥已发', 6: '认证完成', -+ 7: '大小已发', 8: '大小确认', -+ 9: '接收中', 10: '转发中', 11: '数据包已发', 12: 'ACK正常', 13: '等待重发', -+ 14: '接收完成', 15: '传输完成', -+ 16: '结束已发', 17: 'OTA完成', -+ 18: 'APP验证', -+ 255: '错误' -+}; - function updateStagesFromRelayState(rs) { -- var s1 = document.getElementById('s1'); -- var s2 = document.getElementById('s2'); -- var s3 = document.getElementById('s3'); -- var s4 = document.getElementById('s4'); -- var all = [s1, s2, s3, s4]; -- -+ var ids = ['s1', 's2', 's3', 's4', 's5', 's6']; -+ var detailIds = ['sd1', 'sd2', 'sd3', 'sd4', 'sd5', 'sd6']; -+ var all = ids.map(function(id) { return document.getElementById(id); }); -+ var details = detailIds.map(function(id) { return document.getElementById(id); }); - // reset all -- all.forEach(function (s) { s.className = 'stage-node'; }); -- -- if (rs === undefined || rs === null) return; -- -+ all.forEach(function (s) { if (s) s.className = 'stage-node'; }); -+ details.forEach(function (d) { if (d) d.textContent = ''; }); -+ // status bar -+ var statusBar = document.getElementById('stageStatusBar'); -+ var statusText = document.getElementById('stageStatusText'); -+ if (rs === undefined || rs === null) { -+ if (statusBar) statusBar.className = 'stage-status-bar'; -+ if (statusText) statusText.textContent = '待机'; -+ return; -+ } - if (rs === 255) { -- s1.className = 'stage-node err'; -- s2.className = 'stage-node err'; -- s3.className = 'stage-node err'; -- s4.className = 'stage-node err'; -+ all.forEach(function (s) { if (s) s.className = 'stage-node err'; }); -+ if (statusBar) statusBar.className = 'stage-status-bar err'; -+ if (statusText) statusText.textContent = '错误'; - return; - } -- - var activeStage = 0; - if (rs >= 1 && rs <= 6) activeStage = 1; - else if (rs >= 7 && rs <= 8) activeStage = 2; -- else if (rs >= 9 && rs <= 15) activeStage = 3; -- else if (rs >= 16 && rs <= 18) activeStage = 4; -- -+ else if (rs >= 9 && rs <= 13) activeStage = 3; -+ else if (rs >= 14 && rs <= 15) activeStage = 4; -+ else if (rs >= 16 && rs <= 17) activeStage = 5; -+ else if (rs >= 18 && rs <= 18) activeStage = 6; - for (var i = 0; i < activeStage - 1; i++) { -- all[i].className = 'stage-node done'; -+ if (all[i]) all[i].className = 'stage-node done'; -+ } -+ if (activeStage >= 1 && activeStage <= 6) { -+ if (all[activeStage - 1]) all[activeStage - 1].className = 'stage-node live'; -+ } -+ // Show detail text for the active stage -+ var detailText = relayStateDetailMap[rs] || ''; -+ if (activeStage >= 1 && activeStage <= 6 && details[activeStage - 1]) { -+ details[activeStage - 1].textContent = detailText; - } -- if (activeStage >= 1 && activeStage <= 4) { -- all[activeStage - 1].className = 'stage-node live'; -+ // Update status bar -+ if (statusBar && statusText) { -+ if (rs === 0) { -+ statusBar.className = 'stage-status-bar'; -+ statusText.textContent = '待机'; -+ } else { -+ statusBar.className = 'stage-status-bar active'; -+ statusText.textContent = (relayStateDetailMap[rs] || '未知') + ' (' + rs + ')'; -+ } - } - } -- - function updateSystemStatusUi(data) { - if (!data) return; - latestSystemStatus = data; -- - var wifi = data.wifi || {}; - var ota = data.ota || {}; - var healthCode = data.health; -- -- var relayStates = { -- 0: '待机', 1: '等待握手', 2: '握手已发', 3: '挑战已发', -- 4: '收到握手响应', 5: '会话密钥已发', 6: '认证完成', -- 7: '大小已发', 8: '大小确认', 9: '接收中', 10: '转发中', -- 11: '数据包已发', 12: 'ACK正常', 13: '等待重发', -- 14: '接收完成', 15: '传输完成', 16: '结束已发', 17: 'OTA完成', 18: 'APP验证', 255: '错误' -- }; -- -+ // Use global relayStateDetailMap defined in updateStagesFromRelayState section - var connected = !!wifi.connected; - var connText = connected ? (wifi.mode || 'STA') + ' 已连接' : (wifi.mode || 'STA') + ' 未连接'; -- - updateConnectionUi(connected, connText); - updateHealthUi(healthCode, data.free_heap); -- - // version - if (data.version) { - document.getElementById('pillVer').textContent = 'v' + data.version; - document.getElementById('pillVer').title = data.build_time ? ('编译: ' + data.build_time) : ''; - document.getElementById('mVersion').textContent = 'v' + data.version + (data.build_time ? ' (' + data.build_time + ')' : ''); -- - // GD32 版本 - var mGd32App = document.getElementById('mGd32App'); - var mGd32Boot = document.getElementById('mGd32Boot'); -@@ -387,28 +475,23 @@ function updateSystemStatusUi(data) { - } - } - } -- - // temperature - document.getElementById('mTemp').textContent = - Number.isFinite(data.temperature) ? data.temperature.toFixed(1) + '\u00B0C' : '--'; -- - // uptime - var uptimeEl = document.getElementById('pillUptime'); - if (uptimeEl) uptimeEl.textContent = '\u23F1 ' + formatUptime(data.uptime_sec); -- - // wifi / signal - document.getElementById('mWifi').textContent = wifi.connected ? (wifi.ip || '已连接') : '未连接'; - document.getElementById('pillIP').textContent = wifi.ip || '--'; - var rssi = (wifi.rssi !== undefined && wifi.rssi !== -127) ? wifi.rssi + ' dBm' : '--'; - document.getElementById('mRSSI').textContent = rssi; - document.getElementById('pillRSSI').textContent = rssi; -- - // heap - document.getElementById('mHeap').textContent = - data.free_heap !== undefined ? formatBytes(data.free_heap) : '--'; - document.getElementById('mMinHeap').textContent = - data.min_free_heap !== undefined ? formatBytes(data.min_free_heap) : '--'; -- - // ota cache - var otaStored = ota.stored_size || 0; - var mOtaCache = document.getElementById('mOtaCache'); -@@ -421,17 +504,21 @@ function updateSystemStatusUi(data) { - mOtaCache.className = 'v'; - } - } -- -- // relay state -+ // relay state - skip GD32 relay UI when ESP32 OTA is active -+ if (currentOtaTarget === 'esp32' && esp32OtaPhase > 0) { -+ document.getElementById('pillRelayTxt').textContent = esp32OtaPhase >= 6 ? 'ESP32 OTA 完成' : 'ESP32 OTA'; -+ var pr = document.getElementById('pillRelay'); -+ if (pr) pr.className = esp32OtaPhase >= 6 ? 'pill online' : 'pill processing'; -+ return; -+ } - var rs = ota.relay_state; -- var relayText = relayStates[rs] || '待机'; -+ var relayText = relayStateDetailMap[rs] || '待机'; - document.getElementById('pillRelayTxt').textContent = relayText; - var pr = document.getElementById('pillRelay'); - if (rs === 255) pr.className = 'pill offline'; - else if (rs > 0 && rs < 17) pr.className = 'pill processing'; - else if (rs === 17 || rs === 18) pr.className = 'pill online'; - else pr.className = 'pill idle'; -- - // OTA cache progress (GD32 upload to ESP32 flash) - var total = ota.total_size || 0; - var stored = ota.stored_size || 0; -@@ -439,27 +526,46 @@ function updateSystemStatusUi(data) { - var bp = document.getElementById('bOTAProgress'); - bp.textContent = storageProgress + '%'; - bp.className = 'v ' + (storageProgress === 100 ? 'ok' : storageProgress > 0 ? 'warn' : ''); -- - // GD32 relay forwarding progress - var relaySeq = ota.relay_seq || 0; - var relayTotal = ota.relay_total_packets || 0; - var relayProgress = relayTotal > 0 ? Math.min(100, Math.round(relaySeq * 100 / relayTotal)) : 0; -- - var gd32AppAlive = ota.gd32_app_alive; -- if (rs > 0 && rs < 17) { -- // Relay is active — show relay progress -+ if (rs > 0 && rs <= 8) { -+ // Authentication or size negotiation phase -+ setActivePhase('gd32Relay'); -+ if (rs <= 6) { -+ setProgress(0, '认证中: ' + (relayStateDetailMap[rs] || '')); -+ } else { -+ setProgress(0, '协商固件大小...'); -+ } -+ } else if (rs >= 9 && rs <= 13) { -+ // Active data forwarding - setActivePhase('gd32Relay'); - setProgress(relayProgress, '已转发 ' + relaySeq + ' / ' + relayTotal + ' 包'); -+ } else if (rs >= 14 && rs <= 15) { -+ // Transfer complete -+ setActivePhase('gd32Relay'); -+ setProgress(100, '数据转发完成 (' + relayTotal + ' 包)'); -+ } else if (rs === 16) { -+ // OTA end sent -+ setActivePhase('gd32Relay'); -+ setProgress(100, 'OTA 刷写中...'); - } else if (rs === 17) { -- // Relay done, waiting for APP verify -+ // OTA complete - setActivePhase('gd32Relay'); -- setProgress(100, '转发完成,等待 APP 启动...'); -+ setProgress(100, 'OTA 完成,等待 APP 启动...'); - } else if (rs === 18) { - // APP verify phase - setActivePhase('gd32Relay'); - if (gd32AppAlive) { - markPhaseDone('gd32Relay'); - setProgress(100, 'GD32 刷写完成'); -+ // Mark status bar as success -+ var sb = document.getElementById('stageStatusBar'); -+ var st = document.getElementById('stageStatusText'); -+ if (sb) sb.className = 'stage-status-bar ok'; -+ if (st) st.textContent = 'GD32 刷写完成'; - } else { - setProgress(100, '转发完成,验证 APP 存活...'); - } -@@ -471,46 +577,60 @@ function updateSystemStatusUi(data) { - // Relay finished and reset, but APP is alive → truly done - markPhaseDone('gd32Relay'); - setProgress(100, 'GD32 刷写完成'); -+ var sb2 = document.getElementById('stageStatusBar'); -+ var st2 = document.getElementById('stageStatusText'); -+ if (sb2) sb2.className = 'stage-status-bar ok'; -+ if (st2) st2.textContent = 'GD32 刷写完成'; - } -- - // stage flow from real state -- updateStagesFromRelayState(rs); -+ if (currentOtaTarget !== 'esp32') { updateStagesFromRelayState(rs); } - } -- - async function testConnection() { -- if (!latestSystemStatus) { -- addLog('W', '连接', '暂无同步数据,请等待 1 秒自动同步'); -- showToast('暂无同步数据,请稍等 1 秒', 'warning'); -- return; -+ // 如果在降速模式,先恢复正常轮询 -+ if (pollSlowMode) { -+ addLog('I', '连接', '正在重新连接...'); -+ restorePolling(); -+ } -+ // 立即发一次请求检测 -+ try { -+ var resp = await fetch(getBaseUrl() + '/system/status', { -+ signal: AbortSignal.timeout(3000) -+ }); -+ if (resp.ok) { -+ var data = await resp.json(); -+ latestSystemStatus = data; -+ updateSystemStatusUi(data); -+ var wifi = data.wifi || {}; -+ addLog('I', '连接', wifi.connected -+ ? '已连接:' + (wifi.mode || 'STA') + ' ' + (wifi.ip || '') -+ : '未连接:' + (wifi.mode || 'STA')); -+ showToast(wifi.connected ? 'ESP32 当前在线' : 'ESP32 当前未连接', -+ wifi.connected ? 'success' : 'warning'); -+ } else { -+ addLog('E', '连接', 'HTTP ' + resp.status); -+ showToast('设备响应异常: HTTP ' + resp.status, 'error'); -+ } -+ } catch (e) { -+ addLog('E', '连接', '连接失败: ' + e.message); -+ // 连接失败时自动诊断 -+ var diag = await diagnoseNetwork(); -+ addLog('W', '诊断', diag); -+ showToast('无法连接: ' + diag, 'error'); - } -- var wifi = latestSystemStatus.wifi || {}; -- var connected = !!wifi.connected; -- updateSystemStatusUi(latestSystemStatus); -- addLog('I', '连接', connected -- ? '当前已连接:' + (wifi.mode || 'STA') + ' ' + (wifi.ip || '') -- : '当前未连接:' + (wifi.mode || 'STA')); -- showToast(connected ? 'ESP32 当前在线' : 'ESP32 当前未连接', -- connected ? 'success' : 'warning'); - } -- - // ==================== CAN 帧发送 ==================== - async function sendCanFrame() { - var ch = parseInt(document.getElementById('canChannel').value); - var idStr = document.getElementById('canId').value.trim(); - var dlc = parseInt(document.getElementById('canDlc').value) || 8; - var dataStr = document.getElementById('canData').value.trim(); -- - var extId = 0; - if (idStr.toLowerCase().indexOf('0x') === 0) extId = parseInt(idStr, 16); - else { extId = parseInt(idStr, 10); if (isNaN(extId)) extId = parseInt(idStr, 16); } -- - if (isNaN(extId)) { showToast('ID 格式错误', 'error'); return; } -- - var dataHex = dataStr.replace(/\s/g, ''); - if (dataHex.length % 2 !== 0) { showToast('数据长度必须为偶数', 'error'); return; } -- - addLog('I', 'CAN', '发送 CH' + ch + ' ID=0x' + extId.toString(16).toUpperCase() + ' DLC=' + dlc); -- - try { - var resp = await fetch(getBaseUrl() + '/can/send', { - method: 'POST', headers: { 'Content-Type': 'application/json' }, -@@ -534,7 +654,6 @@ async function sendCanFrame() { - showToast('发送失败', 'error'); - } - } -- - function clearCanFields() { - document.getElementById('canId').value = '0x18DA00F1'; - document.getElementById('canDlc').value = '8'; -@@ -542,13 +661,11 @@ function clearCanFields() { - document.getElementById('canResp').style.display = 'none'; - updateCanHint(); - } -- - // ==================== SPI 发送 ==================== - function updateSpiHint() { - var typeHex = document.getElementById('spiType').value.trim(); - var dataHex = document.getElementById('spiData').value.trim().replace(/\s/g, ''); - var hint = document.getElementById('spiHint'); -- - if (!typeHex || !/^[0-9a-fA-F]{1,2}$/.test(typeHex)) { - hint.className = 'helper-note err'; - hint.innerHTML = 'type 需填 1 字节 hex(如 01, B0)。'; -@@ -571,17 +688,14 @@ function updateSpiHint() { - (bytes > 0 ? ',payload: ' + bytes + 'B' : ',无 payload') + - ',线上 ' + totalOnWire + 'B(SOF+TYPE+LEN+PAYLOAD+CRC)'; - } -- - async function sendSpiFrame() { - var typeHex = document.getElementById('spiType').value.trim(); - var dataStr = document.getElementById('spiData').value.trim(); - var timeout = parseInt(document.getElementById('spiTimeout').value) || 1000; -- - if (!typeHex || !/^[0-9a-fA-F]{1,2}$/.test(typeHex)) { - showToast('type 需填 1 字节 hex', 'error'); - return; - } -- - var dataHex = dataStr.replace(/\s/g, ''); - if (dataHex && !/^[0-9a-fA-F]*$/.test(dataHex)) { - showToast('数据包含非 HEX 字符', 'error'); -@@ -591,14 +705,10 @@ async function sendSpiFrame() { - showToast('HEX 数据长度必须为偶数', 'error'); - return; - } -- - var txBytes = dataHex ? dataHex.length / 2 : 0; -- - addLog('I', 'SPI', 'type=0x' + typeHex.toUpperCase() + - (txBytes > 0 ? ', payload=' + dataHex.toUpperCase() : ', 无负载')); -- - var body = { type: typeHex, data: dataHex.toUpperCase(), timeout_ms: timeout }; -- - try { - var resp = await fetch(getBaseUrl() + '/spi/send', { - method: 'POST', -@@ -607,7 +717,6 @@ async function sendSpiFrame() { - signal: AbortSignal.timeout(timeout + 2000) - }); - var result = await resp.json(); -- - if (result.status === 'ok') { - var recvHex = result.recv || ''; - var respTypeName = {'80': 'ACK', '81': 'WAIT', '82': 'ERROR'}; -@@ -634,7 +743,6 @@ async function sendSpiFrame() { - showToast('SPI 发送失败: ' + e.message, 'error'); - } - } -- - function clearSpiFields() { - document.getElementById('spiType').value = '01'; - document.getElementById('spiData').value = ''; -@@ -642,13 +750,11 @@ function clearSpiFields() { - document.getElementById('spiResp').style.display = 'none'; - updateSpiHint(); - } -- - // ==================== 文件处理 ==================== - function handleFileSelect(event, chip) { - var file = event.target.files[0]; - if (file) processFile(file, chip); - } -- - // 通过 File System Access API 选择文件(支持热重载) - async function pickFileViaFSAPI(chip) { - if (!window.showOpenFilePicker) { -@@ -679,7 +785,6 @@ async function pickFileViaFSAPI(chip) { - } - return false; - } -- - function setupDropZone(zoneId, chip) { - var zone = document.getElementById(zoneId); - if (!zone) return; -@@ -697,15 +802,11 @@ function setupDropZone(zoneId, chip) { - } - setupDropZone('uploadZoneEsp32', 'esp32'); - setupDropZone('uploadZoneGd32', 'gd32'); -- - document.getElementById('canData').addEventListener('input', updateCanHint); - document.getElementById('canDlc').addEventListener('input', updateCanHint); -- - document.getElementById('spiType').addEventListener('input', updateSpiHint); - document.getElementById('spiData').addEventListener('input', updateSpiHint); -- - // ==================== Intel HEX → BIN 转换 ==================== -- - /** - * 将 Intel HEX 字符串解析为原始 BIN 字节数组。 - * -@@ -720,17 +821,14 @@ function parseIntelHex(hexText) { - var segments = []; // { offset, data } 列表 - var minAddr = 0xFFFFFFFF; - var maxAddr = 0; -- - var lines = hexText.split(/\r?\n/); - for (var i = 0; i < lines.length; i++) { - var line = lines[i].trim(); - if (!line || line[0] !== ':') continue; -- - // 解析行: :LLAAAATT[DD...]CC - var byteCount = parseInt(line.substr(1, 2), 16); - var addr16 = parseInt(line.substr(3, 4), 16); - var recType = parseInt(line.substr(7, 2), 16); -- - // 校验和验证 - var sum = 0; - for (var j = 1; j < line.length; j += 2) { -@@ -739,7 +837,6 @@ function parseIntelHex(hexText) { - if ((sum & 0xFF) !== 0) { - throw new Error('HEX 行 ' + (i + 1) + ' 校验和错误: ' + line); - } -- - if (recType === 0x01) { - // EOF - break; -@@ -763,11 +860,9 @@ function parseIntelHex(hexText) { - } - // 其他记录类型 (03, 05 等) 忽略 - } -- - if (segments.length === 0) { - throw new Error('HEX 文件中没有有效数据记录'); - } -- - // 合并为连续 bin,间隙填 0xFF - var totalSize = maxAddr - minAddr; - var bin = new Uint8Array(totalSize); -@@ -778,10 +873,8 @@ function parseIntelHex(hexText) { - var off = seg.offset - minAddr; - bin.set(seg.data, off); - } -- - return { data: bin, baseAddr: minAddr, endAddr: maxAddr }; - } -- - /** - * 检测文件内容是否为 Intel HEX 格式。 - * 看前几个非空行是否以 ':' 开头。 -@@ -795,13 +888,10 @@ function isIntelHexText(text) { - } - return false; - } -- - // ==================== 文件处理 ==================== -- - function processFile(file, chip) { - var suffix = chip === 'esp32' ? 'Esp32' : 'Gd32'; - var isHexByName = file.name.toLowerCase().endsWith('.hex'); -- - if (chip === 'gd32' && isHexByName) { - // GD32 固件: .hex 文件自动转换为 .bin,然后替换文件对象 - var reader = new FileReader(); -@@ -825,12 +915,10 @@ function processFile(file, chip) { - var binBlob = new Blob([result.data], { type: 'application/octet-stream' }); - var binFile = new File([binBlob], binName, { type: 'application/octet-stream' }); - gd32File = binFile; -- - var baseHex = '0x' + result.baseAddr.toString(16).toUpperCase().padStart(8, '0'); - addLog('I', '文件', 'HEX → BIN 转换完成: ' + file.name + ' → ' + binName); - addLog('I', '文件', '基地址: ' + baseHex + ', 数据大小: ' + formatBytes(result.data.length)); - showToast('HEX 已自动转换为 BIN', 'success'); -- - document.getElementById('fileInfo' + suffix).classList.add('show'); - document.getElementById('fileName' + suffix).textContent = binName + ' (由 ' + file.name + ' 转换)'; - document.getElementById('fileType' + suffix).textContent = 'Binary (HEX转换)'; -@@ -847,7 +935,6 @@ function processFile(file, chip) { - reader.readAsText(file); - return; // 异步,回调里设置 gd32File - } -- - // .bin 文件或 ESP32 文件: 直接使用 - if (chip === 'esp32') esp32File = file; - else gd32File = file; -@@ -859,23 +946,41 @@ function processFile(file, chip) { - updateUploadButtonLabel(); - addLog('I', '文件', chip.toUpperCase() + ' 已选择: ' + file.name + ' (' + formatBytes(file.size) + ')'); - showToast(chip.toUpperCase() + ' 文件已选择', 'success'); -+ // ESP32 文件: 计算并显示 SHA-256 -+ if (chip === 'esp32') { -+ var shaElem = document.getElementById('fileSha256' + suffix); -+ if (shaElem) { -+ shaElem.textContent = '计算中...'; -+ file.arrayBuffer().then(function(buf) { -+ return crypto.subtle.digest('SHA-256', buf); -+ }).then(function(hashBuffer) { -+ var hashArray = Array.from(new Uint8Array(hashBuffer)); -+ var hashHex = hashArray.map(function(b) { return b.toString(16).padStart(2, '0'); }).join(''); -+ shaElem.textContent = hashHex.toUpperCase(); -+ // 存储到全局变量供上传时使用 -+ window._esp32FileSha256 = hashHex.toUpperCase(); -+ addLog('I', '文件', 'ESP32 固件 SHA-256: ' + hashHex.toUpperCase()); -+ }).catch(function(err) { -+ shaElem.textContent = '计算失败: ' + err.message; -+ }); -+ } -+ } - } -- - function clearFile(chip) { - var suffix = chip === 'esp32' ? 'Esp32' : 'Gd32'; -- if (chip === 'esp32') { esp32File = null; esp32FileHandle = null; } -+ if (chip === 'esp32') { esp32File = null; esp32FileHandle = null; window._esp32FileSha256 = null; } - else { gd32File = null; gd32FileHandle = null; } - document.getElementById('fileInput' + suffix).value = ''; - document.getElementById('fileInfo' + suffix).classList.remove('show'); -+ var shaElem = document.getElementById('fileSha256' + suffix); -+ if (shaElem) shaElem.textContent = '-'; - updateUploadButtonLabel(); - addLog('I', '文件', chip.toUpperCase() + ' 文件已清除'); - } -- - // ==================== 上传固件 ==================== - async function startUpload(chip) { - var file = chip === 'esp32' ? esp32File : gd32File; - if (!file || isUploading) return; -- - // 每次上传前尝试重新从本地读取文件 - var handle = chip === 'esp32' ? esp32FileHandle : gd32FileHandle; - if (handle) { -@@ -911,21 +1016,43 @@ async function startUpload(chip) { - return; - } - } -- - isUploading = true; - var uploadBtn = document.getElementById(chip === 'esp32' ? 'uploadEsp32Btn' : 'uploadGd32Btn'); - var origText = uploadBtn.textContent; - uploadBtn.disabled = true; - uploadBtn.textContent = '上传中...'; -+ switchStageFlow(chip); - setActivePhase(chip === 'esp32' ? 'esp32Ota' : 'gd32Upload'); -+ if (chip === 'esp32') updateEsp32StagesFromPhase(1); -+ // Reset verification panel -+ var verifyInfo = document.getElementById('stageVerifyInfo'); -+ if (verifyInfo) verifyInfo.style.display = 'none'; -+ var sviFront = document.getElementById('sviFrontSha'); -+ var sviDevice = document.getElementById('sviDeviceSha'); -+ var sviResult = document.getElementById('sviResult'); -+ if (sviFront) sviFront.textContent = '-'; -+ if (sviDevice) sviDevice.textContent = '-'; -+ if (sviResult) { sviResult.textContent = '-'; sviResult.className = 'svi-val'; } - addLog('I', '上传', '开始上传: ' + file.name + ' \u2192 ' + chip.toUpperCase()); - showToast('开始上传固件到 ' + chip.toUpperCase(), 'success'); -- - try { - var arrayBuffer = await file.arrayBuffer(); -+ // 刷写时重新计算 SHA-256 -+ if (chip === 'esp32') { -+ try { -+ var hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer); -+ var hashArray = Array.from(new Uint8Array(hashBuffer)); -+ var hashHex = hashArray.map(function(b) { return b.toString(16).padStart(2, '0'); }).join(''); -+ window._esp32FileSha256 = hashHex.toUpperCase(); -+ var shaElem = document.getElementById('fileSha256Esp32'); -+ if (shaElem) shaElem.textContent = window._esp32FileSha256; -+ addLog('I', '文件', '刷写前重新计算 SHA-256: ' + window._esp32FileSha256); -+ } catch (e) { -+ addLog('W', '文件', 'SHA-256 重新计算失败: ' + e.message); -+ } -+ } - var fileSize = file.size; - var body, url = getBaseUrl() + '/ota/upload'; -- - if (chip === 'esp32') { - var headerObj = { type: 'esp32', total_size: fileSize }; - var payload = arrayBuffer; -@@ -958,7 +1085,6 @@ async function startUpload(chip) { - document.getElementById('bOTAProgress').textContent = pct + '%'; - } - }); -- - xhr.addEventListener('load', function () { - isUploading = false; - uploadBtn.disabled = false; -@@ -970,22 +1096,19 @@ async function startUpload(chip) { - setProgress(100, '上传完成'); - if (chip === 'esp32') { - markPhaseDone('esp32Ota'); -- var countdown = 3; -- addLog('I', 'OTA', countdown + ' 秒后自动调用 POST /ota/update 完成升级并重启...'); -- var timer = setInterval(function () { -- countdown--; -- if (countdown > 0) addLog('I', 'OTA', countdown + ' 秒后开始刷写...'); -- else { -- clearInterval(timer); -- addLog('I', 'OTA', '开始刷写!'); -- triggerEsp32Update(); -- } -- }, 1000); -+ updateEsp32StagesFromPhase(2); // OTA 写入阶段 -+ addLog('I', 'OTA', '固件上传完成,准备 SHA-256 校验...'); -+ verifyEsp32Firmware(); - } else { -+ switchStageFlow('gd32'); - markPhaseDone('gd32Upload'); - setActivePhase('gd32Relay'); - setProgress(0, '等待触发转发...'); - } -+ } else if (xhr.status === 403) { -+ addLog('E', '上传', 'ESP32 OTA 被其他通道占用(BLE/WiFi 互斥),请稍后重试'); -+ showToast('ESP32 OTA 被其他通道占用,请稍后重试', 'error'); -+ setProgress(0, 'OTA 被其他通道占用'); - } else { - var detail = formatHttpErrorDetail(xhr.responseText); - var message = '上传失败: HTTP ' + xhr.status + (detail ? ' - ' + detail : ''); -@@ -993,7 +1116,6 @@ async function startUpload(chip) { - showToast(detail ? ('上传失败: ' + detail) : '上传失败', 'error'); - } - }); -- - xhr.addEventListener('error', function () { - isUploading = false; - uploadBtn.disabled = false; -@@ -1002,7 +1124,6 @@ async function startUpload(chip) { - addLog('E', '上传', '上传过程发生错误'); - showToast('上传出错', 'error'); - }); -- - xhr.open('POST', url); - xhr.send(body); - } catch (e) { -@@ -1014,28 +1135,118 @@ async function startUpload(chip) { - updateUploadButtonLabel(); - } - } -- - // ==================== ESP32 OTA 完成升级 ==================== --async function triggerEsp32Update() { -- addLog('I', 'OTA', '=== ESP32 OTA 完成,开始重启 ==='); -- showToast('ESP32 正在重启...', 'success'); -+async function verifyEsp32Firmware() { -+ updateEsp32StagesFromPhase(3); // 校验中 -+ var sha256 = window._esp32FileSha256; -+ if (sha256) { -+ addLog('I', 'OTA', '前端文件 SHA-256: ' + sha256); -+ var verifyInfo = document.getElementById('stageVerifyInfo'); -+ if (verifyInfo) verifyInfo.style.display = 'block'; -+ var frontSha = document.getElementById('sviFrontSha'); -+ if (frontSha) frontSha.textContent = sha256; -+ } -+ -+ showToast('ESP32 正在校验固件...', 'success'); - try { -+ var requestBody = {}; -+ if (sha256) requestBody.sha256 = sha256; -+ requestBody.verify_only = true; -+ - var resp = await fetch(getBaseUrl() + '/ota/update', { -- method: 'POST', signal: AbortSignal.timeout(5000) -+ method: 'POST', -+ headers: { 'Content-Type': 'application/json' }, -+ body: JSON.stringify(requestBody), -+ signal: AbortSignal.timeout(120000) -+ }); -+ -+ var data = await resp.json(); -+ -+ if (data.status === 'ok') { -+ addLog('I', 'OTA', '设备端 SHA-256: ' + (data.sha256 || '-')); -+ if (data.sha256) { -+ var deviceSha = document.getElementById('sviDeviceSha'); -+ if (deviceSha) deviceSha.textContent = data.sha256; -+ var resultEl = document.getElementById('sviResult'); -+ if (sha256 && sha256.toLowerCase() === data.sha256.toLowerCase()) { -+ addLog('I', 'OTA', 'SHA-256 验证通过!固件完整性确认,自动应用...'); -+ showToast('SHA-256 验证通过,自动应用中...', 'success'); -+ if (resultEl) { resultEl.textContent = '验证通过'; resultEl.className = 'svi-val ok'; } -+ showVerifyActions('none'); -+ applyEsp32Firmware(); -+ } -+ } -+ } else { -+ var detail = data.message || (data.device ? '不匹配' : '未知错误'); -+ addLog('E', 'OTA', '校验失败: ' + detail); -+ showToast('校验失败: ' + detail, 'error'); -+ var resultEl = document.getElementById('sviResult'); -+ if (resultEl) { resultEl.textContent = '❌ ' + detail; resultEl.className = 'svi-val err'; } -+ updateEsp32StagesError(); -+ showVerifyActions('retry'); -+ } -+ } catch (e) { -+ addLog('E', 'OTA', '校验请求失败: ' + e.message); -+ showToast('校验请求失败', 'error'); -+ updateEsp32StagesError(); -+ showVerifyActions('retry'); -+ } -+} -+ -+async function retryEsp32Upload() { -+ addLog('I', 'OTA', '重新上传固件...'); -+ showVerifyActions('none'); -+ var verifyInfo = document.getElementById('stageVerifyInfo'); -+ if (verifyInfo) verifyInfo.style.display = 'none'; -+ var sviFront = document.getElementById('sviFrontSha'); -+ var sviDevice = document.getElementById('sviDeviceSha'); -+ var sviResult = document.getElementById('sviResult'); -+ if (sviFront) sviFront.textContent = '-'; -+ if (sviDevice) sviDevice.textContent = '-'; -+ if (sviResult) { sviResult.textContent = '-'; sviResult.className = 'svi-val'; } -+ startUpload('esp32'); -+} -+ -+async function cancelEsp32Ota() { -+ addLog('I', 'OTA', '已取消 OTA 刷写'); -+ showToast('已取消 OTA', 'error'); -+ showVerifyActions('none'); -+ resetProgress(); -+} -+ -+async function applyEsp32Firmware() { -+ addLog('I', 'OTA', '确认应用固件并重启 ESP32...'); -+ showToast('正在应用固件并重启...', 'success'); -+ try { -+ var resp = await fetch(getBaseUrl() + '/ota/update', { -+ method: 'POST', -+ headers: { 'Content-Type': 'application/json' }, -+ body: JSON.stringify({}), -+ signal: AbortSignal.timeout(120000) - }); -+ - if (resp.ok) { - var data = await resp.json(); -- addLog('I', 'OTA', '升级完成: ' + data.message); -- showToast('ESP32 重启中...', 'success'); -+ addLog('I', 'OTA', '升级完成: ' + (data.message || '')); -+ updateEsp32StagesFromPhase(4); // 应用固件 - isEsp32Rebooting = true; -+ updateEsp32StagesFromPhase(5); // 重启中 - rebootStartTime = Date.now(); -+ } else { -+ var detail = '应用失败: HTTP ' + resp.status; -+ try { var d = await resp.json(); detail += ' - ' + (d.message || ''); } catch (_) {} -+ addLog('E', 'OTA', detail); -+ showToast(detail, 'error'); - } - } catch (e) { -- addLog('E', 'OTA', '升级失败: ' + e.message); -- showToast('升级失败', 'error'); -+ addLog('W', 'OTA', '应用请求出现异常(设备可能已重启): ' + e.message); -+ isEsp32Rebooting = true; -+ updateEsp32StagesFromPhase(5); -+ rebootStartTime = Date.now(); - } - } - -+ - async function restartEsp32() { - if (!confirm('确定要重启 ESP32 吗?')) return; - addLog('I', '系统', '=== 请求重启 ESP32 ==='); -@@ -1056,12 +1267,10 @@ async function restartEsp32() { - showToast('重启失败', 'error'); - } - } -- - // ==================== 触发 OTA 中继 (真实状态驱动) ==================== - async function triggerRelay() { - addLog('I', 'OTA', '触发 GD32 OTA 中继...'); - showToast('正在触发 OTA 中继', 'success'); -- - try { - var resp = await fetch(getBaseUrl() + '/ota/relay', { - method: 'POST', signal: AbortSignal.timeout(5000) -@@ -1081,8 +1290,78 @@ async function triggerRelay() { - showToast('触发失败', 'error'); - } - } -- - // ==================== 轮询状态 ==================== -+var consecutivePollFailures = 0; -+var statusTimerId = null; -+var logsTimerId = null; -+var pollSlowMode = false; // 降速模式(10s 一次) -+var lastDiagTime = 0; // 上次诊断时间 -+var diagResult = ''; // 最近一次诊断结果 -+// 从设备 IP 推导网关 IP(取前三段 + .1) -+function getGatewayIp() { -+ var parts = getEspIp().split('.'); -+ if (parts.length === 4) return parts[0] + '.' + parts[1] + '.' + parts[2] + '.1'; -+ return '192.168.31.1'; -+} -+// 网络诊断:区分浏览器离线 / 网关不通 / 仅设备不通 -+async function diagnoseNetwork() { -+ lastDiagTime = Date.now(); -+ var result = []; -+ // 1) 浏览器是否在线 -+ if (navigator.onLine === false) { -+ result.push('浏览器无网络连接'); -+ diagResult = result.join(';'); -+ return diagResult; -+ } -+ // 2) 尝试触达网关(超时 2s) -+ var gwIp = getGatewayIp(); -+ var gwOk = false; -+ try { -+ await fetch('http://' + gwIp + '/', { mode: 'no-cors', signal: AbortSignal.timeout(2000) }); -+ gwOk = true; // no-cors 模式下不抛异常即视为可达 -+ } catch (_) {} -+ if (!gwOk) { -+ // 再试一次 favicon(有些路由器不响应 /) -+ try { -+ await fetch('http://' + gwIp + '/favicon.ico', { mode: 'no-cors', signal: AbortSignal.timeout(1500) }); -+ gwOk = true; -+ } catch (_) {} -+ } -+ if (!gwOk) { -+ result.push('网关 ' + gwIp + ' 不可达(本地网络不通)'); -+ } else { -+ result.push('网关 ' + gwIp + ' 可达'); -+ // 3) 网关通但设备不通 → 设备自身问题 -+ result.push('设备 ' + getEspIp() + ' 不响应(可能断电、重启中或 WiFi 断开)'); -+ } -+ diagResult = result.join(';'); -+ return diagResult; -+} -+function startPolling() { -+ consecutivePollFailures = 0; -+ pollSlowMode = false; -+ if (statusTimerId) clearInterval(statusTimerId); -+ if (logsTimerId) clearInterval(logsTimerId); -+ statusTimerId = setInterval(pollStatus, 1000); -+ logsTimerId = setInterval(pollLogs, 1000); -+} -+function slowDownPolling() { -+ pollSlowMode = true; -+ if (statusTimerId) clearInterval(statusTimerId); -+ if (logsTimerId) clearInterval(logsTimerId); -+ statusTimerId = setInterval(pollStatus, 10000); -+ logsTimerId = setInterval(pollLogs, 10000); -+} -+function restorePolling() { -+ if (pollSlowMode) { -+ pollSlowMode = false; -+ consecutivePollFailures = 0; -+ if (statusTimerId) clearInterval(statusTimerId); -+ if (logsTimerId) clearInterval(logsTimerId); -+ statusTimerId = setInterval(pollStatus, 1000); -+ logsTimerId = setInterval(pollLogs, 1000); -+ } -+} - async function pollStatus() { - try { - var resp = await fetch(getBaseUrl() + '/system/status', { -@@ -1090,14 +1369,43 @@ async function pollStatus() { - }); - if (resp.ok) { - var data = await resp.json(); -+ // 恢复:如果之前在降速模式,切回 1 秒轮询 -+ if (consecutivePollFailures > 0 || pollSlowMode) { -+ var wasSlow = pollSlowMode; -+ consecutivePollFailures = 0; -+ if (wasSlow) { -+ restorePolling(); -+ addLog('I', '系统', '设备已恢复在线,轮询恢复正常'); -+ showToast('设备已恢复在线', 'success'); -+ } -+ } - if (isEsp32Rebooting) { - isEsp32Rebooting = false; -+ updateEsp32StagesFromPhase(6); // 启动完成 -+ addLog('I', '系统', 'ESP32 已恢复在线'); -+ showToast('ESP32 已恢复在线', 'success'); -+ } else if (currentOtaTarget === 'esp32' && esp32OtaPhase >= 3 && esp32OtaPhase < 6) { -+ updateEsp32StagesFromPhase(6); - addLog('I', '系统', 'ESP32 已恢复在线'); - showToast('ESP32 已恢复在线', 'success'); - } - updateSystemStatusUi(data); - } - } catch (e) { -+ consecutivePollFailures++; -+ // 传输中屏蔽网关无响应的检测功能(上传期间设备忙,无法响应轮询是正常的) -+ if (consecutivePollFailures === 5 && !pollSlowMode && !isUploading) { -+ addLog('W', '诊断', '设备连续 5 次无响应,正在诊断网络...'); -+ var diag = await diagnoseNetwork(); -+ addLog('W', '诊断', diag); -+ updateConnectionUi(false, '未连接'); -+ applyDisconnectedRuntimeState(); -+ var cst = document.getElementById('connStatusTxt'); -+ if (cst) cst.textContent = '诊断中: ' + diag; -+ // 降速到 10 秒继续监测,等待设备恢复 -+ slowDownPolling(); -+ addLog('I', '系统', '已切换为慢速轮询(10s),持续监测设备恢复...'); -+ } - if (isUploading) { - applyUploadingRuntimeState(); - } else if (isEsp32Rebooting) { -@@ -1111,14 +1419,13 @@ async function pollStatus() { - } else { - applyRebootingRuntimeState(); - } -- } else { -+ } else if (!pollSlowMode) { - updateConnectionUi(false, '未连接'); - applyDisconnectedRuntimeState(); - } - } - } --setInterval(pollStatus, 1000); -- -+startPolling(); - // ==================== ESP32 实时日志拉取 (增量) ==================== - var lastLogTimestamp = 0; - async function pollLogs() { -@@ -1129,7 +1436,6 @@ async function pollLogs() { - if (!resp.ok) return; - var logs = await resp.json(); - if (!Array.isArray(logs) || logs.length === 0) return; -- - var levelMap = { E: 'E', W: 'W', I: 'I', D: 'D' }; - logs.forEach(function (line) { - var m = line.match(/^\[([EWID])\]\[([^\]]+)\]\s*(.*)$/); -@@ -1141,13 +1447,20 @@ async function pollLogs() { - }); - } catch (e) { /* 静默 */ } - } --setInterval(pollLogs, 1000); -- - setTimeout(function () { - updateUploadButtonLabel(); - updateCanHint(); - addLog('I', '系统', '控制台初始化完成,等待固件文件...'); - updateSpiHint(); -+ // IP 输入变化时自动恢复轮询 -+ var ipInput = document.getElementById('espIp'); -+ if (ipInput) { -+ ipInput.addEventListener('change', function () { -+ if (pollSlowMode) { -+ addLog('I', '系统', 'IP 已修改,重新启动轮询...'); -+ restorePolling(); -+ } -+ }); -+ } - }, 500); - -- diff --git a/docs/esp32_architecture_review_2026-06-27.md b/docs/esp32_architecture_review_2026-06-27.md new file mode 100644 index 0000000..00bb6d4 --- /dev/null +++ b/docs/esp32_architecture_review_2026-06-27.md @@ -0,0 +1,301 @@ +# ESP32 软件架构扫描报告(2026-06-27) + +## 背景 +上一轮评审(2026-06-25,见 `docs/esp32_architecture_review_2026-06-25.md`)已完成 8 项优化:根目录清理、OtaCoordinator 状态查询单次取值、9 个 GD32 存储 wrapper inline 化、HTTP URI 路由表化、OtaRelay commonReject helper、SpiMaster *Unlocked 折叠、HealthMonitor RAII LockGuard、编译验证。 + +本轮扫描基于上述优化完成后的代码,新发现约 74 项可优化点,分三类: +- **新发现**(约 65 项):上轮完全未涉及 +- **遗留**(3 项):上轮评审已提到但当时标"不在本次范围" +- **残留**(3 项):上轮已做但未完全覆盖 + +按风险/收益分 P0 / P1 / P2 三档。 + +--- + +## P0 优先(功能正确性 / 安全性 bug) + +### P0-1 metaVerifyChecksum 全零跳过校验是隐性后门 +- **文件**:[main/storage/gd32_flash_store.cpp:299-306](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L299-L306) +- **问题**:checksum 全零时直接返回 `ESP_OK`,注释"旧数据兼容"。meta 被意外清零时校验会"通过",掩盖固件损坏 +- **建议**:返回 `ESP_ERR_INVALID_STATE`,由调用方决定是否容忍 + +### P0-2 RSSI 状态机 CRITICAL→NORMAL 无法降级 +- **文件**:[main/monitor/health_monitor.cpp:333-357](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L333-L357) +- **问题**:"恢复到 NORMAL"只在 `cur == SYS_WARNING` 时触发,CRITICAL 状态下 RSSI 恢复正常不会降级 +- **建议**:明确状态转换表,补 CRITICAL → NORMAL 直接降级路径 + +### P0-3 OTA_MAX_FIRMWARE_SIZE 注释与 0x36000 不一致 +- **文件**:[main/include/app_config.h:62](file:///g:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h#L62) +- **问题**:`216*1024 = 221184`,注释"对齐 GD32 APP_MAX_SIZE (0x36000)",但 `0x36000 = 221296`,相差 112 字节,并非对齐 +- **建议**:确认正确值,用十六进制表达 `0x36000` 或修正注释 + +### P0-4 凭证硬编码入仓库 +- **文件**:[main/include/app_config.h:31,48,112-115](file:///g:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h#L31) +- **问题**:WiFi 密码、AP 密码、MASTER_KEY 明文入仓库,README 也标注"⚠️ 占位值,生产环境需更换" +- **建议**:迁移到 NVS/Kconfig + env 文件,CI 加凭证扫描规则 + +### P0-5 BleEsp32FinishCtx 三处 free 易漏 +- **文件**:[main/server/ble_ota.cpp:854,158,190,210](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L854) +- **问题**:ctx 在 3 个不同分支释放,`if (ctx) free(ctx)` 防御式写法暗示历史 bug;第 190 行后 `restartEsp32()` 不返回,理论上 free 不执行 +- **建议**:RAII 封装 `BleFinishCtxDeleter`,任务入口 `BleFinishCtxDeleter guard{ctx};`,所有 return 路径自动释放 +- **备注**:上轮评审 G 项已提到("不是精简,是健壮性,留后续"),本轮方案改为 RAII deleter(上轮原方案是改静态单例) + +### P0-6 GD32 固件大小校验逻辑双通道重复 +- **文件**:[main/server/ble_ota.cpp:1115-1121](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L1115-L1121) vs [main/server/http_handlers.cpp:302-308](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp#L302-L308) +- **问题**:两处完全相同的校验:`uint32_t rounded = ((total + PKT - 1) / PKT) * PKT; if (total == 0 || total > MAX || rounded > MAX) {...}`,但 BLE 用 `BLE_OTA_FINISH_ERR_GD32_SIZE` 上报,HTTP 用 `HTTPD_500` 返回,错误码语义不对齐 +- **建议**:抽取 `OtaCoordinator::validateGd32Size(total_size) -> esp_err_t`,两路调用方各自翻译为通道特定错误回报 + +### P0-7 doRelayPackets 180 行 + goto + 重试阈值混乱 +- **文件**:[main/relay/ota_relay.cpp:936-1115](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L936-L1115) +- **问题**:约 180 行,`for` → 内嵌 `while(1)` → 内嵌 `for` 重试 → 2 处 `goto resend_packet`(行 1057、1091);重试计数器阈值不一致:行 1015 `>=10U`、行 1032 `>=3U`、行 1050 `>=3U`、行 1075 `>=10U`、行 1084 `>=10U`,同一 `retry_count` 既限 3 又限 10 +- **建议**:拆分为 `preparePacket(seq)` + `sendPacketWithRetry(seq, tx_buf)` + 外层 `doRelayPackets`;重试阈值提取常量 `kMaxResendOnNack`/`kMaxResendOnStructError`/`kMaxResendOnPollExhaust` + +### P0-8 ble_ota_process_data_batch 245 行 +- **文件**:[main/server/ble_ota.cpp:975-1219](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L975-L1219) +- **问题**:单函数处理 ESP32 与 GD32 两条独立数据流,ESP32 分支内嵌 6 个子逻辑,GD32 分支内嵌 9 个子逻辑,循环内多处 `offset += packet_len; if (offset + 7 > recv_len) break;` 重复推进 +- **建议**:拆分为 `process_esp32_packet(...)` 与 `process_gd32_packet(...)`,主循环只负责解包 + 分发 + +--- + +## P1 优先(数据竞争 / 资源泄漏 / 双通道一致性) + +### P1-A OtaRelay 状态成员封装破坏(一组原子改动) + +**头文件**:[main/relay/ota_relay.h:228-237](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.h#L228-L237) + +**问题**:把 `relayState/relaySeq/relayTotalPackets/gd32AppAlive/gd32Version/taskHandle/s_relayListener/s_stateMutex` 全部声明为 `public static`,但 cpp 内部多处直接访问绕过带锁的 setter/getter,与外部带锁读取形成数据竞争: + +| 位置 | 直接访问 | +|---|---| +| [ota_relay.cpp:331-332](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L331-L332) | `handleSizeNeg` 直接写 `relayTotalPackets/relaySeq` 无锁 | +| [ota_relay.cpp:1102,1104-1105](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L1102-L1105) | `doRelayPackets` 直接写读 `relaySeq` 无锁 | +| [ota_relay.cpp:1189,1197-1198,1209](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L1189-L1209) | `doFinishAndVerify` 直接写读 `gd32AppAlive/gd32Version` 无锁 | +| [ota_relay.cpp:470,478](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L470-L478) | `forwardFirmware` 直接读 `relayState` 无锁 | +| [ota_relay.cpp:1272,1288](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L1272) | `taskEntry` 直接访问 `s_relayListener` 绕过 getter | + +**建议**:所有静态状态成员改 `private`,统一通过 setter/getter;relay task 内部若已持锁,在 `StateLockGuard` 块内访问。`s_stateMutex` 改 private,`StateLockGuard` 作为友元或保留 public 嵌套类。 + +### P1-9 getSnapshot 双重加锁且可能不一致 +- **文件**:[main/ota/ota_coordinator.cpp:571-591](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/ota_coordinator.cpp#L571-L591) +- **问题**:行 576-581 持锁读取 `m_sysState` 填入 `snap.sys_state` 后释放;行 582 调 `getSystemStateString()` 又内部调 `getSystemState()` 二次加锁。两次取值之间状态可能变化,导致 `snap.sys_state`(第一次取的值)与 `snap.sys_state_str`(第二次取的值)描述不同时刻状态,前端展示自相矛盾 +- **建议**:让 `getSystemStateString` 接受 `ota_system_state_t` 参数(重载 `static const char* stateToString(ota_system_state_t)`),`getSnapshot` 持锁时一次性取值并就地转字符串 +- **备注**:上轮 A.2 只改了 `isIdle/isGd32Busy/isEsp32Busy`,`getSnapshot` 未覆盖 + +### P1-10 getSystemStateString 等仍独立加锁 +- **文件**:[main/ota/ota_coordinator.cpp:509-549](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/ota_coordinator.cpp#L509-L549) +- **问题**:`isIdle`、`isGd32Busy`、`isEsp32Busy`、`getSystemStateString` 各自调 `getSystemState()`(加锁取值再解锁)。`if (!isIdle() && !isGd32Busy())` 这类连续调用会加锁 2~3 次 +- **建议**:提供"取一次状态后多次判断"的接口,或让调用方先 `getSystemState()` 取一次再传给各判断函数 +- **备注**:上轮 A.2 残留 + +### P1-11 esp32_ota_core finalize 3 处清理序列重复 +- **文件**:[main/ota/esp32_ota_core.cpp:281-288,312-318,332-339](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/esp32_ota_core.cpp#L281-L339) +- **问题**:3 处几乎相同:`esp_ota_abort(m_handle); m_ended = false; releaseMutex(); resetState(); return xxx;`(SHA 计算失败 / SHA 不匹配 / esp_ota_end 失败) +- **建议**:提取私有方法 `finalizeCleanupAndReturn(esp_err_t code)`,或用 `goto cleanup` + +### P1-12 esp32_ota_core begin 3 处清理序列重复 +- **文件**:[main/ota/esp32_ota_core.cpp:126-130,135-141,151-158](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/esp32_ota_core.cpp#L126-L158) +- **问题**:3 处清理序列:分区找不到 / 固件太大 / `esp_ota_begin` 失败,模式高度相似,差异仅在是否清零 `m_handle` 和返回码 +- **建议**:提取 `beginCleanupAndReturn(esp_err_t code)`,或统一出错点设置返回码后 `goto begin_fail` + +### P1-13 doFinishAndVerify 100 行双职责 +- **文件**:[main/relay/ota_relay.cpp:1118-1216](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L1118-L1216) +- **问题**:单函数承担两个协议阶段:(1) 发送 FINISH_OTA 并等 ACK(1120-1176);(2) APP 存活轮询验证(1184-1213)。函数名含 "Verify",但两个阶段职责不同、超时参数不同、重试策略不同,行 1191 `for (attempt < 30)` 与 FINISH_OTA 的 `for (retry < 10)` 嵌套语义混淆 +- **建议**:拆分为 `doSendFinishOta()` + `doWaitForAppAlive()`,`doFinishAndVerify` 改为顺序调用两者并做 session 清理 + +### P1-14 APP 存活探测模式三处重复 +- **文件**:[main/relay/ota_relay.cpp:1191-1207,1240-1261](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L1191-L1207) + [main/ota/ota_coordinator.cpp:596-615](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/ota_coordinator.cpp#L596-L615) +- **问题**:同一"调 `SpiMaster::appPing` + 写 `gd32AppAlive`/`gd32Version` + 日志版本号"模式在 3 处重复:(1) `doFinishAndVerify` 轮询(30 次重试);(2) `taskEntry` 启动探测(1 次);(3) `OtaCoordinator::probeGd32AppOnce`(1 次)。日志格式、版本号字段打印、成功/失败处理高度相似但不完全相同 +- **建议**:提取 `static void OtaRelay::probeAndCacheAppAlive(uint32_t timeout_ms, uint32_t* out_uptime)`,三处调用方分别指定重试次数和超时 + +### P1-15 setWifiConnected 未加锁 +- **文件**:[main/monitor/health_monitor.cpp:320-323,248](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L320-L323) +- **问题**:直接写 `volatile bool m_wifiConnected`,未走 `m_mutex`。虽然 `volatile` 保证可见性,但与 `m_healthState` 等其他状态的访问模式不一致。`checkBeforeOta` 行 248 读 `m_wifiConnected` 也未加锁,存在读写竞态 +- **建议**:统一通过 LockGuard 保护,或显式说明为何此字段例外(如使用 `std::atomic`) +- **备注**:上轮 F 给 HealthMonitor 加了 LockGuard,但 `m_wifiConnected` 漏了 + +### P1-16 温度 esp_timer_handle_t 是局部变量,资源无法回收 +- **文件**:[main/monitor/health_monitor.cpp:182-194](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L182-L194) +- **问题**:`esp_timer_handle_t temp_timer` 是局部变量未存为成员。任务若被删除,定时器持续运行,回调仍会访问 `instance()` 的成员 +- **建议**:将 `temp_timer` 存为成员,提供 `deinit()` 在任务删除时 `esp_timer_stop` + `esp_timer_delete` + +### P1-17 metaUpdate 读-改-写非原子 +- **文件**:[main/storage/gd32_flash_store.cpp:188-198](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L188-L198) +- **问题**:`metaLoad` → 修改 → `metaSave` 中间无锁,若两个任务同时调用 `metaUpdate`,会丢失更新 +- **建议**:在 Gd32FlashStore 内增加 NVS 互斥锁,或将"由调用方串行化"约束以运行时断言强制 + +### P1-18 metaLoad NVS open 失败一律当 OK +- **文件**:[main/storage/gd32_flash_store.cpp:142-145](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L142-L145) +- **问题**:`nvs_open` 失败时直接 `return ESP_OK;`(注释说"命名空间不存在时返回全零结构"),但 NVS open 失败原因可能不止"不存在"(如 NVS 损坏),一律当 OK 会掩盖真实错误 +- **建议**:区分 `ESP_ERR_NVS_NOT_FOUND`(OK + 全零)与其他错误(返回 err) + +### P1-19 DebugLog init 不检查 uartInit 返回值 +- **文件**:[main/monitor/debug_log.cpp:252-272](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L252-L272) +- **问题**:`uartInit();`(行 258)返回值被丢弃,UART 初始化失败仍设 `m_initialized = true` 并打印"调试日志已初始化" +- **建议**:检查返回值,失败时降级(仅 ring buffer)并在日志中标注 UART 不可用 + +### P1-20 ringDrainJson 容量估算与边界检查 +- **文件**:[main/monitor/debug_log.cpp:116-151](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L116-L151) +- **问题**:`capacity = count * (LOG_RING_SIZE * 2) + 16`,count=64、LOG_RING_SIZE=256 → 最大 32784 字节,单次 malloc 在 ESP32-C3(~160KB heap)上风险较大;`for` 循环 `pos < capacity - 4` 的边界检查在 `first` 分支后逗号写入(行 133)未做边界检查,可能溢出 +- **建议**:分块 realloc,或对单条日志超长时截断并标记 + +### P1-21 关键任务创建失败不停止初始化 +- **文件**:[main/core/main.cpp:69-73,101-105,117-121,133-137](file:///g:/Robot_v2/DCU/ESP32_COMM/main/core/main.cpp#L69-L137) +- **问题**:任务创建失败仅 `DEBUG_ERR`,不停止后续初始化,可能导致部分功能缺失但系统"看起来正常启动" +- **建议**:关键任务(health_mon、ota_relay)失败时 `ESP_ERROR_CHECK(ESP_FAIL)` 或进入 safe state + +### P1-22 s_ota_recv_buf 全局静态被双 handler 共享 +- **文件**:[main/server/http_handlers.cpp:36,249,337](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp#L36) +- **问题**:`static uint8_t s_ota_recv_buf[OTA_BLOCK_SIZE];` 在 ESP32 与 GD32 两条路径间共享。httpd 默认单线程不会并发,但若未来 `max_open_sockets > 1` 启用并发请求,两个 `ota_upload_handler` 同时跑会互相覆盖 buffer +- **建议**:改为 handler 内栈分配(若 `OTA_BLOCK_SIZE < 4KB` 可行),或改为 per-request 分配 + +### P1-23 一次性任务创建失败上报不一致 +- **文件**:[main/server/ble_ota.cpp:816-821,875-878,896-899](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L816-L899) +- **问题**: + - ble_gd32_relay_wait_task 创建失败:`free(ctx); sendFinishEvent(ERR_GD32_RELAY, ...);` — 完整上报 + - ble_esp32_finish_and_reboot_task 创建失败:`free(finish_ctx); m_esp32RebootPending = false;` — **未上报 FINISH 错误事件**,前端会一直等 + - ble_esp32_restart_task 创建失败:`m_esp32RebootPending = false;` — **未上报任何错误** +- **建议**:统一"任务创建失败 → 释放 ctx → 复位 pending 标志 → 上报 FINISH 错误事件"三步 + +### P1-24 reorder slot malloc 失败静默丢弃 +- **文件**:[main/server/ble_ota.cpp:513-518](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L513-L518) +- **问题**:malloc 失败时返回 `ESP_OK` 让上层认为已处理,但实际数据丢失。若客户端窗口已用完(无重传预算),固件写入会永久卡在缺口。注释说"靠客户端重传"但客户端无法感知设备端 malloc 失败 +- **建议**:malloc 失败时至少发一次 `sendWindowAck` 报告当前 `expected`,让客户端立即重传缺口;或返回错误触发 abort + +### P1-25 protocol.h 死代码 +- **文件**:[main/transport/protocol.h:16-18,25-29](file:///g:/Robot_v2/DCU/ESP32_COMM/main/transport/protocol.h#L16-L29) + [main/transport/protocol.cpp:96-118](file:///g:/Robot_v2/DCU/ESP32_COMM/main/transport/protocol.cpp#L96-L118) +- **问题**: + 1. `protocol.h` 仍定义 `FRAME_SOF 0xEB`、`FRAME_MAX_PAYLOAD 64`、`FRAME_OVERHEAD 6`,但 `protocol.cpp` 注释(第 8-10 行)明确说"帧构建、AES-CBC 加解密、CRC16 等中间件代码已移除",这三个常量无任何引用 + 2. `session_ctx_t.iv[16]` 在 `sessionKeyInit` 中被 `memset(iv, 0, 16)` 后再无任何写入;`session_ctx_t.used` 初始化为 0 后再无任何代码置 1。整个 `session_ctx_t` 实际只用到 `key[16]` +- **建议**:删除 `FRAME_SOF`/`FRAME_MAX_PAYLOAD`/`FRAME_OVERHEAD`;精简 `session_ctx_t` 为 `{ uint8_t key[16]; }` 或整体移除 + +### P1-26 GD32 OTA 写入序列两路径重复 +- **文件**:[main/server/ble_ota.cpp:1113-1208](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L1113-L1208)(BLE)vs [main/server/http_handlers.cpp:314-413](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp#L314-L413)(HTTP) +- **问题**:两路径都执行相同序列:`beginGd32Storage` → `saveGd32Meta` → 循环 `writeGd32Chunk` + `updateGd32Meta` → `completeGd32Storage` + `finalizeGd32Checksum`。但 HTTP 路径多了 `eraseGd32Partition` 和 `clearGd32Meta`(行 327、335),BLE 路径没有。这暗示 BLE 路径可能依赖前次 HTTP 上传后的已擦除状态,或在 `beginGd32Storage` 内隐式擦除——这种隐式假设是 bug 温床 +- **建议**:抽取 `OtaCoordinator::storeGd32FirmwareFromStream(...)` 接收"读块回调",BLE 与 HTTP 各自实现回调,统一擦除/写入/meta/完成序列 + +--- + +## P2 优先(样板抽取 / 配置化 / 风格统一) + +### P2-A 跨模块样板抽取(高 ROI) + +| # | 主题 | 涉及位置 | +|---|---|---| +| P2-27 | Hex 编解码工具统一 | [http_handlers.cpp:460-463,558-562,629-633](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp#L460-L633)、[ble_ota.cpp:174-178,642-649,861-863](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L174-L863) 共 6+ 处。**关键 bug**:SHA-256 在 BLE 路径大小写混用(`shaBytesToHexUpper` 大写 vs 行 861-863 `%02x` 小写),HTTP 路径用大写 `%02X`,前端比对 SHA 时大小写敏感会出错 | +| P2-28 | SHA-256 → hex 循环 | [esp32_ota_core.cpp:230-234,254-257,290-293,323-325](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/esp32_ota_core.cpp#L230-L325) 4 处 + [gd32_flash_store.cpp:283-286,316-320](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L283-L320) 2 处。提取 `static void sha256ToHex(const uint8_t in[32], char out[65])` | +| P2-29 | HTTP JSON body 接收+解析 | [http_handlers.cpp:71-95,436-452,522-535,595-608](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp#L71-L608) 4 处。缓冲区上限不一(256/512)。提取 `http_recv_json(httpd_req_t*, char*, size_t, cJSON**)` | +| P2-30 | HTTP 错误响应样板 | [http_handlers.cpp](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp) 17 处 `httpd_resp_send_err + return ESP_FAIL`。提取 `http_send_json_error/http_send_json_ok` | +| P2-31 | 忙状态判断 if 链 | [ota_coordinator.cpp:136-140](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/ota_coordinator.cpp#L136-L140)(beginGd32Storage) vs [ota_coordinator.cpp:346-350](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/ota_coordinator.cpp#L346-L350)(beginEsp32Ota)。提取 `bool isAnyChannelBusy() const` | +| P2-32 | 三个校验函数重复 metaLoad 样板 | [gd32_flash_store.cpp:265-275,292-298,331-351](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L265-L351) | + +### P2-B 函数过长 / 高圈复杂度 + +| # | 函数 | 行数 | 文件 | +|---|---|---|---| +| P2-33 | ble_ota_process_data_batch | 245 行 | [ble_ota.cpp:975-1219](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L975-L1219)(见 P0-8) | +| P2-34 | ota_upload_handler | 240 行 | [http_handlers.cpp:180-420](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp#L180-L420)。ESP32/GD32 双路径混杂,共享 `pre_read_buf`/`pre_read_len`。拆为 `handle_esp32_upload`/`handle_gd32_upload` | +| P2-35 | ble_ota_ctrl_access | 190 行 | [ble_ota.cpp:717-907](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L717-L907)。3 命令分支长度极不均(0xF7 90 行 / 0xF8 54 行 / 0xFA 19 行),`session_id` 提取重复 | +| P2-36 | doRelayPackets | 180 行 + goto | [ota_relay.cpp:936-1115](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L936-L1115)(见 P0-7) | +| P2-37 | doFinishAndVerify | 100 行双职责 | [ota_relay.cpp:1118-1216](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L1118-L1216)(见 P1-13) | +| P2-38 | app_main | 124 行 | [main.cpp:30-154](file:///g:/Robot_v2/DCU/ESP32_COMM/main/core/main.cpp#L30-L154)。拆为 `initEarlyServices` + `createOtaTasks` + `markAppValid` | + +### P2-C 配置化(magic number → app_config.h) + +| # | 主题 | 位置 | +|---|---|---| +| P2-39 | 任务栈/优先级散落硬编码 | [main.cpp:63,65,95,97,111,113,127,129](file:///g:/Robot_v2/DCU/ESP32_COMM/main/core/main.cpp#L63)、[ble_ota.cpp:812,871,892,1723](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L812)、[wifi_ota.cpp:623](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/wifi_ota.cpp#L623)、[http_handlers.cpp:905-913](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp#L905-L913)(task_metrics 必须手工同步,已出现一处不对齐风险) | +| P2-40 | HTTP server config | [wifi_ota.cpp:419-424](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/wifi_ota.cpp#L419-L424)(`max_uri_handlers=32` 实际只用 18) | +| P2-41 | BLE 连接参数 | [ble_ota.cpp:1395-1401](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L1395-L1401)(itvl/latency/supervision) | +| P2-42 | AP 静态 IP | [wifi_ota.cpp:234-236,240,258-259](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/wifi_ota.cpp#L234-L259) | +| P2-43 | BLE_OTA_ACK_WINDOW 位置 | [ble_ota.cpp:58](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L58) 用 `#define` 应移入 app_config.h 作 `inline constexpr` | +| P2-44 | debug_log magic number | [debug_log.cpp:199,317,352,365,369](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L199)(1024/512/600/32) | +| P2-45 | 健康巡检周期 | [health_monitor.cpp:190,208](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L190)(1000000/5000) | +| P2-46 | 温度采样间隔 | [health_monitor.cpp:190](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L190) | + +### P2-D 死代码 / 未使用配置 + +| # | 项 | 位置 | +|---|---|---| +| P2-47 | `DEBUG_LOG_ENABLED` 全代码库零引用 | [app_config.h:83](file:///g:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h#L83) | +| P2-48 | `GD32_CHIP_UID[]` 占位 | [app_config.h:120-128](file:///g:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h#L120-L128) | +| P2-49 | `BLE_OTA_PACKET_OVERHEAD` 零引用 | [app_config.h:104](file:///g:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h#L104) | +| P2-50 | `WIFI_RSSI_WEAK` 零引用 | [health_monitor.h:33](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.h#L33) | +| P2-51 | `DEBUG_LOG_NONE/ALL` 零引用 | [debug_log.h:35,40](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.h#L35) | +| P2-52 | `WIFI_STA_USE_STATIC_IP` 临时调试开关 | [app_config.h:36](file:///g:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h#L36) | +| P2-53 | `ota_snapshot.h` 仅聚合无价值 | [ota_snapshot.h](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/ota_snapshot.h) 全文 | + +### P2-E 其他低风险风格类 + +| # | 项 | 位置 | +|---|---|---| +| P2-54 | SpiMaster 公共接口混合多个抽象层 | [spi_master.h:60-100](file:///g:/Robot_v2/DCU/ESP32_COMM/main/transport/spi_master.h#L60-L100)(上轮 D 残留) | +| P2-55 | DebugLog 未用 RAII(与 HealthMonitor 风格不一) | [debug_log.cpp:53-60,78-108,162-174](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L53-L174) | +| P2-56 | DebugLog logLevel 暴露在 public | [debug_log.h:86](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.h#L86) | +| P2-57 | DebugLog 三路 sink 抽象为列表 | [debug_log.cpp:300-333](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L300-L333)(上轮 E 项遗留) | +| P2-58 | DebugLog send() 返回值无人检查,可改 void | [debug_log.cpp:329-332](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L329-L332) | +| P2-59 | ensureUdpSocket 失败后每条日志都重试 | [debug_log.cpp:278-292](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L278-L292) | +| P2-60 | DebugLog log() 三次缓冲区拼接 | [debug_log.cpp:342-373](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L342-L373) | +| P2-61 | DebugLog 头注释与实现不一致(UDP 无开关) | [debug_log.h:6-7](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.h#L6-L7) vs [debug_log.cpp:311-324](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L311-L324) | +| P2-62 | DebugLog 重复 include | [debug_log.h:23-24](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.h#L23-L24) vs [debug_log.cpp:20,23](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.cpp#L20) | +| P2-63 | health_monitor WIFI_RSSI_NORMAL 别名位置错误 | [health_monitor.h:32-33](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.h#L32-L33) | +| P2-64 | health_state_t 与 health_status_t 两套枚举语义重叠 | [health_monitor.h:16-29](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.h#L16-L29) | +| P2-65 | health_monitor 阈值检查样板可抽 applyThresholdAlarm | [health_monitor.cpp:129-156,333-357](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L129-L357) | +| P2-66 | health_monitor checkBeforeOta 与 checkBeforeOtaRelay 分级样板重复 | [health_monitor.cpp:232-311](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L232-L311) | +| P2-67 | health_monitor taskEntry 职责混杂 | [health_monitor.cpp:165-210](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L165-L210) | +| P2-68 | health_monitor logEvent 与相邻 DEBUG 重复日志 | [health_monitor.cpp:220-223](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L220-L223) | +| P2-69 | health_monitor tempMonitorCallback 注释硬编码温度值 | [health_monitor.cpp:293](file:///g:/Robot_v2/DCU/ESP32_COMM/main/monitor/health_monitor.cpp#L293) | +| P2-70 | gd32_flash_store NVS 字段名硬编码 | [gd32_flash_store.cpp:136-185](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L136-L185) | +| P2-71 | gd32_flash_store tag "OTA" 与模块名不一致 | [gd32_flash_store.cpp](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp) 全文 | +| P2-72 | gd32_flash_store SHA 长度 32 裸数字 | [gd32_flash_store.h:26](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.h#L26) 等 | +| P2-73 | gd32_flash_store flashWriteChunk 错误清理重复 | [gd32_flash_store.cpp:74-108](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L74-L108) | +| P2-74 | gd32_flash_store computeFlashSha256 错误清理重复 | [gd32_flash_store.cpp:224-262](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L224-L262) | +| P2-75 | gd32_flash_store 全零 checksum 检查可简化为 memcmp | [gd32_flash_store.cpp:299-302](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L299-L302) | +| P2-76 | gd32_flash_store flashWriteChunk 非对齐警告条件恒真 | [gd32_flash_store.cpp:65-72](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L65-L72) | +| P2-77 | gd32_flash_store m_partition 懒初始化无线程安全 | [gd32_flash_store.cpp:30-40](file:///g:/Robot_v2/DCU/ESP32_COMM/main/storage/gd32_flash_store.cpp#L30-L40) | +| P2-78 | main.cpp 4 处 xTaskCreatePinnedToCore 样板 | [main.cpp:60-137](file:///g:/Robot_v2/DCU/ESP32_COMM/main/core/main.cpp#L60-L137) | +| P2-79 | main.cpp 启动横幅分隔线硬编码 4 次 | [main.cpp:50,56,149,153](file:///g:/Robot_v2/DCU/ESP32_COMM/main/core/main.cpp#L50-L153) | +| P2-80 | main.cpp xRet 变量重复使用 | [main.cpp:59,69,92,101,108,117,124,133](file:///g:/Robot_v2/DCU/ESP32_COMM/main/core/main.cpp#L59-L133) | +| P2-81 | http_handlers.cpp 单文件 TAG 混用 OTA/WIFI/SPI | [http_handlers.cpp](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp) 全文 | +| P2-82 | canFdSend 返回 -1 无法区分失败模式 | [canfd_bridge.cpp:59-68](file:///g:/Robot_v2/DCU/ESP32_COMM/main/transport/canfd_bridge.cpp#L59-L68) | +| P2-83 | otaCrc8 实为 XOR 校验和,命名误导 | [spi_master.cpp:180-187](file:///g:/Robot_v2/DCU/ESP32_COMM/main/transport/spi_master.cpp#L180-L187) | +| P2-84 | otaParseFrame/otaFindFrame 验证逻辑重复 | [spi_master.cpp:225-279](file:///g:/Robot_v2/DCU/ESP32_COMM/main/transport/spi_master.cpp#L225-L279) | +| P2-85 | CAN 帧 vs SPI OTA 帧两层 framing 未抽象 | [canfd_bridge.cpp:12-51](file:///g:/Robot_v2/DCU/ESP32_COMM/main/transport/canfd_bridge.cpp#L12-L51) + [spi_master.cpp:198-279](file:///g:/Robot_v2/DCU/ESP32_COMM/main/transport/spi_master.cpp#L198-L279) | +| P2-86 | BLE OTA WiFi 共存标志位冗余 | [ble_ota.cpp:226-250](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/ble_ota.cpp#L226-L250) | +| P2-87 | http_handlers task_metrics 硬编码任务栈大小 | [http_handlers.cpp:905-913](file:///g:/Robot_v2/DCU/ESP32_COMM/main/server/http_handlers.cpp#L905-L913) | +| P2-88 | notifyRelayStarted/Complete 纯转发中间层 | [ota_coordinator.cpp:264-307](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/ota_coordinator.cpp#L264-L307) | +| P2-89 | notifyEsp32FinalizeComplete 与 notifyEsp32Rebooting 几乎相同 | [ota_coordinator.cpp:409-428](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/ota_coordinator.cpp#L409-L428) | +| P2-90 | doFinishAndVerify 成功路径与 reset() session 清理重复 | [ota_relay.cpp:1178-1182 vs 487-504](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L1178-L1182) | +| P2-91 | metaLoad 单次中继被调 3 次 | [ota_coordinator.cpp:213-221](file:///g:/Robot_v2/DCU/ESP32_COMM/main/ota/ota_coordinator.cpp#L213-L221) + [ota_relay.cpp:311-316,438-440](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L311-L440) | +| P2-92 | forwardFirmware 中 expected_ack=0xF0F0F0F0 魔法数字 | [ota_relay.cpp:1024](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L1024) | +| P2-93 | doRelayPackets:918 WARN 级别输出纯配置信息(调试残留) | [ota_relay.cpp:918](file:///g:/Robot_v2/DCU/ESP32_COMM/main/relay/ota_relay.cpp#L918) | +| P2-94 | app_config 配置项类型不一致(uint8_t 应为 bool) | [app_config.h:25,36,83](file:///g:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h#L25) | + +--- + +## 不建议改的地方(保留上轮结论) + +- OTA 事件状态机的 5 步驱动顺序、健康检查时机、`OtaCoordinator` 的 watchdog 自愈、`forceCleanup` 仅在 `isIdle()` 启动。 +- `Esp32OtaCore::s_mutex + s_occupied` 双层 mutex 语义("持锁查占用" + "持续占用期")。 +- 单例模式已统一(`static XXX inst; return inst;` + `= delete` 拷贝构造/赋值)。 +- 无 TODO/FIXME/XXX/HACK 标记,无注释掉的死代码。 + +--- + +## 预估收益 + +- 修复 4 项功能性 bug(校验后门 / RSSI 状态机降级 / 固件大小注释 / 凭证安全)。 +- 消除 6 处数据竞争(OtaRelay 状态访问 + setWifiConnected)。 +- 6 个超长函数拆分(245/240/190/180/124/100 行),可维护性显著提升。 +- `main/` 行数预计再减约 400~500 行(去重 + 死代码)。 +- 双通道(BLE/HTTP)一致性通过 `validateGd32Size` / `storeGd32FirmwareFromStream` 抽取彻底对齐。 +- protocol.h / app_config.h 死代码清理约减 50+ 行。 + +--- + +## 推荐落地顺序 + +1. **第 1 批(P0)**:P0-1 ~ P0-8。先修功能正确性 / 安全性 bug。P0-7/P0-8 函数拆分风险高,需配合 GD32 全链路 OTA 回归。 +2. **第 2 批(P1.A)**:OtaRelay 状态成员封装一组(P1-A + P1-15),消除数据竞争,原子改动。 +3. **第 3 批(P1.B)** +4. **第 4 批(P2.A/B)**:样板抽取(P2-27 ~ P2-32)+ 函数拆分(P2-33 ~ P2-38)。 +5. **第 5 批(P2.C/D/E)**:配置化(P2-39 ~ P2-46)+ 死代码删除(P2-47 ~ P2-53)+ 风格统一(P2-54 ~ P2-94),可随业务变更顺手做。 diff --git a/esp-idfmainCMakeFiles__idf_main.dirmonitordebug_log.cpp.obj.d b/esp-idfmainCMakeFiles__idf_main.dirmonitordebug_log.cpp.obj.d new file mode 100644 index 0000000..c70b1a6 --- /dev/null +++ b/esp-idfmainCMakeFiles__idf_main.dirmonitordebug_log.cpp.obj.d @@ -0,0 +1,180 @@ +esp-idf/main/CMakeFiles/__idf_main.dir/monitor/debug_log.cpp.obj: \ + G:\Robot_v2\DCU\ESP32_COMM\main\monitor\debug_log.cpp \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/string.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/cdefs.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/cdefs.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/config.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/ieeefp.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/features.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/picolibc.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/float.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_default_types.h \ + G:/Robot_v2/DCU/ESP32_COMM/build/config/sdkconfig.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/stdint.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/stddef.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_intsup.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_stdint.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_locale.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/strings.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/string.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/stdio.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/stdio.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/stdarg.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/lock.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/lock.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/esp32xx/include/sys/socket.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/include/lwip/sockets.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/sockets.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/opt.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/lwipopts.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/stdlib.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/stdlib.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/alloca.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_wait.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/unistd.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/unistd.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/endian.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_endian.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_pthreadtypes.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/sched.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_timespec.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/types.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/select.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/select.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_sigset.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_timeval.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_select.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/unistd.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/fcntl.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/fcntl.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_default_fcntl.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/time.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/time.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/time.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/time.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_time.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/ioctl.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/poll.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_system/include/esp_task.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/FreeRTOS.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/config/include/freertos/FreeRTOSConfig.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/assert.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/assert.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/config/riscv/include/freertos/FreeRTOSConfig_arch.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/projdefs.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/portable.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/deprecated_definitions.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/stdbool.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/spinlock.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_cpu.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/soc_caps.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/include/soc/soc_caps_eval.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/csr.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/param.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/syslimits.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/param.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/encoding.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_assert.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/rv_utils.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/interrupt_reg.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/register/soc/interrupt_core0_reg.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/soc.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_bit_defs.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/register/soc/reg_base.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_attr.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_assert.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/interrupt.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/esp_private/interrupt_intc.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/csr_pie.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/csr_dsp.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_intr_alloc.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_err.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_compiler.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_intr_types.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_macros.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_rom/include/esp_rom_sys.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/inttypes.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/reset_reasons.h \ + F:/software/.espressif/v6.0/esp-idf/components/heap/include/esp_heap_caps.h \ + F:/software/.espressif/v6.0/esp-idf/components/heap/include/multi_heap.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_system/include/esp_system.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_idf_version.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/esp_newlib.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/reent.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/limits.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/limits.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/mpu_wrappers.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/esp_additions/include/freertos/idf_additions.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/task.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/list.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/queue.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/task.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/semphr.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/queue.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/stream_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/message_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/stream_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/event_groups.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/timers.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_random.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/sntp/sntp_get_set_time.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/sockets_ext.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/freertos/include/arch/sys_arch.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/debug.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/arch.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/esp32xx/include/arch/cc.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/errno.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/errno.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/errno.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/ctype.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/def.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip4_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip6_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/def.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip6_zone.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/netif.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/err.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/pbuf.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/stats.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/mem.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/memp.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/priv/memp_std.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/priv/memp_priv.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/priv/mem_priv.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/inet.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/errno.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/net/if.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/if_api.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/netdb.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/include/lwip/netdb.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/netdb.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/arpa/inet.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log_config.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log_level.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log_color.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log_timestamp.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log_write.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log_format.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log_args.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_log_attr.h \ + F:/software/.espressif/v6.0/esp-idf/components/log/include/esp_private/log_attr.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_driver_uart/include/driver/uart.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hal_uart/include/hal/uart_types.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/clk_tree_defs.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_check.h \ + G:\Robot_v2\DCU\ESP32_COMM\main\monitor\debug_log.h \ + G:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/riscv32-esp-elf/include/c++/15.2.0/cstdint \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/riscv32-esp-elf/include/c++/15.2.0/riscv32-esp-elf/rv32imc_zicsr_zifencei/ilp32/no-rtti/bits/c++config.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/riscv32-esp-elf/include/c++/15.2.0/riscv32-esp-elf/rv32imc_zicsr_zifencei/ilp32/no-rtti/bits/os_defines.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/riscv32-esp-elf/include/c++/15.2.0/riscv32-esp-elf/rv32imc_zicsr_zifencei/ilp32/no-rtti/bits/cpu_defines.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/riscv32-esp-elf/include/c++/15.2.0/pstl/pstl_config.h \ + G:/Robot_v2/DCU/ESP32_COMM/main/include/version.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/netinet/in.h diff --git a/esp-idfmainCMakeFiles__idf_main.dirmonitorhealth_monitor.cpp.obj.d b/esp-idfmainCMakeFiles__idf_main.dirmonitorhealth_monitor.cpp.obj.d new file mode 100644 index 0000000..11d16cc --- /dev/null +++ b/esp-idfmainCMakeFiles__idf_main.dirmonitorhealth_monitor.cpp.obj.d @@ -0,0 +1,180 @@ +esp-idf/main/CMakeFiles/__idf_main.dir/monitor/health_monitor.cpp.obj: \ + G:\Robot_v2\DCU\ESP32_COMM\main\monitor\health_monitor.cpp \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/stdio.h \ + G:/Robot_v2/DCU/ESP32_COMM/build/config/sdkconfig.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/stddef.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/features.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/picolibc.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/stdio.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/cdefs.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/cdefs.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/config.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/ieeefp.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/float.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_default_types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/stdint.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_intsup.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_stdint.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/stdarg.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/lock.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/lock.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/string.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_locale.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/strings.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/string.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/FreeRTOS.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/config/include/freertos/FreeRTOSConfig.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/assert.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/stdlib.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/stdlib.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/alloca.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_wait.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/assert.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/config/riscv/include/freertos/FreeRTOSConfig_arch.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/projdefs.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/portable.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/deprecated_definitions.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/stdbool.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/spinlock.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_cpu.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/soc_caps.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/include/soc/soc_caps_eval.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/csr.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/param.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/syslimits.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/endian.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_endian.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/param.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/encoding.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_assert.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/rv_utils.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/interrupt_reg.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/register/soc/interrupt_core0_reg.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/soc.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_bit_defs.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/register/soc/reg_base.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_attr.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_assert.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/interrupt.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/esp_private/interrupt_intc.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/csr_pie.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/csr_dsp.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_intr_alloc.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_err.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_compiler.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_intr_types.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_macros.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_rom/include/esp_rom_sys.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/inttypes.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/reset_reasons.h \ + F:/software/.espressif/v6.0/esp-idf/components/heap/include/esp_heap_caps.h \ + F:/software/.espressif/v6.0/esp-idf/components/heap/include/multi_heap.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_system/include/esp_system.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_idf_version.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/esp_newlib.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/reent.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/limits.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/limits.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/mpu_wrappers.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/esp_additions/include/freertos/idf_additions.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/task.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/list.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/queue.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/task.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/semphr.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/queue.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/stream_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/message_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/stream_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/event_groups.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/timers.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_wifi/include/esp_wifi.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_wifi/include/esp_wifi_types.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_wifi/include/esp_wifi_types_generic.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_event/include/esp_event_base.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_wifi/include/local/esp_wifi_types_native.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_wifi/include/esp_wifi_types_generic.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_event/include/esp_event.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_wifi/include/esp_wifi_crypto_types.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_wifi/include/esp_wifi_default.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_netif/include/esp_netif.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_netif/include/esp_netif_ip_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_netif/include/esp_netif_types.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_netif/include/esp_netif_defaults.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_timer/include/esp_timer.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/etm/include/esp_etm.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_driver_tsens/include/driver/temperature_sensor.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hal_ana_conv/include/hal/temperature_sensor_types.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/clk_tree_defs.h \ + G:\Robot_v2\DCU\ESP32_COMM\main\monitor\health_monitor.h \ + G:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/riscv32-esp-elf/include/c++/15.2.0/cstdint \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/riscv32-esp-elf/include/c++/15.2.0/riscv32-esp-elf/rv32imc_zicsr_zifencei/ilp32/no-rtti/bits/c++config.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/riscv32-esp-elf/include/c++/15.2.0/riscv32-esp-elf/rv32imc_zicsr_zifencei/ilp32/no-rtti/bits/os_defines.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/riscv32-esp-elf/include/c++/15.2.0/riscv32-esp-elf/rv32imc_zicsr_zifencei/ilp32/no-rtti/bits/cpu_defines.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/riscv32-esp-elf/include/c++/15.2.0/pstl/pstl_config.h \ + G:/Robot_v2/DCU/ESP32_COMM/main/include/version.h \ + G:\Robot_v2\DCU\ESP32_COMM\main\monitor\debug_log.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/esp32xx/include/sys/socket.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/include/lwip/sockets.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/sockets.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/opt.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/lwipopts.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/unistd.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/unistd.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_pthreadtypes.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/sched.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_timespec.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/types.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/select.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/select.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_sigset.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_timeval.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_select.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/unistd.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/fcntl.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/fcntl.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_default_fcntl.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/time.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/time.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/time.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/time.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_time.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/ioctl.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/poll.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_system/include/esp_task.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_random.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/sntp/sntp_get_set_time.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/sockets_ext.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/freertos/include/arch/sys_arch.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/debug.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/arch.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/esp32xx/include/arch/cc.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/errno.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/errno.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/errno.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/ctype.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/def.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip4_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip6_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/def.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip6_zone.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/netif.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/err.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/pbuf.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/stats.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/mem.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/memp.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/priv/memp_std.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/priv/memp_priv.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/priv/mem_priv.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/inet.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/errno.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/net/if.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/if_api.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/netinet/in.h diff --git a/esp-idftransportCMakeFiles__idf_transport.dir____maintransportprotocol.cpp.obj.d b/esp-idftransportCMakeFiles__idf_transport.dir____maintransportprotocol.cpp.obj.d new file mode 100644 index 0000000..ac16adf --- /dev/null +++ b/esp-idftransportCMakeFiles__idf_transport.dir____maintransportprotocol.cpp.obj.d @@ -0,0 +1,165 @@ +esp-idf/transport/CMakeFiles/__idf_transport.dir/__/__/main/transport/protocol.cpp.obj: \ + G:\Robot_v2\DCU\ESP32_COMM\main\transport\protocol.cpp \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/string.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/cdefs.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/cdefs.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/config.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/ieeefp.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/features.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/picolibc.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/float.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_default_types.h \ + G:/Robot_v2/DCU/ESP32_COMM/build/config/sdkconfig.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/stdint.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/stddef.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_intsup.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_stdint.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_locale.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/strings.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/string.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/stdio.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/stdio.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/stdarg.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/lock.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/lock.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_random.h \ + F:/software/.espressif/v6.0/esp-idf/components/mbedtls/port/include/aes/esp_aes.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/stdbool.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hal_security/include/hal/aes_types.h \ + G:/Robot_v2/DCU/ESP32_COMM/main/include/app_config.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/riscv32-esp-elf/include/c++/15.2.0/cstdint \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/riscv32-esp-elf/include/c++/15.2.0/riscv32-esp-elf/rv32imc_zicsr_zifencei/ilp32/no-rtti/bits/c++config.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/riscv32-esp-elf/include/c++/15.2.0/riscv32-esp-elf/rv32imc_zicsr_zifencei/ilp32/no-rtti/bits/os_defines.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/riscv32-esp-elf/include/c++/15.2.0/riscv32-esp-elf/rv32imc_zicsr_zifencei/ilp32/no-rtti/bits/cpu_defines.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/riscv32-esp-elf/include/c++/15.2.0/pstl/pstl_config.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/FreeRTOS.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/config/include/freertos/FreeRTOSConfig.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/assert.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/stdlib.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/stdlib.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/alloca.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_wait.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/assert.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/config/riscv/include/freertos/FreeRTOSConfig_arch.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/projdefs.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/portable.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/deprecated_definitions.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/portable/riscv/include/freertos/portmacro.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/spinlock.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_cpu.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/soc_caps.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/include/soc/soc_caps_eval.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/csr.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/param.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/syslimits.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/endian.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_endian.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/param.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/encoding.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_assert.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/rv_utils.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/interrupt_reg.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/register/soc/interrupt_core0_reg.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/soc.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_bit_defs.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/register/soc/reg_base.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_attr.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_assert.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/interrupt.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/esp_private/interrupt_intc.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/csr_pie.h \ + F:/software/.espressif/v6.0/esp-idf/components/riscv/include/riscv/csr_dsp.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_intr_alloc.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_err.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_compiler.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_hw_support/include/esp_intr_types.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_macros.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_rom/include/esp_rom_sys.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/inttypes.h \ + F:/software/.espressif/v6.0/esp-idf/components/soc/esp32c3/include/soc/reset_reasons.h \ + F:/software/.espressif/v6.0/esp-idf/components/heap/include/esp_heap_caps.h \ + F:/software/.espressif/v6.0/esp-idf/components/heap/include/multi_heap.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_system/include/esp_system.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_common/include/esp_idf_version.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/esp_newlib.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/reent.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/limits.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/lib/gcc/riscv32-esp-elf/15.2.0/include/limits.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/mpu_wrappers.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/esp_additions/include/freertos/idf_additions.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/task.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/list.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/queue.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/task.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/semphr.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/queue.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/stream_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/message_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/stream_buffer.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/event_groups.h \ + F:/software/.espressif/v6.0/esp-idf/components/freertos/FreeRTOS-Kernel/include/freertos/timers.h \ + G:/Robot_v2/DCU/ESP32_COMM/main/include/version.h \ + G:\Robot_v2\DCU\ESP32_COMM\main\transport\protocol.h \ + G:/Robot_v2/DCU/ESP32_COMM/main/monitor/debug_log.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/esp32xx/include/sys/socket.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/include/lwip/sockets.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/sockets.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/opt.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/lwipopts.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/unistd.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/unistd.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/types.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_pthreadtypes.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/sched.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_timespec.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/types.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/select.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/select.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_sigset.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_timeval.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_select.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/unistd.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/fcntl.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/fcntl.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/_default_fcntl.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/time.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/time.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/time.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/time.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/machine/_time.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/ioctl.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/poll.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_system/include/esp_task.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/sntp/sntp_get_set_time.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/sockets_ext.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/freertos/include/arch/sys_arch.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/debug.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/arch.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/esp32xx/include/arch/cc.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/errno.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/sys/errno.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/sys/errno.h \ + C:/Espressif/tools/riscv32-esp-elf/esp-15.2.0_20251204/riscv32-esp-elf/picolibc/include/ctype.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/def.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip4_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip6_addr.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/def.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/ip6_zone.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/netif.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/err.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/pbuf.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/stats.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/mem.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/memp.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/priv/memp_std.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/priv/memp_priv.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/priv/mem_priv.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/inet.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/errno.h \ + F:/software/.espressif/v6.0/esp-idf/components/esp_libc/platform_include/net/if.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/lwip/src/include/lwip/if_api.h \ + F:/software/.espressif/v6.0/esp-idf/components/lwip/port/include/netinet/in.h diff --git a/main/core/main.cpp b/main/core/main.cpp index 58ddbfd..2ac4112 100644 --- a/main/core/main.cpp +++ b/main/core/main.cpp @@ -67,7 +67,10 @@ extern "C" void app_main(void) 0 ); if (xRet != pdPASS) { + // 关键任务:负责温度与 WiFi RSSI 上报,缺失会让运维陷入盲区, + // 无法及时发现硬件异常,必须重启恢复 DEBUG_ERR("MAIN", "创建 health_monitor_task 失败"); + ESP_ERROR_CHECK(ESP_FAIL); // 关键任务失败:终止启动并触发看门狗重启 } else { DEBUG_INFO("MAIN", "健康监控任务已创建"); } @@ -99,7 +102,10 @@ extern "C" void app_main(void) 0 // 核0 ); if (xRet != pdPASS) { + // 关键任务:OTA 中继是本设备核心职责(经 SPI 派发固件分片到目标 MCU), + // 缺失则 OTA 刷写主流程彻底瘫痪,必须重启恢复 DEBUG_ERR("MAIN", "创建 ota_relay_task 失败"); + ESP_ERROR_CHECK(ESP_FAIL); // 关键任务失败:终止启动并触发看门狗重启 } else { DEBUG_INFO("MAIN", "OTA 中继任务已创建"); } @@ -115,7 +121,10 @@ extern "C" void app_main(void) 0 ); if (xRet != pdPASS) { + // 关键任务:WiFi OTA 是 OTA 刷写主通道之一,缺失则 WiFi 侧固件升级不可用, + // 系统失去主要刷写路径,必须重启恢复 DEBUG_ERR("MAIN", "创建 wifi_ota_task 失败"); + ESP_ERROR_CHECK(ESP_FAIL); // 关键任务失败:终止启动并触发看门狗重启 } else { DEBUG_INFO("MAIN", "WiFi OTA 任务已创建"); } @@ -131,7 +140,12 @@ extern "C" void app_main(void) 0 // ESP32-C3 单核,固定核0 ); if (xRet != pdPASS) { + // 关键任务:BLE OTA 是 OTA 刷写主通道之一(与 WiFi OTA 互为冗余), + // 缺失则 BLE 侧固件升级完全不可用,必须重启恢复 + // 注:此处为 BLE OTA 主服务任务,区别于 ble_ota.cpp 内部的 + // ble_ota_data_worker 子任务(后者可降级,不在本文件创建) DEBUG_ERR("MAIN", "创建 ble_ota_task 失败"); + ESP_ERROR_CHECK(ESP_FAIL); // 关键任务失败:终止启动并触发看门狗重启 } else { DEBUG_INFO("MAIN", "BLE OTA 任务已创建"); } diff --git a/main/include/app_config.h b/main/include/app_config.h index f9f5d93..85eedfd 100644 --- a/main/include/app_config.h +++ b/main/include/app_config.h @@ -1,4 +1,4 @@ -/* +/* * app_config.h - 统一配置文件(C++ 版本) * ESP32-C3 OTA 中继项目 * @@ -59,7 +59,7 @@ inline constexpr uint32_t HTTP_SERVER_STACK_SIZE = 8192; ///< HTTP 服 // OTA 配置 // ============================================================ inline constexpr size_t OTA_BLOCK_SIZE = 8192; -inline constexpr size_t OTA_MAX_FIRMWARE_SIZE = 216 * 1024; // 216KB,对齐 GD32 APP_MAX_SIZE (0x36000) +inline constexpr size_t OTA_MAX_FIRMWARE_SIZE = 216 * 1024; // 216KB = 221184B = 0x35F90,固件大小上限 inline constexpr size_t GD32_OTA_PACKET_SIZE = 48; // GD32 OTA 实际转发包载荷 inline constexpr uint32_t OTA_POLL_RETRIES = 40; // 每包 ACK 轮询次数 inline constexpr uint32_t OTA_RESEND_RETRIES = 10; // 每包重发次数上限 @@ -98,6 +98,7 @@ inline constexpr uint8_t BLE_OTA_CTRL_TRIGGER_GD32 = 0xF7; ///< BLE 控 inline constexpr uint8_t BLE_OTA_CTRL_TRIGGER_ESP32 = 0xF8; ///< BLE 控制命令:触发 ESP32 本机 OTA inline constexpr uint8_t BLE_OTA_CTRL_VERIFY_SHA256 = 0xF9; ///< BLE 控制命令:SHA-256 哈希验证 inline constexpr uint8_t BLE_OTA_CTRL_RESTART_ESP32 = 0xFA; ///< BLE 控制命令:重启 ESP32 +inline constexpr uint8_t BLE_OTA_CTRL_ABORT_OTA = 0xFB; ///< BLE 控制命令:中止当前 OTA(不断开 BLE) inline constexpr uint8_t BLE_OTA_CMD_GD32_DATA = 0x00; ///< BLE 数据通道:GD32 固件包标识 inline constexpr uint8_t BLE_OTA_CMD_ESP32_DATA = 0x01; ///< BLE 数据通道:ESP32 固件包标识 inline constexpr size_t BLE_OTA_CHUNK_PAYLOAD = 480; ///< BLE 单包数据载荷大小(字节) diff --git a/main/include/version.h b/main/include/version.h index 2b3054e..8d1b367 100644 --- a/main/include/version.h +++ b/main/include/version.h @@ -4,11 +4,11 @@ // 固件版本 #define FW_VERSION_MAJOR 0 #define FW_VERSION_MINOR 3 -#define FW_VERSION_PATCH 33 -#define FW_VERSION_STRING "0.3.33" +#define FW_VERSION_PATCH 34 +#define FW_VERSION_STRING "0.3.34" // 编译时间 -#define BUILD_TIME "2026-06-25 01:39:58" +#define BUILD_TIME "2026-06-28 04:26:17" // Git 信息 -#define GIT_HASH "7a41eac" +#define GIT_HASH "6ef9730" diff --git a/main/monitor/debug_log.cpp b/main/monitor/debug_log.cpp index cbc626b..c8a28b3 100644 --- a/main/monitor/debug_log.cpp +++ b/main/monitor/debug_log.cpp @@ -1,4 +1,4 @@ -/* +/* * debug_log.cpp - 调试日志输出实现 * * 日志会同时写入三处: @@ -120,6 +120,8 @@ char* DebugLog::ringDrainJson(int *out_len) int count = ringCount(); int capacity = count * (LOG_RING_SIZE * 2) + 16; if (capacity < 16) capacity = 16; + // 当 count 较大(如 64)时 capacity 可能达 32KB+,单次 malloc 有风险。 + // 当前为简化实现保留单次 malloc;若后续日志量增大,可改为按 8KB 分块 realloc。 char *buf = (char*)malloc(capacity); if (!buf) return NULL; @@ -130,10 +132,22 @@ char* DebugLog::ringDrainJson(int *out_len) int entryLen = 0; while (ringPopOldest(entry, sizeof(entry), &entryLen) && entryLen > 0) { - if (!first) buf[pos++] = ','; + // 逗号写入前做边界检查,预留 4 字节给 ']' + '\0' 及末尾余量 + if (!first) { + if (pos + 1 >= capacity - 4) break; + buf[pos++] = ','; + } first = false; buf[pos++] = '"'; + + // 单条日志转义后超过 LOG_RING_SIZE * 2 时截断,标记 "..." 后缀 + int content_start = pos; + bool truncated = false; for (int i = 0; i < entryLen && pos < capacity - 4; i++) { + if (pos - content_start >= LOG_RING_SIZE * 2) { + truncated = true; + break; + } char c = entry[i]; if (c == '"') { buf[pos++] = '\\'; buf[pos++] = '"'; } else if (c == '\\') { buf[pos++] = '\\'; buf[pos++] = '\\'; } @@ -142,6 +156,14 @@ char* DebugLog::ringDrainJson(int *out_len) else if (c == '\t') { buf[pos++] = '\\'; buf[pos++] = 't'; } else if ((unsigned char)c >= 0x20) { buf[pos++] = c; } } + if (truncated) { + // 确保 "..." + '"' + ']' + '\0' (共 6 字节) 有空间才写入标记 + if (pos + 6 <= capacity) { + buf[pos++] = '.'; + buf[pos++] = '.'; + buf[pos++] = '.'; + } + } buf[pos++] = '"'; } buf[pos++] = ']'; @@ -216,6 +238,8 @@ esp_err_t DebugLog::uartInit() } m_uartInitialized = true; + // 标记 UART 真正可用,send() 依据此标志决定是否调用 uart_write_bytes + m_uartAvailable = true; ESP_LOGD(TAG, "UART0 调试输出已初始化 (TX=GPIO%d, RX=GPIO%d, %d baud)", UART_DEBUG_TX_GPIO, UART_DEBUG_RX_GPIO, UART_DEBUG_BAUD_RATE); @@ -255,7 +279,13 @@ esp_err_t DebugLog::init() return ESP_OK; } - uartInit(); + // 检查 uartInit 返回值;失败时仍让 ring buffer 模式工作, + // 但记录"UART 不可用"日志,并在 send() 中跳过 uart_write_bytes 调用。 + esp_err_t uart_ret = uartInit(); + if (uart_ret != ESP_OK) { + ESP_LOGW(TAG, "UART 不可用,仅 ring buffer 模式 (err=%s)", esp_err_to_name(uart_ret)); + m_uartAvailable = false; + } m_logRingMutex = xSemaphoreCreateMutex(); @@ -302,8 +332,8 @@ esp_err_t DebugLog::send(const uint8_t *data, size_t len) esp_err_t udp_ret = ESP_FAIL; esp_err_t uart_ret = ESP_FAIL; - // UART 通道 - if (m_uartInitialized) { + // UART 通道(UART 不可用时跳过 uart_write_bytes 调用) + if (m_uartAvailable) { uart_ret = uartSend(data, len); } diff --git a/main/monitor/debug_log.h b/main/monitor/debug_log.h index 39d0252..568e9ff 100644 --- a/main/monitor/debug_log.h +++ b/main/monitor/debug_log.h @@ -1,4 +1,4 @@ -/* +/* * debug_log.h - 调试日志输出 * * 当前日志会并行写入: @@ -95,6 +95,9 @@ private: struct sockaddr_in m_debugAddr; bool m_initialized = false; bool m_uartInitialized = false; + // 记录 UART 是否真正可用(uartInit 失败时为 false,跳过 uart_write_bytes)。 + // m_uartInitialized 仅表示曾尝试初始化,m_uartAvailable 才是写日志的判据。 + bool m_uartAvailable = false; static constexpr int LOG_RING_SLOTS = 64; static constexpr int LOG_RING_SIZE = 256; diff --git a/main/monitor/health_monitor.cpp b/main/monitor/health_monitor.cpp index 380b8ed..5b542a1 100644 --- a/main/monitor/health_monitor.cpp +++ b/main/monitor/health_monitor.cpp @@ -1,4 +1,4 @@ -/* +/* * health_monitor.cpp - 系统健康监控 * 包含:健康状态管理 + WiFi RSSI 监控 + 温度监控 * @@ -46,6 +46,21 @@ void HealthMonitor::init() } } +/** + * @brief 反初始化:停止并删除温度监控定时器,回收 esp_timer 资源。 + * 当前 HealthMonitor 任务为无限循环,无显式删除路径, + * 本方法预留给未来停用场景(如安全关机后停止监控)使用。 + */ +void HealthMonitor::deinit() +{ + if (m_tempTimer != nullptr) { + esp_timer_stop(m_tempTimer); + esp_timer_delete(m_tempTimer); + m_tempTimer = nullptr; + DEBUG_INFO("HEALTH", "温度监控定时器已停止并删除"); + } +} + // ============================================================ // 线程安全的健康状态读写 // ============================================================ @@ -55,10 +70,8 @@ void HealthMonitor::init() */ health_state_t HealthMonitor::getState() { - if (m_mutex) xSemaphoreTake(m_mutex, portMAX_DELAY); - health_state_t s = m_healthState; - if (m_mutex) xSemaphoreGive(m_mutex); - return s; + LockGuard lock(m_mutex); + return m_healthState; } /** @@ -67,9 +80,8 @@ health_state_t HealthMonitor::getState() */ void HealthMonitor::setState(health_state_t newState) { - if (m_mutex) xSemaphoreTake(m_mutex, portMAX_DELAY); + LockGuard lock(m_mutex); m_healthState = newState; - if (m_mutex) xSemaphoreGive(m_mutex); } // ============================================================ @@ -90,10 +102,8 @@ health_state_t HealthMonitor::getCurrent() */ float HealthMonitor::getLastTemperature() { - if (m_mutex) xSemaphoreTake(m_mutex, portMAX_DELAY); - float t = m_lastTemperature; - if (m_mutex) xSemaphoreGive(m_mutex); - return t; + LockGuard lock(m_mutex); + return m_lastTemperature; } /** @@ -104,10 +114,13 @@ float HealthMonitor::getLastTemperature() */ bool HealthMonitor::getLastWifiRssi(int8_t *out_rssi, uint32_t *out_age_ms) { - if (m_mutex) xSemaphoreTake(m_mutex, portMAX_DELAY); - int64_t lastMs = m_lastWifiRssiMs; - int8_t rssi = m_lastWifiRssi; - if (m_mutex) xSemaphoreGive(m_mutex); + int64_t lastMs; + int8_t rssi; + { + LockGuard lock(m_mutex); + lastMs = m_lastWifiRssiMs; + rssi = m_lastWifiRssi; + } if (lastMs <= 0) { return false; @@ -138,9 +151,10 @@ void HealthMonitor::tempMonitorCallback(void *arg) return; } - if (self.m_mutex) xSemaphoreTake(self.m_mutex, portMAX_DELAY); - self.m_lastTemperature = temp; - if (self.m_mutex) xSemaphoreGive(self.m_mutex); + { + LockGuard lock(self.m_mutex); + self.m_lastTemperature = temp; + } if (temp >= TEMP_CRITICAL) { self.setState(SYS_SAFE_SHUTDOWN); @@ -180,17 +194,17 @@ void HealthMonitor::taskEntry(void *pvParameters) } // 定时器,每1秒读取一次温度 - esp_timer_handle_t temp_timer; esp_timer_create_args_t timer_args = {}; timer_args.callback = &tempMonitorCallback; timer_args.arg = nullptr; timer_args.name = "temp_monitor"; timer_args.dispatch_method = ESP_TIMER_TASK; - if (esp_timer_create(&timer_args, &temp_timer) == ESP_OK) { - esp_timer_start_periodic(temp_timer, 1000000); // 1秒 + if (esp_timer_create(&timer_args, &self.m_tempTimer) == ESP_OK) { + esp_timer_start_periodic(self.m_tempTimer, 1000000); // 1秒 DEBUG_INFO("MON", "温度监控定时器已启动 (1秒间隔)"); } else { + self.m_tempTimer = nullptr; DEBUG_ERR("MON", "创建温度监控定时器失败"); } @@ -246,7 +260,12 @@ health_status_t HealthMonitor::checkBeforeOta() DEBUG_WARN("HEALTH", "OTA 前检查: SYS_WARNING,继续传输但需注意"); } - if (!m_wifiConnected) { + bool wifi_connected; + { + LockGuard lock(m_mutex); + wifi_connected = m_wifiConnected; + } + if (!wifi_connected) { DEBUG_WARN("HEALTH", "OTA 前检查: WiFi 未连接"); return HEALTH_MON_CRITICAL; } @@ -320,6 +339,7 @@ health_status_t HealthMonitor::checkBeforeOtaRelay() */ void HealthMonitor::setWifiConnected(bool connected) { + LockGuard lock(m_mutex); m_wifiConnected = connected; } @@ -348,10 +368,13 @@ health_status_t HealthMonitor::updateFromWifiRssi(int8_t rssi) return HEALTH_MON_WARNING; } + // RSSI 已恢复正常(>= WIFI_RSSI_NORMAL):从 WARNING 或 CRITICAL 任一状态降级到 NORMAL。 + // 历史缺陷:原仅当 cur == SYS_WARNING 时降级,导致 CRITICAL 状态下 RSSI 恢复正常无法降级。 + // 注意:SAFE_SHUTDOWN 不应被 RSSI 恢复降级,仅处理 WARNING/CRITICAL。 health_state_t cur = getState(); - if (cur == SYS_WARNING && rssi >= WIFI_RSSI_NORMAL) { + if ((cur == SYS_WARNING || cur == SYS_CRITICAL) && rssi >= WIFI_RSSI_NORMAL) { setState(SYS_NORMAL); - DEBUG_INFO("HEALTH", "WiFi RSSI 恢复: %d dBm >= %d dBm", rssi, WIFI_RSSI_NORMAL); + DEBUG_INFO("HEALTH", "WiFi RSSI 恢复: %d dBm >= %d dBm (从 %d 降级)", rssi, WIFI_RSSI_NORMAL, (int)cur); logEvent("WIFI恢复", rssi); } diff --git a/main/monitor/health_monitor.h b/main/monitor/health_monitor.h index 3b0c170..134d131 100644 --- a/main/monitor/health_monitor.h +++ b/main/monitor/health_monitor.h @@ -1,4 +1,4 @@ -/* +/* * health_monitor.h - 系统健康监控头文件 * * HealthMonitor 类封装健康状态管理、温度监控和 WiFi RSSI 监控。 @@ -10,6 +10,7 @@ #include "freertos/semphr.h" #include "freertos/task.h" #include "driver/temperature_sensor.h" +#include "esp_timer.h" #include "app_config.h" // T9.3: 健康状态等级 @@ -48,6 +49,13 @@ public: /** @brief 初始化健康监控模块,创建内部互斥锁与基础状态。 */ void init(); + /** + * @brief 反初始化:停止并删除温度监控定时器,回收 esp_timer 资源。 + * 在任务删除路径中调用;当前 HealthMonitor 任务为无限循环, + * 无显式删除路径,本方法预留给未来停用场景使用。 + */ + void deinit(); + /** @brief 健康监控 FreeRTOS 任务入口。 */ static void taskEntry(void *pvParameters); @@ -97,12 +105,32 @@ private: SemaphoreHandle_t m_mutex = NULL; temperature_sensor_handle_t m_tempHandle = NULL; + // 温度监控 esp_timer 句柄存为成员,便于 deinit() 时停止并删除定时器, + // 避免任务被删除后定时器仍在运行、回调继续访问 instance() 成员。 + esp_timer_handle_t m_tempTimer = nullptr; health_state_t m_healthState = SYS_NORMAL; float m_lastTemperature = 0.0f; int8_t m_lastWifiRssi = -127; int64_t m_lastWifiRssiMs = 0; TaskHandle_t m_taskHandle = NULL; + // m_wifiConnected 由 m_mutex 保护(setWifiConnected 写 / checkBeforeOta 读)。 + // volatile 保留为历史兼容,实际同步由 LockGuard 提供。 volatile bool m_wifiConnected = false; ///< WiFi 连接状态(由 WiFi 事件推送) static void tempMonitorCallback(void *arg); + + // RAII 锁守卫(与 SpiMaster::LockGuard 风格一致) + class LockGuard { + public: + explicit LockGuard(SemaphoreHandle_t mutex) : m_lock(mutex) { + if (m_lock) { xSemaphoreTake(m_lock, portMAX_DELAY); } + } + ~LockGuard() { + if (m_lock) { xSemaphoreGive(m_lock); } + } + LockGuard(const LockGuard&) = delete; + LockGuard& operator=(const LockGuard&) = delete; + private: + SemaphoreHandle_t m_lock; + }; }; diff --git a/main/ota/esp32_ota_core.cpp b/main/ota/esp32_ota_core.cpp index 41bbc65..7e8b736 100644 --- a/main/ota/esp32_ota_core.cpp +++ b/main/ota/esp32_ota_core.cpp @@ -69,7 +69,7 @@ Esp32OtaCore& Esp32OtaCore::instance() /** * @brief 复位所有状态变量。 - */ + */ void Esp32OtaCore::resetState() { m_active = false; @@ -81,6 +81,39 @@ void Esp32OtaCore::resetState() m_ended = false; } +/** + * @brief begin() 失败后的统一清理。 + * 仅释放 mutex + 复位状态(不调 esp_ota_abort, + * begin 失败时 m_handle 未成功创建,无需也不能 abort)。 + * @param code 返回给调用方的错误码。 + * @return 透传 code。 + */ +esp_err_t Esp32OtaCore::beginCleanupAndReturn(esp_err_t code) +{ + releaseMutex(); + resetState(); + return code; +} + +/** + * @brief finalize() 失败后的统一清理。 + * 若 esp_ota_end 未成功(m_ended=false),调 esp_ota_abort 释放 handle; + * 然后复位 m_ended、释放 mutex、复位状态,返回 code。 + * 与现有 abort() 的 m_ended 守卫语义一致,避免 end 成功后误调 abort。 + * @param code 返回给调用方的错误码。 + * @return 透传 code。 + */ +esp_err_t Esp32OtaCore::finalizeCleanupAndReturn(esp_err_t code) +{ + if (!m_ended) { + esp_ota_abort(m_handle); + } + m_ended = false; + releaseMutex(); + resetState(); + return code; +} + /** * @brief 开启 OTA 会话。 * @param total_size 固件总大小(字节)。 @@ -125,8 +158,7 @@ esp_err_t Esp32OtaCore::begin(size_t total_size) if (m_partition == NULL) { DEBUG_ERR(TAG, "找不到可用的 OTA 分区"); - releaseMutex(); - return ESP_FAIL; + return beginCleanupAndReturn(ESP_FAIL); } DEBUG_INFO(TAG, "OTA 目标分区: %s, size=%u, address=0x%lx", @@ -135,9 +167,7 @@ esp_err_t Esp32OtaCore::begin(size_t total_size) if (total_size > m_partition->size) { DEBUG_ERR(TAG, "固件太大: %u > 分区大小: %u", (unsigned)total_size, (unsigned)m_partition->size); - m_partition = nullptr; - releaseMutex(); - return ESP_FAIL; + return beginCleanupAndReturn(ESP_FAIL); } // BLE OTA 修复:传固件实际大小而非分区全尺寸,避免全分区擦除导致 @@ -151,10 +181,7 @@ esp_err_t Esp32OtaCore::begin(size_t total_size) esp_err_t err = esp_ota_begin(m_partition, erase_size, &m_handle); if (err != ESP_OK) { DEBUG_ERR(TAG, "esp_ota_begin 失败: %s", esp_err_to_name(err)); - m_partition = nullptr; - m_handle = 0; - releaseMutex(); - return err; + return beginCleanupAndReturn(err); } m_received = 0; @@ -280,11 +307,7 @@ esp_err_t Esp32OtaCore::finalize(const char *frontend_sha_hex, bool reboot, esp_err_t sha_err = computeShaFromPartition(); if (sha_err != ESP_OK) { DEBUG_ERR(TAG, "SHA 计算失败: %s,中止", esp_err_to_name(sha_err)); - esp_ota_abort(m_handle); - m_ended = false; - releaseMutex(); - resetState(); - return sha_err; + return finalizeCleanupAndReturn(sha_err); } char device_hex[65] = {0}; @@ -311,11 +334,7 @@ esp_err_t Esp32OtaCore::finalize(const char *frontend_sha_hex, bool reboot, } else { DEBUG_ERR(TAG, "SHA-256 不匹配: frontend=%s device=%s", frontend_sha_hex, device_hex); - esp_ota_abort(m_handle); - m_ended = false; - releaseMutex(); - resetState(); - return ESP_ERR_INVALID_VERSION; + return finalizeCleanupAndReturn(ESP_ERR_INVALID_VERSION); } } else if (out_device_sha_hex) { // 未提供前端 SHA,但仍计算设备端 SHA 供输出 @@ -332,11 +351,7 @@ esp_err_t Esp32OtaCore::finalize(const char *frontend_sha_hex, bool reboot, if (err != ESP_OK) { DEBUG_ERR(TAG, "esp_ota_end 失败: %s (0x%x)", esp_err_to_name(err), (unsigned)err); // end 失败时仍可 abort(未真正 end) - esp_ota_abort(m_handle); - m_ended = false; - releaseMutex(); - resetState(); - return err; + return finalizeCleanupAndReturn(err); } m_ended = true; // ★ end 成功,后续不得再 abort diff --git a/main/ota/esp32_ota_core.h b/main/ota/esp32_ota_core.h index 2846097..fe263e0 100644 --- a/main/ota/esp32_ota_core.h +++ b/main/ota/esp32_ota_core.h @@ -130,6 +130,24 @@ private: */ void resetState(); + // begin() 失败后的统一清理:释放 mutex + resetState() + 返回 code + // 不调 esp_ota_abort(begin 失败时 m_handle 未成功创建) + /** + * @brief begin() 失败后的统一清理。 + * @param code 返回给调用方的错误码。 + * @return 透传 code。 + */ + esp_err_t beginCleanupAndReturn(esp_err_t code); + + // finalize() 失败后的统一清理:若 m_ended=false 调 esp_ota_abort 释放 handle; + // 再复位 m_ended + 释放 mutex + resetState() + 返回 code + /** + * @brief finalize() 失败后的统一清理。 + * @param code 返回给调用方的错误码。 + * @return 透传 code。 + */ + esp_err_t finalizeCleanupAndReturn(esp_err_t code); + bool m_active = false; esp_ota_handle_t m_handle = 0; const esp_partition_t *m_partition = nullptr; diff --git a/main/ota/ota_coordinator.cpp b/main/ota/ota_coordinator.cpp index 3e2f671..7ef4ec1 100644 --- a/main/ota/ota_coordinator.cpp +++ b/main/ota/ota_coordinator.cpp @@ -124,41 +124,110 @@ void OtaCoordinator::init() // ============================================================ // GD32 存储阶段 // ============================================================ -esp_err_t OtaCoordinator::beginGd32Storage(uint32_t total_size) +esp_err_t OtaCoordinator::beginGd32Storage(uint32_t total_size, ota_channel_t channel) { bool h = lock(); // ★ 先做卡死自愈检查:若之前忙状态卡死,自动转 ERROR 继续后续流程 if (h) watchdogCheckLocked(); + // ★ 同类型重启:若上一次 GD32 存储仍在进行中(STORING)且来自同一通道, + // 自动中止旧存储并重新开始。覆盖"上传过程中刷新网页再上传"场景。 + // 跨通道(BLE→WiFi 或 WiFi→BLE)仍拒绝,保持互斥。 + // RELAYING 是中继阶段,不可中断,也拒绝。 + if (h && m_sysState == OTA_SYS_GD32_STORING) { + if (m_otaChannel != channel) { + DEBUG_WARN(TAG, "GD32 存储中(通道=%d),拒绝跨通道请求(通道=%d)", + (int)m_otaChannel, (int)channel); + unlock(h); + return ESP_ERR_INVALID_STATE; + } + DEBUG_WARN(TAG, "GD32 存储仍在进行中(同通道),中止旧存储并重新开始"); + Gd32FlashStore::instance().metaClear(); + m_sysState = OTA_SYS_IDLE; + m_busySinceTick = 0; + // epoch 将在下方设置 GD32_STORING 后递增 + } + // 系统忙检查:仅拒绝真正"在传输"的活跃状态 // STORED / COMPLETE 是"待中继/已中继"的非活跃状态 → 允许新存储覆盖 if (h && (m_sysState == OTA_SYS_ESP32_RECEIVING || m_sysState == OTA_SYS_ESP32_FINALIZING || m_sysState == OTA_SYS_ESP32_REBOOTING || - m_sysState == OTA_SYS_GD32_STORING || m_sysState == OTA_SYS_GD32_RELAYING)) { DEBUG_WARN(TAG, "系统忙 (state=%d),拒绝 GD32 存储请求", (int)m_sysState); unlock(h); return ESP_ERR_INVALID_STATE; } - // 如果之前已有存储但未中继(STORED)或刚中继完(COMPLETE),先清理再开始新存储 + // 如果之前已有存储但未中继(STORED)或刚中继完(COMPLETE),允许新存储覆盖。 + // 注意:清理 NVS meta 不在此处做,避免与紧跟的 prepareGd32Storage 内 + // clearGd32Meta + saveGd32Meta 重复一次写入,仅做状态注释说明。 if (h && (m_sysState == OTA_SYS_GD32_STORED || m_sysState == OTA_SYS_GD32_COMPLETE)) { - DEBUG_INFO(TAG, "清理旧 GD32 存储状态,开始新存储 (size=%" PRIu32 ")", total_size); - Gd32FlashStore::instance().metaClear(); + DEBUG_INFO(TAG, "覆盖旧 GD32 存储状态,开始新存储 (size=%" PRIu32 ")", total_size); } if (h) { + m_otaEpoch++; // ★ 新会话开始,递增 epoch + m_otaChannel = channel; // ★ 记录发起通道 m_sysState = OTA_SYS_GD32_STORING; m_gd32StorageDone = false; m_busySinceTick = xTaskGetTickCount(); // ★ 记录忙状态起始时刻 - DEBUG_INFO(TAG, "GD32 固件存储开始: %" PRIu32 " 字节, 系统状态 → GD32_STORING", total_size); + DEBUG_INFO(TAG, "GD32 固件存储开始: %" PRIu32 " 字节, 系统状态 → GD32_STORING (epoch=%u, channel=%d)", + total_size, m_otaEpoch, (int)channel); } unlock(h); return h ? ESP_OK : ESP_ERR_INVALID_STATE; } +// GD32 Flash 扇区大小(与 http_handlers.cpp 中的常量保持一致,用于擦除时向上对齐) +static constexpr uint32_t kGd32FlashSectorSize = 4096U; + +esp_err_t OtaCoordinator::prepareGd32Storage(uint32_t total_size, ota_channel_t channel) +{ + // 1. 校验固件大小(统一 BLE/HTTP 两路径的大小检查) + esp_err_t err = validateGd32Size(total_size); + if (err != ESP_OK) { + DEBUG_WARN(TAG, "prepareGd32Storage: 固件大小非法 raw=%" PRIu32, total_size); + return err; + } + + // 2. 进入 GD32_STORING 状态(系统忙则拒绝) + err = beginGd32Storage(total_size, channel); + if (err != ESP_OK) { + DEBUG_WARN(TAG, "prepareGd32Storage: 协调器拒绝存储(系统忙)"); + return err; + } + + // 3. 擦除 GD32 OTA 分区(按扇区大小向上对齐) + // ★ 原 BLE 路径未擦除分区,此处统一补齐,确保两路径行为一致。 + const uint32_t erase_size = + ((total_size + kGd32FlashSectorSize - 1U) / kGd32FlashSectorSize) * kGd32FlashSectorSize; + DEBUG_INFO(TAG, "prepareGd32Storage: 擦除 gd32_ota 分区 %u bytes (fw=%" PRIu32 ")", + (unsigned)erase_size, total_size); + err = eraseGd32Partition(erase_size); + if (err != ESP_OK) { + DEBUG_ERR(TAG, "prepareGd32Storage: 擦除失败: %s", esp_err_to_name(err)); + abortCurrent(); + return err; + } + + // 4. 清空旧 meta + 写入初始 meta(total_size + stored_size=0 + last_seq=0) + clearGd32Meta(); + ota_meta_t meta = {}; + meta.total_size = total_size; + meta.stored_size = 0; + meta.last_seq = 0; + err = saveGd32Meta(&meta); + if (err != ESP_OK) { + DEBUG_ERR(TAG, "prepareGd32Storage: 元数据保存失败: %s", esp_err_to_name(err)); + abortCurrent(); + return err; + } + + return ESP_OK; +} + void OtaCoordinator::completeGd32Storage() { bool h = lock(); @@ -175,47 +244,9 @@ void OtaCoordinator::completeGd32Storage() // ============================================================ // GD32 存储操作(传输层统一入口,解决 H3 绕过协调器问题) +// ── 以下 8 个 wrapper 已 inline 化至 ota_coordinator.h,直接转发到 +// Gd32FlashStore::instance(),消除跨 TU 调用开销。 // ============================================================ -esp_err_t OtaCoordinator::writeGd32Chunk(uint32_t offset, const uint8_t *data, size_t len) -{ - return Gd32FlashStore::instance().flashWriteChunk(offset, data, len); -} - -esp_err_t OtaCoordinator::saveGd32Meta(const ota_meta_t *meta) -{ - return Gd32FlashStore::instance().metaSave(meta); -} - -esp_err_t OtaCoordinator::loadGd32Meta(ota_meta_t *meta) -{ - return Gd32FlashStore::instance().metaLoad(meta); -} - -esp_err_t OtaCoordinator::updateGd32Meta(uint32_t stored_size, uint32_t last_seq) -{ - return Gd32FlashStore::instance().metaUpdate(stored_size, last_seq); -} - -esp_err_t OtaCoordinator::clearGd32Meta() -{ - return Gd32FlashStore::instance().metaClear(); -} - -esp_err_t OtaCoordinator::verifyGd32Firmware(const uint8_t frontend_sha32[32], - uint8_t out_device_sha[32]) -{ - return Gd32FlashStore::instance().verifyFrontendSha(frontend_sha32, out_device_sha); -} - -esp_err_t OtaCoordinator::finalizeGd32Checksum() -{ - return Gd32FlashStore::instance().metaFinalizeChecksum(); -} - -esp_err_t OtaCoordinator::eraseGd32Partition(uint32_t size) -{ - return Gd32FlashStore::instance().erasePartition(size); -} // ============================================================ // GD32 中继阶段 @@ -369,25 +400,42 @@ void OtaCoordinator::notifyBleCoexEnd() // ============================================================ // ESP32 OTA 阶段 // ============================================================ -esp_err_t OtaCoordinator::beginEsp32Ota(uint32_t total_size) +esp_err_t OtaCoordinator::beginEsp32Ota(uint32_t total_size, ota_channel_t channel) { bool h = lock(); // ★ 卡死自愈检查 if (h) watchdogCheckLocked(); - // 系统忙检查:GD32 传输/中继进行中,或另一通道的 ESP32 OTA 进行中,均拒绝。 - // ★ 必须显式拒绝 ESP32_RECEIVING/FINALIZING/REBOOTING: + // ★ 同类型重启:若上一次 ESP32 OTA 仍在接收中(RECEIVING)且来自同一通道, + // 自动中止旧会话并重新开始。覆盖"上传完成后刷新网页再上传"场景。 + // 跨通道(BLE→WiFi 或 WiFi→BLE)仍拒绝,保持互斥。 + // FINALIZING/REBOOTING 是不可逆阶段,也拒绝。 + if (h && m_sysState == OTA_SYS_ESP32_RECEIVING) { + if (m_otaChannel != channel) { + DEBUG_WARN(TAG, "ESP32 OTA 接收中(通道=%d),拒绝跨通道请求(通道=%d)", + (int)m_otaChannel, (int)channel); + unlock(h); + return ESP_ERR_INVALID_STATE; + } + DEBUG_WARN(TAG, "ESP32 OTA 仍在接收中(同通道),中止旧会话并重新开始"); + Esp32OtaCore::instance().abort(); + m_sysState = OTA_SYS_IDLE; + m_busySinceTick = 0; + // epoch 将在下方 begin 成功后递增,自动使旧 handler 的清理代码失效 + } + + // 系统忙检查:GD32 传输/中继进行中,或 ESP32 收尾/重启中,均拒绝。 + // ★ 必须显式拒绝 ESP32_FINALIZING/REBOOTING: // 若放行,本调用会落到 Esp32OtaCore::begin 去抢全局互斥失败而返回 INVALID_STATE, // 随后 beginEsp32OtaWithRetry 的 forceCleanup 会把另一通道的活跃会话 // (esp_ota_abort + 释放互斥)误中止 → 两通道并发写同一 OTA 分区。 if (h && (m_sysState == OTA_SYS_GD32_STORING || m_sysState == OTA_SYS_GD32_RELAYING || - m_sysState == OTA_SYS_ESP32_RECEIVING || m_sysState == OTA_SYS_ESP32_FINALIZING || m_sysState == OTA_SYS_ESP32_REBOOTING)) { DEBUG_WARN(TAG, "系统忙 (state=%d, %s),拒绝 ESP32 OTA", - (int)m_sysState, getSystemStateString()); + (int)m_sysState, stateToString(m_sysState)); unlock(h); return ESP_ERR_INVALID_STATE; } @@ -397,9 +445,12 @@ esp_err_t OtaCoordinator::beginEsp32Ota(uint32_t total_size) // 委托 Esp32OtaCore 处理分区/互斥逻辑 err = Esp32OtaCore::instance().begin(total_size); if (err == ESP_OK) { + m_otaEpoch++; // ★ 新会话开始,递增 epoch(使旧 handler 的 abortCurrentIfEpoch 失效) + m_otaChannel = channel; // ★ 记录发起通道 m_sysState = OTA_SYS_ESP32_RECEIVING; m_busySinceTick = xTaskGetTickCount(); // ★ 记录起始时刻 - DEBUG_INFO(TAG, "ESP32 OTA 开始: %" PRIu32 " 字节, 系统状态 → ESP32_RECEIVING", total_size); + DEBUG_INFO(TAG, "ESP32 OTA 开始: %" PRIu32 " 字节, 系统状态 → ESP32_RECEIVING (epoch=%u, channel=%d)", + total_size, m_otaEpoch, (int)channel); } else { DEBUG_WARN(TAG, "ESP32 OTA begin 失败: %s", esp_err_to_name(err)); } @@ -409,9 +460,9 @@ esp_err_t OtaCoordinator::beginEsp32Ota(uint32_t total_size) return err; } -esp_err_t OtaCoordinator::beginEsp32OtaWithRetry(uint32_t total_size) +esp_err_t OtaCoordinator::beginEsp32OtaWithRetry(uint32_t total_size, ota_channel_t channel) { - esp_err_t err = beginEsp32Ota(total_size); + esp_err_t err = beginEsp32Ota(total_size, channel); if (err == ESP_ERR_INVALID_STATE) { // ★ 仅当系统确实空闲(无任何活跃 OTA)时才允许 forceCleanup: // 这种情况只可能是"锁残留"——前一次 OTA 异常退出没释放全局互斥, @@ -419,15 +470,17 @@ esp_err_t OtaCoordinator::beginEsp32OtaWithRetry(uint32_t total_size) // 若系统正处于任何活跃 OTA(ESP32/GD32),绝不强制清理——那会 // 把另一通道正在进行的 esp_ota_handle 中止、释放互斥,导致 // 两通道并发写同一 OTA 分区(之前 BLE 与 WiFi 可同时 OTA 的根因)。 - if (!isIdle()) { + // 避免连续 isIdle() + getSystemState() 各加锁一次(共 2 次锁)。 + ota_system_state_t cur = getSystemState(); + if (!isIdleState(cur)) { DEBUG_WARN(TAG, "ESP32 OTA 被拒绝且系统非空闲 (state=%d),不强制清理,直接失败", - (int)getSystemState()); + (int)cur); return err; } // 协调器空闲但 Esp32OtaCore.begin 仍被拒 → 锁残留,强制清理后重试 DEBUG_WARN(TAG, "ESP32 OTA 被拒绝但系统空闲,判定为锁残留,强制清理状态机后重试"); Esp32OtaCore::instance().forceCleanup(); - err = beginEsp32Ota(total_size); + err = beginEsp32Ota(total_size, channel); } return err; } @@ -507,29 +560,60 @@ int OtaCoordinator::esp32FinalizeErrCategory(esp_err_t err) // ============================================================ // 中止 // ============================================================ +void OtaCoordinator::doAbortLocked() +{ + DEBUG_INFO(TAG, "中止当前 OTA 操作 (state=%d, epoch=%u)", (int)m_sysState, m_otaEpoch); + + if (m_sysState == OTA_SYS_GD32_STORING) { + Gd32FlashStore::instance().metaClear(); + } else if (m_sysState == OTA_SYS_GD32_RELAYING) { + OtaRelay::instance().reset(); + } else if (m_sysState == OTA_SYS_ESP32_RECEIVING || + m_sysState == OTA_SYS_ESP32_FINALIZING) { + Esp32OtaCore::instance().abort(); + } + + m_sysState = OTA_SYS_IDLE; + m_gd32StorageDone = false; + m_busySinceTick = 0; + DEBUG_INFO(TAG, "已中止, 系统状态 → IDLE"); +} + void OtaCoordinator::abortCurrent() { bool h = lock(); if (h) { - DEBUG_INFO(TAG, "中止当前 OTA 操作 (state=%d)", (int)m_sysState); - - if (m_sysState == OTA_SYS_GD32_STORING) { - Gd32FlashStore::instance().metaClear(); - } else if (m_sysState == OTA_SYS_GD32_RELAYING) { - OtaRelay::instance().reset(); - } else if (m_sysState == OTA_SYS_ESP32_RECEIVING || - m_sysState == OTA_SYS_ESP32_FINALIZING) { - Esp32OtaCore::instance().abort(); - } - - m_sysState = OTA_SYS_IDLE; - m_gd32StorageDone = false; - m_busySinceTick = 0; // ★ 离开忙状态 - DEBUG_INFO(TAG, "已中止, 系统状态 → IDLE"); + doAbortLocked(); } unlock(h); } +uint32_t OtaCoordinator::getOtaEpoch() const +{ + bool h = lock(); + uint32_t epoch = m_otaEpoch; + unlock(h); + return epoch; +} + +bool OtaCoordinator::abortCurrentIfEpoch(uint32_t epoch) +{ + bool h = lock(); + if (!h) return false; + + if (m_otaEpoch != epoch) { + // ★ epoch 不匹配:新会话已接管,旧 handler 不应中止 + unlock(h); + return false; + } + + // epoch 匹配:执行中止(共享 doAbortLocked,已持锁) + doAbortLocked(); + + unlock(h); + return true; +} + // ============================================================ // 状态查询 // ============================================================ @@ -543,35 +627,61 @@ ota_system_state_t OtaCoordinator::getSystemState() const return s; } -/** @brief 判断系统是否处于空闲状态(IDLE/STORED/COMPLETE)。 */ -bool OtaCoordinator::isIdle() const +ota_channel_t OtaCoordinator::getOtaChannel() const { - return (getSystemState() == OTA_SYS_IDLE || - getSystemState() == OTA_SYS_GD32_STORED || - getSystemState() == OTA_SYS_GD32_COMPLETE); + bool h = lock(); + ota_channel_t ch = m_otaChannel; + unlock(h); + return ch; } -/** @brief 判断 GD32 通道是否正忙(STORING 或 RELAYING)。 */ -bool OtaCoordinator::isGd32Busy() const +/** @brief 判断给定状态值是否属于空闲态(不加锁,纯判断)。 + * 供持锁路径或已取一次状态的调用方复用,避免连续加锁。 */ +bool OtaCoordinator::isIdleState(ota_system_state_t s) const +{ + return (s == OTA_SYS_IDLE || + s == OTA_SYS_GD32_STORED || + s == OTA_SYS_GD32_COMPLETE); +} + +/** @brief 判断给定状态值是否属于 GD32 忙态(不加锁,纯判断)。 */ +bool OtaCoordinator::isGd32BusyState(ota_system_state_t s) const { - ota_system_state_t s = getSystemState(); return (s == OTA_SYS_GD32_STORING || s == OTA_SYS_GD32_RELAYING); } -/** @brief 判断 ESP32 通道是否正忙(RECEIVING/FINALIZING/REBOOTING)。 */ -bool OtaCoordinator::isEsp32Busy() const +/** @brief 判断给定状态值是否属于 ESP32 忙态(不加锁,纯判断)。 */ +bool OtaCoordinator::isEsp32BusyState(ota_system_state_t s) const { - ota_system_state_t s = getSystemState(); return (s == OTA_SYS_ESP32_RECEIVING || s == OTA_SYS_ESP32_FINALIZING || s == OTA_SYS_ESP32_REBOOTING); } -/** @brief 获取系统状态的字符串名称(用于日志和前端显示)。 */ -const char* OtaCoordinator::getSystemStateString() const +/** @brief 判断系统是否处于空闲状态(IDLE/STORED/COMPLETE)。 */ +bool OtaCoordinator::isIdle() const { - switch (getSystemState()) { + return isIdleState(getSystemState()); +} + +/** @brief 判断 GD32 通道是否正忙(STORING 或 RELAYING)。 */ +bool OtaCoordinator::isGd32Busy() const +{ + return isGd32BusyState(getSystemState()); +} + +/** @brief 判断 ESP32 通道是否正忙(RECEIVING/FINALIZING/REBOOTING)。 */ +bool OtaCoordinator::isEsp32Busy() const +{ + return isEsp32BusyState(getSystemState()); +} + +/** @brief 把状态枚举转成可读字符串(不加锁,纯函数)。 + * 供持锁代码路径和已取一次状态值的调用方复用,避免二次加锁。 */ +const char* OtaCoordinator::stateToString(ota_system_state_t s) +{ + switch (s) { case OTA_SYS_IDLE: return "IDLE"; case OTA_SYS_GD32_STORING: return "GD32_STORING"; case OTA_SYS_GD32_STORED: return "GD32_STORED"; @@ -585,6 +695,13 @@ const char* OtaCoordinator::getSystemStateString() const } } +/** @brief 获取系统状态的字符串名称(用于日志和前端显示)。 + * 委托 stateToString(getSystemState()),保持向后兼容。 */ +const char* OtaCoordinator::getSystemStateString() const +{ + return stateToString(getSystemState()); +} + /** @brief 获取 GD32 APP 存活状态(委托 OtaRelay)。 */ bool OtaCoordinator::getGd32AppAlive() const { @@ -608,14 +725,22 @@ OtaSnapshot OtaCoordinator::getSnapshot() const { OtaSnapshot snap{}; - // ── coordinator 自身状态(一次 mutex)── + // ── coordinator 自身状态(一次 mutex,避免二次加锁导致快照不一致)── + // 持锁时一次性取 m_sysState,并调不加锁的 stateToString() + // 就地转字符串。旧实现释放锁后再调 getSystemStateString() 二次加锁, + // 两次取值之间状态可能变化,导致 snap.sys_state 与 snap.sys_state_str + // 描述不同时刻状态。 bool h = lock(); if (h) { snap.sys_state = m_sysState; snap.storage_done = m_gd32StorageDone; + // 就地把状态枚举转字符串,与 sys_state 来自同一时刻 + snap.sys_state_str = stateToString(snap.sys_state); unlock(h); + } else { + // 加锁失败时也填一个与默认 sys_state(=IDLE) 一致的字符串 + snap.sys_state_str = stateToString(snap.sys_state); } - snap.sys_state_str = getSystemStateString(); // ── GD32 NVS 元数据 ── Gd32FlashStore::instance().metaLoad(&snap.meta); @@ -629,6 +754,9 @@ OtaSnapshot OtaCoordinator::getSnapshot() const // ============================================================ // GD32 APP 探测(PR2: 从 /system/status 移出) // ============================================================ +// 注:本函数与 OtaRelay::probeAndCacheAppAlive 逻辑相似,但因依赖方向 +// (OtaCoordinator → OtaRelay)不应调用 OtaRelay 的内部实现细节, +// 故此处保留独立实现。 esp_err_t OtaCoordinator::probeGd32AppOnce(gd32_version_t *out_version, uint32_t *out_uptime) { uint32_t uptime = 0; diff --git a/main/ota/ota_coordinator.h b/main/ota/ota_coordinator.h index 3312a2e..4360e02 100644 --- a/main/ota/ota_coordinator.h +++ b/main/ota/ota_coordinator.h @@ -19,6 +19,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "esp_err.h" +#include "app_config.h" // OTA_MAX_FIRMWARE_SIZE / GD32_OTA_PACKET_SIZE(validateGd32Size 用) #include "storage/gd32_flash_store.h" #include "relay/ota_relay.h" // IRelayListener 接口定义 + RelaySnapshot(H4 解耦) @@ -37,6 +38,14 @@ typedef enum { OTA_SYS_ERROR // 错误状态 } ota_system_state_t; +// ============================================================ +// OTA 传输通道标识(用于同类型重启时区分同通道/跨通道) +// ============================================================ +typedef enum { + OTA_CHANNEL_WIFI = 0, + OTA_CHANNEL_BLE = 1, +} ota_channel_t; + // ============================================================ // OTA 系统全量只读快照(PR1: 收口状态读取) // ============================================================ @@ -108,9 +117,21 @@ public: * @brief 传输层开始接收 GD32 固件前调用。 * 若系统正忙(ESP32 OTA 进行中或 GD32 中继中),返回失败。 * @param total_size 固件总字节数。 + * @param channel 发起通道(WiFi/BLE),用于同类型重启时区分同通道/跨通道。 * @return ESP_OK 成功,ESP_ERR_INVALID_STATE 系统忙。 */ - esp_err_t beginGd32Storage(uint32_t total_size); + esp_err_t beginGd32Storage(uint32_t total_size, ota_channel_t channel); + + /** + * @brief 统一 GD32 OTA 写入前的准备工作:校验大小 + begin + 擦除分区 + 清空 meta + 写入初始 meta。 + * 供 BLE seq=0 首包 / HTTP GD32 upload(非 prepared 复用路径)调用, + * 消除两路径对"擦除/meta 初始化"序列的隐式差异(原 BLE 路径未擦除分区)。 + * 任意步骤失败时内部调 abortCurrent 清理,调用方无需再清理。 + * @param total_size 固件总字节数。 + * @param channel 发起通道(WiFi/BLE)。 + * @return ESP_OK 成功;ESP_ERR_INVALID_SIZE 大小非法;ESP_ERR_INVALID_STATE 系统忙;其他擦除/写入错误。 + */ + esp_err_t prepareGd32Storage(uint32_t total_size, ota_channel_t channel); /** * @brief GD32 固件接收完毕时调用。 @@ -127,21 +148,30 @@ public: * @param len 数据长度。 * @return ESP_OK 成功。 */ - esp_err_t writeGd32Chunk(uint32_t offset, const uint8_t *data, size_t len); + inline esp_err_t writeGd32Chunk(uint32_t offset, const uint8_t *data, size_t len) + { + return Gd32FlashStore::instance().flashWriteChunk(offset, data, len); + } /** * @brief 保存 GD32 OTA 元数据。 * @param meta 元数据结构体。 * @return ESP_OK 成功。 */ - esp_err_t saveGd32Meta(const ota_meta_t *meta); + inline esp_err_t saveGd32Meta(const ota_meta_t *meta) + { + return Gd32FlashStore::instance().metaSave(meta); + } /** * @brief 加载 GD32 OTA 元数据。 * @param meta 输出元数据结构体。 * @return ESP_OK 成功。 */ - esp_err_t loadGd32Meta(ota_meta_t *meta); + inline esp_err_t loadGd32Meta(ota_meta_t *meta) + { + return Gd32FlashStore::instance().metaLoad(meta); + } /** * @brief 更新 GD32 OTA 元数据的进度。 @@ -149,13 +179,19 @@ public: * @param last_seq 最后包序号。 * @return ESP_OK 成功。 */ - esp_err_t updateGd32Meta(uint32_t stored_size, uint32_t last_seq); + inline esp_err_t updateGd32Meta(uint32_t stored_size, uint32_t last_seq) + { + return Gd32FlashStore::instance().metaUpdate(stored_size, last_seq); + } /** * @brief 清除 GD32 OTA 元数据。 * @return ESP_OK 成功。 */ - esp_err_t clearGd32Meta(); + inline esp_err_t clearGd32Meta() + { + return Gd32FlashStore::instance().metaClear(); + } /** * @brief 校验前端 SHA-256 与设备端固件是否一致。 @@ -163,21 +199,52 @@ public: * @param out_device_sha 输出设备端 32 字节 SHA(可为 nullptr)。 * @return ESP_OK 匹配,ESP_ERR_INVALID_CRC 不匹配。 */ - esp_err_t verifyGd32Firmware(const uint8_t frontend_sha32[32], - uint8_t out_device_sha[32]); + inline esp_err_t verifyGd32Firmware(const uint8_t frontend_sha32[32], + uint8_t out_device_sha[32]) + { + return Gd32FlashStore::instance().verifyFrontendSha(frontend_sha32, out_device_sha); + } /** * @brief 计算 GD32 固件 SHA-256 并写入元数据。 * @return ESP_OK 成功。 */ - esp_err_t finalizeGd32Checksum(); + inline esp_err_t finalizeGd32Checksum() + { + return Gd32FlashStore::instance().metaFinalizeChecksum(); + } /** * @brief 擦除 GD32 OTA 分区。 * @param size 擦除大小。 * @return ESP_OK 成功。 */ - esp_err_t eraseGd32Partition(uint32_t size); + inline esp_err_t eraseGd32Partition(uint32_t size) + { + return Gd32FlashStore::instance().erasePartition(size); + } + + /** + * @brief 校验 GD32 固件大小是否合法(BLE/HTTP 双通道共用)。 + * + * 统一规则:raw==0 或 raw>MAX 拒绝;按 GD32_OTA_PACKET_SIZE 向上取整后仍>MAX 拒绝。 + * 调用方各自翻译为通道特定错误码(BLE: BLE_OTA_FINISH_ERR_GD32_SIZE;HTTP: HTTPD_500)。 + * + * @param raw_size 固件原始字节数。 + * @return ESP_OK 合法;ESP_ERR_INVALID_SIZE 非法。 + */ + inline esp_err_t validateGd32Size(uint32_t raw_size) const + { + if (raw_size == 0U || raw_size > (uint32_t)OTA_MAX_FIRMWARE_SIZE) { + return ESP_ERR_INVALID_SIZE; + } + const uint32_t packet_size = (uint32_t)GD32_OTA_PACKET_SIZE; + const uint32_t rounded = ((raw_size + packet_size - 1U) / packet_size) * packet_size; + if (rounded > (uint32_t)OTA_MAX_FIRMWARE_SIZE) { + return ESP_ERR_INVALID_SIZE; + } + return ESP_OK; + } // ── GD32 中继阶段 ── /** @@ -205,17 +272,19 @@ public: * @brief ESP32 OTA 开始接收固件。 * 内部委托 Esp32OtaCore::begin()。 * @param total_size 固件总字节数。 + * @param channel 发起通道(WiFi/BLE),用于同类型重启时区分同通道/跨通道。 * @return ESP_OK 成功,ESP_ERR_INVALID_STATE 系统忙或通道被占用。 */ - esp_err_t beginEsp32Ota(uint32_t total_size); + esp_err_t beginEsp32Ota(uint32_t total_size, ota_channel_t channel); /** * @brief 发起 ESP32 OTA,若被 ESP_ERR_INVALID_STATE 拒绝则 forceCleanup 后重试一次。 * 封装 BLE/WiFi 两端完全相同的重试模式,调用方只需调一次。 * @param total_size 固件总字节数。 + * @param channel 发起通道(WiFi/BLE)。 * @return ESP_OK 成功;其他 = 协调器拒绝或 Esp32OtaCore::begin 失败。 */ - esp_err_t beginEsp32OtaWithRetry(uint32_t total_size); + esp_err_t beginEsp32OtaWithRetry(uint32_t total_size, ota_channel_t channel); /** * @brief ESP32 OTA 固件接收完毕,准备收尾。 @@ -257,6 +326,25 @@ public: */ void abortCurrent(); + /** + * @brief 获取当前 OTA 会话的 epoch(代次)。 + * 每次 beginEsp32Ota/beginGd32Storage 成功时递增。 + * 传输层 handler 在 begin 成功后捕获 epoch, + * 在失败清理时通过 abortCurrentIfEpoch() 检查是否仍为当前会话, + * 避免旧 handler 的清理代码误中止新会话(同类型重启场景)。 + * @return 当前 epoch 值。 + */ + uint32_t getOtaEpoch() const; + + /** + * @brief 仅当 epoch 匹配当前会话时才中止 OTA。 + * 用于旧 handler 的失败清理路径:若新请求已经接管(epoch 不匹配), + * 旧 handler 不应调 abortCurrent,否则会误中止新会话。 + * @param epoch 调用方在 begin 成功时捕获的 epoch。 + * @return true 已中止(epoch 匹配),false 未中止(新会话已接管)。 + */ + bool abortCurrentIfEpoch(uint32_t epoch); + // ── 状态查询 ── /** * @brief 一次性读取系统 OTA 全量状态快照(PR1: 收口状态读取)。 @@ -282,26 +370,68 @@ public: */ ota_system_state_t getSystemState() const; + /** + * @brief 获取当前 OTA 会话的发起通道(WiFi/BLE)。 + * 仅在活跃状态(ESP32_RECEIVING / GD32_STORING)下有意义; + * IDLE 等非活跃状态返回值未定义(默认 OTA_CHANNEL_WIFI)。 + * @return 当前会话通道枚举值。 + */ + ota_channel_t getOtaChannel() const; + + /** + * @brief 把状态枚举转成可读字符串(不加锁,纯函数)。 + * 供持锁代码路径和已取一次状态值的调用方复用,避免二次加锁。 + * @param s 状态枚举值。 + * @return 状态名称字符串(如 "IDLE"、"GD32_STORING" 等)。 + */ + static const char* stateToString(ota_system_state_t s); + + /** + * @brief 检查给定状态值是否属于空闲态(不加锁,纯判断)。 + * 调用方先 getSystemState() 取一次再传入,避免连续加锁。 + * @param s 状态枚举值。 + * @return true 表示 IDLE/STORED/COMPLETE。 + */ + bool isIdleState(ota_system_state_t s) const; + + /** + * @brief 检查给定状态值是否属于 GD32 忙态(不加锁,纯判断)。 + * @param s 状态枚举值。 + * @return true 表示 STORING 或 RELAYING。 + */ + bool isGd32BusyState(ota_system_state_t s) const; + + /** + * @brief 检查给定状态值是否属于 ESP32 忙态(不加锁,纯判断)。 + * @param s 状态枚举值。 + * @return true 表示 RECEIVING/FINALIZING/REBOOTING。 + */ + bool isEsp32BusyState(ota_system_state_t s) const; + /** * @brief 检查系统是否空闲(可接受新的 OTA 请求)。 + * 内部仍加锁取一次状态后调 isIdleState()。 * @return true 系统空闲(IDLE/STORED/COMPLETE)。 */ bool isIdle() const; /** * @brief 检查是否有 GD32 相关操作正在进行。 + * 内部仍加锁取一次状态后调 isGd32BusyState()。 * @return true 表示 STORING 或 RELAYING。 */ bool isGd32Busy() const; /** * @brief 检查是否有 ESP32 OTA 正在进行。 + * 内部仍加锁取一次状态后调 isEsp32BusyState()。 * @return true 表示 RECEIVING/FINALIZING/REBOOTING。 */ bool isEsp32Busy() const; /** * @brief 获取系统状态的可读字符串描述。 + * 内部委托 stateToString(getSystemState()),保持向后兼容。 * @return 状态名称字符串(如 "IDLE"、"GD32_STORING" 等)。 */ const char* getSystemStateString() const; @@ -385,6 +515,10 @@ private: // 必须在已持锁状态下调用。 void watchdogCheckLocked(); + // ★ 已持锁版本的中止序列:根据当前 m_sysState 调用对应子模块的清理, + // 并把状态机复位为 IDLE。abortCurrent / abortCurrentIfEpoch 共享此实现。 + void doAbortLocked(); + // 系统状态 ota_system_state_t m_sysState = OTA_SYS_IDLE; @@ -394,6 +528,15 @@ private: // GD32 存储跟踪 bool m_gd32StorageDone = false; + // OTA 会话代次:每次 beginEsp32Ota/beginGd32Storage 成功时递增。 + // 传输层 handler 在 begin 后捕获 epoch,失败清理时通过 abortCurrentIfEpoch + // 检查 epoch 是否仍匹配,避免旧 handler 误中止同类型重启后的新会话。 + uint32_t m_otaEpoch = 0; + + // 当前 OTA 会话的发起通道(WiFi/BLE),用于同类型重启时区分同通道/跨通道。 + // 仅在同类型活跃状态(ESP32_RECEIVING / GD32_STORING)下有意义。 + ota_channel_t m_otaChannel = OTA_CHANNEL_WIFI; + // 互斥锁(M1/L3: mutable 允许在 const 方法中加锁,消除 const_cast) mutable SemaphoreHandle_t m_mutex = nullptr; diff --git a/main/relay/ota_relay.cpp b/main/relay/ota_relay.cpp index 2443ef2..ae4512a 100644 --- a/main/relay/ota_relay.cpp +++ b/main/relay/ota_relay.cpp @@ -27,6 +27,30 @@ // ★ H4 解耦:OtaRelay 不再直接 #include "ota_coordinator.h", // 通过 IRelayListener 接口回调通知协调器,依赖方向变为单向。 +// ============================================================ +// 公共拒绝事件判断(文件内工具) +// ============================================================ +// 统一各状态 handler 头部的 ABORT/TIMEOUT/HEALTH_CRIT 拒绝判断。 +// 返回 OTA_RESULT_OK 表示非拒绝事件;其他为对应错误码。 +// allowHealth: 是否将 RELAY_EVT_HEALTH_CRIT 视为拒绝(仅 TRANSFERRING 传 true)。 +static ota_result_t commonReject(relay_event_t e, bool allowHealth = false) +{ + if (e == RELAY_EVT_ABORT) return OTA_RESULT_ERR_USER_ABORT; + if (e == RELAY_EVT_TIMEOUT) return OTA_RESULT_ERR_TIMEOUT; + if (allowHealth && e == RELAY_EVT_HEALTH_CRIT) return OTA_RESULT_ERR_HEALTH; + return OTA_RESULT_OK; +} + +// ============================================================ +// doRelayPackets 重试阈值(与历史代码字面值保持一致,仅命名化) +// ============================================================ +// 原代码中 retry_count 阈值:行 1015/1075/1084 用 10U,行 1032/1050 用 3U。 +// 含义:NACK/轮询耗尽属于"通信异常",给予较多重试机会(10次); +// 结构化错误属于"GD32 报告具体错误",较少重试(3次)。 +static constexpr uint32_t kMaxResendOnNack = 10U; // ACK 失败/轮询耗尽重发上限 +static constexpr uint32_t kMaxResendOnStructError = 3U; // 结构化错误重发上限 +static constexpr uint32_t kLastPacketAckSeq = 0xF0F0F0F0U; // 末包 ACK 序号约定 + // ============================================================ // 静态成员定义 // ============================================================ @@ -177,7 +201,7 @@ TaskHandle_t OtaRelay::getTaskHandle() // ============================================================ ota_result_t OtaRelay::processEvent(relay_event_t event, const uint8_t *payload, size_t len) { - relay_state_t cur = relayState; + relay_state_t cur = getState(); // ERROR 状态:仅接受 reset(通过 handleError 的 EVT_START → IDLE) if (cur == RELAY_ERROR) { @@ -237,16 +261,11 @@ ota_result_t OtaRelay::handleHandshaking(relay_event_t event, const uint8_t *pay (void)payload; (void)len; - if (event == RELAY_EVT_ABORT) { - DEBUG_ERR("OTA", "HANDSHAKING 收到 ABORT"); + ota_result_t rej = commonReject(event); + if (rej != OTA_RESULT_OK) { + DEBUG_ERR("OTA", "HANDSHAKING 拒绝事件: %d", (int)event); setState(RELAY_ERROR); - return OTA_RESULT_ERR_USER_ABORT; - } - - if (event == RELAY_EVT_TIMEOUT) { - DEBUG_ERR("OTA", "HANDSHAKING 超时"); - setState(RELAY_ERROR); - return OTA_RESULT_ERR_TIMEOUT; + return rej; } if (event != RELAY_EVT_START && event != RELAY_EVT_RESP_OK) { @@ -291,16 +310,11 @@ ota_result_t OtaRelay::handleSizeNeg(relay_event_t event, const uint8_t *payload (void)payload; (void)len; - if (event == RELAY_EVT_ABORT) { - DEBUG_ERR("OTA", "SIZE_NEG 收到 ABORT"); + ota_result_t rej = commonReject(event); + if (rej != OTA_RESULT_OK) { + DEBUG_ERR("OTA", "SIZE_NEG 拒绝事件: %d", (int)event); setState(RELAY_ERROR); - return OTA_RESULT_ERR_USER_ABORT; - } - - if (event == RELAY_EVT_TIMEOUT) { - DEBUG_ERR("OTA", "SIZE_NEG 超时"); - setState(RELAY_ERROR); - return OTA_RESULT_ERR_TIMEOUT; + return rej; } // 加载元数据获取固件大小 @@ -324,8 +338,8 @@ ota_result_t OtaRelay::handleSizeNeg(relay_event_t event, const uint8_t *payload m_transferCurrentSeq = 0; m_transferFlashOffset = 0; m_transferRetryCount = 0; - relayTotalPackets = m_transferTotalPackets; - relaySeq = 0; + setRelayTotalPackets(m_transferTotalPackets); + setRelaySeq(0); setState(RELAY_TRANSFERRING); DEBUG_INFO("OTA", "=== 阶段2 完成: 总包数=%" PRIu32 ", → TRANSFERRING ===", m_transferTotalPackets); @@ -340,16 +354,11 @@ ota_result_t OtaRelay::handleTransferring(relay_event_t event, const uint8_t *pa (void)payload; (void)len; - if (event == RELAY_EVT_ABORT) { - DEBUG_ERR("OTA", "TRANSFERRING 收到 ABORT"); + ota_result_t rej = commonReject(event, /*allowHealth=*/true); + if (rej != OTA_RESULT_OK) { + DEBUG_ERR("OTA", "TRANSFERRING 拒绝事件: %d", (int)event); setState(RELAY_ERROR); - return OTA_RESULT_ERR_USER_ABORT; - } - - if (event == RELAY_EVT_HEALTH_CRIT) { - DEBUG_ERR("OTA", "TRANSFERRING 健康检查危险,中止"); - setState(RELAY_ERROR); - return OTA_RESULT_ERR_HEALTH; + return rej; } DEBUG_INFO("OTA", "=== 阶段3: 数据包传输 (%" PRIu32 " 包) ===", m_transferTotalPackets); @@ -373,16 +382,11 @@ ota_result_t OtaRelay::handleFinishing(relay_event_t event, const uint8_t *paylo (void)payload; (void)len; - if (event == RELAY_EVT_ABORT) { - DEBUG_ERR("OTA", "FINISHING 收到 ABORT"); + ota_result_t rej = commonReject(event); + if (rej != OTA_RESULT_OK) { + DEBUG_ERR("OTA", "FINISHING 拒绝事件: %d", (int)event); setState(RELAY_ERROR); - return OTA_RESULT_ERR_USER_ABORT; - } - - if (event == RELAY_EVT_TIMEOUT) { - DEBUG_ERR("OTA", "FINISHING 超时"); - setState(RELAY_ERROR); - return OTA_RESULT_ERR_TIMEOUT; + return rej; } DEBUG_INFO("OTA", "=== 阶段4-5: 结束OTA + APP验证 ==="); @@ -469,31 +473,21 @@ ota_result_t OtaRelay::forwardFirmware() global_timeout_ms, (uint32_t)start_tick, (uint32_t)m_transferDeadline); // ── 驱动状态机 ── - // 步骤0: IDLE → HANDSHAKING(handleIdle 内部 reset() + setState(HANDSHAKING)) - ota_result_t res = processEvent(RELAY_EVT_START, nullptr, 0); - if (res != OTA_RESULT_OK) goto cleanup; + // 5 步顺序推进:IDLE → HANDSHAKING → SIZE_NEG → TRANSFERRING → FINISHING → DONE + // 每步同步执行对应 handler(内部完成协议交互并推进状态)。 + // 任一步返回非 OK 即跳出并复位。 + ota_result_t res = OTA_RESULT_OK; + while (true) { + relay_state_t cur = getState(); + if (cur == RELAY_DONE || cur == RELAY_ERROR) break; + res = processEvent(RELAY_EVT_START, nullptr, 0); + if (res != OTA_RESULT_OK) { + break; + } + } - // 步骤1: HANDSHAKING(内部完成握手+认证 → SIZE_NEG) - // ★ 注意:processEvent(START) 在 HANDSHAKING handler 内同步执行 doHandshakeReq + doChallengeAndAuth - // 期间 SPI 阻塞轮询 8~30 秒。超时检查必须放在同步 handler 执行之后。 - res = processEvent(RELAY_EVT_START, nullptr, 0); - if (res != OTA_RESULT_OK) goto cleanup; - - // 步骤2: SIZE_NEG(内部完成大小协商 → TRANSFERRING) - res = processEvent(RELAY_EVT_START, nullptr, 0); - if (res != OTA_RESULT_OK) goto cleanup; - - // 步骤3: TRANSFERRING(内部完成所有数据包 → FINISHING) - res = processEvent(RELAY_EVT_START, nullptr, 0); - if (res != OTA_RESULT_OK) goto cleanup; - - // 步骤4: FINISHING(结束OTA + APP验证 → DONE) - res = processEvent(RELAY_EVT_START, nullptr, 0); - if (res != OTA_RESULT_OK) goto cleanup; - -cleanup: if (res != OTA_RESULT_OK) { - DEBUG_ERR("OTA", "中继失败: result=%d, state=%d", (int)res, (int)relayState); + DEBUG_ERR("OTA", "中继失败: result=%d, state=%d", (int)res, (int)getState()); reset(); } return res; @@ -951,6 +945,158 @@ ota_result_t OtaRelay::doSendFirmwareSize(uint32_t size) * 流程:从 Flash 读取 → AES 加密 → SPI 发送 → 等待 ACK → 失败则重发。 * 每 OTA_PACKET_HEALTH_CHECK_INTERVAL 包执行一次健康检查。 */ +ota_result_t OtaRelay::prepareRelayPacket(uint32_t seq, const ota_meta_t &meta, uint8_t tx_buf[64], size_t *out_chunk_size) +{ + uint8_t plain48[GD32_OTA_PACKET_SIZE]; + memset(plain48, 0xFF, sizeof(plain48)); + size_t chunk_size = GD32_OTA_PACKET_SIZE; + if (seq == m_transferTotalPackets - 1) { + chunk_size = meta.total_size - (seq * GD32_OTA_PACKET_SIZE); + } + + esp_err_t err = Gd32FlashStore::instance().flashReadChunk(m_transferFlashOffset, plain48, chunk_size); + if (err != ESP_OK) { + DEBUG_ERR("OTA", "Flash 读取失败, offset=%" PRIu32 ", err=%d", m_transferFlashOffset, err); + return OTA_RESULT_ERR_STORAGE; + } + + tx_buf[0] = 0x00; tx_buf[1] = 0x00; tx_buf[2] = 0x00; tx_buf[3] = 0x00; + tx_buf[4] = (uint8_t)(seq & 0xFFU); + tx_buf[5] = (uint8_t)((seq >> 8) & 0xFFU); + tx_buf[6] = 0x00; + tx_buf[7] = 0x00; + + if (!aes128EcbEncrypt48Bytes(m_kSession, plain48, &tx_buf[8])) { + DEBUG_ERR("OTA", "包 %" PRIu32 " 加密失败", seq); + return OTA_RESULT_ERR_AUTH; + } + + uint32_t if_end = (seq == m_transferTotalPackets - 1) ? 0xFFFFFFFFU : 0x00000000U; + tx_buf[56] = (uint8_t)(if_end & 0xFFU); + tx_buf[57] = (uint8_t)((if_end >> 8) & 0xFFU); + tx_buf[58] = (uint8_t)((if_end >> 16) & 0xFFU); + tx_buf[59] = (uint8_t)((if_end >> 24) & 0xFFU); + calculateCrc32WithKey(m_kSession, plain48, &tx_buf[60]); + + DEBUG_DBG("OTA", "包 %" PRIu32 ": chunk=%u if_end=0x%08" PRIX32 " crc32=%02X%02X%02X%02X", + seq, (unsigned)chunk_size, if_end, + tx_buf[60], tx_buf[61], tx_buf[62], tx_buf[63]); + + m_transferCurrentSeq = seq; + if (out_chunk_size) *out_chunk_size = chunk_size; + return OTA_RESULT_OK; +} + +bool OtaRelay::sendRelayPacketWithRetry(uint32_t seq, const uint8_t tx_buf[64], size_t chunk_size, ota_result_t *out_result) +{ + uint8_t resp_type = 0; + uint8_t resp_payload[32] = {0}; + size_t resp_len = 0; + uint32_t retry_count = 0; + ota_result_t result = OTA_RESULT_OK; + const uint32_t expected_ack = (seq == m_transferTotalPackets - 1) ? kLastPacketAckSeq : (seq + 1U); + + // 注意:goto resend_packet 保持原有语义,仅作用范围缩小到本 helper。 + // 完全去除 goto 需配合 GD32 全链路 OTA 回归验证后再进行。 +resend_packet: + while (1) { + resp_len = 0; + esp_err_t err = SpiMaster::instance().otaTransfer(SPI_OTA_CMD_DATA, tx_buf, 64, + &resp_type, resp_payload, sizeof(resp_payload), &resp_len, 3000); + logRespPayload("DATA_PACKET", resp_type, resp_payload, resp_len); + if (err == ESP_OK) { + err = waitUntilNotWait("DATA_PACKET", &resp_type, resp_payload, sizeof(resp_payload), &resp_len, 1000, 10); + } + if (err != ESP_OK || resp_type != SPI_OTA_RSP_ACK) { + DEBUG_WARN("OTA", "数据包 %" PRIu32 " 发送失败, err=%d, resp_type=0x%02X, resp_len=%zu", seq, err, resp_type, resp_len); + if (++retry_count >= kMaxResendOnNack) { + DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); + result = OTA_RESULT_ERR_TIMEOUT; + break; + } + vTaskDelay(pdMS_TO_TICKS(1)); + continue; // 重发本包 + } + + if (!isStructuredAckWithSeq(resp_payload, resp_len, expected_ack)) { + uint32_t err_seq = 0; + uint8_t err_code = 0; + if (parseStructuredError(resp_payload, resp_len, &err_seq, &err_code)) { + if (err_seq == seq) { + DEBUG_WARN("OTA", "包 %" PRIu32 " 收到结构化错误 err=0x%02X,准备重发", seq, err_code); + if (++retry_count >= kMaxResendOnStructError) { + DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); + result = (err_code == 0x04U) ? OTA_RESULT_ERR_CRC : OTA_RESULT_ERR_GD32; + break; + } + continue; + } + } + + bool got_expected = false; + for (uint32_t retry = 0; retry < 10; ++retry) { + DEBUG_DBG("OTA", "包 %" PRIu32 " 收到非目标ACK(seq不匹配),继续轮询(%" PRIu32 "/5)...", seq, retry + 1); + err = SpiMaster::instance().otaPollResponse(&resp_type, resp_payload, sizeof(resp_payload), &resp_len, 2000); + if (err == ESP_OK && resp_type == SPI_OTA_RSP_WAIT) { + err = waitUntilNotWait("DATA_PACKET", &resp_type, resp_payload, sizeof(resp_payload), &resp_len, 100, 5); + } + if (err != ESP_OK || resp_type != SPI_OTA_RSP_ACK) { + DEBUG_WARN("OTA", "包 %" PRIu32 " poll 超时/非ACK,重发 (retry=%" PRIu32 "/3)", seq, retry_count + 1); + if (++retry_count >= kMaxResendOnStructError) { + DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); + result = OTA_RESULT_ERR_GD32; + break; + } + // ★ resend 前延迟 + vTaskDelay(pdMS_TO_TICKS(1)); + goto resend_packet; + } + if (isStructuredAckWithSeq(resp_payload, resp_len, expected_ack)) { + got_expected = true; + break; + } + if (parseStructuredError(resp_payload, resp_len, &err_seq, &err_code) && err_seq == seq) { + break; + } + } + + if (got_expected) { + m_transferFlashOffset += (uint32_t)chunk_size; + break; + } + + if (parseStructuredError(resp_payload, resp_len, &err_seq, &err_code) && err_seq == seq) { + DEBUG_WARN("OTA", "包 %" PRIu32 " 收到结构化错误 err=0x%02X,准备重发", seq, err_code); + if (++retry_count >= kMaxResendOnNack) { + DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); + result = (err_code == 0x04U) ? OTA_RESULT_ERR_CRC : OTA_RESULT_ERR_GD32; + break; + } + continue; + } + + DEBUG_WARN("OTA", "包 %" PRIu32 " 轮询耗尽未匹配,重发 (retry=%" PRIu32 "/10)", seq, retry_count + 1); + if (++retry_count >= kMaxResendOnNack) { + DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); + result = OTA_RESULT_ERR_GD32; + break; + } + // ★ resend 前延迟:给 GD32 时间处理上一包并更新 SPI 响应缓冲区 + vTaskDelay(pdMS_TO_TICKS(1)); + goto resend_packet; + } + + m_transferFlashOffset += (uint32_t)chunk_size; + break; + } + + if (result != OTA_RESULT_OK) { + *out_result = result; + return true; // 中止外层循环 + } + return false; // 继续下一包 +} + ota_result_t OtaRelay::doRelayPackets(TickType_t deadline_tick) { DEBUG_INFO("OTA", "开始转发 %" PRIu32 " 包", m_transferTotalPackets); @@ -962,13 +1108,10 @@ ota_result_t OtaRelay::doRelayPackets(TickType_t deadline_tick) } uint8_t tx_buf[64] = {0}; - uint8_t resp_type = 0; - uint8_t resp_payload[32] = {0}; - size_t resp_len = 0; ota_result_t result = OTA_RESULT_OK; for (uint32_t seq = 0; seq < m_transferTotalPackets; seq++) { - uint32_t retry_count = 0; + // 健康检查(每 OTA_PACKET_HEALTH_CHECK_INTERVAL 包 + 首末包) if ((seq % OTA_PACKET_HEALTH_CHECK_INTERVAL) == 0 || seq == 0 || seq == m_transferTotalPackets - 1) { health_status_t health = HealthMonitor::instance().checkBeforeOtaRelay(); if (health == HEALTH_MON_SHUTDOWN || health == HEALTH_MON_CRITICAL) { @@ -977,150 +1120,32 @@ ota_result_t OtaRelay::doRelayPackets(TickType_t deadline_tick) } } + // 全局超时检查 if (deadline_tick != 0 && xTaskGetTickCount() >= deadline_tick) { DEBUG_ERR("OTA", "全局超时: 包 %" PRIu32 "/%" PRIu32 " 已超限,中止转发", seq, m_transferTotalPackets); return OTA_RESULT_ERR_USER_ABORT; } - uint8_t plain48[GD32_OTA_PACKET_SIZE]; - memset(plain48, 0xFF, sizeof(plain48)); - size_t chunk_size = GD32_OTA_PACKET_SIZE; - if (seq == m_transferTotalPackets - 1) { - chunk_size = meta.total_size - (seq * GD32_OTA_PACKET_SIZE); - } - - esp_err_t err = Gd32FlashStore::instance().flashReadChunk(m_transferFlashOffset, plain48, chunk_size); - if (err != ESP_OK) { - DEBUG_ERR("OTA", "Flash 读取失败, offset=%" PRIu32 ", err=%d", m_transferFlashOffset, err); - return OTA_RESULT_ERR_STORAGE; - } - - tx_buf[0] = 0x00; tx_buf[1] = 0x00; tx_buf[2] = 0x00; tx_buf[3] = 0x00; - tx_buf[4] = (uint8_t)(seq & 0xFFU); - tx_buf[5] = (uint8_t)((seq >> 8) & 0xFFU); - tx_buf[6] = 0x00; - tx_buf[7] = 0x00; - - if (!aes128EcbEncrypt48Bytes(m_kSession, plain48, &tx_buf[8])) { - DEBUG_ERR("OTA", "包 %" PRIu32 " 加密失败", seq); - return OTA_RESULT_ERR_AUTH; - } - - uint32_t if_end = (seq == m_transferTotalPackets - 1) ? 0xFFFFFFFFU : 0x00000000U; - tx_buf[56] = (uint8_t)(if_end & 0xFFU); - tx_buf[57] = (uint8_t)((if_end >> 8) & 0xFFU); - tx_buf[58] = (uint8_t)((if_end >> 16) & 0xFFU); - tx_buf[59] = (uint8_t)((if_end >> 24) & 0xFFU); - calculateCrc32WithKey(m_kSession, plain48, &tx_buf[60]); - - DEBUG_DBG("OTA", "包 %" PRIu32 ": chunk=%u if_end=0x%08" PRIX32 " crc32=%02X%02X%02X%02X", - seq, (unsigned)chunk_size, if_end, - tx_buf[60], tx_buf[61], tx_buf[62], tx_buf[63]); - - m_transferCurrentSeq = seq; - - resend_packet: - while (1) { - resp_len = 0; - err = SpiMaster::instance().otaTransfer(SPI_OTA_CMD_DATA, tx_buf, sizeof(tx_buf), - &resp_type, resp_payload, sizeof(resp_payload), &resp_len, 3000); - logRespPayload("DATA_PACKET", resp_type, resp_payload, resp_len); - if (err == ESP_OK) { - err = waitUntilNotWait("DATA_PACKET", &resp_type, resp_payload, sizeof(resp_payload), &resp_len, 1000, 10); - } - if (err != ESP_OK || resp_type != SPI_OTA_RSP_ACK) { - DEBUG_WARN("OTA", "数据包 %" PRIu32 " 发送失败, err=%d, resp_type=0x%02X, resp_len=%zu", seq, err, resp_type, resp_len); - if (++retry_count >= 10U) { - DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); - result = OTA_RESULT_ERR_TIMEOUT; - break; - } - vTaskDelay(pdMS_TO_TICKS(1)); - continue; // 重发本包 - } - - uint32_t expected_ack = (seq == m_transferTotalPackets - 1) ? 0xF0F0F0F0U : (seq + 1U); - - if (!isStructuredAckWithSeq(resp_payload, resp_len, expected_ack)) { - uint32_t err_seq = 0; - uint8_t err_code = 0; - if (parseStructuredError(resp_payload, resp_len, &err_seq, &err_code)) { - if (err_seq == seq) { - DEBUG_WARN("OTA", "包 %" PRIu32 " 收到结构化错误 err=0x%02X,准备重发", seq, err_code); - if (++retry_count >= 3U) { - DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); - result = (err_code == 0x04U) ? OTA_RESULT_ERR_CRC : OTA_RESULT_ERR_GD32; - break; - } - continue; - } - } - - bool got_expected = false; - for (uint32_t retry = 0; retry < 10; ++retry) { - DEBUG_DBG("OTA", "包 %" PRIu32 " 收到非目标ACK(seq不匹配),继续轮询(%" PRIu32 "/5)...", seq, retry + 1); - err = SpiMaster::instance().otaPollResponse(&resp_type, resp_payload, sizeof(resp_payload), &resp_len, 2000); - if (err == ESP_OK && resp_type == SPI_OTA_RSP_WAIT) { - err = waitUntilNotWait("DATA_PACKET", &resp_type, resp_payload, sizeof(resp_payload), &resp_len, 100, 5); - } - if (err != ESP_OK || resp_type != SPI_OTA_RSP_ACK) { - DEBUG_WARN("OTA", "包 %" PRIu32 " poll 超时/非ACK,重发 (retry=%" PRIu32 "/3)", seq, retry_count + 1); - if (++retry_count >= 3U) { - DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); - result = OTA_RESULT_ERR_GD32; - break; - } - // ★ resend 前延迟 - vTaskDelay(pdMS_TO_TICKS(1)); - goto resend_packet; - } - if (isStructuredAckWithSeq(resp_payload, resp_len, expected_ack)) { - got_expected = true; - break; - } - if (parseStructuredError(resp_payload, resp_len, &err_seq, &err_code) && err_seq == seq) { - break; - } - } - - if (got_expected) { - m_transferFlashOffset += (uint32_t)chunk_size; - break; - } - - if (parseStructuredError(resp_payload, resp_len, &err_seq, &err_code) && err_seq == seq) { - DEBUG_WARN("OTA", "包 %" PRIu32 " 收到结构化错误 err=0x%02X,准备重发", seq, err_code); - if (++retry_count >= 10U) { - DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); - result = (err_code == 0x04U) ? OTA_RESULT_ERR_CRC : OTA_RESULT_ERR_GD32; - break; - } - continue; - } - - DEBUG_WARN("OTA", "包 %" PRIu32 " 轮询耗尽未匹配,重发 (retry=%" PRIu32 "/10)", seq, retry_count + 1); - if (++retry_count >= 10U) { - DEBUG_ERR("OTA", "包 %" PRIu32 " 重发次数超限", seq); - result = OTA_RESULT_ERR_GD32; - break; - } - // ★ resend 前延迟:给 GD32 时间处理上一包并更新 SPI 响应缓冲区 - vTaskDelay(pdMS_TO_TICKS(1)); - goto resend_packet; - } - - m_transferFlashOffset += (uint32_t)chunk_size; - break; - } - + // 准备单包:读 Flash + 加密 + 组装 + size_t chunk_size = 0; + result = prepareRelayPacket(seq, meta, tx_buf, &chunk_size); if (result != OTA_RESULT_OK) { - break; + return result; } - relaySeq = seq + 1; - if (seq % (m_transferTotalPackets / 10 + 1) == 0) { - uint32_t progress = (relaySeq * 100U) / m_transferTotalPackets; - DEBUG_INFO("OTA", "转发进度: %" PRIu32 "/%" PRIu32 " 包 (%" PRIu32 "%%)", relaySeq, m_transferTotalPackets, progress); + // 发送单包并等待 ACK(内含重试循环) + if (sendRelayPacketWithRetry(seq, tx_buf, chunk_size, &result)) { + return result; // 中止 + } + + setRelaySeq(seq + 1); + // 每 5% 或前 5 包/最后 5 包打印进度,避免 9 秒+静默期无法判断是否卡死 + uint32_t step = m_transferTotalPackets / 20 + 1; // ~5% 间隔 + if (seq < 5 || seq >= m_transferTotalPackets - 5 || seq % step == 0) { + // 进度日志直接用本地 seq+1,避免再次加锁读 relaySeq + uint32_t done = seq + 1; + uint32_t progress = (done * 100U) / m_transferTotalPackets; + DEBUG_INFO("OTA", "转发进度: %" PRIu32 "/%" PRIu32 " 包 (%" PRIu32 "%%)", done, m_transferTotalPackets, progress); } // ★ 包间延迟:给 GD32 Bootloader 充足时间完成 flash 写入并准备下一包 ACK。 @@ -1132,8 +1157,36 @@ ota_result_t OtaRelay::doRelayPackets(TickType_t deadline_tick) return result; } -/** @brief 协议子步骤:发送 FINISH_OTA 指令并验证 GD32 APP 存活。 */ -ota_result_t OtaRelay::doFinishAndVerify() +// ============================================================ +// ============================================================ +// 统一 OtaRelay 内部两处 APP 探测逻辑(doWaitForAppAlive 轮询 + taskEntry 启动探测)。 +// 成功:setGd32AppAlive(true) + setGd32Version + 打印版本号;失败:打印 warning。 +// 注:OtaCoordinator::probeGd32AppOnce 逻辑相似,但因依赖方向不调用本函数。 +bool OtaRelay::probeAndCacheAppAlive(uint32_t timeout_ms, uint32_t *out_uptime) +{ + uint32_t uptime = 0; + gd32_version_t ver = {}; + + esp_err_t err = SpiMaster::instance().appPing(timeout_ms, &uptime, &ver); + if (err == ESP_OK) { + // 持锁一次性写入两个状态字段,保证 getSnapshot() 读取一致 + StateLockGuard lock; + gd32AppAlive = true; + gd32Version = ver; + if (out_uptime) *out_uptime = uptime; + DEBUG_INFO("OTA", "GD32 APP 存活确认: uptime=%" PRIu32 " sec, APP=v%d.%d.%d.%d, BOOT=v%d.%d.%d.%d", + uptime, + ver.app_major, ver.app_minor, ver.app_patch, ver.app_build, + ver.boot_major, ver.boot_minor, ver.boot_patch, ver.boot_build); + return true; + } + if (out_uptime) *out_uptime = 0; + DEBUG_WARN("OTA", "GD32 APP 探测失败: %s", esp_err_to_name(err)); + return false; +} + +/** @brief 协议子步骤:发送 FINISH_OTA 指令并等待 GD32 ACK(10 次重试)。 */ +ota_result_t OtaRelay::doSendFinishOta() { DEBUG_INFO("OTA", "发送 FINISH_OTA (0x0A 0xAC),等待 GD32 确认即将跳转 APP"); @@ -1193,6 +1246,43 @@ ota_result_t OtaRelay::doFinishAndVerify() return OTA_RESULT_ERR_GD32; } + return OTA_RESULT_OK; +} + +/** @brief 协议子步骤:轮询等待 GD32 APP 存活(30 次重试)。 */ +ota_result_t OtaRelay::doWaitForAppAlive(uint32_t timeout_ms) +{ + // ★ APP 存活验证 + setGd32AppAlive(false); + + for (uint32_t attempt = 0; attempt < 30; ++attempt) { + vTaskDelay(pdMS_TO_TICKS(500)); + if (probeAndCacheAppAlive(timeout_ms)) { + DEBUG_INFO("OTA", "=== OTA 全流程完成 (APP 已启动) ==="); + break; + } + DEBUG_INFO("OTA", "等待 GD32 APP 启动... (%" PRIu32 "/30)", attempt + 1); + } + + if (!getGd32AppAlive()) { + DEBUG_ERR("OTA", "GD32 APP alive confirm timeout, OTA failed"); + reset(); + return OTA_RESULT_ERR_GD32; + } + + return OTA_RESULT_OK; +} + +/** @brief 协议子步骤:发送 FINISH_OTA + 等待 APP 存活(顺序两阶段 + 会话清理)。 */ +ota_result_t OtaRelay::doFinishAndVerify() +{ + // 阶段1: 发送 FINISH_OTA 并等待 ACK + ota_result_t res = doSendFinishOta(); + if (res != OTA_RESULT_OK) { + return res; + } + + // 会话密钥/上下文清理 Protocol::sessionKeyClear(&m_sessionCtx); memset(&m_sessionCtx, 0, sizeof(session_ctx_t)); memset(m_kSession, 0, sizeof(m_kSession)); @@ -1203,34 +1293,8 @@ ota_result_t OtaRelay::doFinishAndVerify() Gd32FlashStore::instance().metaClear(); - // ★ APP 存活验证 - gd32AppAlive = false; - - for (uint32_t attempt = 0; attempt < 30; ++attempt) { - vTaskDelay(pdMS_TO_TICKS(500)); - uint32_t uptime = 0; - gd32_version_t ping_ver = {}; - esp_err_t ping_err = SpiMaster::instance().appPing(1000, &uptime, &ping_ver); - if (ping_err == ESP_OK) { - gd32AppAlive = true; - gd32Version = ping_ver; - DEBUG_INFO("OTA", "GD32 APP 存活确认: uptime=%" PRIu32 " sec, APP=v%d.%d.%d.%d, BOOT=v%d.%d.%d.%d", - uptime, - ping_ver.app_major, ping_ver.app_minor, ping_ver.app_patch, ping_ver.app_build, - ping_ver.boot_major, ping_ver.boot_minor, ping_ver.boot_patch, ping_ver.boot_build); - DEBUG_INFO("OTA", "=== OTA 全流程完成 (APP 已启动) ==="); - break; - } - DEBUG_INFO("OTA", "等待 GD32 APP 启动... (%" PRIu32 "/30)", attempt + 1); - } - - if (!gd32AppAlive) { - DEBUG_ERR("OTA", "GD32 APP alive confirm timeout, OTA failed"); - reset(); - return OTA_RESULT_ERR_GD32; - } - - return OTA_RESULT_OK; + // 阶段2: APP 存活轮询(单次 appPing 超时 1000ms,共 30 次) + return doWaitForAppAlive(1000); } // ============================================================ @@ -1258,24 +1322,7 @@ void OtaRelay::taskEntry(void *pvParameters) { DEBUG_INFO("OTA", "启动初始 GD32 探测..."); vTaskDelay(pdMS_TO_TICKS(3000)); // 等待 GD32 APP 就绪 - uint32_t uptime = 0; - gd32_version_t ping_ver = {}; - esp_err_t ping_err = SpiMaster::instance().appPing(2000, &uptime, &ping_ver); - if (ping_err == ESP_OK) { - StateLockGuard lock; - gd32AppAlive = true; - gd32Version = ping_ver; - DEBUG_INFO("OTA", "启动 GD32 探测成功: uptime=%" PRIu32 "s, " - "APP=v%d.%d.%d.%d, BOOT=v%d.%d.%d.%d", - uptime, - ping_ver.app_major, ping_ver.app_minor, - ping_ver.app_patch, ping_ver.app_build, - ping_ver.boot_major, ping_ver.boot_minor, - ping_ver.boot_patch, ping_ver.boot_build); - } else { - DEBUG_WARN("OTA", "启动 GD32 探测失败: %s (GD32 可能未就绪)", - esp_err_to_name(ping_err)); - } + probeAndCacheAppAlive(2000); } while (1) { @@ -1287,7 +1334,8 @@ void OtaRelay::taskEntry(void *pvParameters) DEBUG_INFO("OTA", "收到 OTA 调度信号: %" PRIu32 ",开始转发...", msg); // ★ H4: 通过 IRelayListener 接口回调通知协调器(不再直接依赖 OtaCoordinator) - if (s_relayListener) s_relayListener->onRelayStarted(); + IRelayListener *listener = getRelayListener(); + if (listener) listener->onRelayStarted(); ota_result_t result = instance().forwardFirmware(); uint32_t result_msg = (uint32_t)result; @@ -1303,6 +1351,7 @@ void OtaRelay::taskEntry(void *pvParameters) } instance().reset(); // ★ H4: 通过 IRelayListener 接口回调通知协调器中继结束 - if (s_relayListener) s_relayListener->onRelayComplete(result == OTA_RESULT_OK); + listener = getRelayListener(); + if (listener) listener->onRelayComplete(result == OTA_RESULT_OK); } } diff --git a/main/relay/ota_relay.h b/main/relay/ota_relay.h index 40410de..e7320f9 100644 --- a/main/relay/ota_relay.h +++ b/main/relay/ota_relay.h @@ -224,21 +224,12 @@ public: // ── Flash 操作已迁移至 Gd32FlashStore(storage/gd32_flash_store.h)── - // ── 公开状态成员 ── - static relay_state_t relayState; ///< 当前状态机状态 - static uint32_t relaySeq; ///< 当前中继会话序号 - static uint32_t relayTotalPackets; ///< 本次中继总包数 - static volatile bool gd32AppAlive; ///< GD32 APP 存活标志 - static gd32_version_t gd32Version; ///< GD32 固件版本信息 - static TaskHandle_t taskHandle; ///< OTA 中继任务句柄 - static IRelayListener *s_relayListener; ///< 中继事件监听器(解决 H4) - // ── 线程安全锁 ── - static SemaphoreHandle_t s_stateMutex; ///< 状态成员互斥锁 - static void initMutex(); ///< 初始化状态互斥锁(app_main 中调用) + static void initMutex(); ///< 初始化状态互斥锁(taskEntry 首行调用) static constexpr TickType_t kLockTimeout = pdMS_TO_TICKS(50); ///< 锁获取超时时间 - /** @brief RAII 状态锁守护类,自动获取/释放 s_stateMutex。 */ + /** @brief RAII 状态锁守护类,自动获取/释放 s_stateMutex。 + * 作为 public 嵌套类保留,内部代码在持锁块内可直接访问 private 状态成员。 */ class StateLockGuard { public: explicit StateLockGuard(TickType_t timeout = kLockTimeout) { @@ -268,6 +259,23 @@ private: static QueueHandle_t dispatchQueue; ///< 事件分发队列 static QueueHandle_t resultQueue; ///< 结果回传队列 + // ── 状态成员(全部改 private,统一通过 public setter/getter 访问)── + // 外部代码使用 getState()/getRelaySeq()/... 读取,setState()/setRelaySeq()/... 写入。 + // 内部代码:若已在 StateLockGuard 块内(reset/getSnapshot/启动探测),可直接访问; + // 否则必须使用 setter/getter,禁止裸读写(消除数据竞争)。 + static relay_state_t relayState; ///< 当前状态机状态 + static uint32_t relaySeq; ///< 当前中继会话序号 + static uint32_t relayTotalPackets; ///< 本次中继总包数 + static volatile bool gd32AppAlive; ///< GD32 APP 存活标志 + static gd32_version_t gd32Version; ///< GD32 固件版本信息 + static IRelayListener *s_relayListener; ///< 中继事件监听器(解决 H4) + static SemaphoreHandle_t s_stateMutex; ///< 状态成员互斥锁(由 StateLockGuard 使用) + + // taskHandle 为 write-once-at-init 字段:仅 taskEntry 启动首行写入一次, + // 之后只读。getTaskHandle() 无锁返回,因写入发生在任务创建后、任务循环开始前, + // 与任何外部读取者构成 happens-before(任务创建同步),无数据竞争。 + static TaskHandle_t taskHandle; ///< OTA 中继任务句柄 + OtaRelay() = default; ~OtaRelay() = default; OtaRelay(const OtaRelay&) = delete; @@ -328,10 +336,44 @@ private: */ ota_result_t doRelayPackets(TickType_t deadline_tick); /** - * @brief 发送结束指令并验证 APP 存活. + * @brief 准备单包:读 Flash + 组装 tx_buf + 加密 + CRC(doRelayPackets 子步骤)。 + * @param seq 当前包序号。 + * @param meta 固件元数据(用于计算末包 chunk_size)。 + * @param tx_buf 输出缓冲区(64 字节)。 + * @param out_chunk_size 输出本包有效字节数(用于 sendRelayPacketWithRetry 推进 offset)。 + * @return OTA_RESULT_OK 成功,OTA_RESULT_ERR_STORAGE 读 Flash 失败,OTA_RESULT_ERR_AUTH 加密失败。 + */ + ota_result_t prepareRelayPacket(uint32_t seq, const ota_meta_t &meta, uint8_t tx_buf[64], size_t *out_chunk_size); + /** + * @brief 发送单包并等待 ACK(doRelayPackets 子步骤)。 + * + * 内含重试循环,使用以下命名常量(与历史阈值保持一致): + * kMaxResendOnNack=10(ACK 失败/轮询耗尽重发上限) + * kMaxResendOnStructError=3(结构化错误重发上限) + * + * @param seq 当前包序号。 + * @param tx_buf 已组装好的 64 字节发送缓冲区。 + * @param chunk_size 本包有效字节数(成功时用于推进 m_transferFlashOffset)。 + * @param[out] out_result 失败时写入错误码(OK 时不修改)。 + * @return true 应中止外层循环(result 已设);false 继续下一包。 + */ + bool sendRelayPacketWithRetry(uint32_t seq, const uint8_t tx_buf[64], size_t chunk_size, ota_result_t *out_result); + /** + * @brief 发送结束指令并验证 APP 存活(顺序调用 doSendFinishOta + doWaitForAppAlive). * @return OTA_RESULT_OK 成功,负值表示错误。 */ ota_result_t doFinishAndVerify(); + /** + * @brief 发送 FINISH_OTA 指令并等待 GD32 ACK(10 次重试). + * @return OTA_RESULT_OK 成功,OTA_RESULT_ERR_GD32 失败。 + */ + ota_result_t doSendFinishOta(); + /** + * @brief 轮询等待 GD32 APP 存活(30 次重试). + * @param timeout_ms 单次 appPing 超时(毫秒)。 + * @return OTA_RESULT_OK 成功,OTA_RESULT_ERR_GD32 超时失败。 + */ + ota_result_t doWaitForAppAlive(uint32_t timeout_ms); // ── 传输进度跟踪(状态机内部)── uint32_t m_transferTotalPackets = 0; @@ -343,6 +385,14 @@ private: // ── 加密/工具方法(不变)── static int aes128EcbEncryptBlockLocal(const uint8_t *key, const uint8_t *in, uint8_t *out); static void logRespPayload(const char *stage, uint8_t resp_type, const uint8_t *payload, size_t len); + /** + * @brief 探测 GD32 APP 存活并缓存版本(统一 OtaRelay 内部探测逻辑). + * 成功打印版本号,失败打印 warning。 + * @param timeout_ms appPing 超时(毫秒)。 + * @param out_uptime 可选,输出 GD32 uptime(秒)。 + * @return true 探测成功,false 失败。 + */ + static bool probeAndCacheAppAlive(uint32_t timeout_ms, uint32_t *out_uptime = nullptr); esp_err_t waitUntilNotWait(const char *stage, uint8_t *resp_type, uint8_t *resp_payload, size_t resp_max, size_t *resp_len, uint32_t timeout_ms, diff --git a/main/server/ble_ota.cpp b/main/server/ble_ota.cpp index 47f8e76..8676ecb 100644 --- a/main/server/ble_ota.cpp +++ b/main/server/ble_ota.cpp @@ -1,4 +1,4 @@ -/* +/* * ble_ota.cpp - BLE OTA GATT 服务器实现 (ESP-IDF 6.0 NimBLE) * 所有功能封装为 BleOta 类方法。 * T5.1 - T5.5 @@ -54,6 +54,7 @@ #define BLE_OTA_FINISH_REBOOT_NOW 0x07 #define BLE_OTA_FINISH_GD32_RELAY_START 0x10 #define BLE_OTA_FINISH_GD32_RELAY_DONE 0x11 +#define BLE_OTA_FINISH_ABORTED 0x20 // 用户主动中止(BLE 不断开) #define BLE_OTA_FINISH_ERR_FINALIZE 0x81 #define BLE_OTA_ACK_WINDOW 32 #define BLE_ATT_ERR_OTA_BUSY 0x80 // ESP32 OTA 被其他通道占用 @@ -81,6 +82,17 @@ struct BleEsp32FinishCtx { uint16_t session_id; }; +// RAII 包装:保证任务所有 return 路径自动释放 ctx,避免历史多次漏 free。 +// 注意:若调用 restartEsp32()(内部 esp_restart 不返回),析构不会执行, +// 但这是设备重启路径,资源由系统回收。 +struct BleFinishCtxDeleter { + BleEsp32FinishCtx *p; + explicit BleFinishCtxDeleter(BleEsp32FinishCtx *ctx) : p(ctx) {} + ~BleFinishCtxDeleter() { if (p) { free(p); p = nullptr; } } + BleFinishCtxDeleter(const BleFinishCtxDeleter&) = delete; + BleFinishCtxDeleter& operator=(const BleFinishCtxDeleter&) = delete; +}; + // ============================================================ // 前向声明 // ============================================================ @@ -136,6 +148,8 @@ static void ble_esp32_restart_task(void *pvParameters) static void ble_esp32_finish_and_reboot_task(void *pvParameters) { BleEsp32FinishCtx *ctx = (BleEsp32FinishCtx *)pvParameters; + // RAII:保证所有 return 路径自动释放 ctx(restartEsp32 不返回路径除外) + BleFinishCtxDeleter ctx_guard(ctx); char *frontend_sha_hex = ctx ? ctx->sha_hex : nullptr; uint16_t session_id = ctx ? ctx->session_id : 0; DEBUG_INFO(TAG, "ESP32 OTA 异步收尾任务启动"); @@ -155,7 +169,6 @@ static void ble_esp32_finish_and_reboot_task(void *pvParameters) DEBUG_WARN(TAG, "异步收尾时 OTA 状态无效,放弃重启"); sendFinishEvent(BLE_OTA_FINISH_ERR_FINALIZE, packBleFinishValue(session_id, 1)); - if (ctx) free(ctx); BleOta::instance().m_esp32RebootPending = false; esp32_reorder_free(); vTaskDelete(NULL); @@ -187,8 +200,7 @@ static void ble_esp32_finish_and_reboot_task(void *pvParameters) // 4) 通知全部发出后由协调器统一重启(notify + delay + esp_restart) DEBUG_INFO(TAG, "[FINISH] 通知已发送,重启..."); - if (ctx) free(ctx); - OtaCoordinator::instance().restartEsp32(); // 不返回 + OtaCoordinator::instance().restartEsp32(); // 不返回,ctx 由系统回收 } else if (fr.err == ESP_ERR_INVALID_VERSION) { DEBUG_ERR(TAG, "[FINISH] SHA-256 不匹配,OTA 已中止"); sendFinishEvent(BLE_OTA_FINISH_ERR_SHA_MISMATCH, packBleFinishValue(session_id, 0)); @@ -207,7 +219,6 @@ static void ble_esp32_finish_and_reboot_task(void *pvParameters) esp32_reorder_free(); } - if (ctx) free(ctx); vTaskDelete(NULL); } @@ -234,7 +245,7 @@ static void ble_ota_boost_wifi_ps() s_wifi_ps_boosted = true; // ★ L1: 通过协调器通知 WiFi 层(替代直接调用 WifiOta) OtaCoordinator::instance().notifyBleCoexStart(); - DEBUG_INFO(TAG, "BLE OTA 进行中:暂停 WiFi RSSI 轮询(保持 WIFI_PS_NONE,STA 仍可响应 HTTP)"); + DEBUG_INFO(TAG, "BLE OTA 数据传输进行中:暂停 WiFi RSSI 轮询(保持 WIFI_PS_NONE,STA 仍可响应 HTTP)"); } /** @@ -246,7 +257,7 @@ static void ble_ota_restore_wifi_ps() s_wifi_ps_boosted = false; // ★ L1: 通过协调器通知 WiFi 层(替代直接调用 WifiOta) OtaCoordinator::instance().notifyBleCoexEnd(); - DEBUG_INFO(TAG, "BLE OTA 结束:恢复 WiFi RSSI 轮询"); + DEBUG_INFO(TAG, "BLE OTA 数据传输结束:恢复 WiFi RSSI 轮询(GD32 中继仍可能进行中)"); } // ============================================================ @@ -312,6 +323,7 @@ struct Esp32OtaReorderBuf { static Esp32OtaReorderBuf s_reorder; static uint32_t s_fallback_ooo_count = 0; // 回退模式下自上次 ACK 以来的乱序包计数 +static uint32_t s_gd32_ooo_count = 0; // GD32 路径自上次 ACK 以来的乱序/重复包计数 // 前向声明(alloc/free 互相引用) static void esp32_reorder_free(); @@ -512,9 +524,14 @@ static esp_err_t esp32_write_ordered(uint16_t seq, uint8_t cmd, // 后续包处理会覆盖它,必须拷贝一份 uint8_t *copy = (uint8_t *)malloc(data_len); if (!copy) { - DEBUG_WARN(TAG, "Reorder slot malloc 失败: seq=%u,丢弃该包(靠客户端重传)", - seq); - return ESP_OK; // 不算失败,靠客户端重传补 + uint32_t curReceived = (uint32_t)Esp32OtaCore::instance().getReceived(); + DEBUG_ERR(TAG, "Reorder slot malloc 失败: seq=%u expected=%u bytes=%u," + "ACK 报告缺口并 abort", + seq, (unsigned)s_reorder.expected, (unsigned)curReceived); + sendWindowAck(cmd, + s_reorder.expected > 0 ? (uint16_t)(s_reorder.expected - 1) : 0, + curReceived); + return ESP_ERR_NO_MEM; } memcpy(copy, data, data_len); s_reorder.slots[idx] = copy; @@ -548,7 +565,7 @@ static esp_err_t esp32_write_ordered(uint16_t seq, uint8_t cmd, static esp_err_t ble_esp32_ota_begin(size_t total_size) { // ★ 通过协调器发起(内部已含 forceCleanup 重试) - esp_err_t err = OtaCoordinator::instance().beginEsp32OtaWithRetry(total_size); + esp_err_t err = OtaCoordinator::instance().beginEsp32OtaWithRetry(total_size, OTA_CHANNEL_BLE); if (err != ESP_OK) { return err; } @@ -564,21 +581,53 @@ static esp_err_t ble_esp32_ota_begin(size_t total_size) } /** - * @brief 中止 ESP32 OTA 会话并清理(薄包装)。 + * @brief BLE OTA 会话状态统一复位(共用清理内核)。 + * + * 集中处理三个清理动作: + * 1. abortCurrent():通知协调器按当前状态分发清理 + * (GD32_STORING→metaClear, ESP32_RECEIVING→core.abort, IDLE→空操作), + * 确保下次 begin() 不会被残留状态假拒绝。 + * 2. core.abort() 兜底:若 Esp32OtaCore 仍 isActive(异常路径),单独再 abort。 + * 3. BLE OTA 会话状态字段复位:m_esp32LastSeq/m_esp32LastCmd/m_esp32RebootPending + * + esp32_reorder_free() 释放 reorder buffer + s_gd32_ooo_count 归零。 + * + * 不做:OTA 数据队列清理(调用方按需做)、WiFi PS 恢复(调用方按需做)。 + * 这两个动作在不同调用路径下时序/前置条件不同,交给调用方更安全。 + * + * 调用方: + * - ble_esp32_ota_abort(): ESP32 OTA 异常/中止路径 + * - 0xFB handler: 用户主动中止 + * - BLE_GAP_EVENT_DISCONNECT handler: BLE 断开(含 GD32 存储断开) + */ +static void ble_ota_session_reset() +{ + // ★ 无条件通知协调器释放通道占用(m_sysState → IDLE)。 + // abortCurrent() 对 IDLE 状态是空操作,对其他状态均复位到 IDLE, + // 确保后续 begin() 不会被残留的 ESP32_RECEIVING / ERROR 等状态假拒绝。 + OtaCoordinator::instance().abortCurrent(); + // 若 ESP32 OTA core 仍活跃(兜底),单独再 abort 一次 + if (Esp32OtaCore::instance().isActive()) { + Esp32OtaCore::instance().abort(); + } + // 复位 BLE OTA 会话状态,避免下次会话读到旧值 + BleOta::instance().m_esp32LastSeq = 0xFFFFU; + BleOta::instance().m_esp32LastCmd = 0; + BleOta::instance().m_esp32RebootPending = false; + // 释放 ESP32 reorder buffer(无 buffer 时为空操作) + esp32_reorder_free(); + // 复位 GD32 路径乱序包计数器(节流 ACK 计数),保证下次 GD32 上传从干净状态开始 + s_gd32_ooo_count = 0; +} + +/** + * @brief ESP32 OTA 异常/中止时调用:会话复位 + WiFi PS 恢复。 * * 关键:必须先通知协调器释放 m_sysState(避免下次 begin 被 ESP32_RECEIVING * 状态假拒绝),再调 core.abort() 释放 mutex。两者重复 abort 幂等安全。 */ static void ble_esp32_ota_abort() { - // ★ 无条件通知协调器释放通道占用(m_sysState → IDLE)。 - // abortCurrent() 对 IDLE 状态是空操作,对其他状态均复位到 IDLE, - // 确保后续 begin() 不会被残留的 ESP32_RECEIVING / ERROR 等状态假拒绝。 - OtaCoordinator::instance().abortCurrent(); - Esp32OtaCore::instance().abort(); - BleOta::instance().m_esp32RebootPending = false; - // ★ 释放 reorder buffer - esp32_reorder_free(); + ble_ota_session_reset(); // ★ BLE OTA 中止:恢复 WiFi 射频 ble_ota_restore_wifi_ps(); } @@ -685,7 +734,7 @@ static void ble_gd32_relay_wait_task(void *pvParameters) ota_result_t result = (ota_result_t)((int32_t)result_msg); if (result == OTA_RESULT_OK) { - DEBUG_INFO(TAG, "GD32 BLE OTA 中继完成,通知前端"); + DEBUG_INFO(TAG, "GD32 BLE OTA 中继完成,通知前端 (stage=0x11, session=%u)", session_id); sendFinishEvent(BLE_OTA_FINISH_GD32_RELAY_DONE, packBleFinishValue(session_id, 0)); } else { @@ -798,6 +847,7 @@ static int ble_ota_ctrl_access(uint16_t conn_handle, uint16_t attr_handle, sendFinishEvent(BLE_OTA_FINISH_GD32_RELAY_START, packBleFinishValue(session_id, 0)); + DEBUG_INFO(TAG, "已通知前端: GD32 中继已开始 (stage=0x10, session=%u)", session_id); BleGd32RelayWaitCtx *ctx = (BleGd32RelayWaitCtx *)malloc(sizeof(BleGd32RelayWaitCtx)); if (!ctx) { @@ -876,6 +926,8 @@ static int ble_ota_ctrl_access(uint16_t conn_handle, uint16_t attr_handle, DEBUG_ERR(TAG, "创建 ESP32 OTA 收尾任务失败"); free(finish_ctx); BleOta::instance().m_esp32RebootPending = false; + sendFinishEvent(BLE_OTA_FINISH_ERR_FINALIZE, + packBleFinishValue(session_id, (uint16_t)ESP_ERR_NO_MEM)); } else { DEBUG_INFO(TAG, "ESP32 OTA 收尾任务已创建(前端 SHA 已传入)"); } @@ -896,12 +948,64 @@ static int ble_ota_ctrl_access(uint16_t conn_handle, uint16_t attr_handle, if (ok != pdPASS) { DEBUG_ERR(TAG, "创建 0xFA 重启任务失败"); BleOta::instance().m_esp32RebootPending = false; + sendFinishEvent(BLE_OTA_FINISH_ERR_FINALIZE, 0); } + } else if (buf[0] == BLE_OTA_CTRL_ABORT_OTA) { + // 0xFB: 中止当前 OTA(保持 BLE 连接) + // - 数据接收阶段(GD32_STORING / ESP32_RECEIVING):立即中止 + 清队列 + 回复 ABORTED + // - 中继/收尾阶段(GD32_RELAYING / ESP32_FINALIZING):拒绝中止(SPI 转发不可中断) + // - IDLE/已完成状态:幂等回复 ABORTED + uint16_t session_id = 0; + if (len >= 3) { + session_id = (uint16_t)(buf[1] | (buf[2] << 8)); + } + + ota_system_state_t cur_state = OtaCoordinator::instance().getSystemState(); + DEBUG_WARN(TAG, "收到 0xFB 中止请求: state=%s session=%u", + OtaCoordinator::stateToString(cur_state), session_id); + + if (cur_state == OTA_SYS_GD32_RELAYING) { + DEBUG_WARN(TAG, "GD32 SPI 中继进行中,无法中止(请等待完成或断开 BLE)"); + sendFinishEvent(BLE_OTA_FINISH_ERR_GD32_RELAY, + packBleFinishValue(session_id, 2)); + return 0; + } + if (cur_state == OTA_SYS_ESP32_FINALIZING) { + DEBUG_WARN(TAG, "ESP32 OTA 收尾进行中,无法中止"); + sendFinishEvent(BLE_OTA_FINISH_ERR_FINALIZE, + packBleFinishValue(session_id, 2)); + return 0; + } + + // ── 可中止状态:清队列 + abort core + 复位协调器 ── + if (s_otaDataQueue) { + BleOtaDataBatch stale; + size_t dropped = 0; + while (xQueueReceive(s_otaDataQueue, &stale, 0) == pdTRUE) { + free(stale.data); + dropped++; + } + if (dropped > 0) { + DEBUG_INFO(TAG, "中止时清理 OTA 数据队列: %zu 项", dropped); + } + } + + // abortCurrent 内部按状态分发:GD32_STORING→metaClear, ESP32_RECEIVING→core.abort, IDLE→空操作 + // ble_ota_session_reset() 统一负责 abortCurrent + core.abort 兜底 + 会话状态复位 + // (m_esp32LastSeq/m_esp32LastCmd/m_esp32RebootPending/reorder/s_gd32_ooo_count) + ble_ota_session_reset(); + + ble_ota_restore_wifi_ps(); + + DEBUG_INFO(TAG, "OTA 已中止(BLE 保持连接),通知前端 (stage=0x20)"); + sendFinishEvent(BLE_OTA_FINISH_ABORTED, + packBleFinishValue(session_id, 0)); } // 0xF7 = 触发 GD32 中继 // 0xF8 = 触发 ESP32 本机 OTA 收尾:设备端读 OTA 分区算 SHA 并与前端 32B SHA 比对, // 一致走 end+set_boot+reboot,不一致返回 0x85 ERR_SHA_MISMATCH(不重启) // 0xFA = 重启 ESP32(配套前端 expectedReboot 自动重连) + // 0xFB = 中止当前 OTA(不断开 BLE):仅数据接收阶段有效,中继/收尾阶段拒绝 return 0; // BLE_ATT_ERR_OK 在 NimBLE 中就是 0 } @@ -966,6 +1070,248 @@ static int ble_ota_data_in_access(uint16_t conn_handle, uint16_t attr_handle, return 0; } +/** + * @brief 处理单个 ESP32 OTA 数据包(ble_ota_process_data_batch 子步骤)。 + * @return true 中止整个 batch;false 继续下一包(offset 由外层统一推进)。 + */ +static bool process_esp32_packet(uint8_t cmd, uint16_t seq, uint32_t total_size, + const uint8_t *buf, size_t data_offset, size_t data_len, + uint32_t expected_payload) +{ + if (seq == 0) { + if (total_size == 0) { + DEBUG_ERR(TAG, "ESP32 固件大小非法: %" PRIu32, total_size); + return true; + } + // ★ 会话判定:若已有活跃 ESP32 OTA 会话,根据已接收字节数区分两种情况: + // ① m_received == 0:seq=0 尚未写入 flash,说明是客户端 ACK 超时后 + // 重传的 seq=0(同一会话续传),跳过 begin。 + // ② m_received > 0:seq=0 已写入且有后续数据,说明这是新的上传会话 + // (前端 abort 后 BLE 断开未完成就发起了新传输),需 abort 旧会话 + // 再 begin,否则 reorder buffer 以旧 expected 去重导致全部丢弃。 + if (Esp32OtaCore::instance().isActive()) { + if (Esp32OtaCore::instance().getReceived() > 0) { + // ★ 跨通道保护:当前活跃会话可能是 WiFi 通道发起的, + // BLE 不应无条件 abort 别人的会话。只有当活跃会话也是 BLE + // 通道时才允许 abort + rebegin(同会话重传/新 BLE 会话场景); + // 否则直接互斥拒绝,让前端看到"被其他通道占用"。 + ota_channel_t curCh = OtaCoordinator::instance().getOtaChannel(); + if (curCh != OTA_CHANNEL_BLE) { + DEBUG_WARN(TAG, "ESP32 OTA 被其他通道占用(ch=%d),BLE 拒绝抢占", + (int)curCh); + sendFinishEvent(BLE_OTA_FINISH_ERR_MUTEX, 0); + return true; + } + DEBUG_WARN(TAG, "ESP32 OTA 新会话检测: received=%u > 0, " + "中止旧会话并重新开始", + (unsigned)Esp32OtaCore::instance().getReceived()); + ble_esp32_ota_abort(); + esp_err_t err = ble_esp32_ota_begin(total_size); + if (err != ESP_OK) { + if (err == ESP_ERR_INVALID_STATE) { + sendFinishEvent(BLE_OTA_FINISH_ERR_MUTEX, 0); + } + return true; + } + } else { + DEBUG_INFO(TAG, "ESP32 OTA 会话已激活,seq=0 视为重传,跳过 begin"); + } + } else { + // 分区选择/大小校验/互斥由 Esp32OtaCore::begin 内部完成 + esp_err_t err = ble_esp32_ota_begin(total_size); + if (err != ESP_OK) { + if (err == ESP_ERR_INVALID_STATE) { + sendFinishEvent(BLE_OTA_FINISH_ERR_MUTEX, 0); + } + return true; + } + } + } + + // ★ 会话有效性检查:如果 begin() 失败(如互斥拒绝),后续包也应丢弃 + if (!Esp32OtaCore::instance().isActive()) { + DEBUG_WARN(TAG, "ESP32 OTA 会话无效,丢弃 seq=%u", seq); + return false; // 外层推进 offset,继续下一包 + } + + // ★ 防重复写入保护:如果已接收字节 >= 声明大小,跳过(重试引起的重复包) + uint32_t curReceived = (uint32_t)Esp32OtaCore::instance().getReceived(); + if (total_size > 0 && curReceived >= total_size) { + DEBUG_WARN(TAG, "ESP32 OTA 已收满 (%u >= %" PRIu32 "),跳过重复 seq=%u", + (unsigned)curReceived, total_size, seq); + sendWindowAck(cmd, seq, curReceived); + return false; // 外层推进 offset,继续下一包 + } + + // ★ 使用带序号顺序保护的写入:拒绝乱序包并立即 ACK 通知发送端 + esp_err_t err = esp32_write_ordered(seq, cmd, &buf[data_offset], data_len); + if (err != ESP_OK) { + DEBUG_ERR(TAG, "ESP32 OTA 写入失败: seq=%u err=%s", + seq, esp_err_to_name(err)); + ble_esp32_ota_abort(); // core 内部完成 abort + mutex release + 状态复位 + return true; + } + + DEBUG_DBG(TAG, "ESP32 OTA 写入成功: seq=%u received=%u", + seq, (unsigned int)Esp32OtaCore::instance().getReceived()); + + // 完成判定:以实际按序写入字节数为准。 + bool isLastPacket = (expected_payload < BLE_OTA_CHUNK_PAYLOAD); + bool transferComplete = (total_size > 0 && + Esp32OtaCore::instance().getReceived() >= total_size); + + // ACK 触发条件 + if ((((uint32_t)seq + 1U) % BLE_OTA_ACK_WINDOW) == 0U || + transferComplete || + isLastPacket) { + if (isLastPacket || transferComplete) { + DEBUG_INFO(TAG, "ESP32 尾包/完成检测: seq=%u data_len=%zu received=%u total=%" PRIu32, + seq, data_len, (unsigned int)Esp32OtaCore::instance().getReceived(), total_size); + } + sendWindowAck(cmd, seq, (uint32_t)Esp32OtaCore::instance().getReceived()); + } + + if (transferComplete) { + DEBUG_INFO(TAG, "★ ESP32 固件接收完毕: %u / %" PRIu32 " 字节 (共 %u 块)", + (unsigned int)Esp32OtaCore::instance().getReceived(), total_size, seq + 1); + OtaCoordinator::instance().notifyEsp32ReceiveComplete(); + BleOta::instance().m_esp32LastCmd = cmd; + BleOta::instance().m_esp32LastSeq = seq; + ble_ota_restore_wifi_ps(); + } + return false; +} + +/** + * @brief 处理单个 GD32 OTA 数据包(ble_ota_process_data_batch 子步骤)。 + * @return true 中止整个 batch;false 继续下一包(offset 由外层统一推进)。 + */ +static bool process_gd32_packet(uint8_t cmd, uint16_t seq, uint32_t total_size, + const uint8_t *buf, size_t data_offset, size_t data_len, + uint32_t expected_payload) +{ + if (seq == 0 && total_size > 0) { + // ★ 统一调用 prepareGd32Storage 完成:validate + begin + erase + clearMeta + saveMeta + // 原 BLE 路径未擦除分区,现通过 prepareGd32Storage 与 HTTP 路径对齐。 + esp_err_t prep_err = OtaCoordinator::instance().prepareGd32Storage(total_size, OTA_CHANNEL_BLE); + if (prep_err == ESP_ERR_INVALID_SIZE) { + DEBUG_ERR(TAG, "GD32 固件大小非法: raw=%" PRIu32 " max=%zu", + total_size, OTA_MAX_FIRMWARE_SIZE); + sendFinishEvent(BLE_OTA_FINISH_ERR_GD32_SIZE, total_size); + return true; + } else if (prep_err == ESP_ERR_INVALID_STATE) { + DEBUG_ERR(TAG, "GD32 OTA 协调器拒绝存储(系统忙)"); + sendFinishEvent(BLE_OTA_FINISH_ERR_MUTEX, 0); + return true; + } else if (prep_err != ESP_OK) { + DEBUG_ERR(TAG, "GD32 OTA 准备失败: %s", esp_err_to_name(prep_err)); + sendFinishEvent(BLE_OTA_FINISH_ERR_MUTEX, 0); + return true; + } + + DEBUG_INFO(TAG, "GD32 固件存储开始: total=%" PRIu32 ", payload=%zu", + total_size, data_len); + ble_ota_boost_wifi_ps(); + } + + ota_meta_t meta = {}; + if (OtaCoordinator::instance().loadGd32Meta(&meta) != ESP_OK || meta.total_size == 0) { + DEBUG_ERR(TAG, "GD32 元数据无效,拒绝写入: seq=%u", seq); + return true; + } + + // ── 顺序检查:GD32 Flash 必须按序写入,无 reorder buffer ── + // 改进:乱序/重复包不再中止整个 batch,而是丢弃当前包 + 节流 ACK 报告缺口, + // 让前端尽快知道设备端实际进度(last_seq)并重传缺口。 + // 策略与 ESP32 fallback 路径一致(参考 esp32_write_ordered 行 459-477)。 + if (seq > 0 && meta.last_seq != (uint32_t)(seq - 1)) { + // 重复包:seq <= last_seq(已写入过) + if (seq <= meta.last_seq) { + DEBUG_DBG(TAG, "GD32 重复包: seq=%u <= last_seq=%" PRIu32 ",跳过", + seq, meta.last_seq); + // 重复包不触发 ACK 风暴,只在窗口边界发一次 + if ((((uint32_t)seq + 1U) % BLE_OTA_ACK_WINDOW) == 0U) { + sendWindowAck(cmd, (uint16_t)meta.last_seq, meta.stored_size); + } + return false; // 外层推进 offset,继续下一包 + } + + // 乱序包:seq > last_seq + 1(缺口) + // 节流策略:每 BLE_OTA_ACK_WINDOW 个乱序包才发一次 ACK,或在窗口边界处发 ACK。 + // 避免大量乱序包涌入时触发 ACK 风暴,耗尽 NimBLE mbuf 池导致 ESP_ERR_NO_MEM。 + s_gd32_ooo_count++; + bool shouldAck = + (s_gd32_ooo_count >= BLE_OTA_ACK_WINDOW) || + ((((uint32_t)seq + 1U) % BLE_OTA_ACK_WINDOW) == 0U); + if (shouldAck) { + DEBUG_WARN(TAG, "GD32 乱序包跳过: seq=%u (last_seq=%" PRIu32 "),节流 ACK bytes=%" PRIu32 " (ooo=%u)", + seq, meta.last_seq, meta.stored_size, (unsigned)s_gd32_ooo_count); + sendWindowAck(cmd, (uint16_t)meta.last_seq, meta.stored_size); + s_gd32_ooo_count = 0; + } else { + DEBUG_DBG(TAG, "GD32 乱序包跳过: seq=%u (last_seq=%" PRIu32 "),抑制 ACK (ooo=%u)", + seq, meta.last_seq, (unsigned)s_gd32_ooo_count); + } + return false; // 外层推进 offset,继续下一包 + } + + // 顺序包到达,复位乱序计数器 + if (s_gd32_ooo_count != 0) { + s_gd32_ooo_count = 0; + } + + // --- GD32 写入 Flash --- + uint32_t flash_offset = meta.stored_size; + if (flash_offset >= meta.total_size) { + DEBUG_WARN(TAG, "GD32 OTA 已收满 (%" PRIu32 " >= %" PRIu32 "),跳过重复 seq=%u", + flash_offset, meta.total_size, seq); + sendWindowAck(cmd, seq, flash_offset); + return false; // 外层推进 offset,继续下一包 + } + if (flash_offset + (uint32_t)data_len > meta.total_size) { + DEBUG_ERR(TAG, "GD32 写入越界: offset=%" PRIu32 " len=%zu total=%" PRIu32, + flash_offset, data_len, meta.total_size); + return true; + } + + esp_err_t err = OtaCoordinator::instance().writeGd32Chunk(flash_offset, &buf[data_offset], data_len); + if (err != ESP_OK) { + DEBUG_ERR(TAG, "Flash 写入失败: seq=%u offset=%" PRIu32 " err=%s", + seq, flash_offset, esp_err_to_name(err)); + return true; + } + + uint32_t stored_size = flash_offset + (uint32_t)data_len; + err = OtaCoordinator::instance().updateGd32Meta(stored_size, seq); + if (err != ESP_OK) { + DEBUG_WARN(TAG, "元数据更新失败 (seq=%u)", seq); + } + + if ((((uint32_t)seq + 1U) % BLE_OTA_ACK_WINDOW) == 0U || + (meta.total_size > 0 && stored_size >= meta.total_size) || + (expected_payload < BLE_OTA_CHUNK_PAYLOAD)) { + sendWindowAck(cmd, seq, stored_size); + } + + DEBUG_DBG(TAG, "Flash 写入成功: seq=%u offset=%" PRIu32 " stored=%" PRIu32, + seq, flash_offset, stored_size); + + // --- 检查 GD32 固件是否接收完毕 --- + if (OtaCoordinator::instance().loadGd32Meta(&meta) == ESP_OK && + meta.total_size > 0 && + meta.stored_size >= meta.total_size) { + DEBUG_INFO(TAG, "★ GD32 固件接收完毕: %" PRIu32 " / %" PRIu32 " 字节 (共 %u 块)", + meta.stored_size, meta.total_size, seq + 1); + OtaCoordinator::instance().completeGd32Storage(); + ble_ota_restore_wifi_ps(); + esp_err_t sha_err = OtaCoordinator::instance().finalizeGd32Checksum(); + if (sha_err != ESP_OK) { + DEBUG_ERR(TAG, "GD32 固件 SHA-256 计算失败: %s", esp_err_to_name(sha_err)); + } + } + return false; +} + /** * @brief 处理一批 OTA 数据包(在工作线程上下文中调用)。 * 包含 ESP32 和 GD32 的数据接收逻辑。 @@ -1012,203 +1358,22 @@ static void ble_ota_process_data_batch(const uint8_t *buf, size_t recv_len) DEBUG_DBG(TAG, "DATA_IN: cmd=0x%02X seq=%u total=%" PRIu32 " payload=%zu offset=%zu", cmd, seq, total_size, data_len, offset); - // --- ESP32 自身 OTA:直接写 OTA 分区 --- + // 分发到对应通道的包处理器 + bool abort_batch = false; if (cmd == BLE_OTA_CMD_ESP32_DATA) { - if (seq == 0) { - if (total_size == 0) { - DEBUG_ERR(TAG, "ESP32 固件大小非法: %" PRIu32, total_size); - return; - } - // ★ 会话判定:若已有活跃 ESP32 OTA 会话,根据已接收字节数区分两种情况: - // ① m_received == 0:seq=0 尚未写入 flash,说明是客户端 ACK 超时后 - // 重传的 seq=0(同一会话续传),跳过 begin。 - // ② m_received > 0:seq=0 已写入且有后续数据,说明这是新的上传会话 - // (前端 abort 后 BLE 断开未完成就发起了新传输),需 abort 旧会话 - // 再 begin,否则 reorder buffer 以旧 expected 去重导致全部丢弃。 - if (Esp32OtaCore::instance().isActive()) { - if (Esp32OtaCore::instance().getReceived() > 0) { - DEBUG_WARN(TAG, "ESP32 OTA 新会话检测: received=%u > 0, " - "中止旧会话并重新开始", - (unsigned)Esp32OtaCore::instance().getReceived()); - ble_esp32_ota_abort(); - esp_err_t err = ble_esp32_ota_begin(total_size); - if (err != ESP_OK) { - if (err == ESP_ERR_INVALID_STATE) { - sendFinishEvent(BLE_OTA_FINISH_ERR_MUTEX, 0); - } - return; - } - } else { - DEBUG_INFO(TAG, "ESP32 OTA 会话已激活,seq=0 视为重传,跳过 begin"); - } - } else { - // 分区选择/大小校验/互斥由 Esp32OtaCore::begin 内部完成 - esp_err_t err = ble_esp32_ota_begin(total_size); - if (err != ESP_OK) { - if (err == ESP_ERR_INVALID_STATE) { - sendFinishEvent(BLE_OTA_FINISH_ERR_MUTEX, 0); - } - return; - } - } - } - - // ★ 会话有效性检查:如果 begin() 失败(如互斥拒绝),后续包也应丢弃 - if (!Esp32OtaCore::instance().isActive()) { - DEBUG_WARN(TAG, "ESP32 OTA 会话无效,丢弃 seq=%u", seq); - offset += packet_len; - if (offset + 7 > recv_len) break; - continue; - } - - // ★ 防重复写入保护:如果已接收字节 >= 声明大小,跳过(重试引起的重复包) - uint32_t curReceived = (uint32_t)Esp32OtaCore::instance().getReceived(); - if (total_size > 0 && curReceived >= total_size) { - DEBUG_WARN(TAG, "ESP32 OTA 已收满 (%u >= %" PRIu32 "),跳过重复 seq=%u", - (unsigned)curReceived, total_size, seq); - sendWindowAck(cmd, seq, curReceived); - offset += packet_len; - if (offset + 7 > recv_len) break; - continue; - } - - // ★ 使用带序号顺序保护的写入:拒绝乱序包并立即 ACK 通知发送端 - esp_err_t err = esp32_write_ordered(seq, cmd, &buf[data_offset], data_len); - if (err != ESP_OK) { - DEBUG_ERR(TAG, "ESP32 OTA 写入失败: seq=%u err=%s", - seq, esp_err_to_name(err)); - ble_esp32_ota_abort(); // core 内部完成 abort + mutex release + 状态复位 - return; - } - - DEBUG_DBG(TAG, "ESP32 OTA 写入成功: seq=%u received=%u", - seq, (unsigned int)Esp32OtaCore::instance().getReceived()); - - // 完成判定:以实际按序写入字节数为准。 - bool isLastPacket = (expected_payload < BLE_OTA_CHUNK_PAYLOAD); - bool transferComplete = (total_size > 0 && - Esp32OtaCore::instance().getReceived() >= total_size); - - // ACK 触发条件 - if ((((uint32_t)seq + 1U) % BLE_OTA_ACK_WINDOW) == 0U || - transferComplete || - isLastPacket) { - if (isLastPacket || transferComplete) { - DEBUG_INFO(TAG, "ESP32 尾包/完成检测: seq=%u data_len=%zu received=%u total=%" PRIu32, - seq, data_len, (unsigned int)Esp32OtaCore::instance().getReceived(), total_size); - } - sendWindowAck(cmd, seq, (uint32_t)Esp32OtaCore::instance().getReceived()); - } - - if (transferComplete) { - DEBUG_INFO(TAG, "★ ESP32 固件接收完毕: %u / %" PRIu32 " 字节 (共 %u 块)", - (unsigned int)Esp32OtaCore::instance().getReceived(), total_size, seq + 1); - OtaCoordinator::instance().notifyEsp32ReceiveComplete(); - BleOta::instance().m_esp32LastCmd = cmd; - BleOta::instance().m_esp32LastSeq = seq; - ble_ota_restore_wifi_ps(); - } - } - // --- GD32 OTA:首个分块时创建存储元数据 --- - else if (cmd == BLE_OTA_CMD_GD32_DATA) { - if (seq == 0 && total_size > 0) { - uint32_t rounded_size = (uint32_t)(((total_size + (uint32_t)GD32_OTA_PACKET_SIZE - 1U) / (uint32_t)GD32_OTA_PACKET_SIZE) * (uint32_t)GD32_OTA_PACKET_SIZE); - if (total_size == 0U || total_size > OTA_MAX_FIRMWARE_SIZE || rounded_size > OTA_MAX_FIRMWARE_SIZE) { - DEBUG_ERR(TAG, "GD32 固件大小非法: raw=%" PRIu32 " rounded48=%" PRIu32 " max=%zu", - total_size, rounded_size, OTA_MAX_FIRMWARE_SIZE); - sendFinishEvent(BLE_OTA_FINISH_ERR_GD32_SIZE, total_size); - return; - } - - if (OtaCoordinator::instance().beginGd32Storage(total_size) != ESP_OK) { - DEBUG_ERR(TAG, "GD32 OTA 协调器拒绝存储(系统忙)"); - return; - } - - ota_meta_t meta = {}; - meta.total_size = total_size; - meta.stored_size = 0; - meta.last_seq = 0; - - esp_err_t err = OtaCoordinator::instance().saveGd32Meta(&meta); - if (err != ESP_OK) { - DEBUG_ERR(TAG, "元数据保存失败"); - OtaCoordinator::instance().abortCurrent(); - return; - } - - DEBUG_INFO(TAG, "GD32 固件存储开始: total=%" PRIu32 ", payload=%zu", - meta.total_size, data_len); - ble_ota_boost_wifi_ps(); - } - - ota_meta_t meta = {}; - if (OtaCoordinator::instance().loadGd32Meta(&meta) != ESP_OK || meta.total_size == 0) { - DEBUG_ERR(TAG, "GD32 元数据无效,拒绝写入: seq=%u", seq); - return; - } - - if (seq > 0 && meta.last_seq != (uint32_t)(seq - 1)) { - DEBUG_ERR(TAG, "GD32 分包序号不连续: seq=%u last_seq=%" PRIu32, - seq, meta.last_seq); - return; - } - - // --- GD32 写入 Flash --- - uint32_t flash_offset = meta.stored_size; - if (flash_offset >= meta.total_size) { - DEBUG_WARN(TAG, "GD32 OTA 已收满 (%" PRIu32 " >= %" PRIu32 "),跳过重复 seq=%u", - flash_offset, meta.total_size, seq); - sendWindowAck(cmd, seq, flash_offset); - offset += packet_len; - if (offset + 7 > recv_len) break; - continue; - } - if (flash_offset + (uint32_t)data_len > meta.total_size) { - DEBUG_ERR(TAG, "GD32 写入越界: offset=%" PRIu32 " len=%zu total=%" PRIu32, - flash_offset, data_len, meta.total_size); - return; - } - - esp_err_t err = OtaCoordinator::instance().writeGd32Chunk(flash_offset, &buf[data_offset], data_len); - if (err != ESP_OK) { - DEBUG_ERR(TAG, "Flash 写入失败: seq=%u offset=%" PRIu32 " err=%s", - seq, flash_offset, esp_err_to_name(err)); - return; - } - - uint32_t stored_size = flash_offset + (uint32_t)data_len; - err = OtaCoordinator::instance().updateGd32Meta(stored_size, seq); - if (err != ESP_OK) { - DEBUG_WARN(TAG, "元数据更新失败 (seq=%u)", seq); - } - - if ((((uint32_t)seq + 1U) % BLE_OTA_ACK_WINDOW) == 0U || - (meta.total_size > 0 && stored_size >= meta.total_size) || - (expected_payload < BLE_OTA_CHUNK_PAYLOAD)) { - sendWindowAck(cmd, seq, stored_size); - } - - DEBUG_DBG(TAG, "Flash 写入成功: seq=%u offset=%" PRIu32 " stored=%" PRIu32, - seq, flash_offset, stored_size); - - // --- 检查 GD32 固件是否接收完毕 --- - if (OtaCoordinator::instance().loadGd32Meta(&meta) == ESP_OK && - meta.total_size > 0 && - meta.stored_size >= meta.total_size) { - DEBUG_INFO(TAG, "★ GD32 固件接收完毕: %" PRIu32 " / %" PRIu32 " 字节 (共 %u 块)", - meta.stored_size, meta.total_size, seq + 1); - OtaCoordinator::instance().completeGd32Storage(); - ble_ota_restore_wifi_ps(); - esp_err_t sha_err = OtaCoordinator::instance().finalizeGd32Checksum(); - if (sha_err != ESP_OK) { - DEBUG_ERR(TAG, "GD32 固件 SHA-256 计算失败: %s", esp_err_to_name(sha_err)); - } - } + abort_batch = process_esp32_packet(cmd, seq, total_size, + buf, data_offset, data_len, expected_payload); + } else if (cmd == BLE_OTA_CMD_GD32_DATA) { + abort_batch = process_gd32_packet(cmd, seq, total_size, + buf, data_offset, data_len, expected_payload); } else { DEBUG_WARN(TAG, "DATA_IN 未知 cmd: 0x%02X offset=%zu", cmd, offset); } + if (abort_batch) { + return; + } + // 推进到下一个包 offset += packet_len; @@ -1425,11 +1590,33 @@ static int ble_ota_gap_event(struct ble_gap_event *event, void *arg) BleOta::instance().m_bleConnected = false; BleOta::instance().m_connHandle = BLE_HS_CONN_HANDLE_NONE; - // ★ BLE 断开时恢复 WiFi 射频,并中止未完成的 ESP32 OTA + // ★ BLE 断开时恢复 WiFi 射频 ble_ota_restore_wifi_ps(); - if (Esp32OtaCore::instance().isActive()) { - DEBUG_WARN(TAG, "BLE 断开,中止未完成的 ESP32 OTA"); - ble_esp32_ota_abort(); + + // ★ 断开时清理"BLE 数据接收阶段"残留的 OTA 会话状态,避免下次刷写被 + // 系统 busy 假拒绝。注意区分可中止 / 不可中止状态: + // - GD32_STORING / ESP32_RECEIVING / ESP32_FINALIZING:BLE 是唯一数据源, + // 断开即等价于中止,必须 abortCurrent() + 清理底层 core。 + // - GD32_RELAYING / ESP32_REBOOTING:SPI 中继/重启不依赖 BLE,不能打断。 + // 这些状态保留,等中继/重启自然结束。 + // - IDLE / STORED / COMPLETE / ERROR:abortCurrent 幂等安全,直接调即可。 + { + ota_system_state_t cur = OtaCoordinator::instance().getSystemState(); + if (cur == OTA_SYS_GD32_RELAYING || cur == OTA_SYS_ESP32_REBOOTING) { + DEBUG_WARN(TAG, "BLE 断开但 %s 进行中,保留状态等待自然结束", + OtaCoordinator::stateToString(cur)); + } else { + if (cur == OTA_SYS_GD32_STORING) { + DEBUG_WARN(TAG, "BLE 断开,中止未完成的 GD32 固件存储"); + } else if (cur == OTA_SYS_ESP32_RECEIVING || + cur == OTA_SYS_ESP32_FINALIZING) { + DEBUG_WARN(TAG, "BLE 断开,中止未完成的 ESP32 OTA"); + } + // ble_ota_session_reset() 统一负责 abortCurrent + core.abort 兜底 + + // 会话状态复位(m_esp32LastSeq/m_esp32LastCmd/m_esp32RebootPending/ + // reorder/s_gd32_ooo_count) + ble_ota_session_reset(); + } } // 恢复广播以接受新连接 ble_ota_start_advertising(); diff --git a/main/server/http_handlers.cpp b/main/server/http_handlers.cpp index adb5ec0..eda1d2e 100644 --- a/main/server/http_handlers.cpp +++ b/main/server/http_handlers.cpp @@ -30,15 +30,14 @@ #include "canfd_bridge.h" #include "ble_ota.h" -static constexpr uint32_t GD32_OTA_FLASH_SECTOR_SIZE = 4096U; - -// OTA 接收共享缓冲区 -static uint8_t s_ota_recv_buf[OTA_BLOCK_SIZE]; - -static uint32_t align_up_u32(uint32_t value, uint32_t alignment) -{ - return ((value + alignment - 1U) / alignment) * alignment; -} +// OTA 准备状态:/ota/prepare 完成后设置,/ota/upload 消费后清除 +// ★ 同时记录 prepare 时的 epoch,clear 时仅在同一会话才中止,避免误清新会话 +static bool s_esp32_prepared = false; +static uint32_t s_esp32_prepare_size = 0; +static uint32_t s_esp32_prepare_epoch = 0; +static bool s_gd32_prepared = false; +static uint32_t s_gd32_prepare_size = 0; +static uint32_t s_gd32_prepare_epoch = 0; void http_set_cors_headers(httpd_req_t *req) { @@ -48,6 +47,27 @@ void http_set_cors_headers(httpd_req_t *req) httpd_resp_set_hdr(req, "Access-Control-Max-Age", "600"); } +static void clear_esp32_prepare() +{ + if (s_esp32_prepared) { + // ★ 仅在 prepare 仍属当前会话时中止;同类型重启已发生时跳过,避免误清新会话 + OtaCoordinator::instance().abortCurrentIfEpoch(s_esp32_prepare_epoch); + s_esp32_prepared = false; + s_esp32_prepare_size = 0; + s_esp32_prepare_epoch = 0; + } +} + +static void clear_gd32_prepare() +{ + if (s_gd32_prepared) { + OtaCoordinator::instance().abortCurrentIfEpoch(s_gd32_prepare_epoch); + s_gd32_prepared = false; + s_gd32_prepare_size = 0; + s_gd32_prepare_epoch = 0; + } +} + esp_err_t http_options_handler(httpd_req_t *req) { http_set_cors_headers(req); @@ -126,7 +146,8 @@ esp_err_t esp32_ota_update_handler(httpd_req_t *req) httpd_resp_set_type(req, "application/json"); httpd_resp_send(req, resp, strlen(resp)); - DEBUG_INFO("OTA", "响应已发送,重启..."); + DEBUG_INFO("OTA", "响应已发送,等待 TCP 刷新..."); + vTaskDelay(pdMS_TO_TICKS(500)); OtaCoordinator::instance().restartEsp32(); return ESP_OK; } @@ -177,9 +198,122 @@ static int recv_json_header(httpd_req_t *req, char *buf, int buf_size) return total; } +esp_err_t ota_prepare_handler(httpd_req_t *req) +{ + http_set_cors_headers(req); + DEBUG_INFO("WIFI", "=== HTTP: POST /ota/prepare ==="); + + char buf[256] = {0}; + int ret = httpd_req_recv(req, buf, sizeof(buf) - 1); + if (ret <= 0) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "recv failed"); + return ESP_FAIL; + } + buf[ret] = '\0'; + + cJSON *root = cJSON_Parse(buf); + if (!root) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "JSON parse failed"); + return ESP_FAIL; + } + + cJSON *type_item = cJSON_GetObjectItem(root, "type"); + cJSON *size_item = cJSON_GetObjectItem(root, "total_size"); + if (!type_item || !cJSON_IsString(type_item) || + !size_item || !cJSON_IsNumber(size_item)) { + cJSON_Delete(root); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "missing type or total_size"); + return ESP_FAIL; + } + + const char *type = cJSON_GetStringValue(type_item); + uint32_t total_size = (uint32_t)cJSON_GetNumberValue(size_item); + cJSON_Delete(root); + + if (type && strcmp(type, "esp32") == 0) { + clear_esp32_prepare(); + + esp_err_t err = OtaCoordinator::instance().beginEsp32OtaWithRetry(total_size, OTA_CHANNEL_WIFI); + if (err != ESP_OK) { + if (err == ESP_ERR_INVALID_STATE) { + httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, + "ESP32 OTA occupied by another channel"); + } else { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "OTA begin failed"); + } + return ESP_FAIL; + } + + s_esp32_prepared = true; + s_esp32_prepare_size = total_size; + s_esp32_prepare_epoch = OtaCoordinator::instance().getOtaEpoch(); + const char resp[] = "{\"status\": \"ok\", \"target\": \"esp32\"}"; + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, resp, strlen(resp)); + DEBUG_INFO("WIFI", "ESP32 OTA 准备完成: %u 字节", total_size); + return ESP_OK; + } + + if (type && strcmp(type, "gd32") == 0) { + clear_gd32_prepare(); + + if (OtaRelay::getState() != RELAY_IDLE) { + DEBUG_WARN("WIFI", "GD32 prepare 前复位中继状态: %d", OtaRelay::getState()); + OtaRelay::instance().reset(); + } + + // ★ 统一调用 prepareGd32Storage 完成:validate + begin + erase + clearMeta + saveMeta + // 与 BLE 路径 / ota_upload_handler 共享同一序列,消除手写重复 + esp_err_t prep_err = OtaCoordinator::instance().prepareGd32Storage(total_size, OTA_CHANNEL_WIFI); + if (prep_err == ESP_ERR_INVALID_SIZE) { + DEBUG_ERR("WIFI", "prepare: firmware size invalid: %u", total_size); + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "firmware size invalid"); + return ESP_FAIL; + } else if (prep_err == ESP_ERR_INVALID_STATE) { + DEBUG_ERR("WIFI", "GD32 prepare 协调器拒绝(系统忙)"); + httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, + "system busy with another OTA operation"); + return ESP_FAIL; + } else if (prep_err != ESP_OK) { + DEBUG_ERR("WIFI", "GD32 prepare 失败: %s", esp_err_to_name(prep_err)); + char err_msg[96]; + snprintf(err_msg, sizeof(err_msg), "prepare gd32_ota failed: %s", + esp_err_to_name(prep_err)); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, err_msg); + return ESP_FAIL; + } + + WifiOta::instance().stateLock(); + WifiOta::instance().setGd32StorageDone(false); + WifiOta::instance().stateUnlock(); + + s_gd32_prepared = true; + s_gd32_prepare_size = total_size; + s_gd32_prepare_epoch = OtaCoordinator::instance().getOtaEpoch(); + const char resp[] = "{\"status\": \"ok\", \"target\": \"gd32\"}"; + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, resp, strlen(resp)); + DEBUG_INFO("WIFI", "GD32 OTA 准备完成: %u 字节", total_size); + return ESP_OK; + } + + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "unknown type"); + return ESP_FAIL; +} + esp_err_t ota_upload_handler(httpd_req_t *req) { http_set_cors_headers(req); + + // 每请求分配 OTA 接收缓冲区:OTA_BLOCK_SIZE=8KB > 4KB 不适合栈分配, + // 改为 per-request malloc 避免 ESP32/GD32 两条路径共享全局 buffer, + // max_open_sockets=4 时多连接并发也互不干扰。 + uint8_t *recv_buf = (uint8_t *)malloc(OTA_BLOCK_SIZE); + if (!recv_buf) { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "alloc failed"); + return ESP_FAIL; + } + DEBUG_WARN("WIFI", "=== HTTP: POST /ota/upload === content_len=%d", req->content_len); DEBUG_INFO("WIFI", "收到固件上传请求, 内容长度: %d 字节", req->content_len); @@ -191,6 +325,7 @@ esp_err_t ota_upload_handler(httpd_req_t *req) int peek_ret = httpd_req_recv(req, &peek, 1); if (peek_ret <= 0) { httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "接收失败"); + free(recv_buf); return ESP_FAIL; } @@ -231,22 +366,49 @@ esp_err_t ota_upload_handler(httpd_req_t *req) if (is_esp32_ota) { if (total_size == 0) { + clear_esp32_prepare(); httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "missing total_size"); + free(recv_buf); return ESP_FAIL; } - esp_err_t err = OtaCoordinator::instance().beginEsp32OtaWithRetry(total_size); - if (err != ESP_OK) { - if (err == ESP_ERR_INVALID_STATE) { - httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "ESP32 OTA occupied by another channel"); + // ★ 若前端已通过 /ota/prepare 完成擦除,直接复用 prepared 状态, + // 避免在大量数据传输前再次长时间阻塞 HTTP worker。 + bool used_prepare = false; + if (s_esp32_prepared) { + if (total_size == s_esp32_prepare_size) { + used_prepare = true; + s_esp32_prepared = false; + s_esp32_prepare_size = 0; + DEBUG_INFO("WIFI", "ESP32 OTA 使用已 prepared 状态,跳过擦除"); } else { - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "OTA begin failed"); + DEBUG_WARN("WIFI", "ESP32 prepared size mismatch (prepared=%u, upload=%u),重新初始化", + s_esp32_prepare_size, total_size); + clear_esp32_prepare(); + } + } + + esp_err_t err; + if (!used_prepare) { + err = OtaCoordinator::instance().beginEsp32OtaWithRetry(total_size, OTA_CHANNEL_WIFI); + if (err != ESP_OK) { + clear_esp32_prepare(); + if (err == ESP_ERR_INVALID_STATE) { + httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "ESP32 OTA occupied by another channel"); + } else { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "OTA begin failed"); + } + free(recv_buf); + return ESP_FAIL; } - return ESP_FAIL; } Esp32OtaCore &core = Esp32OtaCore::instance(); - uint8_t *fw_buf = s_ota_recv_buf; + uint8_t *fw_buf = recv_buf; + + // ★ 捕获当前 epoch,用于失败清理时检查会话是否仍属于本 handler + // 若同类型重启已发生(新请求接管),旧 handler 的 abortCurrentIfEpoch 不会中止新会话 + uint32_t my_epoch = OtaCoordinator::instance().getOtaEpoch(); size_t total_firmware_written = 0; size_t total_received = json_header_len; @@ -259,16 +421,23 @@ esp_err_t ota_upload_handler(httpd_req_t *req) int ret = httpd_req_recv(req, (char *)fw_buf, to_read); if (ret <= 0) { DEBUG_ERR("WIFI", "HTTP recv failed (ret=%d), aborting OTA", ret); - core.abort(); + // ★ 客户端断开/接收失败:用 abortCurrentIfEpoch 复位协调器状态机 + // 若 epoch 不匹配(新请求已接管),跳过中止,避免误清新会话 + OtaCoordinator::instance().abortCurrentIfEpoch(my_epoch); + clear_esp32_prepare(); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "HTTP recv failed"); + free(recv_buf); return ESP_FAIL; } err = core.write(fw_buf, (size_t)ret); if (err != ESP_OK) { DEBUG_ERR("WIFI", "OTA write failed: %s", esp_err_to_name(err)); - core.abort(); + // ★ 同上:写入失败也用 abortCurrentIfEpoch,避免误中止新会话 + OtaCoordinator::instance().abortCurrentIfEpoch(my_epoch); + clear_esp32_prepare(); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "OTA write failed"); + free(recv_buf); return ESP_FAIL; } @@ -289,6 +458,7 @@ esp_err_t ota_upload_handler(httpd_req_t *req) const char resp[] = "{\"status\": \"ok\", \"message\": \"stored, call POST /ota/update to finish\", \"target\": \"esp32\"}"; httpd_resp_set_type(req, "application/json"); httpd_resp_send(req, resp, strlen(resp)); + free(recv_buf); return ESP_OK; } @@ -299,53 +469,70 @@ esp_err_t ota_upload_handler(httpd_req_t *req) } else { gd32_valid_size = gd32_fw_size; } - const uint32_t gd32_rounded_size = (uint32_t)(((gd32_valid_size + (uint32_t)GD32_OTA_PACKET_SIZE - 1U) / (uint32_t)GD32_OTA_PACKET_SIZE) * (uint32_t)GD32_OTA_PACKET_SIZE); - if (gd32_valid_size == 0U || gd32_valid_size > OTA_MAX_FIRMWARE_SIZE || gd32_rounded_size > OTA_MAX_FIRMWARE_SIZE) { - DEBUG_ERR("WIFI", "firmware size invalid: valid=%u rounded48=%u max=%u", - (unsigned)gd32_valid_size, (unsigned)gd32_rounded_size, (unsigned)OTA_MAX_FIRMWARE_SIZE); + if (OtaCoordinator::instance().validateGd32Size(gd32_valid_size) != ESP_OK) { + DEBUG_ERR("WIFI", "firmware size invalid: valid=%u max=%u", + (unsigned)gd32_valid_size, (unsigned)OTA_MAX_FIRMWARE_SIZE); + clear_gd32_prepare(); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "firmware too large or alignment overflow"); - return ESP_FAIL; - } - if (OtaRelay::getState() != RELAY_IDLE) { - DEBUG_WARN("WIFI", "GD32 OTA 开始前复位中继状态: %d", OtaRelay::getState()); - OtaRelay::instance().reset(); - } - - if (OtaCoordinator::instance().beginGd32Storage(gd32_valid_size) != ESP_OK) { - DEBUG_ERR("WIFI", "GD32 OTA 协调器拒绝存储(系统忙)"); - httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "system busy with another OTA operation"); + free(recv_buf); return ESP_FAIL; } - WifiOta::instance().stateLock(); - WifiOta::instance().setGd32StorageDone(false); - WifiOta::instance().stateUnlock(); - - const uint32_t gd32_erase_size = align_up_u32(gd32_valid_size, GD32_OTA_FLASH_SECTOR_SIZE); - DEBUG_INFO("WIFI", "擦除 gd32_ota 分区: %u bytes (fw=%u)", - (unsigned)gd32_erase_size, (unsigned)gd32_valid_size); - esp_err_t erase_err = OtaCoordinator::instance().eraseGd32Partition(gd32_erase_size); - if (erase_err != ESP_OK) { - DEBUG_ERR("WIFI", "擦除 gd32_ota 分区失败: %s", esp_err_to_name(erase_err)); - char err_msg[96]; - snprintf(err_msg, sizeof(err_msg), "erase gd32_ota failed: %s", esp_err_to_name(erase_err)); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, err_msg); - return ESP_FAIL; + // ★ 若前端已通过 /ota/prepare 完成擦除,直接复用 prepared 状态 + bool used_gd32_prepare = false; + if (s_gd32_prepared) { + if (gd32_valid_size == s_gd32_prepare_size) { + used_gd32_prepare = true; + s_gd32_prepared = false; + s_gd32_prepare_size = 0; + DEBUG_INFO("WIFI", "GD32 OTA 使用已 prepared 状态,跳过擦除"); + } else { + DEBUG_WARN("WIFI", "GD32 prepared size mismatch (prepared=%u, upload=%u),重新初始化", + s_gd32_prepare_size, gd32_valid_size); + clear_gd32_prepare(); + } } - OtaCoordinator::instance().clearGd32Meta(); - uint8_t *buf = s_ota_recv_buf; + if (!used_gd32_prepare) { + if (OtaRelay::getState() != RELAY_IDLE) { + DEBUG_WARN("WIFI", "GD32 OTA 开始前复位中继状态: %d", OtaRelay::getState()); + OtaRelay::instance().reset(); + } + + // ★ 统一调用 prepareGd32Storage 完成:begin + erase + clearMeta + saveMeta + // 与 BLE 路径(process_gd32_packet seq=0)共用同一序列,消除隐式差异。 + esp_err_t prep_err = OtaCoordinator::instance().prepareGd32Storage(gd32_valid_size, OTA_CHANNEL_WIFI); + if (prep_err == ESP_ERR_INVALID_STATE) { + DEBUG_ERR("WIFI", "GD32 OTA 协调器拒绝存储(系统忙)"); + clear_gd32_prepare(); + httpd_resp_send_err(req, HTTPD_403_FORBIDDEN, "system busy with another OTA operation"); + free(recv_buf); + return ESP_FAIL; + } else if (prep_err != ESP_OK) { + DEBUG_ERR("WIFI", "GD32 OTA 准备失败: %s", esp_err_to_name(prep_err)); + clear_gd32_prepare(); + char err_msg[96]; + snprintf(err_msg, sizeof(err_msg), "prepare gd32_ota failed: %s", esp_err_to_name(prep_err)); + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, err_msg); + free(recv_buf); + return ESP_FAIL; + } + + WifiOta::instance().stateLock(); + WifiOta::instance().setGd32StorageDone(false); + WifiOta::instance().stateUnlock(); + } + + // ★ 捕获当前 epoch,用于失败清理时检查会话是否仍属于本 handler + uint32_t my_epoch = OtaCoordinator::instance().getOtaEpoch(); + + uint8_t *buf = recv_buf; int received = 0; + // expected_fw_size_raw 仅用于后续日志输出;meta 已由 prepareGd32Storage + // (非 prepared 路径)或 /ota/prepare(prepared 路径)写入,此处不再重复保存。 uint32_t expected_fw_size_raw = gd32_valid_size; - ota_meta_t meta_raw = { - .total_size = expected_fw_size_raw, - .stored_size = 0, - .last_seq = 0, - .checksum = {0} - }; - OtaCoordinator::instance().saveGd32Meta(&meta_raw); uint32_t gd32_offset = 0; DEBUG_INFO("WIFI", "GD32 raw upload start: %u bytes", expected_fw_size_raw); @@ -359,7 +546,10 @@ esp_err_t ota_upload_handler(httpd_req_t *req) if (err != ESP_OK) { DEBUG_ERR("WIFI", "GD32 first chunk write failed: len=%d err=%s", first_chunk_len, esp_err_to_name(err)); + // ★ 首块写入失败:用 abortCurrentIfEpoch 避免误中止新会话 + OtaCoordinator::instance().abortCurrentIfEpoch(my_epoch); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "write gd32_ota failed"); + free(recv_buf); return ESP_FAIL; } received = first_chunk_len; @@ -378,7 +568,10 @@ esp_err_t ota_upload_handler(httpd_req_t *req) if (ret <= 0) { DEBUG_ERR("WIFI", "GD32 upload interrupted: received=%d expected=%d", received, req->content_len); + // ★ 客户端断开(刷新网页/关浏览器/网络掉):用 abortCurrentIfEpoch 避免误中止新会话 + OtaCoordinator::instance().abortCurrentIfEpoch(my_epoch); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "upload interrupted"); + free(recv_buf); return ESP_FAIL; } @@ -386,7 +579,10 @@ esp_err_t ota_upload_handler(httpd_req_t *req) if (err != ESP_OK) { DEBUG_ERR("WIFI", "GD32 write failed: offset=%u len=%d err=%s", (unsigned)gd32_offset, ret, esp_err_to_name(err)); + // ★ 写入失败:同上,用 abortCurrentIfEpoch + OtaCoordinator::instance().abortCurrentIfEpoch(my_epoch); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "write gd32_ota failed"); + free(recv_buf); return ESP_FAIL; } @@ -416,6 +612,7 @@ esp_err_t ota_upload_handler(httpd_req_t *req) "\"message\": \"stored, call POST /ota/relay with sha256 to trigger\"}"; httpd_resp_set_type(req, "application/json"); httpd_resp_send(req, resp, strlen(resp)); + free(recv_buf); return ESP_OK; } diff --git a/main/server/wifi_ota.cpp b/main/server/wifi_ota.cpp index 90538b8..68bf04a 100644 --- a/main/server/wifi_ota.cpp +++ b/main/server/wifi_ota.cpp @@ -1,4 +1,4 @@ -/* +/* * wifi_ota.cpp - WiFi OTA 服务器实现 * WifiOta 类封装 WiFi 初始化、HTTP OTA 服务器和网络状态查询。 * T4.1 - T4.5 @@ -442,144 +442,38 @@ esp_err_t WifiOta::httpServerStart() } } - httpd_uri_t upload_uri = { - .uri = "/ota/upload", - .method = HTTP_POST, - .handler = ota_upload_handler, - .user_ctx = NULL + struct UriEntry { + const char *uri; + httpd_method_t method; + esp_err_t (*handler)(httpd_req_t *); }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &upload_uri); - httpd_uri_t upload_options_uri = { - .uri = "/ota/upload", - .method = HTTP_OPTIONS, - .handler = http_options_handler, - .user_ctx = NULL + static const UriEntry kHttpRoutes[] = { + {"/ota/upload", HTTP_POST, ota_upload_handler}, + {"/ota/relay", HTTP_POST, ota_relay_trigger_handler}, + {"/ota/update", HTTP_POST, esp32_ota_update_handler}, + {"/restart", HTTP_POST, restart_handler}, + {"/logs", HTTP_GET, logs_handler}, + {"/system/status", HTTP_GET, system_status_handler}, + {"/system/probe", HTTP_GET, system_probe_handler}, + {"/can/send", HTTP_POST, can_send_handler}, + {"/spi/send", HTTP_POST, spi_send_handler}, }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &upload_options_uri); - httpd_uri_t relay_uri = { - .uri = "/ota/relay", - .method = HTTP_POST, - .handler = ota_relay_trigger_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &relay_uri); - httpd_uri_t relay_options_uri = { - .uri = "/ota/relay", - .method = HTTP_OPTIONS, - .handler = http_options_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &relay_options_uri); + for (const auto &r : kHttpRoutes) { + httpd_uri_t main_uri = {}; + main_uri.uri = r.uri; + main_uri.method = r.method; + main_uri.handler = r.handler; + main_uri.user_ctx = NULL; + httpd_register_uri_handler(WifiOta::instance().m_httpServer, &main_uri); - httpd_uri_t esp32_update_uri = { - .uri = "/ota/update", - .method = HTTP_POST, - .handler = esp32_ota_update_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &esp32_update_uri); - httpd_uri_t esp32_update_options_uri = { - .uri = "/ota/update", - .method = HTTP_OPTIONS, - .handler = http_options_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &esp32_update_options_uri); - - httpd_uri_t restart_uri = { - .uri = "/restart", - .method = HTTP_POST, - .handler = restart_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &restart_uri); - httpd_uri_t restart_options_uri = { - .uri = "/restart", - .method = HTTP_OPTIONS, - .handler = http_options_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &restart_options_uri); - - httpd_uri_t logs_uri = { - .uri = "/logs", - .method = HTTP_GET, - .handler = logs_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &logs_uri); - httpd_uri_t logs_options_uri = { - .uri = "/logs", - .method = HTTP_OPTIONS, - .handler = http_options_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &logs_options_uri); - - // 系统状态查询端点 - httpd_uri_t status_uri = { - .uri = "/system/status", - .method = HTTP_GET, - .handler = system_status_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &status_uri); - httpd_uri_t status_options_uri = { - .uri = "/system/status", - .method = HTTP_OPTIONS, - .handler = http_options_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &status_options_uri); - - // PR2: GD32 APP 探测端点 - httpd_uri_t system_probe_uri = { - .uri = "/system/probe", - .method = HTTP_GET, - .handler = system_probe_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &system_probe_uri); - httpd_uri_t system_probe_options_uri = { - .uri = "/system/probe", - .method = HTTP_OPTIONS, - .handler = http_options_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &system_probe_options_uri); - - // CAN 帧转发 - httpd_uri_t can_send_uri = { - .uri = "/can/send", - .method = HTTP_POST, - .handler = can_send_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &can_send_uri); - httpd_uri_t can_send_options_uri = { - .uri = "/can/send", - .method = HTTP_OPTIONS, - .handler = http_options_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &can_send_options_uri); - - // SPI 直接发送 - httpd_uri_t spi_send_uri = { - .uri = "/spi/send", - .method = HTTP_POST, - .handler = spi_send_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &spi_send_uri); - httpd_uri_t spi_send_options_uri = { - .uri = "/spi/send", - .method = HTTP_OPTIONS, - .handler = http_options_handler, - .user_ctx = NULL - }; - httpd_register_uri_handler(WifiOta::instance().m_httpServer, &spi_send_options_uri); + httpd_uri_t opts_uri = {}; + opts_uri.uri = r.uri; + opts_uri.method = HTTP_OPTIONS; + opts_uri.handler = http_options_handler; + opts_uri.user_ctx = NULL; + httpd_register_uri_handler(WifiOta::instance().m_httpServer, &opts_uri); + } DEBUG_INFO("WIFI", "注册端点: /ota/upload, /ota/relay, /ota/update, /restart, /logs, /system/status, /system/probe, /can/send, /spi/send"); diff --git a/main/storage/gd32_flash_store.cpp b/main/storage/gd32_flash_store.cpp index dd296f2..dd293a4 100644 --- a/main/storage/gd32_flash_store.cpp +++ b/main/storage/gd32_flash_store.cpp @@ -14,6 +14,8 @@ #include "gd32_flash_store.h" #include "monitor/debug_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" // ============================================================ // 单例 @@ -24,6 +26,34 @@ Gd32FlashStore& Gd32FlashStore::instance() return inst; } +// 构造时创建 NVS 互斥锁。 +// 单例静态局部变量在首次 instance() 调用时构造,此时 FreeRTOS 已就绪。 +Gd32FlashStore::Gd32FlashStore() +{ + m_nvsMutex = xSemaphoreCreateMutex(); + if (!m_nvsMutex) { + DEBUG_ERR("OTA", "Gd32FlashStore NVS mutex 创建失败"); + } +} + +// ============================================================ +// NVS 互斥锁辅助 +// ============================================================ +bool Gd32FlashStore::lockNvs(uint32_t timeout_ms) +{ + // 参考 OtaCoordinator::lock:返回 take 结果,由调用方传回 unlockNvs 决定是否 give。 + if (!m_nvsMutex) return false; + return xSemaphoreTake(m_nvsMutex, pdMS_TO_TICKS(timeout_ms)) == pdTRUE; +} + +void Gd32FlashStore::unlockNvs(bool handle) +{ + // 仅在对应 lockNvs 成功 take 时才 give,避免误释放。 + if (handle && m_nvsMutex) { + xSemaphoreGive(m_nvsMutex); + } +} + // ============================================================ // 分区懒初始化 // ============================================================ @@ -132,8 +162,8 @@ esp_err_t Gd32FlashStore::erasePartition(uint32_t size) // NVS 元数据持久化 // ============================================================ -/** @brief 从 NVS 加载 OTA 元数据,命名空间不存在时返回全零结构。 */ -esp_err_t Gd32FlashStore::metaLoad(ota_meta_t *meta) +/** @brief 已持锁版本:从 NVS 加载 OTA 元数据,命名空间不存在时返回全零结构。 */ +esp_err_t Gd32FlashStore::metaLoadLocked(ota_meta_t *meta) { if (!meta) return ESP_ERR_INVALID_ARG; memset(meta, 0, sizeof(ota_meta_t)); @@ -141,7 +171,15 @@ esp_err_t Gd32FlashStore::metaLoad(ota_meta_t *meta) nvs_handle_t handle; esp_err_t err = nvs_open(OTA_META_NVS_NAMESPACE, NVS_READONLY, &handle); if (err != ESP_OK) { - return ESP_OK; + // 区分 nvs_open 失败原因,不再一律当 OK。 + // ESP_ERR_NVS_NOT_FOUND = 命名空间不存在(首次使用/被擦除),视为空元数据,保持兼容。 + // 其他错误(NVS 未初始化/分区丢失/损坏等)返回真实错误码, + // 避免掩盖底层故障导致调用方误判为"空元数据"继续执行。 + if (err == ESP_ERR_NVS_NOT_FOUND) { + return ESP_OK; + } + DEBUG_WARN("OTA", "metaLoad nvs_open 失败(非 NOT_FOUND): %s", esp_err_to_name(err)); + return err; } uint32_t val = 0; @@ -159,8 +197,24 @@ esp_err_t Gd32FlashStore::metaLoad(ota_meta_t *meta) return ESP_OK; } -/** @brief 将 OTA 元数据写入 NVS。 */ -esp_err_t Gd32FlashStore::metaSave(const ota_meta_t *meta) +/** @brief 从 NVS 加载 OTA 元数据(加锁包装)。 */ +esp_err_t Gd32FlashStore::metaLoad(ota_meta_t *meta) +{ + if (!meta) return ESP_ERR_INVALID_ARG; + bool locked = lockNvs(kNvsLockTimeoutMs); + if (!locked) { + // 加锁失败不能跳过锁直接访问 NVS,清零输出并返回超时 + memset(meta, 0, sizeof(ota_meta_t)); + DEBUG_WARN("OTA", "metaLoad 加锁超时"); + return ESP_ERR_TIMEOUT; + } + esp_err_t err = metaLoadLocked(meta); + unlockNvs(true); + return err; +} + +/** @brief 已持锁版本:将 OTA 元数据写入 NVS。 */ +esp_err_t Gd32FlashStore::metaSaveLocked(const ota_meta_t *meta) { if (!meta) return ESP_ERR_INVALID_ARG; @@ -184,25 +238,57 @@ esp_err_t Gd32FlashStore::metaSave(const ota_meta_t *meta) return err; } -/** @brief 传输过程中更新元数据的 stored_size 和 last_seq 字段。 */ +/** @brief 将 OTA 元数据写入 NVS(加锁包装)。 */ +esp_err_t Gd32FlashStore::metaSave(const ota_meta_t *meta) +{ + if (!meta) return ESP_ERR_INVALID_ARG; + bool locked = lockNvs(kNvsLockTimeoutMs); + if (!locked) { + DEBUG_WARN("OTA", "metaSave 加锁超时"); + return ESP_ERR_TIMEOUT; + } + esp_err_t err = metaSaveLocked(meta); + unlockNvs(true); + return err; +} + +/** + * @brief 传输过程中更新元数据的 stored_size 和 last_seq 字段。 + * 整个读-改-写过程持锁,避免并发 metaUpdate 丢失更新。 + * 内部调用已持锁的 metaLoadLocked/metaSaveLocked,不再经公开方法重复加锁。 + */ esp_err_t Gd32FlashStore::metaUpdate(uint32_t stored_size, uint32_t last_seq) { + bool locked = lockNvs(kNvsLockTimeoutMs); + if (!locked) { + DEBUG_WARN("OTA", "metaUpdate 加锁超时"); + return ESP_ERR_TIMEOUT; + } + ota_meta_t meta; - esp_err_t err = metaLoad(&meta); - if (err != ESP_OK) return err; - - meta.stored_size = stored_size; - meta.last_seq = last_seq; - - return metaSave(&meta); + esp_err_t err = metaLoadLocked(&meta); + if (err == ESP_OK) { + meta.stored_size = stored_size; + meta.last_seq = last_seq; + err = metaSaveLocked(&meta); + } + unlockNvs(true); + return err; } /** @brief OTA 完成或中止时清除元数据(全零写入)。 */ esp_err_t Gd32FlashStore::metaClear() { DEBUG_INFO("OTA", "清除 OTA 元数据"); + bool locked = lockNvs(kNvsLockTimeoutMs); + if (!locked) { + DEBUG_WARN("OTA", "metaClear 加锁超时"); + return ESP_ERR_TIMEOUT; + } ota_meta_t meta = {}; - return metaSave(&meta); + esp_err_t err = metaSaveLocked(&meta); + unlockNvs(true); + return err; } // ============================================================ @@ -296,13 +382,12 @@ esp_err_t Gd32FlashStore::metaVerifyChecksum() if (err != ESP_OK) return err; // 检查 checksum 是否全零(未计算过) - bool all_zero = true; - for (size_t i = 0; i < sizeof(meta.checksum); ++i) { - if (meta.checksum[i] != 0x00) { all_zero = false; break; } - } - if (all_zero) { - DEBUG_WARN("OTA", "metaVerify: checksum 未计算,跳过校验(旧数据兼容)"); - return ESP_OK; + // 注意:返回 INVALID_STATE 而非 ESP_OK,由调用方决定是否容忍。 + // 历史上这里直接返回 OK 跳过校验,但 meta 被意外清零时会掩盖固件损坏。 + static const uint8_t kZeroSha[32] = {0}; + if (memcmp(meta.checksum, kZeroSha, sizeof(meta.checksum)) == 0) { + DEBUG_WARN("OTA", "metaVerify: checksum 未计算,返回 INVALID_STATE(调用方决定容忍或失败)"); + return ESP_ERR_INVALID_STATE; } uint8_t computed[32] = {0}; diff --git a/main/storage/gd32_flash_store.h b/main/storage/gd32_flash_store.h index 96120f1..3539daa 100644 --- a/main/storage/gd32_flash_store.h +++ b/main/storage/gd32_flash_store.h @@ -6,7 +6,8 @@ * 不再由 OtaRelay 承担存储职责(解决 H1 职责超载)。 * * 线程模型: - * - 所有方法均为 static,由调用方(协调器/OtaRelay/传输层)保证串行化 + * - 通过 instance() 单例访问,NVS 元数据读写由内部 mutex 保护 + * - metaLoad/metaSave/metaUpdate/metaClear 串行化,读-改-写原子 * - 分区指针懒初始化,首次使用时查找并缓存 */ #ifndef GD32_FLASH_STORE_H @@ -15,6 +16,8 @@ #include #include "esp_err.h" #include "esp_partition.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" #define OTA_META_NVS_NAMESPACE "ota_meta" ///< OTA 元数据 NVS 命名空间 @@ -71,9 +74,11 @@ public: // ── NVS 元数据持久化 ── /** - * @brief 从 NVS 加载 OTA 元数据(命名空间不存在时返回全零结构)。 - * @param meta 输出元数据结构体指针。 - * @return ESP_OK 成功。 + * @brief 从 NVS 加载 OTA 元数据。 + * @param meta 输出元数据结构体指针(任何错误路径下都会被清零)。 + * @return ESP_OK 成功(含命名空间不存在的兼容路径,返回全零结构); + * ESP_ERR_TIMEOUT 加锁超时; + * 其他错误码 = nvs_open 真实失败(如 NVS 未初始化/损坏),不再一律当 OK。 */ esp_err_t metaLoad(ota_meta_t *meta); @@ -135,12 +140,24 @@ public: const esp_partition_t* getPartition(); private: - Gd32FlashStore() = default; + Gd32FlashStore(); ~Gd32FlashStore() = default; Gd32FlashStore(const Gd32FlashStore&) = delete; Gd32FlashStore& operator=(const Gd32FlashStore&) = delete; const esp_partition_t *m_partition = nullptr; ///< gd32_ota 分区指针(懒初始化) + + // ── NVS 互斥锁(保护元数据读-改-写原子性)── + // lockNvs 返回是否成功 take;unlockNvs(handle) 据此决定是否 give, + // 参考 OtaCoordinator::lock/unlock 模式(避免共享 bool 被并发覆盖导致死锁)。 + SemaphoreHandle_t m_nvsMutex = nullptr; + bool lockNvs(uint32_t timeout_ms); + void unlockNvs(bool handle); + static constexpr uint32_t kNvsLockTimeoutMs = 100; ///< NVS 加锁超时(ms) + + // 已持锁版本:由持锁的公开方法或 metaUpdate 调用,不再自行加锁,避免递归 take 死锁 + esp_err_t metaLoadLocked(ota_meta_t *meta); + esp_err_t metaSaveLocked(const ota_meta_t *meta); }; #endif // GD32_FLASH_STORE_H diff --git a/main/transport/protocol.cpp b/main/transport/protocol.cpp index 3fe27e8..2885e5d 100644 --- a/main/transport/protocol.cpp +++ b/main/transport/protocol.cpp @@ -1,4 +1,4 @@ -/* +/* * protocol.cpp - 协议层实现 * T2.1, T2.2, T1.3 * ESP-IDF 6.0 适配版本 - 使用硬件 AES-128-CBC + 硬件 RNG @@ -99,21 +99,17 @@ int Protocol::sessionKeyInit(session_ctx_t *sess, const uint8_t *k_session) memset(sess, 0, sizeof(session_ctx_t)); memcpy(sess->key, k_session, 16); - memset(sess->iv, 0, 16); - sess->used = 0; return 0; } /** - * @brief 清除会话上下文中的密钥和状态。 + * @brief 清除会话上下文中的密钥数据。 * @param sess 会话上下文指针。 */ void Protocol::sessionKeyClear(session_ctx_t *sess) { if (sess) { memset(sess->key, 0, 16); - memset(sess->iv, 0, 16); - sess->used = 0; } } diff --git a/main/transport/protocol.h b/main/transport/protocol.h index 6d14ce0..929014f 100644 --- a/main/transport/protocol.h +++ b/main/transport/protocol.h @@ -1,4 +1,4 @@ -/* +/* * protocol.h - 帧格式、CRC16、AES 加密解密 * T2.2, T1.3 * ESP-IDF 6.0 适配版本 - 使用硬件 AES-128-CBC + 硬件 RNG @@ -12,11 +12,6 @@ #include #include "app_config.h" -// 帧格式: SOF(1B) + CMD(1B) + LEN(2B LE) + PAYLOAD(LEN) + CRC16(2B LE) -#define FRAME_SOF (0xEB) // 帧起始标记 -#define FRAME_MAX_PAYLOAD (64) // 安全最大 payload -#define FRAME_OVERHEAD (6) // SOF + CMD + LEN(2) + CRC16(2) - // 安全常量(定义在 app_config.h 中) inline constexpr size_t MASTER_KEY_HERE_LEN = 16; ///< AES-128 主密钥长度(字节) inline constexpr size_t CHIP_UID_LEN = 12; ///< GD32 芯片 UID 长度(字节) @@ -24,8 +19,6 @@ inline constexpr size_t CHIP_UID_LEN = 12; ///< GD32 芯片 UID 长度( // AES 会话上下文 typedef struct { uint8_t key[16]; // 会话密钥 K_session - uint8_t iv[16]; // CBC 模式 IV - int used; // 会话是否已初始化标志 } session_ctx_t; /** @@ -64,7 +57,7 @@ public: */ static int sessionKeyInit(session_ctx_t *sess, const uint8_t *k_session); /** - * @brief 清除会话上下文的密钥和 IV 数据。 + * @brief 清除会话上下文中的密钥数据。 * @param sess 会话上下文指针。 */ static void sessionKeyClear(session_ctx_t *sess); diff --git a/main/transport/spi_master.cpp b/main/transport/spi_master.cpp index 0ab6454..44dbc88 100644 --- a/main/transport/spi_master.cpp +++ b/main/transport/spi_master.cpp @@ -1,4 +1,4 @@ -/* +/* * spi_master.cpp - SPI Master 驱动实现 * T1.1 - T1.4 * ESP-IDF 6.0 适配版本 @@ -707,7 +707,7 @@ esp_err_t SpiMaster::transferWithRetry(uint8_t cmd, const uint8_t *payload, DEBUG_INFO("SPI", "SPI 传输 (带重试): cmd=0x%02X, 最大重试=%d", cmd, max_retries); while (retry_count < max_retries) { - ret = syncTransferUnlocked(cmd, payload, payload_len, + ret = transferRequestResponse(cmd, payload, payload_len, recv_buf, recv_len, 3000); if (ret == ESP_OK) { if (retry_count > 0) { @@ -728,34 +728,3 @@ esp_err_t SpiMaster::transferWithRetry(uint8_t cmd, const uint8_t *payload, // CANFD 接口已迁移至 transport/canfd_bridge.cpp(M4 解耦) -// ============================================================ -// 无锁内部版本实现 -// ============================================================ -/** - * @brief 无锁版本的同步 SPI 传输实现。 - * @param cmd 命令字节。 - * @param payload 负载数据缓冲区。 - * @param payload_len 负载数据长度。 - * @param recv_buf 接收数据缓冲区。 - * @param recv_len 接收数据长度。 - * @param timeout_ms 超时时间(毫秒)。 - * @return ESP_OK 表示成功,否则返回错误码。 - */ -esp_err_t SpiMaster::syncTransferUnlocked(uint8_t cmd, const uint8_t *payload, - size_t payload_len, uint8_t *recv_buf, - size_t recv_len, uint32_t timeout_ms) -{ - return transferRequestResponse(cmd, payload, payload_len, recv_buf, recv_len, timeout_ms); -} - -/** - * @brief 无锁版本的 SPI 发送实现。 - * @param cmd 命令字节。 - * @param payload 负载数据缓冲区。 - * @param len 负载数据长度。 - * @return ESP_OK 表示成功,否则返回错误码。 - */ -esp_err_t SpiMaster::sendUnlocked(uint8_t cmd, const uint8_t *payload, size_t len) -{ - return transferRequestResponse(cmd, payload, len, NULL, 0, 0U); -} diff --git a/main/transport/spi_master.h b/main/transport/spi_master.h index e850f06..e7e6bec 100644 --- a/main/transport/spi_master.h +++ b/main/transport/spi_master.h @@ -1,4 +1,4 @@ -/* +/* * spi_master.h - SPI Master 驱动头文件 * CANFD 3通道发送接口 + 底层 SPI 总线 * @@ -182,27 +182,6 @@ private: // 无锁内部版本(供已持锁的公共方法调用,避免递归死锁) - /** - * @brief 无锁版本的同步 SPI 传输(调用者须已持有锁)。 - * @param cmd 命令字节。 - * @param payload 负载数据缓冲区。 - * @param payload_len 负载数据长度。 - * @param recv_buf 接收数据缓冲区。 - * @param recv_len 接收数据长度。 - * @param timeout_ms 超时时间(毫秒)。 - * @return ESP_OK 表示成功,否则返回错误码。 - */ - esp_err_t syncTransferUnlocked(uint8_t cmd, const uint8_t *payload, - size_t payload_len, uint8_t *recv_buf, - size_t recv_len, uint32_t timeout_ms); - /** - * @brief 无锁版本的 SPI 发送(调用者须已持有锁)。 - * @param cmd 命令字节。 - * @param payload 负载数据缓冲区。 - * @param len 负载数据长度。 - * @return ESP_OK 表示成功,否则返回错误码。 - */ - esp_err_t sendUnlocked(uint8_t cmd, const uint8_t *payload, size_t len); /** * @brief 无锁版本的 OTA 响应轮询(调用者须已持有锁)。 * @param resp_type 输出响应类型。 diff --git a/sdkconfig.bak.mbedtls_trim b/sdkconfig.bak.mbedtls_trim deleted file mode 100644 index c3e91b9..0000000 --- a/sdkconfig.bak.mbedtls_trim +++ /dev/null @@ -1,4032 +0,0 @@ -# -# Automatically generated file. DO NOT EDIT. -# Espressif IoT Development Framework (ESP-IDF) 6.0.0 Project Configuration -# -# default: -CONFIG_SOC_ADC_SUPPORTED=y -# default: -CONFIG_SOC_DEDICATED_GPIO_SUPPORTED=y -# default: -CONFIG_SOC_UART_SUPPORTED=y -# default: -CONFIG_SOC_GDMA_SUPPORTED=y -# default: -CONFIG_SOC_UHCI_SUPPORTED=y -# default: -CONFIG_SOC_AHB_GDMA_SUPPORTED=y -# default: -CONFIG_SOC_GPTIMER_SUPPORTED=y -# default: -CONFIG_SOC_TWAI_SUPPORTED=y -# default: -CONFIG_SOC_BT_SUPPORTED=y -# default: -CONFIG_SOC_ASYNC_MEMCPY_SUPPORTED=y -# default: -CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y -# default: -CONFIG_SOC_TEMP_SENSOR_SUPPORTED=y -# default: -CONFIG_SOC_XT_WDT_SUPPORTED=y -# default: -CONFIG_SOC_PHY_SUPPORTED=y -# default: -CONFIG_SOC_WIFI_SUPPORTED=y -# default: -CONFIG_SOC_SUPPORTS_SECURE_DL_MODE=y -# default: -CONFIG_SOC_EFUSE_KEY_PURPOSE_FIELD=y -# default: -CONFIG_SOC_EFUSE_HAS_EFUSE_RST_BUG=y -# default: -CONFIG_SOC_EFUSE_SUPPORTED=y -# default: -CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y -# default: -CONFIG_SOC_RTC_MEM_SUPPORTED=y -# default: -CONFIG_SOC_RTC_TIMER_V1_SUPPORTED=y -# default: -CONFIG_SOC_I2S_SUPPORTED=y -# default: -CONFIG_SOC_RMT_SUPPORTED=y -# default: -CONFIG_SOC_SDM_SUPPORTED=y -# default: -CONFIG_SOC_GPSPI_SUPPORTED=y -# default: -CONFIG_SOC_LEDC_SUPPORTED=y -# default: -CONFIG_SOC_I2C_SUPPORTED=y -# default: -CONFIG_SOC_SYSTIMER_SUPPORTED=y -# default: -CONFIG_SOC_SUPPORT_COEXISTENCE=y -# default: -CONFIG_SOC_AES_SUPPORTED=y -# default: -CONFIG_SOC_MPI_SUPPORTED=y -# default: -CONFIG_SOC_SHA_SUPPORTED=y -# default: -CONFIG_SOC_HMAC_SUPPORTED=y -# default: -CONFIG_SOC_DIG_SIGN_SUPPORTED=y -# default: -CONFIG_SOC_FLASH_ENC_SUPPORTED=y -# default: -CONFIG_SOC_SECURE_BOOT_SUPPORTED=y -# default: -CONFIG_SOC_MEMPROT_SUPPORTED=y -# default: -CONFIG_SOC_BOD_SUPPORTED=y -# default: -CONFIG_SOC_CLK_TREE_SUPPORTED=y -# default: -CONFIG_SOC_ASSIST_DEBUG_SUPPORTED=y -# default: -CONFIG_SOC_WDT_SUPPORTED=y -# default: -CONFIG_SOC_SPI_FLASH_SUPPORTED=y -# default: -CONFIG_SOC_RNG_SUPPORTED=y -# default: -CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y -# default: -CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y -# default: -CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y -# default: -CONFIG_SOC_PM_SUPPORTED=y -# default: -CONFIG_SOC_XTAL_SUPPORT_40M=y -# default: -CONFIG_SOC_AES_SUPPORT_DMA=y -# default: -CONFIG_SOC_AES_GDMA=y -# default: -CONFIG_SOC_AES_SUPPORT_AES_128=y -# default: -CONFIG_SOC_AES_SUPPORT_AES_256=y -# default: -CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y -# default: -CONFIG_SOC_ADC_ARBITER_SUPPORTED=y -# default: -CONFIG_SOC_ADC_DIG_IIR_FILTER_SUPPORTED=y -# default: -CONFIG_SOC_ADC_MONITOR_SUPPORTED=y -# default: -CONFIG_SOC_ADC_DMA_SUPPORTED=y -# default: -CONFIG_SOC_ADC_PERIPH_NUM=2 -# default: -CONFIG_SOC_ADC_MAX_CHANNEL_NUM=5 -# default: -CONFIG_SOC_ADC_ATTEN_NUM=4 -# default: -CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=1 -# default: -CONFIG_SOC_ADC_PATT_LEN_MAX=8 -# default: -CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=12 -# default: -CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12 -# default: -CONFIG_SOC_ADC_DIGI_RESULT_BYTES=4 -# default: -CONFIG_SOC_ADC_DIGI_DATA_BYTES_PER_CONV=4 -# default: -CONFIG_SOC_ADC_DIGI_IIR_FILTER_NUM=2 -# default: -CONFIG_SOC_ADC_DIGI_MONITOR_NUM=2 -# default: -CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=83333 -# default: -CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=611 -# default: -CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=12 -# default: -CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 -# default: -CONFIG_SOC_ADC_CALIBRATION_V1_SUPPORTED=y -# default: -CONFIG_SOC_ADC_SELF_HW_CALI_SUPPORTED=y -# default: -CONFIG_SOC_ADC_SHARED_POWER=y -# default: -CONFIG_SOC_APB_BACKUP_DMA=y -# default: -CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y -# default: -CONFIG_SOC_SHARED_IDCACHE_SUPPORTED=y -# default: -CONFIG_SOC_CACHE_FREEZE_SUPPORTED=y -# default: -CONFIG_SOC_CACHE_MEMORY_IBANK_SIZE=0x4000 -# default: -CONFIG_SOC_CPU_CORES_NUM=1 -# default: -CONFIG_SOC_CPU_INTR_NUM=32 -# default: -CONFIG_SOC_CPU_HAS_FLEXIBLE_INTC=y -# default: -CONFIG_SOC_CPU_HAS_CSR_PC=y -# default: -CONFIG_SOC_CPU_BREAKPOINTS_NUM=8 -# default: -CONFIG_SOC_CPU_WATCHPOINTS_NUM=8 -# default: -CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x80000000 -# default: -CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=3072 -# default: -CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16 -# default: -CONFIG_SOC_DS_KEY_CHECK_MAX_WAIT_US=1100 -# default: -CONFIG_SOC_AHB_GDMA_VERSION=1 -# default: -CONFIG_SOC_GPIO_PORT=1 -# default: -CONFIG_SOC_GPIO_PIN_COUNT=22 -# default: -CONFIG_SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER=y -# default: -CONFIG_SOC_GPIO_SUPPORT_FORCE_HOLD=y -# default: -CONFIG_SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP=y -# default: -CONFIG_SOC_GPIO_IN_RANGE_MAX=21 -# default: -CONFIG_SOC_GPIO_OUT_RANGE_MAX=21 -# default: -CONFIG_SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK=0 -# default: -CONFIG_SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_PIN_CNT=6 -# default: -CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0x00000000003FFFC0 -# default: -CONFIG_SOC_GPIO_CLOCKOUT_BY_GPIO_MATRIX=y -# default: -CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3 -# default: -CONFIG_SOC_I2C_NUM=1 -# default: -CONFIG_SOC_HP_I2C_NUM=1 -# default: -CONFIG_SOC_I2C_SUPPORT_XTAL=y -# default: -CONFIG_SOC_I2C_SUPPORT_RTC=y -# default: -CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y -# default: -CONFIG_SOC_I2C_SUPPORT_SLAVE=y -# default: -CONFIG_SOC_I2C_SLAVE_SUPPORT_BROADCAST=y -# default: -CONFIG_SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE=y -# default: -CONFIG_SOC_I2S_HW_VERSION_2=y -# default: -CONFIG_SOC_I2S_SUPPORTS_PCM=y -# default: -CONFIG_SOC_I2S_SUPPORTS_PDM=y -# default: -CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y -# default: -CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y -# default: -CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y -# default: -CONFIG_SOC_I2S_PDM_MAX_TX_LINES=2 -# default: -CONFIG_SOC_I2S_PDM_MAX_RX_LINES=1 -# default: -CONFIG_SOC_I2S_SUPPORTS_TDM=y -# default: -CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y -# default: -CONFIG_SOC_LEDC_SUPPORT_XTAL_CLOCK=y -# default: -CONFIG_SOC_LEDC_TIMER_NUM=4 -# default: -CONFIG_SOC_LEDC_CHANNEL_NUM=6 -# default: -CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=14 -# default: -CONFIG_SOC_LEDC_SUPPORT_FADE_STOP=y -# default: -CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=1 -# default: -CONFIG_SOC_MMU_PERIPH_NUM=1 -# default: -CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000 -# default: -CONFIG_SOC_MPU_REGIONS_MAX_NUM=8 -# default: -CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=48 -# default: -CONFIG_SOC_RMT_SUPPORT_RX_PINGPONG=y -# default: -CONFIG_SOC_RMT_SUPPORT_TX_LOOP_COUNT=y -# default: -CONFIG_SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH=128 -# default: -CONFIG_SOC_RTC_CNTL_CPU_PD_REG_FILE_NUM=108 -# default: -CONFIG_SOC_SLEEP_SYSTIMER_STALL_WORKAROUND=y -# default: -CONFIG_SOC_SLEEP_TGWDT_STOP_WORKAROUND=y -# default: -CONFIG_SOC_RTCIO_PIN_COUNT=0 -# default: -CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4 -# default: -CONFIG_SOC_MPI_OPERATIONS_NUM=3 -# default: -CONFIG_SOC_RSA_MAX_BIT_LEN=3072 -# default: -CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968 -# default: -CONFIG_SOC_SHA_SUPPORT_DMA=y -# default: -CONFIG_SOC_SHA_SUPPORT_RESUME=y -# default: -CONFIG_SOC_SHA_GDMA=y -# default: -CONFIG_SOC_SHA_SUPPORT_SHA1=y -# default: -CONFIG_SOC_SHA_SUPPORT_SHA224=y -# default: -CONFIG_SOC_SHA_SUPPORT_SHA256=y -# default: -CONFIG_SOC_SPI_PERIPH_NUM=2 -# default: -CONFIG_SOC_SPI_MAX_CS_NUM=6 -# default: -CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64 -# default: -CONFIG_SOC_SPI_SUPPORT_DDRCLK=y -# default: -CONFIG_SOC_SPI_SLAVE_SUPPORT_SEG_TRANS=y -# default: -CONFIG_SOC_SPI_SUPPORT_CD_SIG=y -# default: -CONFIG_SOC_SPI_SUPPORT_CONTINUOUS_TRANS=y -# default: -CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2=y -# default: -CONFIG_SOC_SPI_SUPPORT_CLK_APB=y -# default: -CONFIG_SOC_SPI_SUPPORT_CLK_XTAL=y -# default: -CONFIG_SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT=y -# default: -CONFIG_SOC_SPI_SCT_SUPPORTED=y -# default: -CONFIG_SOC_SPI_SCT_REG_NUM=14 -# default: -CONFIG_SOC_SPI_SCT_BUFFER_NUM_MAX=y -# default: -CONFIG_SOC_SPI_SCT_CONF_BITLEN_MAX=0x3FFFA -# default: -CONFIG_SOC_MEMSPI_IS_INDEPENDENT=y -# default: -CONFIG_SOC_SPI_MAX_PRE_DIVIDER=16 -# default: -CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE=y -# default: -CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y -# default: -CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y -# default: -CONFIG_SOC_SPI_MEM_SUPPORT_IDLE_INTR=y -# default: -CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y -# default: -CONFIG_SOC_SPI_MEM_SUPPORT_CHECK_SUS=y -# default: -CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y -# default: -CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y -# default: -CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y -# default: -CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y -# default: -CONFIG_SOC_MEMSPI_SRC_FREQ_26M_SUPPORTED=y -# default: -CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y -# default: -CONFIG_SOC_SYSTIMER_COUNTER_NUM=2 -# default: -CONFIG_SOC_SYSTIMER_ALARM_NUM=3 -# default: -CONFIG_SOC_SYSTIMER_BIT_WIDTH_LO=32 -# default: -CONFIG_SOC_SYSTIMER_BIT_WIDTH_HI=20 -# default: -CONFIG_SOC_SYSTIMER_FIXED_DIVIDER=y -# default: -CONFIG_SOC_SYSTIMER_INT_LEVEL=y -# default: -CONFIG_SOC_SYSTIMER_ALARM_MISS_COMPENSATE=y -# default: -CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32 -# default: -CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 -# default: -CONFIG_SOC_MWDT_SUPPORT_XTAL=y -# default: -CONFIG_SOC_TWAI_CONTROLLER_NUM=1 -# default: -CONFIG_SOC_TWAI_MASK_FILTER_NUM=1 -# default: -CONFIG_SOC_EFUSE_DIS_DOWNLOAD_ICACHE=y -# default: -CONFIG_SOC_EFUSE_DIS_PAD_JTAG=y -# default: -CONFIG_SOC_EFUSE_DIS_USB_JTAG=y -# default: -CONFIG_SOC_EFUSE_DIS_DIRECT_BOOT=y -# default: -CONFIG_SOC_EFUSE_SOFT_DIS_JTAG=y -# default: -CONFIG_SOC_EFUSE_DIS_ICACHE=y -# default: -CONFIG_SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK=y -# default: -CONFIG_SOC_EFUSE_XTS_AES_KEY_128=y -# default: -CONFIG_SOC_SECURE_BOOT_V2_RSA=y -# default: -CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=3 -# default: -CONFIG_SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS=y -# default: -CONFIG_SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY=y -# default: -CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=32 -# default: -CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES=y -# default: -CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_128=y -# default: -CONFIG_SOC_MEMPROT_CPU_PREFETCH_PAD_SIZE=16 -# default: -CONFIG_SOC_MEMPROT_MEM_ALIGN_SIZE=512 -# default: -CONFIG_SOC_UART_NUM=2 -# default: -CONFIG_SOC_UART_HP_NUM=2 -# default: -CONFIG_SOC_UART_FIFO_LEN=128 -# default: -CONFIG_SOC_UART_BITRATE_MAX=5000000 -# default: -CONFIG_SOC_UART_SUPPORT_APB_CLK=y -# default: -CONFIG_SOC_UART_SUPPORT_RTC_CLK=y -# default: -CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y -# default: -CONFIG_SOC_UART_SUPPORT_WAKEUP_INT=y -# default: -CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y -# default: -CONFIG_SOC_UHCI_NUM=1 -# default: -CONFIG_SOC_COEX_HW_PTI=y -# default: -CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 -# default: -CONFIG_SOC_MAC_BB_PD_MEM_SIZE=192 -# default: -CONFIG_SOC_WIFI_LIGHT_SLEEP_CLK_WIDTH=12 -# default: -CONFIG_SOC_PM_SUPPORT_WIFI_WAKEUP=y -# default: -CONFIG_SOC_PM_SUPPORT_BT_WAKEUP=y -# default: -CONFIG_SOC_PM_SUPPORT_CPU_PD=y -# default: -CONFIG_SOC_PM_SUPPORT_WIFI_PD=y -# default: -CONFIG_SOC_PM_SUPPORT_BT_PD=y -# default: -CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y -# default: -CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y -# default: -CONFIG_SOC_PM_SUPPORT_MAC_BB_PD=y -# default: -CONFIG_SOC_PM_CPU_RETENTION_BY_RTCCNTL=y -# default: -CONFIG_SOC_PM_MODEM_RETENTION_BY_BACKUPDMA=y -# default: -CONFIG_SOC_PM_MODEM_PD_BY_SW=y -# default: -CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y -# default: -CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y -# default: -CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y -# default: -CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y -# default: -CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D2=y -# default: -CONFIG_SOC_TEMPERATURE_SENSOR_SUPPORT_FAST_RC=y -# default: -CONFIG_SOC_TEMPERATURE_SENSOR_SUPPORT_XTAL=y -# default: -CONFIG_SOC_WIFI_HW_TSF=y -# default: -CONFIG_SOC_WIFI_FTM_SUPPORT=y -# default: -CONFIG_SOC_WIFI_GCMP_SUPPORT=y -# default: -CONFIG_SOC_WIFI_WAPI_SUPPORT=y -# default: -CONFIG_SOC_WIFI_TXOP_SUPPORT=y -# default: -CONFIG_SOC_WIFI_CSI_SUPPORT=y -# default: -CONFIG_SOC_WIFI_MESH_SUPPORT=y -# default: -CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y -# default: -CONFIG_SOC_WIFI_PHY_NEEDS_USB_WORKAROUND=y -# default: -CONFIG_SOC_BLE_SUPPORTED=y -# default: -CONFIG_SOC_BLE_MESH_SUPPORTED=y -# default: -CONFIG_SOC_BLE_50_SUPPORTED=y -# default: -CONFIG_SOC_BLE_DEVICE_PRIVACY_SUPPORTED=y -# default: -CONFIG_SOC_BLUFI_SUPPORTED=y -# default: -CONFIG_SOC_PHY_COMBO_MODULE=y -# default: -CONFIG_IDF_CMAKE=y -# default: -CONFIG_IDF_TOOLCHAIN="gcc" -# default: -CONFIG_IDF_TOOLCHAIN_GCC=y -# default: -CONFIG_IDF_TARGET_ARCH_RISCV=y -# default: -CONFIG_IDF_TARGET_ARCH="riscv" -# default: -CONFIG_IDF_TARGET="esp32c3" -# default: -CONFIG_IDF_INIT_VERSION="6.0.0" -# default: -CONFIG_IDF_TARGET_ESP32C3=y -# default: -CONFIG_IDF_FIRMWARE_CHIP_ID=0x0005 - -# -# Build type -# -# default: -CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y -# default: -# CONFIG_APP_BUILD_TYPE_RAM is not set -# default: -CONFIG_APP_BUILD_GENERATE_BINARIES=y -# default: -CONFIG_APP_BUILD_BOOTLOADER=y -# default: -CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y -# default: -# CONFIG_APP_REPRODUCIBLE_BUILD is not set -# default: -# CONFIG_APP_NO_BLOBS is not set -# end of Build type - -# -# Bootloader config -# - -# -# Bootloader manager -# -# default: -CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y -# default: -CONFIG_BOOTLOADER_PROJECT_VER=1 -# end of Bootloader manager - -# -# Application Rollback -# -# default: -# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set -# end of Application Rollback - -# -# Recovery Bootloader and Rollback -# -# end of Recovery Bootloader and Rollback - -# default: -CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0 -# default: -CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y -# default: -# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set -# default: -# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set - -# -# Log -# -# default: -CONFIG_BOOTLOADER_LOG_VERSION_1=y -# default: -CONFIG_BOOTLOADER_LOG_VERSION=1 -# default: -# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set -# default: -# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set -# default: -# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set -# default: -CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y -# default: -# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set -# default: -# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set -# default: -CONFIG_BOOTLOADER_LOG_LEVEL=3 - -# -# Format -# -# default: -# CONFIG_BOOTLOADER_LOG_COLORS is not set -# default: -CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y -# end of Format - -# -# Settings -# -# default: -CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y -# default: -CONFIG_BOOTLOADER_LOG_MODE_TEXT=y -# end of Settings -# end of Log - -# default: -CONFIG_BOOTLOADER_CPU_CLK_FREQ_MHZ=80 - -# -# Serial Flash Configurations -# -# default: -# CONFIG_BOOTLOADER_FLASH_DC_AWARE is not set -# default: -CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y -# end of Serial Flash Configurations - -# default: -# CONFIG_BOOTLOADER_FACTORY_RESET is not set -# default: -# CONFIG_BOOTLOADER_APP_TEST is not set -# default: -CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y -# default: -CONFIG_BOOTLOADER_WDT_ENABLE=y -# default: -# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set -# default: -CONFIG_BOOTLOADER_WDT_TIME_MS=9000 -# default: -# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set -# default: -# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set -# default: -# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set -# default: -CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 -# default: -# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set -# end of Bootloader config - -# -# Security features -# -# default: -CONFIG_SECURE_BOOT_V2_RSA_SUPPORTED=y -# default: -CONFIG_SECURE_BOOT_V2_PREFERRED=y -# default: -# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set -# default: -# CONFIG_SECURE_BOOT is not set -# default: -# CONFIG_SECURE_FLASH_ENC_ENABLED is not set -# default: -CONFIG_SECURE_ROM_DL_MODE_ENABLED=y -# end of Security features - -# -# Application manager -# -# default: -CONFIG_APP_COMPILE_TIME_DATE=y -# default: -# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set -# default: -# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set -# default: -# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set -# default: -CONFIG_APP_RETRIEVE_LEN_ELF_SHA=9 -# end of Application manager - -# default: -CONFIG_ESP_ROM_HAS_CRC_LE=y -# default: -CONFIG_ESP_ROM_HAS_CRC_BE=y -# default: -CONFIG_ESP_ROM_HAS_MZ_CRC32=y -# default: -CONFIG_ESP_ROM_HAS_JPEG_DECODE=y -# default: -CONFIG_ESP_ROM_UART_CLK_IS_XTAL=y -# default: -CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=3 -# default: -CONFIG_ESP_ROM_HAS_RETARGETABLE_LOCKING=y -# default: -CONFIG_ESP_ROM_HAS_ERASE_0_REGION_BUG=y -# default: -CONFIG_ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV=y -# default: -CONFIG_ESP_ROM_GET_CLK_FREQ=y -# default: -CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y -# default: -CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y -# default: -CONFIG_ESP_ROM_HAS_SPI_FLASH=y -# default: -CONFIG_ESP_ROM_HAS_SPI_FLASH_MMAP=y -# default: -CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y -# default: -CONFIG_ESP_ROM_HAS_NEWLIB=y -# default: -CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y -# default: -CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y -# default: -CONFIG_ESP_ROM_NEEDS_SET_CACHE_MMU_SIZE=y -# default: -CONFIG_ESP_ROM_RAM_APP_NEEDS_MMU_INIT=y -# default: -CONFIG_ESP_ROM_HAS_SW_FLOAT=y -# default: -CONFIG_ESP_ROM_USB_OTG_NUM=-1 -# default: -CONFIG_ESP_ROM_HAS_VERSION=y -# default: -CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y -# default: -CONFIG_ESP_ROM_CONSOLE_OUTPUT_SECONDARY=y -# default: -CONFIG_ESP_ROM_HAS_SUBOPTIMAL_NEWLIB_ON_MISALIGNED_MEMORY=y - -# -# Boot ROM Behavior -# -# default: -CONFIG_BOOT_ROM_LOG_ALWAYS_ON=y -# default: -# CONFIG_BOOT_ROM_LOG_ALWAYS_OFF is not set -# default: -# CONFIG_BOOT_ROM_LOG_ON_GPIO_HIGH is not set -# default: -# CONFIG_BOOT_ROM_LOG_ON_GPIO_LOW is not set -# end of Boot ROM Behavior - -# -# Serial flasher config -# -# default: -# CONFIG_ESPTOOLPY_NO_STUB is not set -# default: -# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set -# default: -# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set -# default: -CONFIG_ESPTOOLPY_FLASHMODE_DIO=y -# default: -# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set -# default: -CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y -# default: -CONFIG_ESPTOOLPY_FLASHMODE="dio" -# default: -CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -# default: -# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set -# default: -# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set -# default: -# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set -# default: -CONFIG_ESPTOOLPY_FLASHFREQ="80m" -# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set -# default: -CONFIG_ESPTOOLPY_FLASHSIZE="4MB" -# default: -# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set -# default: -CONFIG_ESPTOOLPY_BEFORE_RESET=y -# default: -# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set -# default: -CONFIG_ESPTOOLPY_BEFORE="default-reset" -# default: -CONFIG_ESPTOOLPY_AFTER_RESET=y -# default: -# CONFIG_ESPTOOLPY_AFTER_NORESET is not set -# default: -CONFIG_ESPTOOLPY_AFTER="hard-reset" -# default: -CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 -# end of Serial flasher config - -# -# Partition Table -# -# CONFIG_PARTITION_TABLE_SINGLE_APP is not set -# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set -# CONFIG_PARTITION_TABLE_TWO_OTA is not set -# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="config/partitions.csv" -# default: -CONFIG_PARTITION_TABLE_FILENAME="config/partitions.csv" -# default: -CONFIG_PARTITION_TABLE_OFFSET=0x8000 -# default: -CONFIG_PARTITION_TABLE_MD5=y -# end of Partition Table - -# -# ESP32_COMM Configuration -# -# default: -CONFIG_ESP_WIFI_SSID="ESP32_OTA_RELAY" -# default: -CONFIG_ESP_WIFI_PASSWORD="12345678" -# default: -CONFIG_ESP_WIFI_CHANNEL=6 -# default: -CONFIG_ESP_MAX_STA_CONN=4 -# end of ESP32_COMM Configuration - -# -# Compiler options -# -# default: -CONFIG_COMPILER_OPTIMIZATION_DEBUG=y -# default: -# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set -# default: -# CONFIG_COMPILER_OPTIMIZATION_PERF is not set -# default: -# CONFIG_COMPILER_OPTIMIZATION_NONE is not set -# default: -CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y -# default: -# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set -# default: -# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set -# default: -# CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE is not set -# default: -CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y -# default: -CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 -# default: -# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set -# default: -CONFIG_COMPILER_HIDE_PATHS_MACROS=y -# default: -# CONFIG_COMPILER_CXX_EXCEPTIONS is not set -# default: -# CONFIG_COMPILER_CXX_RTTI is not set -# default: -CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y -# default: -# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set -# default: -# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set -# default: -# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set -# default: -# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set -# default: -# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set -# default: -# CONFIG_COMPILER_SAVE_RESTORE_LIBCALLS is not set -# default: -# CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS is not set -# default: -# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set -# default: -# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set -# default: -# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set -# default: -# CONFIG_COMPILER_DISABLE_GCC15_WARNINGS is not set -# default: -# CONFIG_COMPILER_DUMP_RTL_FILES is not set -# default: -CONFIG_COMPILER_RT_LIB_GCCLIB=y -# default: -CONFIG_COMPILER_RT_LIB_NAME="gcc" -# default: -CONFIG_COMPILER_ORPHAN_SECTIONS_ERROR=y -# default: -# CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING is not set -# default: -# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set -# default: -# CONFIG_COMPILER_STATIC_ANALYZER is not set -# default: -CONFIG_COMPILER_CXX_GLIBCXX_CONSTEXPR_NO_CHANGE=y -# default: -# CONFIG_COMPILER_CXX_GLIBCXX_CONSTEXPR_COLD_CONSTEXPR is not set -# default: -# CONFIG_COMPILER_CXX_GLIBCXX_CONSTEXPR_COLD is not set -# end of Compiler options - -# -# Component config -# - -# -# Bluetooth -# -CONFIG_BT_ENABLED=y -# CONFIG_BT_BLUEDROID_ENABLED is not set -CONFIG_BT_NIMBLE_ENABLED=y -# CONFIG_BT_CONTROLLER_ONLY is not set -# default: -CONFIG_BT_CONTROLLER_ENABLED=y -# default: -# CONFIG_BT_CONTROLLER_DISABLED is not set - -# -# NimBLE Options -# - -# -# General -# -# default: -CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y -# default: -# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set -# default: -CONFIG_BT_NIMBLE_PINNED_TO_CORE=0 -CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=4096 -# default: -CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE=y -# end of General - -# -# Roles and Profiles -# -# CONFIG_BT_NIMBLE_ROLE_CENTRAL is not set -CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y -CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y -# CONFIG_BT_NIMBLE_ROLE_OBSERVER is not set -# default: -CONFIG_BT_NIMBLE_GATT_SERVER=y -# end of Roles and Profiles - -# -# Security (SMP) -# -# CONFIG_BT_NIMBLE_SECURITY_ENABLE is not set -# end of Security (SMP) - -# -# GAP -# -# default: -CONFIG_BT_NIMBLE_RPA_TIMEOUT=900 -CONFIG_BT_NIMBLE_WHITELIST_SIZE=12 -# default: -CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=y -# default: -CONFIG_BT_NIMBLE_MAX_CONN_REATTEMPT=3 -# default: -CONFIG_BT_NIMBLE_HS_PVCY=y -# default: -# CONFIG_BT_NIMBLE_HOST_ALLOW_CONNECT_WITH_SCAN is not set -# default: -# CONFIG_BT_NIMBLE_HOST_QUEUE_CONG_CHECK is not set -CONFIG_BT_NIMBLE_MAX_CONNECTIONS=3 -CONFIG_BT_NIMBLE_MAX_CCCDS=8 -CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS=y -CONFIG_BT_NIMBLE_HS_STOP_TIMEOUT_MS=2000 -CONFIG_BT_NIMBLE_USE_ESP_TIMER=y -# default: -# CONFIG_BT_NIMBLE_HS_FLOW_CTRL is not set -# end of GAP - -# -# GATT / ATT -# -# default: -CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=256 -# default: -CONFIG_BT_NIMBLE_ATT_MAX_PREP_ENTRIES=64 -# default: -CONFIG_BT_NIMBLE_GATT_MAX_PROCS=4 -# default: -# CONFIG_BT_NIMBLE_BLE_GATT_BLOB_TRANSFER is not set -# default: -# CONFIG_BT_NIMBLE_GATT_CACHING is not set -# default: -# CONFIG_BT_NIMBLE_INCL_SVC_DISCOVERY is not set -# end of GATT / ATT - -# -# L2CAP -# -CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM=0 -# end of L2CAP - -# -# Memory Settings -# -# default: -CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=12 -# default: -CONFIG_BT_NIMBLE_MSYS_1_BLOCK_SIZE=256 -# default: -CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=24 -# default: -CONFIG_BT_NIMBLE_MSYS_2_BLOCK_SIZE=320 -# default: -CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=24 -# default: -CONFIG_BT_NIMBLE_TRANSPORT_ACL_SIZE=255 -# default: -CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=70 -# default: -CONFIG_BT_NIMBLE_TRANSPORT_EVT_COUNT=30 -# default: -CONFIG_BT_NIMBLE_TRANSPORT_EVT_DISCARD_COUNT=8 -# default: -CONFIG_BT_NIMBLE_L2CAP_COC_SDU_BUFF_COUNT=1 -# default: -# CONFIG_BT_NIMBLE_MEMPOOL_RUNTIME_ALLOC is not set -# end of Memory Settings - -# -# BLE 5.x Features -# -# default: -CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=y -# default: -CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_2M_PHY=y -# default: -CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_CODED_PHY=y -# default: -# CONFIG_BT_NIMBLE_EXT_ADV is not set -# default: -# CONFIG_BT_NIMBLE_EXT_SCAN is not set -# default: -# CONFIG_BT_NIMBLE_ISO is not set -# end of BLE 5.x Features - -# -# Services -# -# default: -CONFIG_BT_NIMBLE_PROX_SERVICE=y -# default: -CONFIG_BT_NIMBLE_ANS_SERVICE=y -# default: -CONFIG_BT_NIMBLE_CTS_SERVICE=y -# default: -CONFIG_BT_NIMBLE_HTP_SERVICE=y -# default: -CONFIG_BT_NIMBLE_IPSS_SERVICE=y -# default: -CONFIG_BT_NIMBLE_TPS_SERVICE=y -# default: -CONFIG_BT_NIMBLE_IAS_SERVICE=y -# default: -CONFIG_BT_NIMBLE_LLS_SERVICE=y -# default: -CONFIG_BT_NIMBLE_SPS_SERVICE=y -# default: -CONFIG_BT_NIMBLE_HR_SERVICE=y -# default: -# CONFIG_BT_NIMBLE_HID_SERVICE is not set -# default: -CONFIG_BT_NIMBLE_BAS_SERVICE=y -# default: -# CONFIG_BT_NIMBLE_SVC_BAS_BATTERY_LEVEL_NOTIFY is not set -# default: -CONFIG_BT_NIMBLE_DIS_SERVICE=y -# default: -# CONFIG_BT_NIMBLE_SVC_DIS_MANUFACTURER_NAME is not set -# default: -# CONFIG_BT_NIMBLE_SVC_DIS_SERIAL_NUMBER is not set -# default: -# CONFIG_BT_NIMBLE_SVC_DIS_HARDWARE_REVISION is not set -# default: -# CONFIG_BT_NIMBLE_SVC_DIS_FIRMWARE_REVISION is not set -# default: -# CONFIG_BT_NIMBLE_SVC_DIS_SOFTWARE_REVISION is not set -# default: -# CONFIG_BT_NIMBLE_SVC_DIS_SYSTEM_ID is not set -# default: -# CONFIG_BT_NIMBLE_SVC_DIS_PNP_ID is not set -# default: -# CONFIG_BT_NIMBLE_SVC_DIS_INCLUDED is not set -# default: -CONFIG_BT_NIMBLE_GAP_SERVICE=y -# default: -CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" -# default: -CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_ENC=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHEN=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHOR=0 -# default: -# CONFIG_BT_NIMBLE_SVC_GAP_GATT_SECURITY_LEVEL is not set -# default: -# CONFIG_BT_NIMBLE_SVC_GAP_RPA_ONLY is not set -# default: -CONFIG_BT_NIMBLE_SVC_GAP_CAR_CHAR_NOT_SUPP=y -# default: -# CONFIG_BT_NIMBLE_SVC_GAP_CAR_NOT_SUPP is not set -# default: -# CONFIG_BT_NIMBLE_SVC_GAP_CAR_SUPP is not set -# default: -CONFIG_BT_NIMBLE_SVC_GAP_CENT_ADDR_RESOLUTION=-1 - -# -# GAP Appearance write permissions -# -# default: -# CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE is not set -# default: -CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ENC=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHN=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHR=0 -# end of GAP Appearance write permissions - -# -# GAP device name write permissions -# -# default: -# CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE is not set -# end of GAP device name write permissions - -# -# Peripheral Preferred Connection Parameters (PPCP) settings -# -# default: -CONFIG_BT_NIMBLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_PPCP_SLAVE_LATENCY=0 -# default: -CONFIG_BT_NIMBLE_SVC_GAP_PPCP_SUPERVISION_TMO=0 -# end of Peripheral Preferred Connection Parameters (PPCP) settings -# end of Services - -# -# Extra Features -# -# default: -# CONFIG_BT_NIMBLE_DYNAMIC_SERVICE is not set -# CONFIG_BT_NIMBLE_BLUFI_ENABLE is not set -# default: -# CONFIG_BT_NIMBLE_ENC_ADV_DATA is not set -# default: -# CONFIG_BT_NIMBLE_ADV_UUID_CONCAT is not set -# default: -# CONFIG_BT_NIMBLE_GATTC_PROC_PREEMPTION_PROTECT is not set -# default: -# CONFIG_BT_NIMBLE_GATTC_AUTO_PAIR is not set -# default: -CONFIG_BT_NIMBLE_EATT_CHAN_NUM=0 -# default: -# CONFIG_BT_NIMBLE_SUBRATE is not set -# default: -CONFIG_BT_NIMBLE_DTM_MODE_TEST=y -# default: -CONFIG_BT_NIMBLE_MEM_OPTIMIZATION=y -# default: -CONFIG_BT_NIMBLE_STATIC_TO_DYNAMIC=y -# default: -CONFIG_BT_NIMBLE_SM_SIGN_CNT=y -# default: -CONFIG_BT_NIMBLE_CPFD_CAFD=y -# default: -CONFIG_BT_NIMBLE_RECONFIG_MTU=y -# default: -# CONFIG_BT_NIMBLE_LOW_SPEED_MODE is not set -# end of Extra Features - -# -# NimBLE Mesh -# -# CONFIG_BT_NIMBLE_MESH is not set -# end of NimBLE Mesh - -# -# Host-controller Transport -# -# default: -CONFIG_UART_HW_FLOWCTRL_DISABLE=y -# default: -# CONFIG_UART_HW_FLOWCTRL_CTS_RTS is not set -# default: -CONFIG_BT_NIMBLE_HCI_UART_FLOW_CTRL=0 -# default: -CONFIG_BT_NIMBLE_HCI_UART_RTS_PIN=19 -# default: -CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23 -# end of Host-controller Transport - -# -# Debugging/Testing -# -# default: -# CONFIG_BT_NIMBLE_MEM_DEBUG is not set -# default: -# CONFIG_BT_NIMBLE_LOG_LEVEL_NONE is not set -# default: -# CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR is not set -# default: -# CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING is not set -# default: -CONFIG_BT_NIMBLE_LOG_LEVEL_INFO=y -# default: -# CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG is not set -# default: -CONFIG_BT_NIMBLE_LOG_LEVEL=1 -# default: -CONFIG_BT_NIMBLE_PRINT_ERR_NAME=y -# default: -# CONFIG_BT_NIMBLE_DEBUG is not set -# CONFIG_BT_NIMBLE_TEST_THROUGHPUT_TEST is not set -# end of Debugging/Testing - -# -# Vendor / Optimization -# -# default: -# CONFIG_BT_NIMBLE_VS_SUPPORT is not set -# default: -# CONFIG_BT_NIMBLE_HIGH_DUTY_ADV_ITVL is not set -# end of Vendor / Optimization - -# -# Helper Utils -# -# default: -CONFIG_BT_NIMBLE_CHK_HOST_STATUS=y -# default: -CONFIG_BT_NIMBLE_UTIL_API=y -# default: -CONFIG_BT_NIMBLE_EXTRA_ADV_FIELDS=y -# end of Helper Utils -# end of NimBLE Options - -# -# Controller Options -# -# default: -CONFIG_BT_CTRL_MODE_EFF=1 -CONFIG_BT_CTRL_BLE_MAX_ACT=10 -# default: -CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=10 -CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0 -# default: -CONFIG_BT_CTRL_PINNED_TO_CORE=0 -# default: -CONFIG_BT_CTRL_HCI_MODE_VHCI=y -# default: -# CONFIG_BT_CTRL_HCI_MODE_UART_H4 is not set -# default: -CONFIG_BT_CTRL_HCI_TL=1 -# default: -CONFIG_BT_CTRL_ADV_DUP_FILT_MAX=30 -# default: -CONFIG_BT_BLE_CCA_MODE_NONE=y -# default: -# CONFIG_BT_BLE_CCA_MODE_HW is not set -# default: -# CONFIG_BT_BLE_CCA_MODE_SW is not set -# default: -CONFIG_BT_BLE_CCA_MODE=0 -# default: -CONFIG_BT_CTRL_HW_CCA_VAL=75 -# default: -CONFIG_BT_CTRL_HW_CCA_EFF=0 -# default: -CONFIG_BT_CTRL_CE_LENGTH_TYPE_ORIG=y -# default: -# CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE is not set -# default: -# CONFIG_BT_CTRL_CE_LENGTH_TYPE_SD is not set -# default: -CONFIG_BT_CTRL_CE_LENGTH_TYPE_EFF=0 -# default: -CONFIG_BT_CTRL_TX_ANTENNA_INDEX_0=y -# default: -# CONFIG_BT_CTRL_TX_ANTENNA_INDEX_1 is not set -# default: -CONFIG_BT_CTRL_TX_ANTENNA_INDEX_EFF=0 -# default: -CONFIG_BT_CTRL_RX_ANTENNA_INDEX_0=y -# default: -# CONFIG_BT_CTRL_RX_ANTENNA_INDEX_1 is not set -# default: -CONFIG_BT_CTRL_RX_ANTENNA_INDEX_EFF=0 -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N24 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N21 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N18 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N15 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N12 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N9 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N6 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N3 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N0 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P3 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P6 is not set -# default: -CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9=y -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P12 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P15 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P18 is not set -# default: -# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P20 is not set -# default: -CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=11 -# default: -CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y -# default: -CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100 -# default: -CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20 -# default: -CONFIG_BT_CTRL_BLE_SCAN_DUPL=y -# default: -CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE=y -# default: -# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA is not set -# default: -# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE is not set -# default: -CONFIG_BT_CTRL_SCAN_DUPL_TYPE=0 -# default: -CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE=100 -# default: -CONFIG_BT_CTRL_DUPL_SCAN_CACHE_REFRESH_PERIOD=0 -# default: -# CONFIG_BT_CTRL_BLE_MESH_SCAN_DUPL_EN is not set -# default: -# CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EN is not set -# default: -CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_DIS=y -# default: -CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EFF=0 - -# -# MODEM SLEEP Options -# -# default: -# CONFIG_BT_CTRL_MODEM_SLEEP is not set -# end of MODEM SLEEP Options - -# default: -CONFIG_BT_CTRL_SLEEP_MODE_EFF=0 -# default: -CONFIG_BT_CTRL_SLEEP_CLOCK_EFF=0 -# default: -CONFIG_BT_CTRL_HCI_TL_EFF=1 -# default: -# CONFIG_BT_CTRL_AGC_RECORRECT_EN is not set -# default: -# CONFIG_BT_CTRL_SCAN_BACKOFF_UPPERLIMITMAX is not set -# default: -# CONFIG_BT_BLE_ADV_DATA_LENGTH_ZERO_AUX is not set -# default: -CONFIG_BT_CTRL_CHAN_ASS_EN=y -# default: -CONFIG_BT_CTRL_LE_PING_EN=y - -# -# BLE disconnects when Instant Passed (0x28) occurs -# -# default: -# CONFIG_BT_CTRL_BLE_LLCP_CONN_UPDATE is not set -# default: -# CONFIG_BT_CTRL_BLE_LLCP_CHAN_MAP_UPDATE is not set -# default: -# CONFIG_BT_CTRL_BLE_LLCP_PHY_UPDATE is not set -# end of BLE disconnects when Instant Passed (0x28) occurs - -# default: -# CONFIG_BT_CTRL_RUN_IN_FLASH_ONLY is not set -# default: -CONFIG_BT_CTRL_DTM_ENABLE=y -# default: -CONFIG_BT_CTRL_BLE_MASTER=y -# default: -# CONFIG_BT_CTRL_BLE_TEST is not set -# default: -CONFIG_BT_CTRL_BLE_SCAN=y -# default: -CONFIG_BT_CTRL_BLE_SECURITY_ENABLE=y -# default: -CONFIG_BT_CTRL_BLE_ADV=y -# default: -# CONFIG_BT_CTRL_CHECK_CONNECT_IND_ACCESS_ADDRESS is not set - -# -# Controller debug log Options (Experimental) -# -# end of Controller debug log Options (Experimental) -# end of Controller Options - -# -# Common Options -# -# default: -CONFIG_BT_ALARM_MAX_NUM=50 - -# -# BLE Log -# -# default: -# CONFIG_BLE_LOG_ENABLED is not set -# end of BLE Log - -# default: -# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set -# default: -# CONFIG_BT_BLE_LOG_UHCI_OUT_ENABLED is not set -# default: -# CONFIG_BT_LE_USED_MEM_STATISTICS_ENABLED is not set -# end of Common Options - -# default: -# CONFIG_BT_HCI_LOG_DEBUG_EN is not set -# end of Bluetooth - -# default: -# CONFIG_BLE_MESH is not set - -# -# Console Library -# -# default: -# CONFIG_CONSOLE_SORTED_HELP is not set -# end of Console Library - -# -# Legacy Driver Configurations -# - -# -# Legacy TWAI Driver Configurations -# -# default: -# CONFIG_TWAI_ISR_IN_IRAM_LEGACY is not set -# default: -# CONFIG_TWAI_SUPPRESS_DEPRECATE_WARN is not set -# default: -# CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set -# end of Legacy TWAI Driver Configurations - -# -# Legacy I2C Driver Configurations -# -# default: -# CONFIG_I2C_SUPPRESS_DEPRECATE_WARN is not set -# default: -# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set -# end of Legacy I2C Driver Configurations -# end of Legacy Driver Configurations - -# -# eFuse Bit Manager -# -# default: -# CONFIG_EFUSE_CUSTOM_TABLE is not set -# default: -# CONFIG_EFUSE_VIRTUAL is not set -# default: -CONFIG_EFUSE_MAX_BLK_LEN=256 -# end of eFuse Bit Manager - -# -# ESP-TLS -# -# default: -CONFIG_ESP_TLS_USING_MBEDTLS=y -# default: -# CONFIG_ESP_TLS_CUSTOM_STACK is not set -# default: -# CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set -# default: -CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y -# default: -# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set -# default: -# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set -# default: -# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set -# default: -# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set -# default: -# CONFIG_ESP_TLS_PSK_VERIFICATION is not set -# default: -# CONFIG_ESP_TLS_INSECURE is not set -# default: -CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y -# end of ESP-TLS - -# -# ADC and ADC Calibration -# -# default: -# CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM is not set -# default: -# CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is not set -# default: -# CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3 is not set -# default: -# CONFIG_ADC_ONESHOT_FORCE_USE_ADC2_ON_C3 is not set -# default: -# CONFIG_ADC_ENABLE_DEBUG_LOG is not set -# end of ADC and ADC Calibration - -# -# Wireless Coexistence -# -# default: -CONFIG_ESP_COEX_ENABLED=y -# default: -CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y -# default: -# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set -# default: -# CONFIG_ESP_COEX_GPIO_DEBUG is not set -# end of Wireless Coexistence - -# -# Common ESP-related -# -# default: -CONFIG_ESP_ERR_TO_NAME_LOOKUP=y -# end of Common ESP-related - -# -# GDMA Configurations -# -# default: -CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y -# default: -CONFIG_GDMA_ISR_HANDLER_IN_IRAM=y -# default: -CONFIG_GDMA_OBJ_DRAM_SAFE=y -# default: -# CONFIG_GDMA_ENABLE_DEBUG_LOG is not set -# end of GDMA Configurations - -# -# ESP-Driver:GPIO Configurations -# -# default: -# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set -# end of ESP-Driver:GPIO Configurations - -# -# ESP-Driver:GPTimer Configurations -# -# default: -CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y -# default: -# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set -# default: -# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set -# default: -CONFIG_GPTIMER_OBJ_CACHE_SAFE=y -# default: -# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set -# end of ESP-Driver:GPTimer Configurations - -# -# ESP-Driver:I2C Configurations -# -# default: -# CONFIG_I2C_ISR_IRAM_SAFE is not set -# default: -# CONFIG_I2C_ENABLE_DEBUG_LOG is not set -# default: -CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y -# end of ESP-Driver:I2C Configurations - -# -# ESP-Driver:I2S Configurations -# -# default: -# CONFIG_I2S_ISR_IRAM_SAFE is not set -# default: -# CONFIG_I2S_CTRL_FUNC_IN_IRAM is not set -# default: -# CONFIG_I2S_ENABLE_DEBUG_LOG is not set -# end of ESP-Driver:I2S Configurations - -# -# ESP-Driver:I3C Master Configurations -# -# default: -# CONFIG_I3C_MASTER_ISR_CACHE_SAFE is not set -# default: -# CONFIG_I3C_MASTER_ENABLE_DEBUG_LOG is not set -# default: -# CONFIG_I3C_MASTER_ISR_HANDLER_IN_IRAM is not set -# end of ESP-Driver:I3C Master Configurations - -# -# ESP-Driver:LEDC Configurations -# -# default: -# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set -# end of ESP-Driver:LEDC Configurations - -# -# ESP-Driver:RMT Configurations -# -# default: -CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y -# default: -CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y -# default: -CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y -# default: -# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set -# default: -# CONFIG_RMT_TX_ISR_CACHE_SAFE is not set -# default: -# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set -# default: -CONFIG_RMT_OBJ_CACHE_SAFE=y -# default: -# CONFIG_RMT_ENABLE_DEBUG_LOG is not set -# default: -# CONFIG_RMT_ISR_IRAM_SAFE is not set -# end of ESP-Driver:RMT Configurations - -# -# ESP-Driver:Sigma Delta Modulator Configurations -# -# default: -# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set -# default: -# CONFIG_SDM_ENABLE_DEBUG_LOG is not set -# end of ESP-Driver:Sigma Delta Modulator Configurations - -# -# ESP-Driver:SPI Configurations -# -# default: -CONFIG_SPI_MASTER_ISR_IN_IRAM=y -# default: -# CONFIG_SPI_SLAVE_IN_IRAM is not set -# default: -CONFIG_SPI_SLAVE_ISR_IN_IRAM=y -# end of ESP-Driver:SPI Configurations - -# -# ESP-Driver:Temperature Sensor Configurations -# -# default: -# CONFIG_TEMP_SENSOR_ENABLE_DEBUG_LOG is not set -# end of ESP-Driver:Temperature Sensor Configurations - -# -# ESP-Driver:TWAI Configurations -# -# default: -# CONFIG_TWAI_ISR_IN_IRAM is not set -# default: -# CONFIG_TWAI_IO_FUNC_IN_IRAM is not set -# default: -# CONFIG_TWAI_ISR_CACHE_SAFE is not set -# default: -# CONFIG_TWAI_ENABLE_DEBUG_LOG is not set -# end of ESP-Driver:TWAI Configurations - -# -# ESP-Driver:UART Configurations -# -# default: -# CONFIG_UART_ISR_IN_IRAM is not set -# end of ESP-Driver:UART Configurations - -# -# ESP-Driver:UHCI Configurations -# -# default: -# CONFIG_UHCI_ISR_HANDLER_IN_IRAM is not set -# default: -# CONFIG_UHCI_ISR_CACHE_SAFE is not set -# default: -# CONFIG_UHCI_ENABLE_DEBUG_LOG is not set -# end of ESP-Driver:UHCI Configurations - -# -# ESP-Driver:USB Serial/JTAG Configuration -# -# default: -CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y -# end of ESP-Driver:USB Serial/JTAG Configuration - -# -# Ethernet -# -# default: -CONFIG_ETH_ENABLED=y -# default: -CONFIG_ETH_USE_SPI_ETHERNET=y -# default: -# CONFIG_ETH_USE_OPENETH is not set -# default: -# CONFIG_ETH_TRANSMIT_MUTEX is not set -# end of Ethernet - -# -# Event Loop Library -# -# default: -# CONFIG_ESP_EVENT_LOOP_PROFILING is not set -# default: -CONFIG_ESP_EVENT_POST_FROM_ISR=y -# default: -CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y -# end of Event Loop Library - -# -# GDB Stub -# -# default: -CONFIG_ESP_GDBSTUB_ENABLED=y -# default: -# CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME is not set -# default: -CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y -# default: -CONFIG_ESP_GDBSTUB_MAX_TASKS=32 -# end of GDB Stub - -# -# ESP HID -# -# default: -CONFIG_ESPHID_TASK_SIZE_BT=2048 -# default: -CONFIG_ESPHID_TASK_SIZE_BLE=4096 -# end of ESP HID - -# -# ESP HTTP client -# -# default: -CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y -# default: -# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set -# default: -# CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH is not set -# default: -# CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT is not set -# default: -# CONFIG_ESP_HTTP_CLIENT_ENABLE_GET_CONTENT_RANGE is not set -# default: -CONFIG_ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT=2000 -# end of ESP HTTP client - -# -# HTTP Server -# -# default: -CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 -# default: -CONFIG_HTTPD_MAX_URI_LEN=512 -# default: -CONFIG_HTTPD_ERR_RESP_NO_DELAY=y -# default: -CONFIG_HTTPD_PURGE_BUF_LEN=32 -# default: -# CONFIG_HTTPD_LOG_PURGE_DATA is not set -# default: -# CONFIG_HTTPD_WS_SUPPORT is not set -# default: -# CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set -# default: -CONFIG_HTTPD_SERVER_EVENT_POST_TIMEOUT=2000 -# end of HTTP Server - -# -# ESP HTTPS OTA -# -# default: -# CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set -# default: -# CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set -# default: -CONFIG_ESP_HTTPS_OTA_EVENT_POST_TIMEOUT=2000 -# default: -# CONFIG_ESP_HTTPS_OTA_ENABLE_PARTIAL_DOWNLOAD is not set -# end of ESP HTTPS OTA - -# -# ESP HTTPS server -# -# default: -# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set -# default: -CONFIG_ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT=2000 -# default: -# CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK is not set -# end of ESP HTTPS server - -# -# Hardware Settings -# -# default: -CONFIG_ESP_HW_SUPPORT_FUNC_IN_IRAM=y - -# -# Chip revision -# -# default: -# CONFIG_ESP32C3_REV_MIN_0 is not set -# default: -# CONFIG_ESP32C3_REV_MIN_1 is not set -# default: -# CONFIG_ESP32C3_REV_MIN_2 is not set -# default: -CONFIG_ESP32C3_REV_MIN_3=y -# default: -# CONFIG_ESP32C3_REV_MIN_4 is not set -# default: -# CONFIG_ESP32C3_REV_MIN_101 is not set -# default: -CONFIG_ESP32C3_REV_MIN_FULL=3 -# default: -CONFIG_ESP_REV_MIN_FULL=3 - -# -# Maximum Supported ESP32-C3 Revision (Rev v1.99) -# -# default: -CONFIG_ESP32C3_REV_MAX_FULL=199 -# default: -CONFIG_ESP_REV_MAX_FULL=199 -# default: -CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0 -# default: -CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=199 - -# -# Maximum Supported ESP32-C3 eFuse Block Revision (eFuse Block Rev v1.99) -# -# end of Chip revision - -# -# MAC Config -# -# default: -CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_STA=y -# default: -CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP=y -# default: -CONFIG_ESP_MAC_ADDR_UNIVERSE_BT=y -# default: -CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH=y -# default: -CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES_FOUR=y -# default: -CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES=4 -# default: -# CONFIG_ESP32C3_UNIVERSAL_MAC_ADDRESSES_TWO is not set -# default: -CONFIG_ESP32C3_UNIVERSAL_MAC_ADDRESSES_FOUR=y -# default: -CONFIG_ESP32C3_UNIVERSAL_MAC_ADDRESSES=4 -# default: -# CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC is not set -# end of MAC Config - -# -# Sleep Config -# -# default: -# CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set -# default: -CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y -# default: -# CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU is not set -# default: -CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND=y -# default: -CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=0 -# default: -# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set -# default: -# CONFIG_ESP_SLEEP_DEBUG is not set -# default: -CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y -# end of Sleep Config - -# -# RTC Clock Config -# -# default: -CONFIG_RTC_CLK_SRC_INT_RC=y -# default: -# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set -# default: -# CONFIG_RTC_CLK_SRC_EXT_OSC is not set -# default: -# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set -# default: -CONFIG_RTC_CLK_CAL_CYCLES=1024 -# default: -CONFIG_RTC_CLK_FUNC_IN_IRAM=y -# default: -CONFIG_RTC_TIME_FUNC_IN_IRAM=y -# end of RTC Clock Config - -# -# Peripheral Control -# -# default: -CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y -# default: -CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y -# end of Peripheral Control - -# -# Main XTAL Config -# -# default: -CONFIG_XTAL_FREQ_40=y -# default: -CONFIG_XTAL_FREQ=40 -# end of Main XTAL Config - -# -# Power Supplier -# - -# -# Brownout Detector -# -# default: -CONFIG_ESP_BROWNOUT_DET=y -# default: -CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y -# default: -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set -# default: -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set -# default: -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set -# default: -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set -# default: -# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set -# default: -CONFIG_ESP_BROWNOUT_DET_LVL=7 -# default: -CONFIG_ESP_BROWNOUT_USE_INTR=y -# end of Brownout Detector -# end of Power Supplier - -# default: -CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y -# default: -CONFIG_ESP_INTR_IN_IRAM=y -# end of Hardware Settings - -# -# ESP-Driver:LCD Controller Configurations -# -# default: -# CONFIG_LCD_ENABLE_DEBUG_LOG is not set -# end of ESP-Driver:LCD Controller Configurations - -# -# LibC -# -# default: -# CONFIG_LIBC_NEWLIB is not set -# default: -CONFIG_LIBC_PICOLIBC=y -# default: -CONFIG_LIBC_PICOLIBC_NEWLIB_COMPATIBILITY=y -# default: -CONFIG_LIBC_MISC_IN_IRAM=y -# default: -CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y -# default: -CONFIG_LIBC_STDOUT_LINE_ENDING_CRLF=y -# default: -# CONFIG_LIBC_STDOUT_LINE_ENDING_LF is not set -# default: -# CONFIG_LIBC_STDOUT_LINE_ENDING_CR is not set -# default: -# CONFIG_LIBC_STDIN_LINE_ENDING_CRLF is not set -# default: -# CONFIG_LIBC_STDIN_LINE_ENDING_LF is not set -# default: -CONFIG_LIBC_STDIN_LINE_ENDING_CR=y -# default: -CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y -# default: -# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set -# default: -# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set -# default: -# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set -# default: -CONFIG_LIBC_OPTIMIZED_MISALIGNED_ACCESS=y -# default: -CONFIG_LIBC_ASSERT_BUFFER_SIZE=200 -# end of LibC - -# -# ESP-MM: Memory Management Configurations -# -# end of ESP-MM: Memory Management Configurations - -# -# ESP NETIF Adapter -# -# default: -CONFIG_ESP_NETIF_LOST_IP_TIMER_ENABLE=y -# default: -CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 -# default: -# CONFIG_ESP_NETIF_PROVIDE_CUSTOM_IMPLEMENTATION is not set -# default: -CONFIG_ESP_NETIF_TCPIP_LWIP=y -# default: -# CONFIG_ESP_NETIF_LOOPBACK is not set -# default: -CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y -# default: -CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC=y -# default: -CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS=y -# default: -# CONFIG_ESP_NETIF_L2_TAP is not set -# default: -# CONFIG_ESP_NETIF_BRIDGE_EN is not set -# default: -# CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF is not set -# end of ESP NETIF Adapter - -# -# Partition API Configuration -# -# end of Partition API Configuration - -# -# PHY -# -# default: -CONFIG_ESP_PHY_ENABLED=y -# default: -CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y -# default: -# CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION is not set -# default: -CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=20 -# default: -CONFIG_ESP_PHY_MAX_TX_POWER=20 -# default: -# CONFIG_ESP_PHY_REDUCE_TX_POWER is not set -# default: -CONFIG_ESP_PHY_ENABLE_USB=y -# default: -# CONFIG_ESP_PHY_ENABLE_CERT_TEST is not set -# default: -CONFIG_ESP_PHY_RF_CAL_PARTIAL=y -# default: -# CONFIG_ESP_PHY_RF_CAL_NONE is not set -# default: -# CONFIG_ESP_PHY_RF_CAL_FULL is not set -# default: -CONFIG_ESP_PHY_CALIBRATION_MODE=0 -# default: -CONFIG_ESP_PHY_PLL_TRACK_PERIOD_MS=1000 -# default: -# CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set -# default: -# CONFIG_ESP_PHY_RECORD_USED_TIME is not set -# default: -CONFIG_ESP_PHY_IRAM_OPT=y -# default: -# CONFIG_ESP_PHY_DEBUG is not set -# end of PHY - -# -# Power Management -# -# default: -# CONFIG_PM_SLEEP_FUNC_IN_IRAM is not set -# default: -# CONFIG_PM_ENABLE is not set -# default: -# CONFIG_PM_SLP_IRAM_OPT is not set -# default: -CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y -# default: -CONFIG_PM_ESP_SLEEP_POWER_DOWN_CPU=y -# end of Power Management - -# -# ESP PSRAM -# - -# -# ESP Ringbuf -# -# default: -# CONFIG_RINGBUF_IN_IRAM is not set -# default: -# CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH is not set -# end of ESP Ringbuf - -# -# ESP-ROM -# -# default: -CONFIG_ESP_ROM_PRINT_IN_IRAM=y -# end of ESP-ROM - -# -# ESP Security Specific -# -# end of ESP Security Specific - -# -# ESP-STDIO -# -# default: -CONFIG_ESP_CONSOLE_UART_DEFAULT=y -# default: -# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG is not set -# default: -# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set -# default: -# CONFIG_ESP_CONSOLE_NONE is not set -# default: -# CONFIG_ESP_CONSOLE_SECONDARY_NONE is not set -# default: -CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y -# default: -CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y -# default: -CONFIG_ESP_CONSOLE_UART=y -# default: -CONFIG_ESP_CONSOLE_UART_NUM=0 -# default: -CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0 -# default: -CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 -# end of ESP-STDIO - -# -# ESP System Settings -# -# default: -# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set -# default: -CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y -# default: -CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 -# default: -CONFIG_ESP_SYSTEM_IN_IRAM=y -# default: -# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set -# default: -CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y -# default: -# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set -# default: -# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set -# default: -CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0 -# default: -CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE=y -# default: -CONFIG_ESP_SYSTEM_RTC_FAST_MEM_AS_HEAP_DEPCHECK=y -# default: -CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y -# default: -CONFIG_ESP_SYSTEM_NO_BACKTRACE=y -# default: -# CONFIG_ESP_SYSTEM_USE_EH_FRAME is not set -# default: -# CONFIG_ESP_SYSTEM_USE_FRAME_POINTER is not set -# default: -CONFIG_ESP_SYSTEM_MEMPROT=y -# default: -CONFIG_ESP_SYSTEM_MEMPROT_PMS=y -# default: -CONFIG_ESP_SYSTEM_MEMPROT_PMS_LOCK=y -# default: -CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 -# default: -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 -# default: -CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 -# default: -CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y -# default: -# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set -# default: -CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 -# default: -CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 -# default: -CONFIG_ESP_INT_WDT=y -# default: -CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 -# default: -CONFIG_ESP_TASK_WDT_EN=y -# default: -CONFIG_ESP_TASK_WDT_INIT=y -# default: -# CONFIG_ESP_TASK_WDT_PANIC is not set -# default: -CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 -# default: -CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y -# default: -# CONFIG_ESP_PANIC_HANDLER_IRAM is not set -# default: -# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set -# default: -CONFIG_ESP_DEBUG_OCDAWARE=y -# default: -CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y -# default: -CONFIG_ESP_SYSTEM_HW_STACK_GUARD=y -# default: -CONFIG_ESP_SYSTEM_HW_PC_RECORD=y -# end of ESP System Settings - -# -# IPC (Inter-Processor Call) -# -# default: -CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 -# end of IPC (Inter-Processor Call) - -# -# ESP Timer (High Resolution Timer) -# -# default: -CONFIG_ESP_TIMER_IN_IRAM=y -# default: -# CONFIG_ESP_TIMER_PROFILING is not set -# default: -CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y -# default: -CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y -# default: -CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 -# default: -CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 -# default: -# CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set -# default: -CONFIG_ESP_TIMER_TASK_AFFINITY=0x0 -# default: -CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y -# default: -CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y -# default: -# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set -# default: -CONFIG_ESP_TIMER_IMPL_SYSTIMER=y -# end of ESP Timer (High Resolution Timer) - -# -# ESP Trace Configuration -# -# default: -# CONFIG_ESP_TRACE_LIB_EXTERNAL is not set -# default: -CONFIG_ESP_TRACE_LIB_NONE=y -# default: -CONFIG_ESP_TRACE_LIB_NAME="none" -# default: -# CONFIG_ESP_TRACE_TRANSPORT_APPTRACE is not set -# default: -CONFIG_ESP_TRACE_TRANSPORT_NONE=y -# default: -CONFIG_ESP_TRACE_TRANSPORT_NAME="none" -# end of ESP Trace Configuration - -# -# Wi-Fi -# -# default: -CONFIG_ESP_WIFI_ENABLED=y -# default: -CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10 -# default: -CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 -# default: -# CONFIG_ESP_WIFI_STATIC_TX_BUFFER is not set -# default: -CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y -# default: -CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1 -# default: -CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32 -# default: -CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y -# default: -# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set -# default: -CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0 -# default: -CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5 -# default: -# CONFIG_ESP_WIFI_CSI_ENABLED is not set -# default: -CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y -# default: -CONFIG_ESP_WIFI_TX_BA_WIN=6 -# default: -CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y -# default: -CONFIG_ESP_WIFI_RX_BA_WIN=6 -# default: -CONFIG_ESP_WIFI_NVS_ENABLED=y -# default: -CONFIG_ESP_WIFI_SOFTAP_BEACON_MAX_LEN=752 -# default: -CONFIG_ESP_WIFI_MGMT_SBUF_NUM=32 -# default: -CONFIG_ESP_WIFI_IRAM_OPT=y -# default: -# CONFIG_ESP_WIFI_EXTRA_IRAM_OPT is not set -# default: -CONFIG_ESP_WIFI_RX_IRAM_OPT=y -# default: -CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y -# default: -CONFIG_ESP_WIFI_ENABLE_SAE_H2E=y -# default: -CONFIG_ESP_WIFI_ENABLE_SAE_PK=y -# default: -CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=y -# default: -CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=y -# default: -CONFIG_ESP_WIFI_WPA3_COMPATIBLE_SUPPORT=y -# default: -# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set -# default: -CONFIG_ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME=50 -# default: -# CONFIG_ESP_WIFI_BSS_MAX_IDLE_SUPPORT is not set -# default: -CONFIG_ESP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME=10 -# default: -CONFIG_ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME=15 -# default: -# CONFIG_ESP_WIFI_FTM_ENABLE is not set -# default: -CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=y -# default: -# CONFIG_ESP_WIFI_GCMP_SUPPORT is not set -# default: -CONFIG_ESP_WIFI_GMAC_SUPPORT=y -# default: -CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y -# default: -# CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT is not set -# default: -CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7 -# default: -CONFIG_ESP_WIFI_MBEDTLS_CRYPTO=y -# default: -CONFIG_ESP_WIFI_MBEDTLS_TLS_CLIENT=y -# default: -# CONFIG_ESP_WIFI_WAPI_PSK is not set -# default: -# CONFIG_ESP_WIFI_SUITE_B_192 is not set -# default: -# CONFIG_ESP_WIFI_11KV_SUPPORT is not set -# default: -# CONFIG_ESP_WIFI_MBO_SUPPORT is not set -# default: -# CONFIG_ESP_WIFI_DPP_SUPPORT is not set -# default: -# CONFIG_ESP_WIFI_11R_SUPPORT is not set -# default: -# CONFIG_ESP_WIFI_WPS_SOFTAP_REGISTRAR is not set - -# -# WPS Configuration Options -# -# default: -# CONFIG_ESP_WIFI_WPS_STRICT is not set -# default: -# CONFIG_ESP_WIFI_WPS_PASSPHRASE is not set -# default: -# CONFIG_ESP_WIFI_WPS_RECONNECT_ON_FAIL is not set -# end of WPS Configuration Options - -# default: -# CONFIG_ESP_WIFI_DEBUG_PRINT is not set -# default: -CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=y -# default: -# CONFIG_ESP_WIFI_ENT_FREE_DYNAMIC_BUFFER is not set -# end of Wi-Fi - -# -# Core dump -# -# default: -# CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH is not set -# default: -# CONFIG_ESP_COREDUMP_ENABLE_TO_UART is not set -# default: -CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y -# end of Core dump - -# -# FAT Filesystem support -# -# default: -CONFIG_FATFS_VOLUME_COUNT=2 -# default: -# CONFIG_FATFS_LFN_NONE is not set -# default: -CONFIG_FATFS_LFN_HEAP=y -# default: -# CONFIG_FATFS_LFN_STACK is not set -# default: -# CONFIG_FATFS_SECTOR_512 is not set -# default: -CONFIG_FATFS_SECTOR_4096=y -# default: -# CONFIG_FATFS_CODEPAGE_DYNAMIC is not set -# default: -CONFIG_FATFS_CODEPAGE_437=y -# default: -# CONFIG_FATFS_CODEPAGE_720 is not set -# default: -# CONFIG_FATFS_CODEPAGE_737 is not set -# default: -# CONFIG_FATFS_CODEPAGE_771 is not set -# default: -# CONFIG_FATFS_CODEPAGE_775 is not set -# default: -# CONFIG_FATFS_CODEPAGE_850 is not set -# default: -# CONFIG_FATFS_CODEPAGE_852 is not set -# default: -# CONFIG_FATFS_CODEPAGE_855 is not set -# default: -# CONFIG_FATFS_CODEPAGE_857 is not set -# default: -# CONFIG_FATFS_CODEPAGE_860 is not set -# default: -# CONFIG_FATFS_CODEPAGE_861 is not set -# default: -# CONFIG_FATFS_CODEPAGE_862 is not set -# default: -# CONFIG_FATFS_CODEPAGE_863 is not set -# default: -# CONFIG_FATFS_CODEPAGE_864 is not set -# default: -# CONFIG_FATFS_CODEPAGE_865 is not set -# default: -# CONFIG_FATFS_CODEPAGE_866 is not set -# default: -# CONFIG_FATFS_CODEPAGE_869 is not set -# default: -# CONFIG_FATFS_CODEPAGE_932 is not set -# default: -# CONFIG_FATFS_CODEPAGE_936 is not set -# default: -# CONFIG_FATFS_CODEPAGE_949 is not set -# default: -# CONFIG_FATFS_CODEPAGE_950 is not set -# default: -CONFIG_FATFS_CODEPAGE=437 -# default: -CONFIG_FATFS_MAX_LFN=255 -# default: -CONFIG_FATFS_API_ENCODING_ANSI_OEM=y -# default: -# CONFIG_FATFS_API_ENCODING_UTF_8 is not set -# default: -CONFIG_FATFS_FS_LOCK=0 -# default: -CONFIG_FATFS_TIMEOUT_MS=10000 -# default: -CONFIG_FATFS_PER_FILE_CACHE=y -# default: -# CONFIG_FATFS_USE_FASTSEEK is not set -# default: -CONFIG_FATFS_USE_STRFUNC_NONE=y -# default: -# CONFIG_FATFS_USE_STRFUNC_WITHOUT_CRLF_CONV is not set -# default: -# CONFIG_FATFS_USE_STRFUNC_WITH_CRLF_CONV is not set -# default: -CONFIG_FATFS_VFS_FSTAT_BLKSIZE=0 -# default: -# CONFIG_FATFS_IMMEDIATE_FSYNC is not set -# default: -# CONFIG_FATFS_USE_LABEL is not set -# default: -CONFIG_FATFS_LINK_LOCK=y -# default: -CONFIG_FATFS_USE_DYN_BUFFERS=y - -# -# File system free space calculation behavior -# -# default: -CONFIG_FATFS_DONT_TRUST_FREE_CLUSTER_CNT=0 -# default: -CONFIG_FATFS_DONT_TRUST_LAST_ALLOC=0 -# end of File system free space calculation behavior -# end of FAT Filesystem support - -# -# FreeRTOS -# - -# -# Kernel -# -# default: -# CONFIG_FREERTOS_SMP is not set -# default: -CONFIG_FREERTOS_UNICORE=y -# default: -CONFIG_FREERTOS_HZ=100 -# default: -CONFIG_FREERTOS_OPTIMIZED_SCHEDULER=y -# default: -# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set -# default: -# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set -# default: -CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y -# default: -CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 -# default: -CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 -# default: -# CONFIG_FREERTOS_USE_IDLE_HOOK is not set -# default: -# CONFIG_FREERTOS_USE_TICK_HOOK is not set -# default: -CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 -# default: -# CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set -# default: -CONFIG_FREERTOS_USE_TIMERS=y -# default: -CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" -# default: -# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set -# default: -CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y -# default: -CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF -# default: -CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 -# default: -CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 -# default: -CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 -# default: -CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 -# default: -CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 -# default: -# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set -# default: -# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set -# default: -# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set -# default: -# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set -# end of Kernel - -# -# Port -# -# default: -CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y -# default: -# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set -# default: -CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y -# default: -# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set -# default: -CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y -# default: -CONFIG_FREERTOS_ISR_STACKSIZE=1536 -# default: -CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y -# default: -CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y -# default: -CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y -# default: -# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set -# default: -CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y -# default: -# CONFIG_FREERTOS_IN_IRAM is not set -# default: -# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set -# end of Port - -# -# Extra -# -# end of Extra - -# default: -CONFIG_FREERTOS_PORT=y -# default: -CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF -# default: -CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y -# default: -CONFIG_FREERTOS_DEBUG_OCDAWARE=y -# default: -CONFIG_FREERTOS_NUMBER_OF_CORES=1 -# end of FreeRTOS - -# -# Hardware Abstraction Layer (HAL) and Low Level (LL) -# -# default: -CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y -# default: -# CONFIG_HAL_ASSERTION_DISABLE is not set -# default: -# CONFIG_HAL_ASSERTION_SILENT is not set -# default: -# CONFIG_HAL_ASSERTION_ENABLE is not set -# default: -CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 -# default: -CONFIG_HAL_GPIO_USE_ROM_IMPL=y -# end of Hardware Abstraction Layer (HAL) and Low Level (LL) - -# -# Heap memory debugging -# -# default: -CONFIG_HEAP_POISONING_DISABLED=y -# default: -# CONFIG_HEAP_POISONING_LIGHT is not set -# default: -# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set -# default: -CONFIG_HEAP_TRACING_OFF=y -# default: -# CONFIG_HEAP_TRACING_STANDALONE is not set -# default: -# CONFIG_HEAP_TRACING_TOHOST is not set -# default: -# CONFIG_HEAP_USE_HOOKS is not set -# default: -# CONFIG_HEAP_TASK_TRACKING is not set -# default: -# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set -# default: -# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set -# end of Heap memory debugging - -# -# Log -# -# default: -CONFIG_LOG_VERSION_1=y -# default: -# CONFIG_LOG_VERSION_2 is not set -# default: -CONFIG_LOG_VERSION=1 - -# -# Log Level -# -# default: -# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set -# default: -# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set -# default: -# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set -# default: -CONFIG_LOG_DEFAULT_LEVEL_INFO=y -# default: -# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set -# default: -# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set -# default: -CONFIG_LOG_DEFAULT_LEVEL=3 -# default: -CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y -# default: -# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set -# default: -# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set -# default: -CONFIG_LOG_MAXIMUM_LEVEL=3 - -# -# Level Settings -# -# default: -# CONFIG_LOG_MASTER_LEVEL is not set -# default: -CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y -# default: -# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set -# default: -# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set -# default: -CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y -# default: -# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set -# default: -CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y -# default: -CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31 -# end of Level Settings -# end of Log Level - -# -# Format -# -# default: -# CONFIG_LOG_COLORS is not set -# default: -CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y -# default: -# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set -# end of Format - -# -# Settings -# -# default: -CONFIG_LOG_MODE_TEXT_EN=y -# default: -CONFIG_LOG_MODE_TEXT=y -# end of Settings - -# default: -CONFIG_LOG_IN_IRAM=y -# end of Log - -# -# LWIP -# -# default: -CONFIG_LWIP_ENABLE=y -# default: -CONFIG_LWIP_LOCAL_HOSTNAME="espressif" -# default: -CONFIG_LWIP_TCPIP_TASK_PRIO=18 -# default: -# CONFIG_LWIP_TCPIP_CORE_LOCKING is not set -# default: -# CONFIG_LWIP_CHECK_THREAD_SAFETY is not set -# default: -CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y -# default: -# CONFIG_LWIP_L2_TO_L3_COPY is not set -# default: -# CONFIG_LWIP_IRAM_OPTIMIZATION is not set -# default: -# CONFIG_LWIP_EXTRA_IRAM_OPTIMIZATION is not set -# default: -CONFIG_LWIP_TIMERS_ONDEMAND=y -# default: -CONFIG_LWIP_ND6=y -# default: -# CONFIG_LWIP_FORCE_ROUTER_FORWARDING is not set -# default: -CONFIG_LWIP_MAX_SOCKETS=10 -# default: -# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set -# default: -# CONFIG_LWIP_SO_LINGER is not set -# default: -CONFIG_LWIP_SO_REUSE=y -# default: -CONFIG_LWIP_SO_REUSE_RXTOALL=y -# default: -# CONFIG_LWIP_SO_RCVBUF is not set -# default: -# CONFIG_LWIP_NETBUF_RECVINFO is not set -# default: -CONFIG_LWIP_IP_DEFAULT_TTL=64 -# default: -CONFIG_LWIP_IP4_FRAG=y -# default: -CONFIG_LWIP_IP6_FRAG=y -# default: -# CONFIG_LWIP_IP4_REASSEMBLY is not set -# default: -# CONFIG_LWIP_IP6_REASSEMBLY is not set -# default: -CONFIG_LWIP_IP_REASS_MAX_PBUFS=10 -# default: -CONFIG_LWIP_IPV6_DUP_DETECT_ATTEMPTS=1 -# default: -# CONFIG_LWIP_IP_FORWARD is not set -# default: -# CONFIG_LWIP_STATS is not set -# default: -CONFIG_LWIP_ESP_GRATUITOUS_ARP=y -# default: -CONFIG_LWIP_GARP_TMR_INTERVAL=60 -# default: -CONFIG_LWIP_ESP_MLDV6_REPORT=y -# default: -CONFIG_LWIP_MLDV6_TMR_INTERVAL=40 -# default: -CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 -# default: -CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y -# default: -# CONFIG_LWIP_DHCP_DOES_ACD_CHECK is not set -# default: -# CONFIG_LWIP_DHCP_DOES_NOT_CHECK_OFFERED_IP is not set -# default: -# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set -# default: -CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y -# default: -# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set -# default: -CONFIG_LWIP_DHCP_OPTIONS_LEN=69 -# default: -CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0 -# default: -CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1 - -# -# DHCP server -# -# default: -CONFIG_LWIP_DHCPS=y -# default: -CONFIG_LWIP_DHCPS_REPORT_CLIENT_HOSTNAME=y -# default: -CONFIG_LWIP_DHCPS_LEASE_UNIT=60 -# default: -CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 -# default: -CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN=64 -# default: -CONFIG_LWIP_DHCPS_STATIC_ENTRIES=y -# end of DHCP server - -# default: -# CONFIG_LWIP_AUTOIP is not set -# default: -CONFIG_LWIP_IPV4=y -# default: -CONFIG_LWIP_IPV6=y -# default: -# CONFIG_LWIP_IPV6_AUTOCONFIG is not set -# default: -CONFIG_LWIP_IPV6_NUM_ADDRESSES=3 -# default: -# CONFIG_LWIP_IPV6_FORWARD is not set -# default: -# CONFIG_LWIP_NETIF_STATUS_CALLBACK is not set -# default: -# CONFIG_LWIP_NETIF_LINK_CALLBACK is not set -# default: -CONFIG_LWIP_NETIF_LOOPBACK=y -# default: -CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 - -# -# TCP -# -# default: -CONFIG_LWIP_MAX_ACTIVE_TCP=16 -# default: -CONFIG_LWIP_MAX_LISTENING_TCP=16 -# default: -CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=y -# default: -CONFIG_LWIP_TCP_MAXRTX=12 -# default: -CONFIG_LWIP_TCP_SYNMAXRTX=12 -# default: -CONFIG_LWIP_TCP_MSS=1440 -# default: -CONFIG_LWIP_TCP_TMR_INTERVAL=250 -# default: -CONFIG_LWIP_TCP_MSL=60000 -# default: -CONFIG_LWIP_TCP_FIN_WAIT_TIMEOUT=20000 -# default: -CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5760 -# default: -CONFIG_LWIP_TCP_WND_DEFAULT=5760 -# default: -CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 -# default: -CONFIG_LWIP_TCP_ACCEPTMBOX_SIZE=6 -# default: -CONFIG_LWIP_TCP_QUEUE_OOSEQ=y -# default: -CONFIG_LWIP_TCP_OOSEQ_TIMEOUT=6 -# default: -CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4 -# default: -# CONFIG_LWIP_TCP_SACK_OUT is not set -# default: -CONFIG_LWIP_TCP_OVERSIZE_MSS=y -# default: -# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set -# default: -# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set -# default: -CONFIG_LWIP_TCP_RTO_TIME=1500 -# end of TCP - -# -# UDP -# -# default: -CONFIG_LWIP_MAX_UDP_PCBS=16 -# default: -CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 -# end of UDP - -# -# Checksums -# -# default: -# CONFIG_LWIP_CHECKSUM_CHECK_IP is not set -# default: -# CONFIG_LWIP_CHECKSUM_CHECK_UDP is not set -# default: -CONFIG_LWIP_CHECKSUM_CHECK_ICMP=y -# end of Checksums - -# default: -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072 -# default: -CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y -# default: -# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set -# default: -CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF -# default: -CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3 -# default: -CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5 -# default: -CONFIG_LWIP_IPV6_ND6_NUM_PREFIXES=5 -# default: -CONFIG_LWIP_IPV6_ND6_NUM_ROUTERS=3 -# default: -CONFIG_LWIP_IPV6_ND6_NUM_DESTINATIONS=10 -# default: -# CONFIG_LWIP_IPV6_ND6_ROUTE_INFO_OPTION_SUPPORT is not set -# default: -# CONFIG_LWIP_PPP_SUPPORT is not set -# default: -# CONFIG_LWIP_SLIP_SUPPORT is not set - -# -# ICMP -# -# default: -CONFIG_LWIP_ICMP=y -# default: -# CONFIG_LWIP_MULTICAST_PING is not set -# default: -# CONFIG_LWIP_BROADCAST_PING is not set -# end of ICMP - -# -# LWIP RAW API -# -# default: -CONFIG_LWIP_MAX_RAW_PCBS=16 -# end of LWIP RAW API - -# -# SNTP -# -# default: -CONFIG_LWIP_SNTP_MAX_SERVERS=1 -# default: -# CONFIG_LWIP_DHCP_GET_NTP_SRV is not set -# default: -CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 -# default: -CONFIG_LWIP_SNTP_STARTUP_DELAY=y -# default: -CONFIG_LWIP_SNTP_MAXIMUM_STARTUP_DELAY=5000 -# end of SNTP - -# -# DNS -# -# default: -CONFIG_LWIP_DNS_MAX_HOST_IP=1 -# default: -CONFIG_LWIP_DNS_MAX_SERVERS=3 -# default: -# CONFIG_LWIP_FALLBACK_DNS_SERVER_SUPPORT is not set -# default: -# CONFIG_LWIP_DNS_SETSERVER_WITH_NETIF is not set -# default: -# CONFIG_LWIP_USE_ESP_GETADDRINFO is not set -# end of DNS - -# default: -CONFIG_LWIP_BRIDGEIF_MAX_PORTS=7 -# default: -CONFIG_LWIP_ESP_LWIP_ASSERT=y - -# -# Hooks -# -# default: -# CONFIG_LWIP_HOOK_TCP_ISN_NONE is not set -# default: -CONFIG_LWIP_HOOK_TCP_ISN_DEFAULT=y -# default: -# CONFIG_LWIP_HOOK_TCP_ISN_CUSTOM is not set -# default: -CONFIG_LWIP_HOOK_IP6_ROUTE_NONE=y -# default: -# CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT is not set -# default: -# CONFIG_LWIP_HOOK_IP6_ROUTE_CUSTOM is not set -# default: -CONFIG_LWIP_HOOK_ND6_GET_GW_NONE=y -# default: -# CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT is not set -# default: -# CONFIG_LWIP_HOOK_ND6_GET_GW_CUSTOM is not set -# default: -CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y -# default: -# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT is not set -# default: -# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM is not set -# default: -CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_NONE=y -# default: -# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_DEFAULT is not set -# default: -# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_CUSTOM is not set -# default: -CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y -# default: -# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set -# default: -# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set -# default: -CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_NONE=y -# default: -# CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_CUSTOM is not set -# default: -# CONFIG_LWIP_HOOK_IP6_INPUT_NONE is not set -# default: -CONFIG_LWIP_HOOK_IP6_INPUT_DEFAULT=y -# default: -# CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM is not set -# end of Hooks - -# default: -# CONFIG_LWIP_DEBUG is not set -# end of LWIP - -# -# mbedTLS -# - -# -# Core Configuration -# -# default: -CONFIG_MBEDTLS_VER_4_X_SUPPORT=y -# default: -# CONFIG_MBEDTLS_COMPILER_OPTIMIZATION_NONE is not set -# default: -CONFIG_MBEDTLS_COMPILER_OPTIMIZATION_SIZE=y -# default: -# CONFIG_MBEDTLS_COMPILER_OPTIMIZATION_PERF is not set -# default: -CONFIG_MBEDTLS_FS_IO=y -# default: -CONFIG_MBEDTLS_THREADING_C=y -# default: -# CONFIG_MBEDTLS_THREADING_ALT is not set -# default: -CONFIG_MBEDTLS_THREADING_PTHREAD=y -# default: -CONFIG_MBEDTLS_ERROR_STRINGS=y -# default: -CONFIG_MBEDTLS_VERSION_C=y -# default: -CONFIG_MBEDTLS_HAVE_TIME=y -# default: -# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set -# default: -# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set -# default: -CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y -# default: -# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set -# default: -# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set -# default: -CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y -# default: -CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 -# default: -CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 -# default: -# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set -# default: -# CONFIG_MBEDTLS_VERSION_FEATURES is not set -# default: -# CONFIG_MBEDTLS_DEBUG is not set -# default: -CONFIG_MBEDTLS_SELF_TEST=y -# end of Core Configuration - -# -# Certificates -# -# default: -CONFIG_MBEDTLS_X509_USE_C=y -# default: -CONFIG_MBEDTLS_PEM_PARSE_C=y -# default: -CONFIG_MBEDTLS_PEM_WRITE_C=y -# default: -CONFIG_MBEDTLS_PK_C=y -# default: -CONFIG_MBEDTLS_PK_PARSE_C=y -# default: -CONFIG_MBEDTLS_PK_WRITE_C=y -# default: -# CONFIG_MBEDTLS_X509_REMOVE_INFO is not set -# default: -CONFIG_MBEDTLS_X509_CRL_PARSE_C=y -# default: -CONFIG_MBEDTLS_X509_CRT_PARSE_C=y -# default: -CONFIG_MBEDTLS_X509_CSR_PARSE_C=y -# default: -# CONFIG_MBEDTLS_X509_CREATE_C is not set -# default: -CONFIG_MBEDTLS_X509_RSASSA_PSS_SUPPORT=y -# default: -# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set -# default: -CONFIG_MBEDTLS_ASN1_PARSE_C=y -# default: -CONFIG_MBEDTLS_ASN1_WRITE_C=y -# default: -CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y - -# -# Certificate Bundle Configuration -# -# default: -CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y -# default: -# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set -# default: -# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set -# default: -# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set -# default: -# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set -# default: -CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 -# end of Certificate Bundle Configuration - -# default: -# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set -# default: -# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_CROSS_SIGNED_VERIFY is not set -# end of Certificates - -# default: -CONFIG_MBEDTLS_TLS_ENABLED=y - -# -# TLS Protocol Configuration -# -# default: -CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y -# default: -# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set -# default: -# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set -# default: -CONFIG_MBEDTLS_TLS_SERVER=y -# default: -CONFIG_MBEDTLS_TLS_CLIENT=y -# default: -CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y -# default: -# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set -# default: -# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set -# default: -# CONFIG_MBEDTLS_TLS_DISABLED is not set -# default: -# CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE is not set -# default: -# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set -# default: -CONFIG_MBEDTLS_SSL_CACHE_C=y -# default: -CONFIG_MBEDTLS_SSL_ALL_ALERT_MESSAGES=y - -# -# TLS Key Exchange Configuration -# -# default: -# CONFIG_MBEDTLS_PSK_MODES is not set -# default: -CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y -# default: -CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y -# default: -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y -# default: -CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y -# end of TLS Key Exchange Configuration - -# default: -CONFIG_MBEDTLS_SSL_SERVER_NAME_INDICATION=y -# default: -CONFIG_MBEDTLS_SSL_ALPN=y -# default: -CONFIG_MBEDTLS_SSL_MAX_FRAGMENT_LENGTH=y -# default: -# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set -# default: -CONFIG_MBEDTLS_SSL_RENEGOTIATION=y -# default: -CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y -# default: -CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y -# default: -# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set -# end of TLS Protocol Configuration - -# default: -# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set - -# -# Symmetric Ciphers -# -# default: -CONFIG_MBEDTLS_AES_C=y -# default: -# CONFIG_MBEDTLS_CAMELLIA_C is not set -# default: -# CONFIG_MBEDTLS_ARIA_C is not set -# default: -# CONFIG_MBEDTLS_DES_C is not set -# default: -CONFIG_MBEDTLS_CCM_C=y -# default: -CONFIG_MBEDTLS_CIPHER_MODE_CBC=y -# default: -CONFIG_MBEDTLS_CIPHER_MODE_CFB=y -# default: -CONFIG_MBEDTLS_CIPHER_MODE_CTR=y -# default: -CONFIG_MBEDTLS_CIPHER_MODE_OFB=y -# default: -CONFIG_MBEDTLS_CIPHER_MODE_XTS=y -# default: -CONFIG_MBEDTLS_GCM_C=y -# default: -# CONFIG_MBEDTLS_NIST_KW_C is not set -# default: -CONFIG_MBEDTLS_AES_ROM_TABLES=y -# default: -# CONFIG_MBEDTLS_AES_FEWER_TABLES is not set -# default: -# CONFIG_MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH is not set -# default: -CONFIG_MBEDTLS_CMAC_C=y -# end of Symmetric Ciphers - -# -# Asymmetric Ciphers -# -# default: -CONFIG_MBEDTLS_BIGNUM_C=y -# default: -CONFIG_MBEDTLS_RSA_C=y -# default: -CONFIG_MBEDTLS_ECP_C=y - -# -# Supported Curves -# -# default: -CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y -# default: -CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y -# default: -CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y -# default: -CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y -# default: -CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y -# default: -CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y -# default: -CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y -# default: -CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y -# end of Supported Curves - -# -# Elliptic Curve Ciphers Configuration -# -# default: -CONFIG_MBEDTLS_ECP_NIST_OPTIM=y -# default: -# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set -# default: -# CONFIG_MBEDTLS_DHM_C is not set -# default: -CONFIG_MBEDTLS_ECDH_C=y -# default: -# CONFIG_MBEDTLS_ECJPAKE_C is not set -# default: -CONFIG_MBEDTLS_ECDSA_C=y -# default: -CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y -# default: -CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y -# default: -CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y -# default: -# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set -# end of Elliptic Curve Ciphers Configuration -# end of Asymmetric Ciphers - -# -# Hash functions -# -# default: -# CONFIG_MBEDTLS_RIPEMD160_C is not set -# default: -CONFIG_MBEDTLS_MD_C=y -# default: -CONFIG_MBEDTLS_MD5_C=y -# default: -CONFIG_MBEDTLS_SHA1_C=y -# default: -# CONFIG_MBEDTLS_SHA224_C is not set -# default: -CONFIG_MBEDTLS_SHA256_C=y -# default: -CONFIG_MBEDTLS_SHA384_C=y -# default: -CONFIG_MBEDTLS_SHA512_C=y -# default: -# CONFIG_MBEDTLS_SHA3_C is not set -# default: -CONFIG_MBEDTLS_ROM_MD5=y -# end of Hash functions - -# -# Hardware Acceleration -# -# default: -CONFIG_MBEDTLS_HARDWARE_SHA=y -# default: -CONFIG_MBEDTLS_HARDWARE_MPI=y -# default: -CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI=y -# default: -CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y -# default: -CONFIG_MBEDTLS_MPI_INTERRUPT_LEVEL=0 -# default: -CONFIG_MBEDTLS_HARDWARE_AES=y -# default: -# CONFIG_MBEDTLS_AES_SOFT_FALLBACK is not set -# default: -CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y -# default: -CONFIG_MBEDTLS_AES_USE_INTERRUPT=y -# default: -CONFIG_MBEDTLS_AES_INTERRUPT_LEVEL=0 -# default: -CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM=y -# default: -# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set -# default: -# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set -# default: -CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL=y -# end of Hardware Acceleration - -# -# Entropy and Random Number Generation -# -# default: -# CONFIG_MBEDTLS_ENTROPY_FORCE_SHA256 is not set -# default: -CONFIG_MBEDTLS_CTR_DRBG_C=y -# default: -CONFIG_MBEDTLS_HMAC_DRBG_C=y -# end of Entropy and Random Number Generation - -# -# Encoding/Decoding -# -# default: -CONFIG_MBEDTLS_BASE64_C=y -# default: -CONFIG_MBEDTLS_PKCS5_C=y -# default: -CONFIG_MBEDTLS_PKCS7_C=y -# default: -CONFIG_MBEDTLS_PKCS1_V15=y -# default: -CONFIG_MBEDTLS_PKCS1_V21=y -# end of Encoding/Decoding - -# -# Stream Cipher -# -# default: -# CONFIG_MBEDTLS_CHACHA20_C is not set -# end of Stream Cipher -# end of mbedTLS - -# -# NVS -# -# default: -# CONFIG_NVS_ENCRYPTION is not set -# default: -# CONFIG_NVS_ASSERT_ERROR_CHECK is not set -# default: -# CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY is not set -# default: -# CONFIG_NVS_BDL_STACK is not set -# end of NVS - -# -# OpenThread -# -# default: -# CONFIG_OPENTHREAD_ENABLED is not set - -# -# OpenThread Spinel -# -# default: -# CONFIG_OPENTHREAD_SPINEL_ONLY is not set -# end of OpenThread Spinel - -# default: -# CONFIG_OPENTHREAD_DEBUG is not set -# end of OpenThread - -# -# Protocomm -# -# default: -# CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0 is not set -# default: -# CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1 is not set -# default: -CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y -# default: -CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_PATCH_VERSION=y -# end of Protocomm - -# -# PThreads -# -# default: -CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 -# default: -CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 -# default: -CONFIG_PTHREAD_STACK_MIN=768 -# default: -CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 -# default: -CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" -# end of PThreads - -# -# SD Protocol Layer Configuration -# -# default: -CONFIG_SD_ENABLE_SDIO_SUPPORT=y -# end of SD Protocol Layer Configuration - -# -# MMU Config -# -# default: -CONFIG_MMU_PAGE_SIZE_64KB=y -# default: -CONFIG_MMU_PAGE_MODE="64KB" -# default: -CONFIG_MMU_PAGE_SIZE=0x10000 -# end of MMU Config - -# -# Main Flash configuration -# - -# -# SPI Flash behavior when brownout -# -# default: -CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC=y -# default: -CONFIG_SPI_FLASH_BROWNOUT_RESET=y -# end of SPI Flash behavior when brownout - -# -# Optional and Experimental Features (READ DOCS FIRST) -# - -# -# Features here require specific hardware (READ DOCS FIRST!) -# -# default: -# CONFIG_SPI_FLASH_AUTO_SUSPEND is not set -# default: -CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 -# default: -# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set -# default: -# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set -# default: -CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y -# end of Optional and Experimental Features (READ DOCS FIRST) -# end of Main Flash configuration - -# -# SPI Flash driver -# -# default: -# CONFIG_SPI_FLASH_VERIFY_WRITE is not set -# default: -# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set -# default: -# CONFIG_SPI_FLASH_ROM_IMPL is not set -# default: -CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y -# default: -# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set -# default: -# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set -# default: -# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set -# default: -CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y -# default: -CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 -# default: -CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 -# default: -CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 -# default: -# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set -# default: -# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set -# default: -# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set - -# -# Auto-detect flash chips -# -# default: -CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y -# default: -CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y -# default: -CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y -# default: -CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y -# default: -CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y -# default: -CONFIG_SPI_FLASH_VENDOR_BOYA_SUPPORT_ENABLED=y -# default: -CONFIG_SPI_FLASH_VENDOR_TH_SUPPORT_ENABLED=y -# default: -CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y -# default: -CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y -# default: -CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y -# default: -CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y -# default: -CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP=y -# default: -CONFIG_SPI_FLASH_SUPPORT_TH_CHIP=y -# end of Auto-detect flash chips - -# default: -CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y -# end of SPI Flash driver - -# -# SPIFFS Configuration -# -# default: -CONFIG_SPIFFS_MAX_PARTITIONS=3 - -# -# SPIFFS Cache Configuration -# -# default: -CONFIG_SPIFFS_CACHE=y -# default: -CONFIG_SPIFFS_CACHE_WR=y -# default: -# CONFIG_SPIFFS_CACHE_STATS is not set -# end of SPIFFS Cache Configuration - -# default: -CONFIG_SPIFFS_PAGE_CHECK=y -# default: -CONFIG_SPIFFS_GC_MAX_RUNS=10 -# default: -# CONFIG_SPIFFS_GC_STATS is not set -# default: -CONFIG_SPIFFS_PAGE_SIZE=256 -# default: -CONFIG_SPIFFS_OBJ_NAME_LEN=32 -# default: -# CONFIG_SPIFFS_FOLLOW_SYMLINKS is not set -# default: -CONFIG_SPIFFS_USE_MAGIC=y -# default: -CONFIG_SPIFFS_USE_MAGIC_LENGTH=y -# default: -CONFIG_SPIFFS_META_LENGTH=4 -# default: -CONFIG_SPIFFS_USE_MTIME=y - -# -# Debug Configuration -# -# default: -# CONFIG_SPIFFS_DBG is not set -# default: -# CONFIG_SPIFFS_API_DBG is not set -# default: -# CONFIG_SPIFFS_GC_DBG is not set -# default: -# CONFIG_SPIFFS_CACHE_DBG is not set -# default: -# CONFIG_SPIFFS_CHECK_DBG is not set -# default: -# CONFIG_SPIFFS_TEST_VISUALISATION is not set -# end of Debug Configuration -# end of SPIFFS Configuration - -# -# TCP Transport -# - -# -# Websocket -# -# default: -CONFIG_WS_TRANSPORT=y -# default: -CONFIG_WS_BUFFER_SIZE=1024 -# default: -# CONFIG_WS_DYNAMIC_BUFFER is not set -# end of Websocket -# end of TCP Transport - -# -# Unity unit testing library -# -# default: -CONFIG_UNITY_ENABLE_FLOAT=y -# default: -CONFIG_UNITY_ENABLE_DOUBLE=y -# default: -# CONFIG_UNITY_ENABLE_64BIT is not set -# default: -# CONFIG_UNITY_ENABLE_COLOR is not set -# default: -CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y -# default: -# CONFIG_UNITY_ENABLE_FIXTURE is not set -# default: -# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set -# end of Unity unit testing library - -# -# Virtual file system -# -# default: -CONFIG_VFS_SUPPORT_IO=y -# default: -CONFIG_VFS_SUPPORT_DIR=y -# default: -CONFIG_VFS_SUPPORT_SELECT=y -# default: -CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y -# default: -# CONFIG_VFS_SELECT_IN_RAM is not set -# default: -# CONFIG_VFS_SUPPORT_TERMIOS is not set -# default: -CONFIG_VFS_MAX_COUNT=8 - -# -# Host File System I/O (Semihosting) -# -# default: -CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1 -# end of Host File System I/O (Semihosting) - -# default: -CONFIG_VFS_INITIALIZE_DEV_NULL=y -# end of Virtual file system - -# -# Wear Levelling -# -# default: -# CONFIG_WL_SECTOR_SIZE_512 is not set -# default: -CONFIG_WL_SECTOR_SIZE_4096=y -# default: -CONFIG_WL_SECTOR_SIZE=4096 -# end of Wear Levelling -# end of Component config - -# default: -# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set - -# Deprecated options for backward compatibility -# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set -# CONFIG_NO_BLOBS is not set -# CONFIG_APP_ROLLBACK_ENABLE is not set -# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set -CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y -# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set -# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set -CONFIG_LOG_BOOTLOADER_LEVEL=3 -# CONFIG_FLASH_ENCRYPTION_ENABLED is not set -# CONFIG_FLASHMODE_QIO is not set -# CONFIG_FLASHMODE_QOUT is not set -CONFIG_FLASHMODE_DIO=y -# CONFIG_FLASHMODE_DOUT is not set -CONFIG_MONITOR_BAUD=115200 -CONFIG_OPTIMIZATION_LEVEL_DEBUG=y -CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y -CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y -# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set -# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set -CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y -# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set -# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set -CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 -# CONFIG_CXX_EXCEPTIONS is not set -CONFIG_STACK_CHECK_NONE=y -# CONFIG_STACK_CHECK_NORM is not set -# CONFIG_STACK_CHECK_STRONG is not set -# CONFIG_STACK_CHECK_ALL is not set -# CONFIG_WARN_WRITE_STRINGS is not set -# CONFIG_BLUEDROID_ENABLED is not set -CONFIG_NIMBLE_ENABLED=y -CONFIG_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y -# CONFIG_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set -CONFIG_NIMBLE_PINNED_TO_CORE=0 -CONFIG_NIMBLE_TASK_STACK_SIZE=4096 -CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096 -# CONFIG_NIMBLE_ROLE_CENTRAL is not set -CONFIG_NIMBLE_ROLE_PERIPHERAL=y -CONFIG_NIMBLE_ROLE_BROADCASTER=y -# CONFIG_NIMBLE_ROLE_OBSERVER is not set -CONFIG_NIMBLE_RPA_TIMEOUT=900 -CONFIG_NIMBLE_MAX_CONNECTIONS=3 -CONFIG_NIMBLE_MAX_CCCDS=8 -CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y -# CONFIG_NIMBLE_HS_FLOW_CTRL is not set -CONFIG_NIMBLE_ATT_PREFERRED_MTU=256 -CONFIG_NIMBLE_L2CAP_COC_MAX_NUM=0 -CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=12 -CONFIG_BT_NIMBLE_ACL_BUF_COUNT=24 -CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255 -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 -CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=30 -CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=8 -CONFIG_NIMBLE_SVC_GAP_DEVICE_NAME="nimble" -CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31 -CONFIG_NIMBLE_SVC_GAP_APPEARANCE=0 -# CONFIG_NIMBLE_MESH is not set -# CONFIG_NIMBLE_DEBUG is not set -# CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_EN is not set -CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS=y -CONFIG_SW_COEXIST_ENABLE=y -CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y -CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y -# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set -# CONFIG_EVENT_LOOP_PROFILING is not set -CONFIG_POST_EVENTS_FROM_ISR=y -CONFIG_POST_EVENTS_FROM_IRAM_ISR=y -CONFIG_GDBSTUB_SUPPORT_TASKS=y -CONFIG_GDBSTUB_MAX_TASKS=32 -# CONFIG_OTA_ALLOW_HTTP is not set -# CONFIG_ESP_SYSTEM_PD_FLASH is not set -CONFIG_ESP32C3_LIGHTSLEEP_GPIO_RESET_WORKAROUND=y -CONFIG_ESP32C3_RTC_CLK_SRC_INT_RC=y -# CONFIG_ESP32C3_RTC_CLK_SRC_EXT_CRYS is not set -# CONFIG_ESP32C3_RTC_CLK_SRC_EXT_OSC is not set -# CONFIG_ESP32C3_RTC_CLK_SRC_INT_8MD256 is not set -CONFIG_ESP32C3_RTC_CLK_CAL_CYCLES=1024 -CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y -CONFIG_BROWNOUT_DET=y -CONFIG_ESP32C3_BROWNOUT_DET=y -CONFIG_BROWNOUT_DET_LVL_SEL_7=y -CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_7=y -# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set -# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_6 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set -# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_5 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set -# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_4 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set -# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_3 is not set -# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set -# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_2 is not set -CONFIG_BROWNOUT_DET_LVL=7 -CONFIG_ESP32C3_BROWNOUT_DET_LVL=7 -CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y -CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y -# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set -# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set -# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set -# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set -CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y -CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y -CONFIG_ESP32C3_TIME_SYSCALL_USE_RTC_SYSTIMER=y -# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set -# CONFIG_ESP32C3_TIME_SYSCALL_USE_RTC is not set -# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set -# CONFIG_ESP32C3_TIME_SYSCALL_USE_SYSTIMER is not set -# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set -# CONFIG_ESP32C3_TIME_SYSCALL_USE_NONE is not set -CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y -# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set -CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 -CONFIG_ESP32_PHY_MAX_TX_POWER=20 -# CONFIG_REDUCE_PHY_TX_POWER is not set -# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set -CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y -CONFIG_CONSOLE_UART_DEFAULT=y -# CONFIG_CONSOLE_UART_CUSTOM is not set -# CONFIG_CONSOLE_UART_NONE is not set -# CONFIG_ESP_CONSOLE_UART_NONE is not set -CONFIG_CONSOLE_UART=y -CONFIG_CONSOLE_UART_NUM=0 -CONFIG_CONSOLE_UART_BAUDRATE=115200 -# CONFIG_ESP32C3_DEFAULT_CPU_FREQ_80 is not set -CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y -CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ=160 -CONFIG_ESP_SYSTEM_PMP_IDRAM_SPLIT=y -CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_VIA_TEE=y -CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y -CONFIG_ESP32C3_MEMPROT_FEATURE=y -CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y -CONFIG_ESP32C3_MEMPROT_FEATURE_LOCK=y -CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 -CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 -CONFIG_MAIN_TASK_STACK_SIZE=3584 -CONFIG_INT_WDT=y -CONFIG_INT_WDT_TIMEOUT_MS=300 -CONFIG_TASK_WDT=y -CONFIG_ESP_TASK_WDT=y -# CONFIG_TASK_WDT_PANIC is not set -CONFIG_TASK_WDT_TIMEOUT_S=5 -CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y -# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set -CONFIG_ESP32C3_DEBUG_OCDAWARE=y -CONFIG_IPC_TASK_STACK_SIZE=1024 -CONFIG_TIMER_TASK_STACK_SIZE=3584 -# CONFIG_ESP32_APPTRACE_ENABLE is not set -CONFIG_ESP32_WIFI_ENABLED=y -CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 -CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 -# CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set -CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y -CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 -CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 -# CONFIG_ESP32_WIFI_CSI_ENABLED is not set -CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y -CONFIG_ESP32_WIFI_TX_BA_WIN=6 -CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y -CONFIG_ESP32_WIFI_RX_BA_WIN=6 -CONFIG_ESP32_WIFI_NVS_ENABLED=y -CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 -CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 -CONFIG_ESP32_WIFI_IRAM_OPT=y -CONFIG_ESP32_WIFI_RX_IRAM_OPT=y -CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y -CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA=y -CONFIG_WPA_MBEDTLS_CRYPTO=y -CONFIG_WPA_MBEDTLS_TLS_CLIENT=y -# CONFIG_WPA_WAPI_PSK is not set -# CONFIG_WPA_SUITE_B_192 is not set -# CONFIG_WPA_11KV_SUPPORT is not set -# CONFIG_WPA_MBO_SUPPORT is not set -# CONFIG_WPA_DPP_SUPPORT is not set -# CONFIG_WPA_11R_SUPPORT is not set -# CONFIG_WPA_WPS_SOFTAP_REGISTRAR is not set -# CONFIG_WPA_WPS_STRICT is not set -# CONFIG_WPA_DEBUG_PRINT is not set -# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set -# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set -CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y -CONFIG_TIMER_TASK_PRIORITY=1 -CONFIG_TIMER_TASK_STACK_DEPTH=2048 -CONFIG_TIMER_QUEUE_LENGTH=10 -# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set -# CONFIG_HAL_ASSERTION_SILIENT is not set -# CONFIG_L2_TO_L3_COPY is not set -CONFIG_ESP_GRATUITOUS_ARP=y -CONFIG_GARP_TMR_INTERVAL=60 -CONFIG_TCPIP_RECVMBOX_SIZE=32 -CONFIG_TCP_MAXRTX=12 -CONFIG_TCP_SYNMAXRTX=12 -CONFIG_TCP_MSS=1440 -CONFIG_TCP_MSL=60000 -CONFIG_TCP_SND_BUF_DEFAULT=5760 -CONFIG_TCP_WND_DEFAULT=5760 -CONFIG_TCP_RECVMBOX_SIZE=6 -CONFIG_TCP_QUEUE_OOSEQ=y -CONFIG_TCP_OVERSIZE_MSS=y -# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set -# CONFIG_TCP_OVERSIZE_DISABLE is not set -CONFIG_UDP_RECVMBOX_SIZE=6 -CONFIG_TCPIP_TASK_STACK_SIZE=3072 -CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y -# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set -CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF -# CONFIG_PPP_SUPPORT is not set -CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 -CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 -CONFIG_ESP32_PTHREAD_STACK_MIN=768 -CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 -CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" -CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y -# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set -# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set -CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y -# CONFIG_SUPPORT_TERMIOS is not set -CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 -# End of deprecated options diff --git a/sdkconfig.ci.c3eco7 b/sdkconfig.ci.c3eco7 deleted file mode 100644 index badfbca..0000000 --- a/sdkconfig.ci.c3eco7 +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_IDF_TARGET="esp32c3" -CONFIG_ESP32C3_REV_MIN_101=y -CONFIG_ESP_WIFI_SSID="ssid_${IDF_TARGET}_${CI_PIPELINE_ID}" -CONFIG_ESP_WIFI_PASSWORD="password_${IDF_TARGET}_${CI_PIPELINE_ID}" diff --git a/sdkconfig.ci.esp32c2_xtal26m b/sdkconfig.ci.esp32c2_xtal26m deleted file mode 100644 index 814241b..0000000 --- a/sdkconfig.ci.esp32c2_xtal26m +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_IDF_TARGET="esp32c2" -CONFIG_XTAL_FREQ_26=y -CONFIG_ESP_WIFI_SSID="ssid_${IDF_TARGET}_${CI_PIPELINE_ID}" -CONFIG_ESP_WIFI_PASSWORD="password_${IDF_TARGET}_${CI_PIPELINE_ID}" diff --git a/sdkconfig.ci.esp32c2eco4_xtal26m b/sdkconfig.ci.esp32c2eco4_xtal26m deleted file mode 100644 index db99cc1..0000000 --- a/sdkconfig.ci.esp32c2eco4_xtal26m +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG_IDF_TARGET="esp32c2" -CONFIG_XTAL_FREQ_26=y -CONFIG_ESP32C2_REV_MIN_200=y -CONFIG_ESP_WIFI_SSID="ssid_${IDF_TARGET}_${CI_PIPELINE_ID}" -CONFIG_ESP_WIFI_PASSWORD="password_${IDF_TARGET}_${CI_PIPELINE_ID}" diff --git a/sdkconfig.ci.wpa_psk b/sdkconfig.ci.wpa_psk deleted file mode 100644 index 240bb63..0000000 --- a/sdkconfig.ci.wpa_psk +++ /dev/null @@ -1,3 +0,0 @@ -CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=n -CONFIG_ESP_WIFI_SSID="ssid_${IDF_TARGET}_${CI_PIPELINE_ID}" -CONFIG_ESP_WIFI_PASSWORD="password_${IDF_TARGET}_${CI_PIPELINE_ID}" diff --git a/tools/app.js b/tools/app.js index a02cf53..5897bf6 100644 --- a/tools/app.js +++ b/tools/app.js @@ -274,6 +274,19 @@ function formatHttpErrorDetail(text) { if (detail.length > 180) detail = detail.slice(0, 177) + '...'; return detail; } +function isOtaBusyError(status, detail) { + var text = (detail || '').toLowerCase(); + return status === 403 || + status === 409 || + status === 423 || + text.indexOf('busy') >= 0 || + text.indexOf('occupied') >= 0 || + text.indexOf('another ota') >= 0 || + text.indexOf('invalid state') >= 0 || + text.indexOf('占用') >= 0 || + text.indexOf('互斥') >= 0 || + text.indexOf('系统忙') >= 0; +} /** @description 显示 Toast 提示消息,支持 info/success/warning/error 类型。 */ function showToast(message, type) { type = type || 'success'; @@ -1289,7 +1302,13 @@ function clearFile(chip) { /** @description 启动 ESP32 本机 OTA 上传流程(互斥获取→分块上传→SHA验证→重启)。 */ async function startUpload(chip) { var file = chip === 'esp32' ? esp32File : gd32File; - if (!file || isUploading) return; + if (!file) return; + if (isUploading) { + var busyChip = (activeUploadChip || window.flashTarget || chip || 'ota').toUpperCase(); + addLog('W', '上传', '已有 ' + busyChip + ' OTA 正在进行,拒绝新的 ' + chip.toUpperCase() + ' 上传'); + showToast('OTA 通道正在占用,请等待当前刷写结束', 'error'); + return; + } pausePollingForUpload(); // ★ 必须先抢锁再 await:守卫同步设置,await 期间重复点击才能被拦下。 // 旧实现把 isUploading 设在 await handle.getFile() 之后 → 用户在 BLE 期间 @@ -1480,9 +1499,10 @@ async function startUpload(chip) { setProgress(45, '上传完成,校验并触发 GD32 OTA...'); triggerGd32Relay(); } - } else if (xhr.status === 403) { - addLog('E', '上传', 'ESP32 OTA 被其他通道占用(BLE/WiFi 互斥),请稍后重试'); - showToast('ESP32 OTA 被其他通道占用,请稍后重试', 'error'); + } else if (isOtaBusyError(xhr.status, formatHttpErrorDetail(xhr.responseText))) { + var busyDetail = formatHttpErrorDetail(xhr.responseText); + addLog('E', '上传', chip.toUpperCase() + ' OTA 被其他通道占用(BLE/WiFi 互斥)' + (busyDetail ? ': ' + busyDetail : '')); + showToast('OTA 通道被占用,请稍后重试', 'error'); cleanupAfterOtaFailure('通道被占用'); } else { var detail = formatHttpErrorDetail(xhr.responseText); diff --git a/tools/ble_ota.js b/tools/ble_ota.js index c651441..5265e9a 100644 --- a/tools/ble_ota.js +++ b/tools/ble_ota.js @@ -19,6 +19,8 @@ const BLE_OTA_FINISH_REBOOT_NOW = 0x07; const BLE_OTA_FINISH_GD32_RELAY_START = 0x10; const BLE_OTA_FINISH_GD32_RELAY_DONE = 0x11; + const BLE_OTA_FINISH_ABORTED = 0x20; // 用户主动中止(BLE 不断开) + const BLE_OTA_CTRL_ABORT_OTA = 0xFB; // 中止当前 OTA(不断开 BLE) const BLE_OTA_FINISH_ERR_FINALIZE = 0x81; const BLE_OTA_FINISH_ERR_MUTEX = 0x83; const BLE_OTA_FINISH_ERR_GD32_SIZE = 0x84; @@ -242,6 +244,10 @@ return value & 0xFFFF; } + function isSessionlessFinish(value) { + return finishSessionId(value) === 0; + } + function hasCurrentWaiter(targetKey) { return !!(state.otaResultWaiter && state.otaResultWaiter.targetKey === targetKey); } @@ -612,6 +618,7 @@ [BLE_OTA_FINISH_REBOOT_NOW]: '[FINISH] ESP32 即将重启', [BLE_OTA_FINISH_GD32_RELAY_START]: '[FINISH] GD32 OTA 转发已开始', [BLE_OTA_FINISH_GD32_RELAY_DONE]: '[FINISH] GD32 OTA 转发完成', + [BLE_OTA_FINISH_ABORTED]: '[FINISH] OTA 已中止(BLE 保持连接)', [BLE_OTA_FINISH_ERR_FINALIZE]: `[FINISH] ESP32 OTA 收尾失败: err=${value}`, [BLE_OTA_FINISH_ERR_MUTEX]: '[FINISH] OTA 被其他通道占用,互斥拒绝', [BLE_OTA_FINISH_ERR_GD32_SIZE]: `[FINISH] GD32 固件超出分区大小: ${formatBytes(value)} (上限 216KB)`, @@ -650,6 +657,29 @@ return; } + // ── 0x20 ABORTED:ESP32 已中止 OTA(BLE 保持连接)── + // 中止可由用户主动点"中止"按钮(abortTask 发 0xFB)触发, + // 也可能是设备端因错误自动中止。两种情况都走同一清理路径。 + if (stage === BLE_OTA_FINISH_ABORTED) { + state.abortRequested = true; + state.expectedReboot = false; + state.reconnecting = false; + state.reconnectAttempts = 0; + state.lastUploadStatus = 'aborted'; + setProgress(0, 'OTA 已中止'); + setBleStageError('OTA 已中止'); + showToast('OTA 已中止(BLE 保持连接)', 'warning'); + // 解除任何等待 ACK / OTA 结果的 Promise + if (state.ackWaiter) { + const waiter = state.ackWaiter; + state.ackWaiter = null; + clearTimeout(waiter.timer); + waiter.reject(new Error('OTA 已中止')); + } + rejectOtaResult('OTA 已中止', { stage: stage, value: value }); + return; + } + if (stage === BLE_OTA_FINISH_ERR_GD32_RELAY) { if (!finishBelongsToWaiter('gd32', value)) return; if (typeof switchStageFlow === 'function') switchStageFlow('gd32'); @@ -687,9 +717,12 @@ // OTA 结果 toast 提示(用户可见,不只是在日志里) if (stage === BLE_OTA_FINISH_ERR_MUTEX) { - if (hasCurrentWaiter('esp32') && !finishBelongsToWaiter('esp32', value)) return; - if (hasCurrentWaiter('gd32') && !finishBelongsToWaiter('gd32', value)) return; + if (!isSessionlessFinish(value)) { + if (hasCurrentWaiter('esp32') && !finishBelongsToWaiter('esp32', value)) return; + if (hasCurrentWaiter('gd32') && !finishBelongsToWaiter('gd32', value)) return; + } showToast('OTA 被其他通道占用,请稍后重试', 'error'); + state.abortRequested = true; setProgress(0, 'OTA 被其他通道占用'); state.lastUploadStatus = 'failed'; setBleStageError('OTA 被其他通道占用'); @@ -884,6 +917,11 @@ } function waitForAck(cmd, expectedSeq, timeoutMs = 8000, opts = {}) { + // ★ 中止快速退出:若 0x20 ABORTED 已先于本调用到达(ackWaiter 当时为 null), + // 这里立即 reject,避免在已中止的会话上盲等超时。 + if (state.abortRequested) { + return Promise.reject(new Error('OTA 已中止')); + } const gen = state.ackGeneration; // ★ 提前返回:只要 cmd 匹配 + generation 匹配 + 字节数足够,即可 resolve // 不再要求 lastAckSeq >= expectedSeq(与 handleNotify 保持一致) @@ -1227,6 +1265,9 @@ // 流水线发送:不逐包 await,减少 BLE 栈 per-packet 开销 // ★ 不再在循环内 setProgress:进度完全由设备 ACK 后的 state.lastAckBytes 驱动 for (let seq = windowStart; seq <= endSeq; seq++) { + // ★ 中止检查放在循环内:abortTask 可能在本窗口发送过程中触发 + // (windowStart 处的检查无法覆盖窗口中途的 abort) + if (state.abortRequested) throw new Error('用户中止'); const start = seq * payloadSize; const end = Math.min(start + payloadSize, totalSize); const payload = fileBytes.slice(start, end); @@ -1271,6 +1312,9 @@ let prevAckBytes = state.lastAckBytes; // ★ 进度跟踪:检测重试是否有效 // ★ 仅以字节数判断是否完成,不依赖 ackOk(generation 不匹配时 ackOk 不可靠) while (state.lastAckBytes < expectedBytes && retryCount < MAX_WINDOW_RETRIES) { + // ★ 中止检查:0x20 ABORTED 可能在上一轮 waitForAck 超时期间到达, + // 或在 sleep/续发过程中到达。立即抛出,避免无谓重试已中止的会话。 + if (state.abortRequested) throw new Error('用户中止'); // 如果 ESP32 已确认所有数据(ACK 延迟到达),直接跳出 if (state.lastAckBytes >= expectedBytes) break; @@ -1296,6 +1340,8 @@ await sleep(10); for (let seq = resumeSeq; seq <= endSeq; seq++) { + // ★ 中止检查:续发过程中可能收到 0x20 ABORTED + if (state.abortRequested) throw new Error('用户中止'); const start = seq * payloadSize; const end = Math.min(start + payloadSize, totalSize); const payload = fileBytes.slice(start, end); @@ -1436,6 +1482,12 @@ bleLog('OTA', `📊 文件 SHA-256: ${fileSha256.toUpperCase()}`); return true; } catch (err) { + // ★ 中止不算失败:0x20 ABORTED handler 已更新 UI(progress/stage/toast), + // 这里只记录日志并返回 false,避免把"用户中止"误报为写入失败。 + if (state.abortRequested || state.lastUploadStatus === 'aborted') { + bleLog('OTA', `写入已中止: ${err.message}`, 'W'); + return false; + } state.lastUploadStatus = 'failed'; state.expectedReboot = false; // ★ 刷写失败,取消重连退避 state.reconnecting = false; @@ -1443,23 +1495,9 @@ bleLog('OTA', `写入失败: ${err.message}`, 'E'); setProgress(0, `写入失败: ${err.message}`); setBleStageError(err.message); - // ★ BLE OTA 失败/中止后强制断开 BLE 连接,并等待实际断连完成: - // 触发 ESP32 后端的 GAP disconnect 处理器 → ble_esp32_ota_abort(), - // 清理 OTA 会话(Esp32OtaCore::abort + reorder buffer 释放 + 协调器状态复位)。 - // 等待 gattserverdisconnected 事件确保断连真正生效,避免用户快速重试时 - // 旧会话尚未清理导致 seq=0 被误判为重传。 - if (state.server && state.device && state.device.gatt && state.device.gatt.connected) { - bleLog('OTA', '刷写失败,断开 BLE 以清理 ESP32 OTA 会话...', 'W'); - try { - await new Promise((resolve) => { - let done = false; - const finish = () => { if (!done) { done = true; resolve(); } }; - state.device.addEventListener('gattserverdisconnected', finish, { once: true }); - setTimeout(finish, 1000); // 1 秒兜底超时 - state.device.gatt.disconnect(); - }); - } catch (_) {} - } + // ★ BLE OTA 失败后不再强制断开 BLE:错误 FINISH 已通过 0xFB 路径或设备端 + // 自动 abort 清理了 ESP32 OTA 会话。保留连接让用户可以快速重试或查看日志。 + // 仅当连接已半死(gatt.connected=false)时由 onDisconnected 走重连路径。 return false; } finally { if (state.ackWaiter) { @@ -1511,12 +1549,8 @@ const ok = await uploadFirmware(); if (!ok || state.abortRequested) { bleLog('AUTO', '一键刷写中止', 'W'); - // ★ 确保 BLE 已断开(触发 ESP32 OTA 会话清理) - // uploadFirmware catch 块已尝试断开,这里做安全网兜底 - if (state.device && state.device.gatt && state.device.gatt.connected) { - bleLog('AUTO', '断开 BLE 以清理 ESP32 OTA 会话...', 'W'); - try { state.device.gatt.disconnect(); } catch (_) {} - } + // ★ 中止/失败后不再断开 BLE:0xFB 已让设备端清理 OTA 会话,保留连接方便重试。 + // 若连接已半死,onDisconnected 会自动走重连路径。 return; } if (cfg.label === 'ESP32') { @@ -1535,10 +1569,40 @@ } } - /** @description 中止当前 BLE OTA 任务并清理状态。 */ - function abortTask() { - state.abortRequested = true; - bleLog('OTA', '已请求中止,等待当前分包返回'); + /** + * @description 中止当前 BLE OTA 任务并清理状态。 + * 改进:发 0xFB 控制命令让 ESP32 主动清理 OTA 会话,BLE 保持连接。 + * 旧做法是直接断开 BLE 触发 GAP disconnect 清理,导致用户需重连。 + * 若 0xFB 发送失败(如 CTRL 特征不可用),回退到设置标志等待发包循环自然退出。 + */ + async function abortTask() { + state.abortRequested = true; // 立即停止发送循环 + bleLog('OTA', '已请求中止,发送 0xFB 让设备端清理(BLE 保持连接)'); + + if (state.ctrlChar && state.server && state.device.gatt.connected) { + try { + const session = state.sessionId || 0; + const abortCmd = new Uint8Array([BLE_OTA_CTRL_ABORT_OTA, session & 0xFF, (session >> 8) & 0xFF]); + await state.ctrlChar.writeValue(abortCmd); + bleLog('OTA', '0xFB 已发送,等待设备端回复 ABORTED (stage=0x20)'); + } catch (e) { + bleLog('OTA', '0xFB 发送失败: ' + e.message + ',回退到本地中止', 'E'); + // CTRL 写入失败时回退:本地立即标记失败,不再等设备端 ABORTED 回复 + state.lastUploadStatus = 'failed'; + if (state.ackWaiter) { + const waiter = state.ackWaiter; + state.ackWaiter = null; + clearTimeout(waiter.timer); + waiter.reject(new Error('中止失败: ' + e.message)); + } + rejectOtaResult('中止失败: ' + e.message, { stage: 0xFB, value: 0 }); + } + } else { + // CTRL 特征未就绪(连接已断或未完成握手),仅本地清理 + bleLog('OTA', 'CTRL 特征不可用,仅本地中止', 'W'); + state.lastUploadStatus = 'failed'; + rejectOtaResult('CTRL 特征不可用', { stage: 0xFB, value: 0 }); + } } // showToast 使用 app.js 中的全局版本 diff --git a/tools/index.html b/tools/index.html index 11fec31..1744bd0 100644 --- a/tools/index.html +++ b/tools/index.html @@ -1,4 +1,4 @@ -