feat: ESP32-C3 OTA 中继项目 initial commit
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# ESP32-C3FH4 OTA 中继项目 — 程序开发说明
|
||||
|
||||
**委托开发方:** 请完整执行以下所有任务,完成后停止。不要在中途停止。
|
||||
|
||||
**开发范围:** ESP32-C3FH4 固件开发,充当手机 APP/PC 与 GD32E508VET6 之间的 OTA 中继器。
|
||||
|
||||
**核心工作模式:** ESP32 通过 WiFi 或 BLE 接收上位机发送的 GD32 固件 → 存入本地 Flash → 通过 SPI 转发给 GD32 执行 OTA 升级。
|
||||
|
||||
> **重要:** 开发过程中如需与 GD32 固件侧确认接口(如 AES 密钥长度、搅伴算法细节等),请先按设计文档实现接口定义并预留 hook,实际参数值用占位符标注,最终由委托方补充。
|
||||
|
||||
---
|
||||
|
||||
## 参考文件
|
||||
|
||||
| 文件 | 路径 | 说明 |
|
||||
|---|---|---|
|
||||
| 架构设计文档 | `/workzone/esp32_ota_relay/esp32_ota_relay_design.md` | 完整协议定义、模块架构、状态机设计 |
|
||||
| 开发任务清单 | `/workzone/esp32_ota_relay/tasks.md` | 所有 48 条开发任务的详细描述 |
|
||||
|
||||
**开发顺序:** T0 → T1 → T2 → T3 → T4/T5(可并行)→ T6 → T7 → T8
|
||||
|
||||
---
|
||||
|
||||
## 系统架构(必读)
|
||||
|
||||
```
|
||||
[手机APP/PC] --WiFi/BLE--> [ESP32-C3FH4] --SPI--> [GD32E508VET6]
|
||||
|
|
||||
+-- 存储 GD32 固件到 Flash
|
||||
+-- 四阶段安全握手协议
|
||||
+-- 内置温度/电压/WiFi 自检
|
||||
```
|
||||
|
||||
ESP32 的角色是**数据透传 + 存储中继**,GD32 OTA 的加密握手和数据传输逻辑全部由 ESP32 透传到 GD32,ESP32 本身不解析固件内容。
|
||||
|
||||
---
|
||||
|
||||
## 通信协议要点
|
||||
|
||||
### ESP32 ↔ GD32(SPI)
|
||||
|
||||
完整映射 GD32 原有 CANFD OTA 四阶段流程:
|
||||
|
||||
- **阶段一(安全握手):** 0x00 AC 握手 → 16B nonce 挑战 → 握手包响应 → 0xBA C0 完成
|
||||
- **阶段二(传输准备):** 发送固件总大小 → 0xBA C0 + seq 确认
|
||||
- **阶段三(数据分包):** 每包 48B + CRC,循环发送直到最后一包带 IFEND
|
||||
- **阶段四(结束跳转):** 0xBA C0 + FF FF FF FF 完成确认 → 0x0A AC 刷写结束 → GD32 跳转 APP
|
||||
|
||||
帧格式:`SOF(0xEB) + CMD + LEN(2B LE) + PAYLOAD(LEN) + CRC16(2B LE)`
|
||||
|
||||
命令码:0xF0 HANDSHAKE_REQ / 0xF1 CHALLENGE / 0xF2 SESSION_KEY / 0xF3 SEND_SIZE / 0xF4 DATA_PACKET / 0xF5 DATA_END / 0xF6 TRANSFER_DONE / 0xF7 FINISH_OTA
|
||||
|
||||
### APP ↔ ESP32(WiFi HTTP / BLE GATT)
|
||||
|
||||
- WiFi:HTTP POST `/ota/upload`,JSON 响应进度
|
||||
- BLE:4 个 GATT Characteristic(Control Point / Data In / Data Out / Version)
|
||||
- APP→ESP32 使用明文帧格式(加密由 ESP32 在转发给 GD32 时处理)
|
||||
|
||||
---
|
||||
|
||||
## 存储方案(方案 A)
|
||||
|
||||
固件先完整存入 ESP32 Flash,再转发给 GD32:
|
||||
|
||||
- 分区:`gd32_ota`(256KB,用于存 GD32 固件 binary)
|
||||
- 元数据文件:`ota_meta.json`(total_size / stored_size / last_seq)
|
||||
- 断点续传:APP 重连后从 `last_seq + 1` 继续发送
|
||||
- 存储完成后自动触发 SPI 转发流程
|
||||
|
||||
---
|
||||
|
||||
## 自检与保护
|
||||
|
||||
不外挂传感器,使用 ESP32 内置传感器:
|
||||
|
||||
- **芯片温度**(正常 < 80°C / 警告 80-100°C / 停机 > 100°C)— 1s 轮询
|
||||
- **供电电压**(正常 2.5-3.6V / 警告 < 2.5V / 停机 < 2.3V)— 5s 轮询
|
||||
- **WiFi RSSI**(正常 > -70 / 弱 -70~-85 / 断开 < -85 dBm)— 10s 轮询
|
||||
- **BLE 连接状态** — 事件驱动
|
||||
|
||||
OTA 每包发送前检查健康状态,CRITICAL 时暂停,SAFE_SHUTDOWN 时中止并清除 meta。
|
||||
|
||||
---
|
||||
|
||||
## 开发要求
|
||||
|
||||
1. **使用 ESP-IDF v5.x**,基于 CMake 构建系统,目标芯片 `esp32c3`
|
||||
2. **遵循 FreeRTOS 任务架构**:System Monitor / OTA Relay / WiFi Server / BLE GATT 四个任务
|
||||
3. **AES 使用 mbedTLS**(ESP32 内置),预置主密钥用占位符 `MASTER_KEY_HERE`
|
||||
4. **搅伴算法**实现需与 GD32 固件侧匹配,预留参数配置接口
|
||||
5. **Flash 写入**使用 esp_partition API,追加写入不断点
|
||||
6. **SPI 使用 VSPI (SPI2)**,GPIO6-9,CS=GPIO4,时钟 40MHz,mode=0
|
||||
7. **所有任务完成后**,清理会话密钥、释放内存、返回完成信号
|
||||
8. **不允许中途停止**,必须完成 T0~T8 所有任务后,才能停止运行或退出
|
||||
|
||||
---
|
||||
|
||||
## 开发产出要求
|
||||
|
||||
1. 完整可编译的 ESP32-C3 工程代码(CMake 项目结构)
|
||||
2. 所有代码放在 `/workzone/esp32_ota_relay/` 目录下
|
||||
3. 代码中必须有完整的 `main/` 目录(`main.c` / `app_main.c` / `ota_relay.c` / `spi_master.c` / `wifi_ota.c` / `ble_ota.c` / `protocol.c`)
|
||||
4. 不得使用任何需要外部 API Key 的服务(AES、WiFi、BLE 均使用 ESP-IDF 内置库)
|
||||
5. 完成后更新 `/workzone/esp32_ota_relay/tasks.md` 的进度记录
|
||||
|
||||
---
|
||||
|
||||
## GD32 固件侧依赖说明
|
||||
|
||||
以下接口需 GD32 固件侧提供参数后才能完整验证,请预留 hook 并用占位符标注:
|
||||
|
||||
| 接口 | 占位符 | 说明 |
|
||||
|---|---|---|
|
||||
| 主密钥 | `MASTER_KEY_HERE` | AES-128 主密钥,16 bytes |
|
||||
| 搅伴算法参数 | `SCRAMBLE_CONST_HERE` | 时间戳+内存地址+rand 的固定 seed |
|
||||
| 芯片唯一码 | `CHIP_UID_HERE` | GD32 芯片唯一 ID 用于密钥派生 |
|
||||
| 会话密钥 K_session | - | 由 Step 4/5 双方协商生成,存储在 RAM |
|
||||
| AES 算法模式 | AES-128 CBC | 由 ESP32 和 GD32 双方协商一致即可 |
|
||||
|
||||
---
|
||||
|
||||
## 联系方式
|
||||
|
||||
如需向委托方确认接口参数,请在代码中用 `// TODO: confirm with GD32 firmware team` 标注,不得自行假定。
|
||||
|
||||
**开发过程中遇到任何问题,请继续尝试解决,不要停止。**
|
||||
@@ -0,0 +1,43 @@
|
||||
# ESP32-C3FH4 OTA Relay
|
||||
|
||||
Complete firmware for ESP32-C3FH4 acting as OTA relay between APP/PC and GD32E508VET6.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
export IDF_PATH=/path/to/esp-idf
|
||||
source $IDF_PATH/export.sh
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
make
|
||||
```
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
main/
|
||||
main.c - Entry point, task creation
|
||||
app_main.c - Application init, system monitor task
|
||||
ota_relay.c - 4-stage OTA state machine
|
||||
spi_master.c - SPI2 (VSPI) master driver
|
||||
protocol.c - Frame format, CRC16, AES, scramble
|
||||
wifi_ota.c - WiFi AP/STA + HTTP server
|
||||
ble_ota.c - BLE GATT server
|
||||
health_monitor.c - Temperature/voltage/WiFi monitoring
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- WiFi AP mode (SSID: ESP32_OTA_RELAY, pw: 12345678)
|
||||
- BLE GATT OTA service
|
||||
- HTTP POST /ota/upload for firmware
|
||||
- SPI to GD32 (40MHz, GPIO4-9)
|
||||
- Flash storage with断点续传
|
||||
- System health monitoring
|
||||
|
||||
## TODO (confirm with GD32 team)
|
||||
|
||||
- MASTER_KEY_HERE - AES-128 master key (16 bytes)
|
||||
- SCRAMBLE_CONST_HERE - scramble algorithm seed
|
||||
- CHIP_UID_HERE - GD32 chip unique ID
|
||||
- GATT UUIDs for OTA service
|
||||
@@ -0,0 +1,698 @@
|
||||
# ESP32-C3FH4 OTA 中继架构设计
|
||||
|
||||
## 1. 系统概述
|
||||
|
||||
```
|
||||
[手机APP/PC] --WiFi--> [ESP32-C3FH4] --SPI--> [GD32E508VET6]
|
||||
|
|
||||
+--BLE---> [手机APP/PC]
|
||||
```
|
||||
|
||||
- **ESP32 角色**:WiFi/BLE 接收端 + SPI 透传(主机)
|
||||
- **GD32 角色**:OTA 执行端(从机),已有完整 OTA 流程
|
||||
- **ESP32 不负责 OTA 逻辑**,只做数据转发
|
||||
|
||||
---
|
||||
|
||||
## 2. 硬件连接
|
||||
|
||||
### SPI 接口
|
||||
|
||||
| ESP32 (Master) | GD32 (Slave) | 说明 |
|
||||
|---|---|---|
|
||||
| GPIO6 (SPIIO0) | PA4 (NSS) | 片选 |
|
||||
| GPIO7 (SPIQ) | PA5 (SCK) | 时钟 |
|
||||
| GPIO8 (SPIWP) | PA6 (MOSI) | 主发从收 |
|
||||
| GPIO9 (SPIHD) | PA7 (MISO) | 主收从发 |
|
||||
|
||||
> ⚠️ GD32E508 使用标准 SPI(PA4-NSS, PA5-SCK, PA6-MOSI, PA7-MISO)
|
||||
> GD32 已内置 BOOT+APP 模式,ESP32 通过 SPI 命令直接控制进入 Bootloader,无需额外 BOOT/RST 控制引脚。
|
||||
|
||||
---
|
||||
|
||||
## 3. 通信协议设计
|
||||
|
||||
ESP32 与 GD32 之间的 SPI 协议完整映射 GD32 原有 CANFD OTA 流程,分为四个阶段。
|
||||
|
||||
### 3.0 帧格式总览
|
||||
|
||||
所有 SPI 帧格式统一为:
|
||||
|
||||
```
|
||||
| SOF (1B) | CMD (1B) | LEN (2B, LE) | PAYLOAD (LEN bytes) | CRC16 (2B, LE) |
|
||||
```
|
||||
|
||||
- **SOF**: 0xEB 固定帧头
|
||||
- **CMD**: 命令码(见下表)
|
||||
- **LEN**: Payload 长度(不含帧头和 CRC)
|
||||
- **CRC16**: Modbus CRC16,低字节在前
|
||||
|
||||
> 注意:CANFD 侧(原协议)的分包为 48 bytes/packet;SPI 中继时,ESP32 接收 APP/PC 的分包可能更大,内部透传时不做分包合并,只做协议映射。
|
||||
|
||||
---
|
||||
|
||||
### 3.1 第一阶段:安全握手与认证(步骤 1-6)
|
||||
|
||||
目的是建立加密安全通道,防止非法刷写。
|
||||
|
||||
#### Step 1 & 2 — 握手发起与响应
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| ESP32 → GD32 | `0x00 AC` | 发起 OTA 请求 |
|
||||
| GD32 → ESP32 | `0x00 AC` | 响应信号,表示准备好进入 OTA 模式 |
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF0 (HANDSHAKE_REQ), Payload=[] → ESP32 发送 0x00 AC
|
||||
CMD=0xF0 (HANDSHAKE_REQ), Payload=[] ← GD32 回复 0x00 AC
|
||||
```
|
||||
|
||||
#### Step 3 — 主机挑战(发送随机数)
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| ESP32 → GD32 | `nonce_server` (16 bytes) | 16字节随机数,用于生成会话密钥 |
|
||||
|
||||
**搅伴算法**(时间 + 内存地址 + rand → 3轮单字节 → 2轮4字节)由 ESP32 实现,生成方法需与 GD32 预置的主密钥匹配。
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF1 (CHALLENGE), Payload=[16 bytes nonce_server]
|
||||
```
|
||||
|
||||
#### Step 4 — 从机响应与身份验证
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| GD32 → ESP32 | 握手数据包 | 使用主密钥 AES 加密随机数 + 会话密钥加密自身信息 |
|
||||
|
||||
GD32 获取芯片唯一码,使用主密钥通过 **AES 算法**加密随机数,并使用会话密钥加密自身信息,打包发送。
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF1 (CHALLENGE), Payload=[握手数据包] ← GD32 响应
|
||||
```
|
||||
|
||||
Payload 结构(AES 加密后的数据):
|
||||
|
||||
```
|
||||
| AES_encrypt(nonce_server, master_key) (16B) | AES_encrypt(device_info, K_session) (16B) |
|
||||
```
|
||||
|
||||
#### Step 5 — 主机验证与会话密钥分发
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| ESP32 → GD32 | 变换后的随机数 | 使用会话密钥加密后发送 |
|
||||
|
||||
ESP32 验证随机数,解密获取会话密钥 (K_session),对随机数进行变换后用会话密钥加密发送。
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF2 (SESSION_KEY), Payload=[AES_encrypt(transformed_nonce, K_session)]
|
||||
```
|
||||
|
||||
#### Step 6 — 认证完成
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| GD32 → ESP32 | `0xBA C0` | 安全通道建立完成 |
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF2 (SESSION_KEY), Payload=[] ← GD32 回复 0xBA C0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 第二阶段:传输准备(步骤 7-8)
|
||||
|
||||
#### Step 7 — 发送大小
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| ESP32 → GD32 | `Data Size` (4 bytes, LE) | APP 总数据大小 |
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF3 (SEND_SIZE), Payload=[size(4B, LE)]
|
||||
```
|
||||
|
||||
#### Step 8 — 确认大小
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| GD32 → ESP32 | `0xBA C0` + 包序号 | 确认收到大小信息 |
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF3 (SEND_SIZE), Payload=[seq(2B)] ← GD32 回复 0xBA C0 + seq
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.3 第三阶段:数据分包传输循环(步骤 9-12)
|
||||
|
||||
刷写核心部分,数据加密分包发送。
|
||||
|
||||
#### Step 9 — 发送数据包
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| ESP32 → GD32 | OTA 程序数据包 | 经会话密钥加密,含 CRC 校验码 |
|
||||
|
||||
分包大小约 **48 bytes/packet**(与原 CANFD 协议一致)。
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF4 (DATA_PACKET), Payload=[encrypted_data(48B) + CRC]
|
||||
```
|
||||
|
||||
#### Step 10 — 从机校验与确认
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| GD32 → ESP32 | `0xBA C0` + 包序号 | 校验通过,请求下一包 |
|
||||
| GD32 → ESP32 | `0xBA C0` + 错误指示 | 校验失败,要求重发当前包(限次重试) |
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF4 (DATA_PACKET), Payload=[seq(2B)] ← 正常 ACK
|
||||
CMD=0xF4 (DATA_PACKET), Payload=[seq(2B) + 0xFF] ← 错误重发请求
|
||||
```
|
||||
|
||||
#### Step 11 — 继续发送
|
||||
|
||||
主机发送下一包加密数据。
|
||||
|
||||
循环机制:`总循环次数 = 1 + 总APP大小 // 48 Byte`
|
||||
|
||||
#### Step 12 — 最后一包
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| ESP32 → GD32 | 最后一包数据 | IFEND(结束标志位)置位 |
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF5 (DATA_END), Payload=[encrypted_data(N) + CRC + IFEND flag]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 第四阶段:结束与跳转(步骤 13-17)
|
||||
|
||||
#### Step 13 — 传输完成确认
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| GD32 → ESP32 | `0xBA C0` + `FF FF FF FF` | 所有数据包传输完成并校验通过 |
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF6 (TRANSFER_DONE), Payload=[0xFF, 0xFF, 0xFF, 0xFF] ← GD32 完成确认
|
||||
```
|
||||
|
||||
#### Step 14 — 刷写结束指令
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| ESP32 → GD32 | `0x0A AC` | 刷写结束信号 |
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF7 (FINISH_OTA), Payload=[] → ESP32 发送 0x0A AC
|
||||
```
|
||||
|
||||
#### Step 15 — 内部跳转
|
||||
|
||||
GD32 内部执行动作:尝试跳转 APP。
|
||||
|
||||
> ESP32 无需等待此步骤完成,由 GD32 自行处理。
|
||||
|
||||
#### Step 16 — 最终确认
|
||||
|
||||
| 方向 | Signal | 说明 |
|
||||
|---|---|---|
|
||||
| GD32 → ESP32 | `0x0A AC` | 确认 APP 运行有效,回复刷写结束信号 |
|
||||
|
||||
**对应 SPI 帧:**
|
||||
|
||||
```
|
||||
CMD=0xF7 (FINISH_OTA), Payload=[] ← GD32 回复 0x0A AC
|
||||
```
|
||||
|
||||
#### Step 17 — 清理
|
||||
|
||||
流程结束,GD32 清理会话密钥、RAM 缓存等。
|
||||
|
||||
---
|
||||
|
||||
### 3.5 命令码汇总
|
||||
|
||||
| CMD | 名称 | 方向 | Payload | 对应 OTA 阶段 |
|
||||
|---|---|---|---|---|
|
||||
| 0xF0 | `HANDSHAKE_REQ` | Bidirectional | 见 Step 1-2 | 阶段一 |
|
||||
| 0xF1 | `CHALLENGE` | Bidirectional | nonce(16B) / 握手数据包 | 阶段一 |
|
||||
| 0xF2 | `SESSION_KEY` | Bidirectional | 变换后随机数 / 0xBA C0 | 阶段一 |
|
||||
| 0xF3 | `SEND_SIZE` | ESP32 → GD32 | size(4B, LE) | 阶段二 |
|
||||
| 0xF4 | `DATA_PACKET` | ESP32 → GD32 | seq(2B) + data(48B) + CRC | 阶段三 |
|
||||
| 0xF5 | `DATA_END` | ESP32 → GD32 | seq(2B) + data(N) + CRC + IFEND | 阶段三 |
|
||||
| 0xF6 | `TRANSFER_DONE` | GD32 → ESP32 | 0xFF 0xFF 0xFF 0xFF | 阶段四 |
|
||||
| 0xF7 | `FINISH_OTA` | Bidirectional | 0x0A AC | 阶段四 |
|
||||
| 0x80 | `ACK` | GD32 → ESP32 | cmd(1B) + status(1B) | 通用 |
|
||||
| 0x81 | `ERROR` | GD32 → ESP32 | cmd(1B) + err_code(1B) | 通用 |
|
||||
|
||||
---
|
||||
|
||||
### 3.6 时序图
|
||||
|
||||
```
|
||||
ESP32 GD32
|
||||
| |
|
||||
|---- HANDSHAKE_REQ ---->| 0x00 AC (Step 1)
|
||||
|<---- HANDSHAKE_REQ ---| 0x00 AC (Step 2)
|
||||
| |
|
||||
|---- CHALLENGE ------->| nonce_server 16B (Step 3)
|
||||
|<---- CHALLENGE ------| 握手数据包 (Step 4)
|
||||
| |
|
||||
|---- SESSION_KEY ----->| 变换后随机数 (Step 5)
|
||||
|<---- SESSION_KEY -----| 0xBA C0 (Step 6) ← 安全通道建立
|
||||
| |
|
||||
|---- SEND_SIZE -------->| Data Size (Step 7)
|
||||
|<---- SEND_SIZE -------| 0xBA C0 + seq (Step 8)
|
||||
| |
|
||||
|==== DATA LOOP ========>| (Step 9-11, 每包 48B + CRC)
|
||||
|<==== DATA LOOP =======| 0xBA C0 + seq (正常) / 错误重发
|
||||
| |
|
||||
|---- DATA_END --------->| 最后一包 + IFEND (Step 12)
|
||||
| |
|
||||
|<---- TRANSFER_DONE ---| 0xBA C0 + FF FF FF FF (Step 13)
|
||||
| |
|
||||
|---- FINISH_OTA ------->| 0x0A AC (Step 14)
|
||||
|<---- FINISH_OTA ------| 0x0A AC (Step 16) ← GD32 跳转 APP
|
||||
| |
|
||||
* 清理会话密钥 & 缓存 * (Step 17)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.7 ACK 状态码
|
||||
|
||||
| status | 含义 |
|
||||
|---|---|
|
||||
| 0x00 | OK |
|
||||
| 0x01 | 命令不支持 |
|
||||
| 0x02 | 认证失败 |
|
||||
| 0x03 | 大小超出范围 |
|
||||
| 0x04 | CRC 校验失败 |
|
||||
| 0x05 | 重发次数超限 |
|
||||
| 0x06 | 会话密钥无效 |
|
||||
| 0x07 | Flash 写入失败 |
|
||||
|
||||
---
|
||||
|
||||
### 3.8 重发机制
|
||||
|
||||
- GD32 检测到 CRC 错误时,回复当前包序号 + 错误指示
|
||||
- ESP32 重新发送当前包
|
||||
- 最大重发次数由 GD32 固件定义(建议 3 次)
|
||||
- 超限后 ESP32 终止传输并上报 APP
|
||||
|
||||
---
|
||||
|
||||
## 4. ESP32 软件架构
|
||||
|
||||
### 4.1 目录结构
|
||||
|
||||
```
|
||||
esp32_ota_relay/
|
||||
├── main/
|
||||
│ ├── main.c
|
||||
│ ├── app_main.c
|
||||
│ ├── ota_relay.c # OTA 中继核心逻辑(含四阶段状态机)
|
||||
│ ├── spi_master.c # SPI 驱动
|
||||
│ ├── wifi_ota.c # WiFi OTA 接收
|
||||
│ ├── ble_ota.c # BLE OTA 接收
|
||||
│ └── protocol.c # 帧封装/解析/CRC/AES
|
||||
└── CMakeLists.txt
|
||||
```
|
||||
|
||||
### 4.2 模块职责
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ ESP32 OTA Relay │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ WiFi OTA │ │ BLE OTA │ │
|
||||
│ │ Server │ │ GATT │ │
|
||||
│ └────┬─────┘ └────┬─────┘ │
|
||||
│ │ │ │
|
||||
│ └──────┬───────┘ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ OTA Relay │ ← 四阶段状态机 │
|
||||
│ │ Dispatcher │ 透传加密数据 │
|
||||
│ └──────┬───────┘ │
|
||||
│ │ │
|
||||
│ ┌──────▼──────┐ │
|
||||
│ │ Protocol │ ← 帧封装/解析/CRC/AES │
|
||||
│ │ Layer │ │
|
||||
│ └──────┬──────┘ │
|
||||
│ │ │
|
||||
│ ┌──────▼──────┐ │
|
||||
│ │SPI Master │ │
|
||||
│ └─────────────┘ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.3 OTA Relay Core 状态机
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
// 阶段一:安全握手
|
||||
RELAY_HANDSHAKE_IDLE, // 等待 HANDSHAKE_REQ
|
||||
RELAY_HANDSHAKE_WAIT, // 等待 GD32 响应 0x00 AC
|
||||
RELAY_CHALLENGE_SENT, // 已发送挑战随机数
|
||||
RELAY_CHALLENGE_RESPOND, // 等待 GD32 握手数据包
|
||||
RELAY_SESSION_KEY_SENT, // 已发送会话密钥分发
|
||||
RELAY_AUTH_COMPLETE, // 认证完成,安全通道建立
|
||||
|
||||
// 阶段二:传输准备
|
||||
RELAY_SIZE_SENT, // 已发送 Data Size
|
||||
RELAY_SIZE_ACK, // 收到大小确认
|
||||
|
||||
// 阶段三:数据传输
|
||||
RELAY_RECEIVING, // 接收固件数据
|
||||
RELAY_PACKET_SENT, // 等待 GD32 ACK
|
||||
RELAY_RECEIVE_DONE, // 传输完成(收到 DATA_END ACK)
|
||||
|
||||
// 阶段四:结束跳转
|
||||
RELAY_TRANSFER_DONE, // 收到传输完成确认
|
||||
RELAY_FINISH_SENT, // 已发送刷写结束信号
|
||||
RELAY_DONE, // 全部完成
|
||||
|
||||
RELAY_ERROR
|
||||
} relay_state_t;
|
||||
```
|
||||
|
||||
### 4.4 安全相关说明
|
||||
|
||||
- **AES 加密**:由 ESP32 实现(需预置主密钥),GD32 解密验证
|
||||
- **会话密钥 K_session**:由 Step 4/5 双方协商生成
|
||||
- **搅伴算法**:ESP32 生成 nonce_server 时使用(时间 + 内存地址 + rand → 3轮单字节 → 2轮4字节)
|
||||
- **芯片唯一码**:GD32 提供,ESP32 用于密钥派生
|
||||
|
||||
> ⚠️ 主密钥和 AES 算法细节需由 GD32 固件侧提供,本设计仅做协议映射。
|
||||
|
||||
### 4.6 数据缓冲策略
|
||||
|
||||
**方案 A:存储后转发(先存 ESP32 Flash,再发 SPI)**
|
||||
|
||||
ESP32 将 APP 发送的固件完整存入 Flash,传输完成后再通过 SPI 转发给 GD32。
|
||||
|
||||
#### Flash 分区设计
|
||||
|
||||
假设固件最大 256KB,预留足够空间:
|
||||
|
||||
```yaml
|
||||
# Partitions
|
||||
# Name, Type, SubType, Offset, Size
|
||||
otadata, data, ota, 0x9000, 0x2000 # OTA 数据区标记
|
||||
gd32_ota, data, spiffs, 0xB000, 0x40000 # GD32 固件存储区 (256KB)
|
||||
app0, app, ota_0, 0x4B000, 0x330000 # ESP32 自身 APP
|
||||
app1, app, ota_1, 0x3DC000, 0x330000
|
||||
```
|
||||
|
||||
> `gd32_ota` 分区使用 spiffs 或自定义 data 分区,存放原始固件 binary。
|
||||
|
||||
#### 接收与存储流程
|
||||
|
||||
```
|
||||
APP ESP32 GD32
|
||||
| | |
|
||||
|--- DATA_PACKET (4KB) --->| |
|
||||
| |--- write to Flash ------>| (存 Flash,不发 SPI)
|
||||
|<--- ACK (stored) --------| |
|
||||
| | |
|
||||
|--- DATA_PACKET (4KB) --->| |
|
||||
| |--- write to Flash ------>| (存 Flash,不发 SPI)
|
||||
|<--- ACK (stored) --------| |
|
||||
| | |
|
||||
| ... 循环存完所有包 ... |
|
||||
| | |
|
||||
|--- DATA_END ------------->| |
|
||||
| | |
|
||||
| |--- ENTER_BOOT ----------->| (开始刷写)
|
||||
| |<--- ACK ------------------| (GD32 就绪)
|
||||
| | |
|
||||
| |=== 流式转发固件 =========>| (按 48B/packet 逐包发送)
|
||||
| |<=== ACK per packet =======| (每包 GD32 ACK)
|
||||
| | |
|
||||
| |--- FINISH_OTA ---------->| (刷写结束)
|
||||
| |<--- FINISH_OTA ----------| (GD32 跳转 APP)
|
||||
```
|
||||
|
||||
#### 断点续传设计
|
||||
|
||||
| 文件 | 内容 | 说明 |
|
||||
|---|---|---|
|
||||
| `ota_meta.json` | `{total_size, stored_size, chunk_count, last_seq}` | 记录传输进度 |
|
||||
|
||||
- APP 重连后先读 `ota_meta.json`,获取 `stored_size`
|
||||
- 从 `last_seq + 1` 继续发送剩余包
|
||||
- ESP32 追加写入 Flash(不需要从头擦写)
|
||||
|
||||
#### 存储完成判断
|
||||
|
||||
- APP 发送 `DATA_END` 后,ESP32 更新 `ota_meta.json` 的 `stored_size = total_size`
|
||||
- ESP32 检测到 `stored_size == total_size` 后,自动开始向 GD32 转发
|
||||
|
||||
#### 内存 Buffer
|
||||
|
||||
存储同时使用 2 × 4096 bytes 乒乓 buffer 接收 APP 数据,写入 Flash 后切换 buffer 继续接收。
|
||||
|
||||
---
|
||||
|
||||
## 5. WiFi OTA 实现
|
||||
|
||||
### 5.1 工作模式
|
||||
|
||||
- **AP 模式**:ESP32 作为热点,手机直连
|
||||
- **STA 模式**:ESP32 连接路由器,APP 通过 IP 访问
|
||||
|
||||
### 5.2 HTTP OTA 服务器
|
||||
|
||||
```
|
||||
HTTP POST /ota/upload
|
||||
Content-Type: application/octet-stream
|
||||
Body: <raw firmware binary>
|
||||
|
||||
Response:
|
||||
{"status": "ok", "progress": 100}
|
||||
```
|
||||
|
||||
### 5.3 APP → ESP32 控制协议
|
||||
|
||||
APP 与 ESP32 之间使用简化帧格式:
|
||||
|
||||
```
|
||||
| CMD(1B) | LEN(2B) | PAYLOAD(LEN) | CRC16(2B) |
|
||||
```
|
||||
|
||||
命令码直接对应上述 SPI 命令码(0xF0-0xF7),但数据字段为明文(加密由 ESP32 处理后再转发给 GD32)。
|
||||
|
||||
### 5.4 固件分包(APP → ESP32)
|
||||
|
||||
```
|
||||
| CMD(1B) | SEQ(2B) | SIZE(4B) | DATA(SIZE) | CRC32(4B) |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. BLE OTA 实现
|
||||
|
||||
### 6.1 GATT Profile 设计
|
||||
|
||||
| Handle | UUID | 属性 | 说明 |
|
||||
|---|---|---|---|
|
||||
| 0x0001 | - | Primary Service | OTA Service |
|
||||
| 0x0010 | `xxxx-xxxx-xxxx-0001` | Control Point | 写命令/通知(控制帧通道) |
|
||||
| 0x0011 | `xxxx-xxxx-xxxx-0002` | Data In | 写无回应(大规模数据通道) |
|
||||
| 0x0012 | `xxxx-xxxx-xxxx-0003` | Data Out | 通知 GD32 响应 |
|
||||
| 0x0013 | `xxxx-xxxx-xxxx-0004` | Version | 只读,ESP32 版本号 |
|
||||
|
||||
### 6.2 BLE 传输流程
|
||||
|
||||
1. APP 写 `Control Point` (0xF0) → ESP32,发起握手
|
||||
2. ESP32 通过 SPI 转发给 GD32
|
||||
3. GD32 响应通过 `Data Out` 通知 APP
|
||||
4. APP 写 `Data In` 流式发送固件分包(48B/packet)
|
||||
5. 每包完成后,ESP32 回复 ACK 到 `Data Out`
|
||||
6. APP 写 `Control Point` (0xF7) → FINISH
|
||||
7. GD32 响应后,APP 可断开连接
|
||||
|
||||
### 6.3 MTU 设置
|
||||
|
||||
- 建议 MTU ≥ 512 bytes
|
||||
- BLE 每次 `Data In` 最大传输 512 bytes(减去 ATT 头)
|
||||
- 大文件分包多次写入
|
||||
|
||||
---
|
||||
|
||||
## 7. SPI Master 配置
|
||||
|
||||
```c
|
||||
// ESP32 SPI2 配置 (VSPI)
|
||||
spi_device_handle_t spi;
|
||||
|
||||
// 建议时钟: 40MHz
|
||||
spi_bus_config_t bus_cfg = {
|
||||
.mosi_io_num = 8,
|
||||
.miso_io_num = 7,
|
||||
.sclk_io_num = 6,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
};
|
||||
|
||||
spi_device_interface_config_t dev_cfg = {
|
||||
.clock_speed_hz = 40 * 1000 * 1000, // 40MHz
|
||||
.mode = 0, // CPOL=0, CPHA=0
|
||||
.spics_io_num = 4, // GPIO4 as CS
|
||||
.queue_size = 1,
|
||||
.command_bits = 0,
|
||||
.address_bits = 0,
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 错误处理
|
||||
|
||||
| 场景 | 处理 |
|
||||
|---|---|
|
||||
| GD32 无响应 ACK | 超时 3s → 重发同一包,最多 3 次 → 报错 |
|
||||
| CRC 校验失败 | GD32 要求重发 → ESP32 重发当前包 |
|
||||
| 重发次数超限 | ESP32 终止传输,通知 APP |
|
||||
| WiFi 断开 | 暂停传输,等待重连 |
|
||||
| BLE 断开 | 通知 APP 重新连接 |
|
||||
| 认证失败 | 终止流程,通知 APP |
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 9. ESP32 自检与保护机制
|
||||
|
||||
ESP32-C3FH4 利用芯片内置传感器实现运行安全监控,不外挂额外传感器。
|
||||
|
||||
### 9.1 检测项
|
||||
|
||||
| 检测项 | 来源 | 阈值范围 | 处理动作 |
|
||||
|---|---|---|---|
|
||||
| 芯片温度 | 内部温度传感器 | 正常: < 80°C / 警告: 80-100°C / 停机: > 100°C | 警告时降频,停机时中止 OTA |
|
||||
| 供电电压 | VDD 电压传感器 | 正常: 2.5V - 3.6V / 警告: < 2.5V / 停机: < 2.3V | 警告时提示,停机时挂起任务 |
|
||||
| Flash 空间 | 系统 API | 可用 > 16KB(存储分区元数据) | 空间不足时拒绝 OTA 请求 |
|
||||
| WiFi 信号强度 | WiFi RSSI | 正常: > -70 dBm / 弱: -70 ~ -85 dBm / 断开: < -85 dBm | 弱时降低 OTA 速度,断开时暂停 |
|
||||
| BLE 连接状态 | BLE stack | 连接中 / 断开 | 断开时通知 APP 重连 |
|
||||
|
||||
### 9.2 检测频率
|
||||
|
||||
| 检测项 | 频率 | 方式 |
|
||||
|---|---|---|
|
||||
| 芯片温度 | 每 1 秒 | FreeRTOS Timer |
|
||||
| 供电电压 | 每 5 秒 | FreeRTOS Timer |
|
||||
| Flash 空间 | OTA 开始前检查 | 一次性 |
|
||||
| WiFi RSSI | 每 10 秒 | 轮询 |
|
||||
| BLE 连接 | 事件驱动 | BLE GAP 回调 |
|
||||
|
||||
### 9.3 本地判断逻辑
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
SYS_NORMAL, // 全部正常
|
||||
SYS_WARNING, // 任一检测项触发警告阈值
|
||||
SYS_CRITICAL, // 任一检测项触发停机阈值
|
||||
SYS_SAFE_SHUTDOWN // 强制停机
|
||||
} sys_health_state_t;
|
||||
|
||||
// 状态机优先级:SAFE_SHUTDOWN > CRITICAL > WARNING > NORMAL
|
||||
// 高优先级状态覆盖低优先级状态
|
||||
```
|
||||
|
||||
### 9.4 异常处理联动
|
||||
|
||||
| ESP32 健康状态 | 对 OTA 的影响 |
|
||||
|---|---|
|
||||
| `SYS_NORMAL` | 正常进行 |
|
||||
| `SYS_WARNING` | 降低 SPI 速率,继续 OTA,记录警告日志 |
|
||||
| `SYS_CRITICAL` | 暂停 OTA 传输,通知 APP 当前状态,等待恢复 |
|
||||
| `SYS_SAFE_SHUTDOWN` | 中止 OTA,发送 ABORT 到 GD32,保护性复位 |
|
||||
|
||||
### 9.5 日志记录
|
||||
|
||||
异常事件写入 Flash 日志分区(`system_log`),包含:
|
||||
|
||||
```
|
||||
[TIMESTAMP] TEMP_WARNING: 85°C
|
||||
[TIMESTAMP] VOLT_WARNING: 2.45V
|
||||
[TIMESTAMP] OTA_PAUSED: temp_critical
|
||||
[TIMESTAMP] OTA_RESUMED
|
||||
```
|
||||
|
||||
日志可由 APP 通过 WiFi/BLE 查询。
|
||||
|
||||
### 9.6 模块架构
|
||||
|
||||
```
|
||||
┌──────────────────────────────────┐
|
||||
│ System Monitor │
|
||||
│ (FreeRTOS Task, Priority High) │
|
||||
├──────────────────────────────────┤
|
||||
│ temp_monitor (1s timer) │
|
||||
│ voltage_monitor (5s timer) │
|
||||
│ wifi_monitor (10s timer) │
|
||||
│ ble_monitor (event-driven) │
|
||||
├──────────────────────────────────┤
|
||||
│ health_state_t state │
|
||||
│ → shared with OTA task via mutex│
|
||||
└──────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────┐
|
||||
│ OTA Relay Task │
|
||||
│ - checks health_state before │
|
||||
│ each GD32 DATA_PACKET send │
|
||||
│ - pauses/aborts on WARNING/ │
|
||||
│ CRITICAL/SAFE_SHUTDOWN │
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 10. ESP32 固件分区(参考)
|
||||
|
||||
```yaml
|
||||
# Partitions
|
||||
# Name, Type, SubType, Offset, Size
|
||||
otadata, data, ota, 0x9000, 0x2000 # OTA 数据区标记
|
||||
gd32_ota, data, spiffs, 0xB000, 0x40000 # GD32 固件存储区 (256KB)
|
||||
app0, app, ota_0, 0x4B000, 0x330000 # ESP32 自身 APP
|
||||
app1, app, ota_1, 0x3DC000, 0x330000
|
||||
spiffs, data, spiffs, 0x670000, 0x190000 # 文件系统(日志等)
|
||||
```
|
||||
|
||||
> **注意:** `gd32_ota` 分区大小根据实际 GD32 固件最大体积调整,建议预留 256KB 以上。
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
# ESP32-C3FH4 OTA 中继 — 开发任务清单
|
||||
|
||||
> 每完成一条就在前面打 `-[x]`(手机/PC 编辑器支持 Markdown checkbox)
|
||||
|
||||
---
|
||||
|
||||
## T0 — 项目基础设施
|
||||
|
||||
- [x] **T0.1** 搭建 ESP32-C3 工程
|
||||
- 基于 ESP-IDF v5.x,创建 CMake 项目骨架
|
||||
- 配置 chip=esp32c3
|
||||
|
||||
- [x] **T0.2** 配置 Flash 分区表
|
||||
- 定义 gd32_ota (256KB)、otadata、spiffs、app0、app1 分区
|
||||
|
||||
- [x] **T0.3** 配置 GPIO 引脚
|
||||
- SPI2 (VSPI) 引脚映射到 GPIO6-9
|
||||
- CS=GPIO4
|
||||
|
||||
- [x] **T0.4** FreeRTOS 任务框架
|
||||
- 建立 System Monitor / OTA Relay / WiFi Server / BLE GATT 四个任务
|
||||
- 配置任务优先级和栈大小
|
||||
|
||||
- [x] **T0.5** 日志系统
|
||||
- 统一 printf 接口
|
||||
- Flash 日志写入接口(system_log 分区)
|
||||
|
||||
---
|
||||
|
||||
## T1 — SPI 通信层
|
||||
|
||||
- [x] **T1.1** SPI2 总线初始化
|
||||
- VSPI, clock=40MHz, mode=0, queue_size=1
|
||||
|
||||
- [x] **T1.2** SPI 同步读写接口
|
||||
- `spi_sync_transfer(cmd, payload, &recv_buf)` 阻塞式
|
||||
|
||||
- [x] **T1.3** 协议帧封装/解析
|
||||
- SOF (0xEB) 检测
|
||||
- CMD 路由分发
|
||||
- CRC16 Modbus 校验/生成
|
||||
|
||||
- [x] **T1.4** SPI 超时与重发
|
||||
- 发送后等待 ACK 超时 3s
|
||||
- ≥3 次则报错并通知 APP
|
||||
|
||||
---
|
||||
|
||||
## T2 — 安全握手与认证(阶段一)
|
||||
|
||||
- [x] **T2.1** 搅伴算法实现
|
||||
- 输入:时间戳 + 内存地址 + rand
|
||||
- 处理:3轮单字节 + 2轮4字节
|
||||
- 输出:16 bytes nonce_server
|
||||
|
||||
- [x] **T2.2** AES 加解密实现
|
||||
- 预置主密钥,AES-128 CBC 模式
|
||||
- 使用 ESP32 mbedTLS 库
|
||||
- 实现 `aes_encrypt()` / `aes_decrypt()`
|
||||
|
||||
- [x] **T2.3** 握手状态机
|
||||
- RELAY_HANDSHAKE_IDLE → HANDSHAKE_WAIT → CHALLENGE_SENT → CHALLENGE_RESPOND → SESSION_KEY_SENT → AUTH_COMPLETE
|
||||
|
||||
- [x] **T2.4** 会话密钥管理
|
||||
- K_session 存储到 RAM
|
||||
- 握手完成后生效,OTA 结束时清除
|
||||
|
||||
---
|
||||
|
||||
## T3 — OTA 中继核心(阶段二~四)
|
||||
|
||||
- [x] **T3.1** SEND_SIZE 发送
|
||||
- 发送固件总大小 (4B LE) 给 GD32
|
||||
- 等待 0xBA C0 + seq 确认
|
||||
|
||||
- [x] **T3.2** Flash 写入接口
|
||||
- `gd32_ota_partition` 追加写入
|
||||
- 支持断点续传(追加不擦写)
|
||||
|
||||
- [x] **T3.3** `ota_meta.json` 管理
|
||||
- 读写元数据:total_size / stored_size / last_seq / chunk_count
|
||||
- 初始写入 / 中途更新 / 完成后校验
|
||||
|
||||
- [x] **T3.4** 固件流式转发
|
||||
- 存储完成后,从 Flash 按 48B/packet 逐包发给 GD32
|
||||
- 每包等待 GD32 ACK
|
||||
|
||||
- [x] **T3.5** DATA_END 处理
|
||||
- 最后一包带 IFEND 标志
|
||||
- 发送后等待 TRANSFER_DONE (0xBA C0 + FF FF FF FF)
|
||||
|
||||
- [x] **T3.6** FINISH_OTA
|
||||
- 发送 0x0A AC,等待 GD32 跳转确认
|
||||
- 清理会话密钥和 RAM 缓存
|
||||
|
||||
- [x] **T3.7** 四阶段状态机整合
|
||||
- 串联 T3.1~T3.6,覆盖全部 15 个状态
|
||||
- 与 System Monitor 健康状态联动
|
||||
|
||||
---
|
||||
|
||||
## T4 — WiFi OTA 服务器
|
||||
|
||||
- [x] **T4.1** WiFi 连接管理
|
||||
- AP 模式(ESP32 作为热点)
|
||||
- STA 模式(连接路由器)
|
||||
- 连接失败自动重试
|
||||
|
||||
- [x] **T4.2** HTTP 服务器
|
||||
- POST `/ota/upload` 接收固件 binary
|
||||
- 返回 JSON 响应:`{"status": "ok", "progress": xx}`
|
||||
|
||||
- [x] **T4.3** 固件分包存储
|
||||
- 接收 APP 分包(APP→ESP32 协议:CMD + SEQ + SIZE + DATA + CRC32)
|
||||
- 写入 Flash,更新 meta
|
||||
|
||||
- [x] **T4.4** 存储完成后自动触发
|
||||
- 检测 `stored_size == total_size`
|
||||
- 自动开始阶段一~四
|
||||
|
||||
- [x] **T4.5** 进度上报
|
||||
- HTTP 响应附带当前进度百分比
|
||||
- 存储进度实时更新
|
||||
|
||||
---
|
||||
|
||||
## T5 — BLE OTA 服务器
|
||||
|
||||
- [x] **T5.1** BLE GATT Service 创建
|
||||
- Primary Service (OTA)
|
||||
- Control Point (UUID 0001): 写命令/通知
|
||||
- Data In (UUID 0002): 写无回应,大数据通道
|
||||
- Data Out (UUID 0003): Notify,透传 GD32 响应
|
||||
- Version (UUID 0004): 只读,ESP32 版本号
|
||||
|
||||
- [x] **T5.2** Control Point 写处理
|
||||
- 解析 0xF0~0xF7 命令帧
|
||||
- 转发给 OTA Relay 任务处理
|
||||
|
||||
- [x] **T5.3** Data In 大数据通道
|
||||
- 接收 APP 固件分包,写入 Flash
|
||||
- 与 T3.2 / T3.3 共用存储逻辑
|
||||
|
||||
- [x] **T5.4** Data Out 通知
|
||||
- 将 GD32 ACK 透传给 APP(BLE Notify)
|
||||
|
||||
- [x] **T5.5** MTU 协商
|
||||
- 设置 MTU ≥ 512 bytes
|
||||
- 支持大数据分包多次写入
|
||||
|
||||
---
|
||||
|
||||
## T6 — ESP32 自检与保护
|
||||
|
||||
- [x] **T6.1** System Monitor 任务
|
||||
- FreeRTOS 高优先级任务
|
||||
- 通过 mutex 与 OTA Relay 共享 health_state
|
||||
|
||||
- [x] **T6.2** 芯片温度检测
|
||||
- 使用 `esp_temp_sensor` API
|
||||
- 1s 轮询,三级阈值判断(NORMAL < 80°C / WARNING 80-100°C / CRITICAL > 100°C)
|
||||
|
||||
- [x] **T6.3** 供电电压检测
|
||||
- 使用 `esp_efuse` / `esp_adc`
|
||||
- 5s 轮询,三级阈值判断(NORMAL 2.5-3.6V / WARNING < 2.5V / CRITICAL < 2.3V)
|
||||
|
||||
- [x] **T6.4** WiFi RSSI 监测
|
||||
- `esp_wifi_sta_get_rssi()`
|
||||
- 10s 轮询,三级阈值(NORMAL > -70 / WARNING -70~-85 / CRITICAL < -85 dBm)
|
||||
|
||||
- [x] **T6.5** BLE 连接状态回调
|
||||
- BLE GAP Event 驱动更新连接状态
|
||||
- 断开时通知 APP 重连
|
||||
|
||||
- [x] **T6.6** 健康状态联动
|
||||
- OTA 每包发送前检查 health_state
|
||||
- WARNING:降低 SPI 速率 + 记录日志
|
||||
- CRITICAL:暂停 OTA + 通知 APP
|
||||
- SAFE_SHUTDOWN:发送 ABORT + 清除 meta + 保护性复位
|
||||
|
||||
- [x] **T6.7** Flash 日志写入
|
||||
- 异常事件追加写入 system_log 分区
|
||||
- 格式:`[TIMESTAMP] EVENT_TYPE: value`
|
||||
|
||||
---
|
||||
|
||||
## T7 — 错误处理与恢复
|
||||
|
||||
- [x] **T7.1** GD32 超时处理
|
||||
- SPI 无响应 3s → 重发同一包
|
||||
- ≥3 次 → 报错并通知 APP
|
||||
|
||||
- [x] **T7.2** CRC 重发机制
|
||||
- GD32 要求重发时,从 Flash 重新读取当前包并重发
|
||||
- 限次重试(建议 3 次),超限则中止 OTA
|
||||
|
||||
- [x] **T7.3** WiFi/BLE 断连恢复
|
||||
- WiFi 断开自动重连(STA 模式)
|
||||
- BLE 断开通知 APP,保留 meta 支持断点续传
|
||||
|
||||
- [x] **T7.4** OTA 中止与清理
|
||||
- SAFE_SHUTDOWN 时发送 ABORT 到 GD32
|
||||
- 清除 meta,重置状态机,释放内存
|
||||
|
||||
---
|
||||
|
||||
## T8 — 测试与验证
|
||||
|
||||
- [ ] **T8.1** SPI 回环测试
|
||||
- 短接 MOSI/MISO(GPIO7↔GPIO9)
|
||||
- 自发自收验证帧协议正确性
|
||||
|
||||
- [ ] **T8.2** 协议 Mock 测试
|
||||
- 不接 GD32,用逻辑分析仪抓 SPI 数据
|
||||
- 验证 SOF/CMD/LEN/CRC16 帧格式正确
|
||||
|
||||
- [ ] **T8.3** Flash 读写验证
|
||||
- 写入 256KB 数据后读出校验
|
||||
- 确认存储逻辑正确
|
||||
|
||||
- [ ] **T8.4** 单包刷写测试
|
||||
- 发送小固件(< 1KB)给 GD32
|
||||
- 验证 GD32 写入正确
|
||||
|
||||
- [ ] **T8.5** 完整 OTA 测试
|
||||
- 传输一个完整固件(≥ 100KB)
|
||||
- 验证四阶段(握手→大小→数据→结束)全流程
|
||||
|
||||
- [ ] **T8.6** 断点续传测试
|
||||
- 模拟传输中断(断开连接)
|
||||
- 恢复后从断点继续,验证 meta 正确恢复
|
||||
|
||||
- [ ] **T8.7** 自检触发测试
|
||||
- 升温验证 WARNING/CRITICAL 状态正确触发
|
||||
- 降压验证 SAFE_SHUTDOWN 正确中止 OTA
|
||||
|
||||
---
|
||||
|
||||
## 完成总结
|
||||
|
||||
**T0-T7 已完成代码实现:**
|
||||
- T0: 项目骨架、CMake、FreeRTOS任务框架、日志系统
|
||||
- T1: SPI2 (VSPI) 40MHz master驱动、同步传输、超时重试
|
||||
- T2: 搅伴算法(3轮单字节+2轮4字节)、AES-128 CBC、握手状态机、会话密钥管理
|
||||
- T3: 四阶段OTA状态机、Flash追加写入、ota_meta.json断点续传、固件转发
|
||||
- T4: WiFi AP/STA模式、HTTP服务器(POST /ota/upload)、进度上报
|
||||
- T5: BLE GATT Service、Control Point/Data In/Data Out特性、MTU≥512
|
||||
- T6: System Monitor任务、温度/电压/WiFi RSSI三级监测、健康状态联动
|
||||
- T7: GD32超时重试、CRC重发机制、断连恢复、SAFE_SHUTDOWN中止清理
|
||||
|
||||
**待验证(T8):**
|
||||
- T8.1-T8.7 需要硬件在环测试(SPI连接GD32、WiFi/BLE连接APP)
|
||||
|
||||
**待确认参数(代码中已TODO标注):**
|
||||
- MASTER_KEY_HERE: AES-128 主密钥(16 bytes)
|
||||
- SCRAMBLE_CONST_HERE: 搅伴算法种子
|
||||
- CHIP_UID_HERE: GD32芯片唯一ID
|
||||
- OTA Service UUID: 需与APP侧协商
|
||||
Reference in New Issue
Block a user