跳转至

TWS 耳机 OTA 架构设计

文档版本: v1.0 | 最后更新: 2026-05-17 芯片: 杰理 AC7002 | BLE 协议: docs/ble-protocol.md §3.3


1. 概览

OTA (Over-The-Air) 升级是 TWS 耳机的关键能力。升级数据流为 三段式 架构:

1
2
3
[云端/CDN] ──https──> [手机 APP] ──BLE──> [耳机固件]
                    (用户交互 / 进度展示)

1.1 核心原则

原则 说明
手机为代理 手机是唯一的网络入口,耳机不直接访问互联网
断点续传 OTA 过程中 BLE 断开后,重连可恢复(需重新 OTA_START)
签名验证 所有固件包携带 HMAC-SHA256 签名,耳机端验证
进度透明 手机端展示实时进度(精度 0.01%),提升用户体验
异常回滚 升级失败自动回滚到旧固件,保持设备可用

2. 固件包格式

2.1 固件包结构 (.twsfw)

┌──────────────────────────────────────────────┐
│  文件头 (64 bytes)                            │
│  ├─ Magic:        "TWSFW" + v1 (4+1 bytes)   │
│  ├─ Version:      BCD 编码 (3 bytes)          │
│  ├─ Hardware ID:  目标硬件型号 (2 bytes)        │
│  ├─ Image Size:   固件大小 (4 bytes, LE)       │
│  ├─ CRC32:        固件数据 CRC (4 bytes)       │
│  ├─ Signature:    HMAC-SHA256 (32 bytes)      │
│  ├─ Reserved:     14 bytes                    │
├──────────────────────────────────────────────┤
│  固件数据 (N bytes)                           │
│  └─ 原始固件二进制 (AC7002 可执行映像)          │
├──────────────────────────────────────────────┤
│  填充 (对齐到 16 bytes)                       │
└──────────────────────────────────────────────┘

2.2 固件包元信息 (Manifest)

云端同时维护一份 JSON manifest,APP 下载固件前先检查:

{
  "firmware_version": "1.24",
  "hardware_id": 0x0100,
  "file_url": "https://ota.example.com/tws/v1.24.twsfw",
  "file_size": 1048576,
  "file_crc32": "0xA1B2C3D4",
  "file_sha256": "e3b0c44298fc1c149afbf4c8996fb924...",
  "release_notes": "修复蓝牙断连问题",
  "release_date": "2026-05-17",
  "min_app_version": "0.1.0",
  "force_update": false
}

3. 云端架构

3.1 组件

组件 技术选型 说明
CDN AWS S3 / CloudFront 存储和分发固件包
OTA API RESTful (FastAPI/Go) 查询最新版本、下载地址
固件签名服务 私有密钥 (HSM) 生成 HMAC-SHA256 签名
管理后台 Web 控制台 上传固件、灰度发布、AB 测试

3.2 API 接口

1
2
3
4
5
6
7
8
9
GET /api/v1/ota/latest?hw=0x0100&fw=0x0123
  → { has_update: bool, manifest: {...} }

GET /api/v1/ota/download/<version>/<hw>.twsfw
  → 固件包二进制 (通过 CDN 302 跳转)

POST /api/v1/ota/report
  Body: { device_id, from_version, to_version, result, error_code }
  → 设备升级结果上报

3.3 灰度发布策略

  1. 白名单 — 仅指定设备 ID 可获取更新
  2. 百分比 — 按设备 ID hash 分配(1% → 5% → 50% → 100%)
  3. 区域/渠道 — 按固件渠道号区分

4. APP 端架构

4.1 升级流程 (手机端)

┌──────────────────────────────────────────────────────────┐
│                        APP                               │
├──────────────────────────────────────────────────────────┤
│  1. 检查更新                                              │
│     ├─ 读取当前版本 (BLE → FEE1 Read)                     │
│     └─ 请求 OTA API → /latest                             │
│                                                          │
│  2. 下载固件 (后台 Service)                                │
│     ├─ 使用 download manager / WorkManager                │
│     ├─ 支持断点续传                                       │
│     └─ 下载完成后校验 SHA256 + CRC32                      │
│                                                          │
│  3. 传输固件 (BLE)                                        │
│     ├─ OTA_START → 通知耳机准备                           │
│     ├─ 分块写入 (Write Without Response, 默认 128 bytes)   │
│     ├─ 监听进度 (FEE7 Notify)                              │
│     └─ OTA_COMMIT → 触发耳机校验                          │
│                                                          │
│  4. 结果确认                                              │
│     ├─ 收到 RESULT_SUCCESS → 提示重启                     │
│     └─ 收到 RESULT_FAIL → 展示错误码 + 重试               │
└──────────────────────────────────────────────────────────┘

4.2 状态机

           ┌─────────┐
           │  IDLE   │ ◄───────────────────┐
           └────┬────┘                     │
                │ 用户点击"检查更新"          │
                ▼                          │
           ┌─────────┐                    │
           │ CHECKING│                    │
           └────┬────┘                    │
                │ 有更新?                   │
           ┌────┴────┐                    │
           ▼         ▼                    │
     ┌─────────┐  ┌──────┐                │
     │DOWNLOAD │  │  IDLE│ (已是最新)       │
     └────┬────┘  └──────┘                │
          │ 下载完成                        │
          ▼                               │
     ┌─────────┐                          │
     │TRANSFER │ ◄──── 重连后 OTA_START    │
     └────┬────┘                          │
          │ 传输完成                        │
          ▼                               │
     ┌─────────┐      ┌────────┐          │
     │ COMMIT  │─────>│ RESULT │───────────┘
     └─────────┘      └────────┘
                     ┌────┴────┐
                     ▼         ▼
                 ┌──────┐  ┌──────┐
                 │SUCCESS│  │ FAIL │
                 └──────┘  └──────┘

4.3 关键类设计

┌─────────────────────────────────────────────┐
│ OtaManager                                   │
├─────────────────────────────────────────────┤
│ - state: OtaState                            │
│ - progress: double                           │
│ - currentFirmware: FirmwareInfo              │
│ - bleService: EarbudBleService               │
│ - apiClient: OtaApiClient                    │
├─────────────────────────────────────────────┤
│ + checkForUpdate(): Future<UpdateInfo>       │
│ + downloadFirmware(url): Future<File>        │
│ + startTransfer(file): Future<void>          │
│ + abortTransfer(): Future<void>              │
│ + retry(): Future<void>                      │
│ - _writeBlocks(): Future<void>               │
│ - _verifyFile(file): Future<bool>            │
│ - _reportResult(result): Future<void>        │
└─────────────────────────────────────────────┘

4.4 性能考量

参数 建议值 说明
BLE MTU ≥ 512 bytes 减少协议开销
数据块大小 128~512 bytes 默认 128, 可协商
写入间隔 无 (Write Without Response) 连续写入
连接间隔 7.5 ms OTA 期间缩短间隔
超时 30s 无响应视为超时 触发重连/重试
下载缓存 原始文件 + SHA256 下载完成后再传输

1 MB 固件预估传输时间:

块大小 块数 写入延迟 总时间 (约)
128 B 8192 ~10 ms/块 ~82 s
256 B 4096 ~12 ms/块 ~49 s
512 B 2048 ~15 ms/块 ~31 s

5. 耳机端架构 (AC7002)

5.1 固件存储布局

┌──────────────────────────────────────────┐
│ Bootloader (32 KB)                       │
│  - 上电自检                              │
│  - 固件完整性校验                         │
│  - 回滚逻辑                              │
├──────────────────────────────────────────┤
│ Active Bank A (512 KB)                   │
│  - 当前运行固件                           │
├──────────────────────────────────────────┤
│ Active Bank B (512 KB)                   │
│  - OTA 新固件写入目标                      │
├──────────────────────────────────────────┤
│ Config / Calibration (16 KB)             │
│  - 配对信息、校准数据                      │
├──────────────────────────────────────────┤
│ OTA Scratch (64 KB)                      │
│  - 临时缓冲 / 日志                         │
└──────────────────────────────────────────┘

5.2 升级流程 (耳机端)

┌───────────────────────────────────────────────┐
│                  耳机                           │
├───────────────────────────────────────────────┤
│  1. 收到 OTA_START (size=1048576)              │
│     ├─ 擦除 Bank B                            │
│     └─ 回复 ACK                                │
│                                               │
│  2. 接收数据块 (循环)                           │
│     ├─ 校验 Sequence Number 连续性              │
│     ├─ 写入 Bank B (offset = seq * block_size)  │
│     └─ 定期发送 OTA_PROGRESS Notify             │
│                                               │
│  3. 收到 OTA_COMMIT (crc32)                    │
│     ├─ 读取整个 Bank B 计算 CRC                 │
│     ├─ 验证固件签名 (HMAC-SHA256)               │
│     ├─ 验证 Hardware ID 匹配                    │
│     ├─ 成功 → 标记 Bank B 为 active             │
│     │         → 发送 RESULT_SUCCESS             │
│     │         → 系统重启 (从 Bank B 启动)        │
│     └─ 失败 → 保留 Bank A                      │
│               → 发送 RESULT_FAIL + 错误码       │
│                                               │
│  4. 重启后                                    │
│     ├─ Bootloader 检查 Bank B 有效性            │
│     ├─ 有效 → 启动 Bank B                      │
│     └─ 无效 → 回滚 Bank A                      │
└───────────────────────────────────────────────┘

5.3 安全机制

机制 说明
HMAC-SHA256 签名 固件包头携带 32 字节签名,耳机使用预置密钥验证
CRC32 校验 每块数据 + 整体固件的 CRC 双重校验
递增序列号 防重放攻击
Hardware ID 匹配 防止刷入不兼容固件变砖
双 Bank 备份 升级失败可回滚,保证设备永远可升级
Watchdog 超时 OTA 过程中若 30s 无数据,自动中止并回滚

6. 异常处理

6.1 异常场景与恢复策略

场景 表现 恢复策略
BLE 断开 OTA 中断, FEE7 上报断连错误 APP 重连后重新 OTA_START (已擦除的 Bank B 可复用)
下载失败 固件下载不完整 APP 重试下载 (断点续传)
电量不足 升级中低电量关机 APP 在升级前检查电量 > 30%
Flash 写入失败 耳机回复 NACK 重试; 若持续失败, 提示硬件故障
CRC 校验失败 OTA_COMMIT 阶段回复 NACK 重新下载 + 重新传输
签名无效 固件被拒绝 提示固件不合法, 联系技术支持
APP 崩溃 升级中断 重启 APP 后检查设备状态, 提供恢复选项

6.2 恢复流程

1
2
3
4
5
6
7
8
9
[升级中断]
    ├─ 耳机侧: Bank B 未标记为 active
    │   → 重启后仍从 Bank A 启动
    └─ APP 侧: 重连后
        ├─ 读取设备版本 → 判断是否已升级成功
        ├─ 若版本未变 → 提供继续/重新升级选项
        └─ 若版本已变 → 升级已完成, 更新 UI

7. 数据流时序图

7.1 正常升级

手机 APP                   云端/CDN                   耳机
    │                        │                       │
    │──── GET /latest ──────>│                       │
    │<─── manifest.json ─────│                       │
    │                        │                       │
    │──── GET firmware ─────>│                       │
    │<─── .twsfw 文件 ───────│                       │
    │ (后台下载, 显示进度)     │                       │
    │                        │                       │
    │  ─── OTA_START(1MB) ─────────────────────────>│
    │  <── ACK ─────────────────────────────────────│
    │                                               │
    │  ─── DATA Block #0 (seq=0, 128B) ───────────>│
    │  ─── DATA Block #1 (seq=1, 128B) ───────────>│
    │  ─── ...                                     │
    │  <── NOTIFY(PROGRESS=12.34%) ────────────────│
    │  ─── ...                                     │
    │  ─── DATA Block #8191 ──────────────────────>│
    │                                               │
    │  ─── OTA_COMMIT(CRC32) ─────────────────────>│
    │  <── NOTIFY(PROGRESS=100.00%) ───────────────│
    │  <── NOTIFY(RESULT_SUCCESS, v1.24) ──────────│
    │                                               │
    │──── POST /report (success) ──>│               │
    │                        │                       │
    │ 提示用户 "升级成功, 重启中"                      │
    │                        │                       │
    │                                               │
    │          [耳机重启, 从 Bank B 启动]             │

7.2 升级失败 + 回滚

手机 APP                        耳机
    │                            │
    ├─── DATA Block #500 ──────>│ (Flash 写入失败)
    │<── NOTIFY(RESULT_FAIL,     │
    │        0x0001) ───────────│
    │                            │
    │  提示 "存储写入失败, 重试"    │
    │                            │
    ├─── OTA_ABORT ────────────>│
    │                            │ (保留 Bank A)
    │                            │
    │  [重连后重新开始]            │
    ├─── OTA_START(1MB) ───────>│ (重新擦除 Bank B)
    └─── ...                    │

8. 开发计划

Phase 1 — 基础能力 (当前阶段)

  • BLE OTA 协议定义 (0xFEE4~0xFEE7)
  • 固件包格式设计
  • OTA 架构文档 (本文档)
  • APP 端 OtaManager 实现 (mock 阶段)
  • 耳机端 Bank 切换逻辑

Phase 2 — 联调测试

  • AC7002 OTA 固件端实现
  • APP 端 BLE OTA 传输实现
  • 端到端 1 MB 固件升级测试
  • 异常场景覆盖测试

Phase 3 — 云端集成

  • OTA API 服务器搭建
  • CDN 配置 + 固件分发
  • 签名服务接入
  • 灰度发布能力

Phase 4 — 生产就绪

  • 断点续传优化
  • 批量升级管理后台
  • 升级结果统计看板
  • 安全审计

9. 附录

9.1 相关文档

文档 说明
docs/ble-protocol.md BLE 通信协议 (含 OTA 详细定义)
docs/ble-services-final.md 最终确认的 GATT 服务表
firmware/src/main.c 固件主程序入口
companion-app/lib/screens/ota_screen.dart APP 端 OTA UI 原型

9.2 关键指标

指标 目标值
1 MB 固件传输时间 < 60s (MTU=512, 块大小=512)
升级成功率 > 99.5%
失败回滚恢复率 100%
最低可升级电量 30%
固件包最大大小 2 MB (受 Bank B 容量限制)

文档结束