ESP IoT Solution BLE OTA 升级方案:基于 esp_ble_conn_mgr 的分扇区固件传输协议与完整实践
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
BLE OTA(Over-The-Air 固件升级)是物联网设备量产后的核心维护能力。本篇文章围绕 ESP IoT Solution 仓库中的 BLE OTA Profile 文档,系统讲解基于esp_ble_conn_mgr实现的分扇区 BLE 固件传输协议:包括 OTA 服务(UUID0x8018)与四个特征值的定义、START/STOP 命令与固件数据包的逐字节格式、CRC16 三级校验机制,并结合 ble_ota 示例 的源码与配置文件,说明从编译烧录到 NimBLE/Bluedroid、Protocomm 安全通道、预加密 OTA 与 Delta OTA 的完整落地路径。读完本文,你将能够理解 BLE OTA 的协议细节,并能在自己的 ESP32 系列项目上复刻这套升级链路。
一、BLE OTA Profile 是什么
BLE OTA Profile 是建立在esp_ble_conn_mgr(连接管理组件)之上的一层"分扇区固件传输"协议。它解决的核心问题是:BLE 链路 MTU 有限、单包载荷小,而固件镜像通常有数百 KB 甚至更大,因此必须把固件切分为多个扇区(Sector),每个扇区再拆成多个**数据包(Packet)**顺序下发,由设备端边收边校验、边写入 flash。
根据 docs/en/bluetooth/ble_ota.rst 的定义,该 Profile 的核心特征为:
- 使用 OTA 服务 UUID
0x8018; - 解析来自主机(Central)的 START / STOP 命令以及固件数据包;
- 数据交付给应用层回调之前,执行严格的三重校验。
三重校验是协议可靠性的关键,也是本文后续重点展开的内容:
- 命令包 CRC16:命令帧必须通过 CRC 校验才会被解析执行;
- 扇区序号与分包序号连续性:扇区不能跳跃、包序号必须严格递增,否则立即回错误 ACK 请求重传;
- 扇区 CRC16:一个扇区(4KB)收满后,先校验整扇区 CRC,通过后才把该扇区数据回调给应用层(写入 OTA 分区)。
配套的服务定义见 docs/en/bluetooth/ble_ota_svc.rst:0x8018服务是传输层的,可以被不同的 OTA Profile 逻辑复用;当与ble_ota_raw配合使用时,命令和固件载荷的解析由该 Profile 完成。
二、OTA 服务与四个特征值
BLE OTA Service 在 GATT 层定义了 4 个特征值,完整说明如下:
| 特征值 | UUID | 权限 | 说明 |
|---|---|---|---|
| RECV_FW_CHAR | 0x8020 | Write, notify | 固件数据接收通道,设备端收到后回复 ACK |
| PROGRESS_BAR_CHAR | 0x8021 | Read, notify | 读取 / 上报升级进度 |
| COMMAND_CHAR | 0x8022 | Write, notify | OTA 命令下发与命令 ACK 回复 |
| CUSTOMER_CHAR | 0x8023 | Write, notify | 厂商自定义数据收发通道 |
从源码实现看,esp_ble_ota_raw.c 中通过esp_ble_ota_svc_init()初始化该服务,通过esp_ble_ota_notify_command_raw()向0x8022发送命令 ACK、通过esp_ble_ota_notify_recv_fw_raw()向0x8020发送固件 ACK,与应用层完全解耦。此外 Profile 初始化时还会同时初始化esp_ble_dis_init()(DIS 服务),对外广播软件/硬件版本信息,方便主机端识别设备固件版本。
三、协议格式详解
以下格式定义与数值均来自 examples/bluetooth/ble_ota/README.md,并被源码 esp_ble_ota_raw.c 一一印证。
3.1 命令包格式(Command Package)
命令包固定 20 字节,布局如下:
| 单位 | Command_ID | PayLoad | CRC16 |
|---|---|---|---|
| 字节 | Byte: 0 ~ 1 | Byte: 2 ~ 17 | Byte: 18 ~ 19 |
Command_ID 取值(源码中以BLE_OTA_CMD_START/STOP/ACK宏定义):
- 0x0001(START):开始 OTA。Payload bytes(2 ~ 5) 填入固件总长度(小端 32 位,源码中通过
get_u32_le(&data[2])读取),其余 Payload 置 0。CRC16 计算 bytes(0 ~ 17)。 - 0x0002(STOP):结束 OTA。剩余 Payload 全部置 0,CRC16 计算 bytes(0 ~ 17)。
- 0x0003(ACK):命令回复。Payload bytes(2 ~ 3) 填入被回复的命令 ID,Payload bytes(4 ~ 5) 为该命令的响应结果——
0x0000表示接受(ACCEPT),0x0001表示拒绝(REJECT),其余 Payload 置 0。CRC16 计算 bytes(0 ~ 17)。
一个值得注意的实现细节:START 命令的 ACK 在ack_status之后还允许携带扩展字段(仍被 bytes(0 ~ 17) 的 CRC 覆盖)——byte 6 为推荐的扇区发送窗口(Central 最多可流水线预发的扇区数),byte 7 保留。该窗口由esp_ble_ota_raw_set_sector_send_window_for_ringbuf()根据应用层环形缓冲容量动态计算:窗口 = ringbuf 容量 / 4096,下限 1、上限 64。这解释了示例中OTA_RINGBUF_SIZE = 8192时窗口为 2 的来源,可在吞吐量与内存占用之间取得平衡。
3.2 固件包格式(Firmware Package)
主机发送的固件包格式如下:
| 单位 | Sector_Index | Packet_Seq | PayLoad |
|---|---|---|---|
| 字节 | Byte: 0 ~ 1 | Byte: 2 | Byte: 3 ~ (MTU_size - 4) |
- Sector_Index:当前写入的扇区号。扇区号从 0 开始递增、不能跳跃,必须发送完 4K(
BLE_OTA_SECTOR_SIZE = 4096)数据后才能进入下一个扇区,否则设备立即回复错误 ACK 请求重传。 - Packet_Seq:扇区内包序号,从 0 递增。特殊值
0xFF表示该扇区的最后一包:此时 Payload 末尾 2 字节为整个扇区 4K 数据的 CRC16,其余字节填充0x0。设备端收到末包后校验数据总长度与 CRC,通过则回复正确 ACK,主机再开始发送下一个扇区。
3.3 固件应答包格式(ACK Package)
设备回复主机的应答包格式如下:
| 单位 | Sector_Index | ACK_Status | CRC16 |
|---|---|---|---|
| 字节 | Byte: 0 ~ 1 | Byte: 2 ~ 3 | Byte: 18 ~ 19 |
ACK_Status 取值(源码中的ble_ota_fw_ack_status_t):
- 0x0000:成功(SUCCESS);
- 0x0001:CRC 错误(CRC_ERR);
- 0x0002:Sector_Index 错误(INDEX_ERR),bytes(4 ~ 5) 携带设备期望的 Sector_Index,便于主机跳转到正确扇区续传;
- 0x0003:Payload 长度错误(LEN_ERR)。
3.4 CRC16 算法与校验流程
源码中的校验算法为CRC16-CCITT(多项式0x1021,初值0x0000),与 BLE OTA 主机工具保持兼容。设备端完整的接收校验流水线(对应文档所述"三重校验")如下:
- 命令包到达后先检查长度是否为固定 20 字节,再以 bytes(0 ~ 17) 计算 CRC 并与 bytes(18 ~ 19) 比对,不通过直接回 REJECT;
- 固件包到达后先比对
Sector_Index是否等于当前期望扇区,不匹配回0x0002; - 非末包(
Packet_Seq != 0xFF)还要比对包序号是否连续,乱序回0x0003; - 末包拆出尾部 2 字节 CRC,与设备端对整扇区累计数据的 CRC 计算结果比对,不一致回
0x0001并清空该扇区接收状态等待重传; - 全部通过后,通过
esp_ble_ota_raw_recv_fw_data_callback()注册的recv_fw回调把整扇区数据交给应用层,同时累计recv_total_len与 START 命令声明的固件总长度比对,防止超长数据。
四、示例工程实战:从编译到 OTA 完成
官方示例位于 examples/bluetooth/ble_ota,其工作流程为:通过 BLE 接收固件,以扇区为单位依次写入 flash,直至升级完成。
4.1 分区表
升级依赖 OTA 分区,示例的 partitions.csv 需保证有足够的 app0 / app1 双 OTA 分区;设备当前固件运行在某个 OTA 分区,新固件写入另一个,esp_ota_set_boot_partition()切换启动分区后重启生效。
4.2 应用层数据流与源码佐证
从 app_main.c 可以看到一条完整的"BLE 数据 → 环形缓冲 → OTA 写入"流水线:
ble_ota_ringbuf_init(OTA_RINGBUF_SIZE)创建 8KB 字节型 RingBuffer;- Profile 的固件接收回调
ota_recv_fw_cb()把每个通过校验的 4K 扇区写入 RingBuffer; - 独立任务
ota_task()通过xRingbufferReceive()阻塞取出数据,调用esp_ota_write()(普通 OTA)或esp_delta_ota_feed_patch()(Delta OTA)写入目标分区; - 当累计写入长度达到
esp_ble_ota_get_fw_length()(即 START 命令声明的固件长度)后,执行esp_ota_end()、esp_ota_set_boot_partition()并esp_restart()重启到新固件。
该任务栈大小OTA_TASK_SIZE = 8192,优先级 5;notify_sem计数信号量用于控制读写速率,避免环形缓冲溢出。
4.3 编译与配置(通用)
示例提供两套主机栈配置:
- Bluedroid(双模):
sdkconfig.ci.bluedroid; - NimBLE(BLE only):
sdkconfig.ci.nimble。
默认配置见 sdkconfig.defaults。标准构建命令为:
idf.py set-target esp32h2 idf.py menuconfig idf.py build flash monitor4.4 主机侧 APP
协议设计为通用 BLE OTA 主机服务,示例配套的主机端 APP 由 Espressif 官方提供(esp-ble-ota-android)。使用 Bluedroid 且需与该 APP 兼容时,若选择Bluedroid - Dual-mode,需按以下步骤关闭 BLE 5.0 特性:
Component config>Bluetooth>Bluedroid Options,取消勾选Enable BLE 5.0 features;Component config>Bluetooth>Controller Options,取消勾选Enable BLE 5 feature。
4.5 ESP32-H2 使用注意
构建 ESP32-H2 的 BLE OTA 示例需使用ESP-IDF 5.0 或更高版本。另外需保证通过 BLE 传输的固件镜像与示例设置的FLASHSIZE(4MB)一致,即确认ESPTOOLPY_FLASHSIZE_4MB已被设置,否则会出现镜像尺寸与 flash 布局不匹配的问题。
五、进阶升级模式
示例在基础"裸传输"之上还支持三种进阶模式,均可通过idf.py menuconfig在Example Configuration → Type of OTA中选择:
5.1 NimBLE 高吞吐配置
若主机栈选择 NimBLE,为获得最大吞吐:
Component config → Bluetooth → Bluetooth → Host → NimBLE - BLE only;Component config → Bluetooth → NimBLE Options → Preferred MTU size in octets设为517(更大 MTU 意味着单个固件包可携带更多载荷,扇区切包次数更少)。
5.2 Protocomm 安全通道 OTA
利用 protocomm 层做安全握手与加密传输:
Example Configuration → Type of OTA → Use protocomm layer for security;Component config → OTA Manager → Type of OTA → Enable protocomm level security。
该模式下 app_main.c 走esp_ble_ota_init()/esp_ble_ota_start()流程,支持 Security 1(PoP 口令)与 Security 2(salt/verifier 认证)两种安全等级,示例中通过CONFIG_EXAMPLE_OTA_SECURITY_VERSION_1/2切换。
5.3 预加密 OTA(Pre-Encrypted OTA)
固件在产线/发布侧先用 rsa_key/private.pem 私钥加密,设备端用内置公钥解密后写入:
- 若使用 ESP32-S3,需将
Component config → Bluetooth → Bluetooth → NimBLE Options → NimBLE Host task stack size设为8192; Example Configuration → Type of OTA → Use Pre-Encrypted OTA;Component config → OTA Manager → Type of OTA → Enable pre encrypted OTA。
源码中通过esp_encrypted_img_decrypt_start()创建解密句柄,再以esp_ble_ota_recv_fw_data_callback(cb, decrypt_handle)将解密流程挂入固件接收回调链。
5.4 Delta OTA(差分升级)
Delta OTA 只传输新旧固件之间的差异补丁,可显著减小 BLE 传输量:
- NimBLE:选择
Use Delta OTA;若目标芯片为 ESP32-H2 / ESP32-C6,还需在 NimBLE Options 中取消Enable BLE 5 feature,并在Example Configuration取消Enable Extended Adv。 - Bluedroid:选择
Use Delta OTA,并取消Enable BLE 5.0 feature与Enable BLE 4.2 feature。
补丁生成工具位于 tools/esp_delta_ota_patch_gen.py,先安装依赖:
pip install -r tools/requirements.txt生成补丁(必须使用设备上当前运行的固件作为base_binary,请务必保留设备内固件的备份):
python esp_delta_ota_patch_gen.py create_patch --chip <target> --base_binary <base_binary> --new_binary <new_binary> --patch_file_name <patch_file_name>校验补丁正确性:
python esp_delta_ota_patch_gen.py verify_patch --chip <target> --base_binary <base_binary> --new_binary <new_binary> --patch_file_name <patch_file_name>生成的补丁文件部署到 OTA 更新服务器后,设备端在 app_main.c 中通过verify_patch_header()(校验补丁魔数0xfccdde10与当前固件 SHA-256 摘要)和verify_chip_id()(校验 chip_id 与CONFIG_IDF_FIRMWARE_CHIP_ID一致)双重把关,再经esp_delta_ota_feed_patch()边接收边还原新固件,最终由esp_delta_ota_finalize()完成落盘。
六、与其他 BLE Profile 的协作与复用
本文所述的 OTA Service(0x8018)是传输层服务,与 Profile 逻辑解耦:同样的四个特征值可以承载不同的 OTA Profile 实现;而ble_ota_raw这类 Profile 只负责命令/固件载荷的解析与校验,最终数据通过应用回调交付。这种分层设计让开发者既可以按本文协议直接复用,也可以基于esp_ble_ota_svc自行实现更贴近业务的安全校验、续传或分包策略。仓库中 ble_profiles 文档 对该组件族的整体结构有更全面的说明,可作为扩展阅读。
七、关键文件索引
- Profile 文档:docs/en/bluetooth/ble_ota.rst(中文版)
- 服务文档:docs/en/bluetooth/ble_ota_svc.rst
- Profile 实现:components/bluetooth/ble_profiles/esp/ble_ota_raw/src/esp_ble_ota_raw.c 与 esp_ble_ota_raw.h
- 示例工程:examples/bluetooth/ble_ota(主程序 app_main.c、分区表 partitions.csv、示例说明 README.md)
结语
BLE OTA 的关键在于"小管道传大文件":通过 4KB 扇区切分、20 字节命令帧、CRC16-CCITT 三级校验,把不可靠的无线链路变成可控的可靠传输。本文从 Profile 协议定义、GATT 特征值、逐字节帧格式到源码级校验流程,再到 Bluedroid/NimBLE、Protocomm、预加密与 Delta OTA 的完整配置路径,均可在当前仓库中逐文件验证。开发者可直接基于 examples/bluetooth/ble_ota 起步,按需裁剪出适合自己产品形态的 BLE 升级方案。
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考