资讯动态

ESP IoT Solution BLE OTA 实战:基于 ble_conn_mgr 与 ble_ota_raw 的无线固件升级方案

发布时间:2026/9/20 1:55:16 来源:尧图企业网站定制
ESP IoT Solution BLE OTA 实战基于 ble_conn_mgr 与 ble_ota_raw 的无线固件升级方案【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution本文以 examples/bluetooth/ble_profiles/ble_ota 示例为核心系统讲解如何在 ESP-IDF 中基于ble_conn_mgrBLE 连接管理组件、ble_ota_rawBLE OTA 协议解析 profile与 ESP-IDF 原生 OTA 接口搭建一条完整的手机/主机 → BLE GATT → 校验 → 分区写入 → 重启升级固件链路。读完本文你将掌握示例的启动流程、菜单配置、分区表要求、OTA 任务与 ring buffer 的协同原理并能据此在自己的项目中落地 BLE OTA 升级能力。一、示例概览BLE OTA 的三大支柱BLE OTAOver-The-Air 固件升级相比串口/USB 升级最大的价值在于无需任何有线连接只要设备处于 BLE 广播范围内即可完成固件更新非常适合消费电子、智能家居等已出货产品的远程维护场景。本示例把链路拆分为三个清晰的分层每一层各司其职层次组件职责BLE GATT 传输层esp_ble_conn_mgr管理广播、连接、MTU、连接参数等通用 BLE 功能OTA 协议解析层ble_ota_rawprofile解析 START/STOP 命令、按 4 KB sector 校验 CRC16、通过特征回 ACK固件落盘层ESP-IDF OTA APIesp_ota_begin/write/end/set_boot_partition将校验通过的固件数据写入下一个 OTA 分区从源码结构看这种分层设计让协议解析ble_ota_raw与业务落盘示例中的 OTA 任务彻底解耦协议层只关心数据对不对应用层只关心往哪写、怎么写。二、示例工作流程从广播到重启的五个步骤按 README.md 的说明示例启动后依次执行初始化 BLE 连接管理器并以 OTA 服务 UUID0x8018开始广播注册 OTA profile 固件回调接收经过校验的 sector 数据将收到的固件数据推入 ring buffer环形缓冲区独立的 OTA 任务从 ring buffer 读取数据写入下一个 OTA 分区完成esp_ota_end()与esp_ota_set_boot_partition()后重启进入新分区。上述流程在 app_main.c 的app_main()中体现得十分直接调用顺序与文档完全一致ESP_ERROR_CHECK(nvs_flash_init()); // NVS存放 OTA 状态等 ble_ota_raw_ringbuf_init(BLE_OTA_RAW_RINGBUF_DEFAULT_SIZE); // 建立固件环形缓冲 ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_event_handler_register(BLE_CONN_MGR_EVENTS, ESP_EVENT_ANY_ID, app_ble_conn_event_handler, NULL)); ESP_ERROR_CHECK(esp_ble_conn_init(config)); // 初始化连接管理器 ESP_ERROR_CHECK(esp_ble_ota_raw_init()); // 注册 OTA 服务 DIS 服务 ESP_ERROR_CHECK(esp_ble_ota_raw_recv_fw_data_callback(app_ble_ota_raw_recv_fw_cb)); // 注册固件回调 ble_ota_raw_task_init(); // 创建 OTA 落盘任务 ESP_ERROR_CHECK(esp_ble_conn_start()); // 开始广播其中连接管理器配置了广播数据的关键参数见 app_main.c 与 app_main.cBLE_OTA_RAW_ADV_UUID16 0x8018广播中携带的 16 位服务 UUID手机端据此识别可升级设备include_service_uuid 1、adv_uuid_type BLE_CONN_UUID_TYPE_16将服务 UUID 放入广播包device_name取自 Kconfig 的CONFIG_EXAMPLE_BLE_OTA_RAW_DEVICE_NAME默认ESP-C919附带一段制造商自定义数据s_ble_ota_raw_manu_data可用于机型识别。app_main中还在默认事件循环上注册了BLE_CONN_MGR_EVENTS处理器用于打印连接/断开、MTU 协商、连接参数更新等事件如itvl_min/itvl_max以 1.25 ms 为单位、supervision_timeout以 10 ms 为单位方便观察传输过程。三、构建与烧录示例是标准 ESP-IDF 工程构建命令与普通项目无异README.mdidf.py set-target your-target idf.py build flash monitor适用前提示例底层依赖 NimBLE即ble_conn_mgr目前仅支持 NimBLE 协议栈Bluedroid 支持计划在后续版本加入见 ble_conn_mgr/README.md。因此目标芯片需要支持 BLE且应选择带 BLE 的 ESP32 系列如 ESP32、ESP32-C3、ESP32-S3 等具体以所用 IDF 版本与目标芯片为准。3.1 默认配置说明sdkconfig.defaults 预置了本次示例所需的开关值得逐条理解CONFIG_BT_ENABLEDy # 启用蓝牙 CONFIG_BT_NIMBLE_ENABLEDy # 使用 NimBLE 协议栈 CONFIG_BLE_OTA_RAW_PROFILEy # 启用 ble_ota_raw OTA profile CONFIG_ESPTOOLPY_FLASHSIZE_4MBy # 4 MB Flash保证能放下两个 OTA 分区 CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU498 # 提升 MTU加快固件传输吞吐 CONFIG_BT_NIMBLE_LOG_LEVEL_WARNINGy CONFIG_PARTITION_TABLE_CUSTOMy # 使用自定义分区表 CONFIG_PARTITION_TABLE_CUSTOM_FILENAMEpartitions.csv CONFIG_PARTITION_TABLE_OFFSET0x8000 CONFIG_PARTITION_TABLE_MD5y其中CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU498很关键BLE 默认 MTU 只有 23 字节单包有效载荷极小调高 MTU 后单次写操作可携带更多固件字节直接决定 OTA 传输速度。3.2 分区表OTA 升级的地基示例自带 partitions.csv这是 BLE OTA 能落盘的前提nvs, data, nvs, , 0x4000, otadata, data, ota, , 0x2000, phy_init, data, phy, , 0x1000, ota_0, app, ota_0, , 1500K, ota_1, app, ota_1, , 1500K,要点otadata分区存放当前/下一个启动分区的选择记录esp_ota_set_boot_partition()修改的就是它ota_0/ota_1两个 APP 分区每个 1500 KB分别承担当前运行固件与待写入固件是 A/B 双分区 OTA 的基本形态注释提醒若增大了 bootloader 体积需同步调整各分区 Offset 避免重叠。3.3 菜单配置运行idf.py menuconfig可修改Example Configuration→BLE OTA Device NameBLE 广播设备名Kconfig 定义见 Kconfig.projbuildBluetooth host/backend 选项根据目标芯片选择所需的蓝牙主机/后端配置。四、源码纵深ring buffer OTA 任务如何协同示例的核心工程代码集中在 main/ble_ota_raw.c与同名的 profile 组件源码区分开这是示例私有的业务层。它的设计思路是BLE 回调中断/低优先级上下文与 Flash 写入耗时操作之间用 FreeRTOS ring buffer 解耦避免在协议回调里直接擦写 Flash 导致卡死或丢包。4.1 固件入口回调BLE 协议层每校验完一个 sector就会回调app_ble_ota_raw_recv_fw_cb示例将其转交给业务函数 ble_ota_raw_recv_fw_cbif (write_to_ringbuf(buf, length) ! length) { ESP_LOGE(TAG, Failed to write firmware chunk to ring buffer); return false; } return true;该函数把整块数据拷贝进 ring bufferxRingbufferSend阻塞等待直到有空间在返回前数据已被复制所以调用方协议层无需再长期持有该缓冲区所有权清晰见 include/ble_ota_raw.h 的注释。4.2 OTA 落盘任务OTA 任务是阻塞式工作线程ble_ota_raw.c核心循环为for (;;) { data xRingbufferReceive(s_ringbuf, item_size, portMAX_DELAY); if (esp_ota_write(s_out_handle, data, item_size) ! ESP_OK) { /* 错误处理 */ } recv_len item_size; if (fw_length 0) { fw_length esp_ble_ota_raw_get_fw_length(); // 从 START 命令获取固件总长 } if (fw_length ! 0 recv_len fw_length) { break; // 收满即结束 } } esp_ota_end(s_out_handle); esp_ota_set_boot_partition(s_ota_update_partition); ESP_LOGI(TAG, OTA success, restart in 0.2 seconds); vTaskDelay(pdMS_TO_TICKS(200)); esp_restart();值得注意的细节流量控制ring buffer 既缓存数据也天然形成背压——缓冲区满时 BLE 回调会阻塞等待从而让主机端放慢发送节奏防止 Flash 写入跟不上。动态总长度任务启动时并不知道固件多大而是在收到第一块数据后通过esp_ble_ota_raw_get_fw_length()从 START 命令负载中取得总长度据此判断是否收完。收满即停recv_len fw_length时跳出循环此时数据总量已超过 START 声明的长度协议层也允许超收。失败兜底任何一步失败都会走到OTA_ERROR标签调用esp_ota_abort()中止会话并删除任务不会留下半成品分区ble_ota_raw.c。4.3 目标分区选择策略ble_ota_raw_resolve_update_partition 决定往哪个分区写当前启动分区是factory类型 → 写入ota_0否则调用esp_ota_get_next_update_partition()拿到下一个可用 OTA 分区找不到时兜底到ota_0。这与 ESP-IDF 标准的 A/B OTA 语义一致固件永远写入非当前分区从而保证即使升级失败旧固件仍可回退。4.4 关键常量与窗口计算include/ble_ota_raw.h 定义了两个关键值BLE_OTA_RAW_SECTOR_BYTES 4096与协议层的 sector 大小保持一致BLE_OTA_RAW_RINGBUF_DEFAULT_SIZE 1024 * 1212 KiB ≈ 3 个 sector。初始化 ring buffer 时示例调用esp_ble_ota_raw_set_sector_send_window_for_ringbuf(ringbuf_size)将START ACK 发送窗口一次允许主机连续发送的 sector 数设为ringbuf_size / 4096并被协议层限制在 1~64 之间见 esp_ble_ota_raw.h。窗口越大主机可以更激进地连续发包吞吐越高但需要更大的 ring buffer 兜底。这是示例中一个非常实用的内存换速度旋钮。五、协议层剖析ble_ota_raw 如何保证固件干净可靠BLE 信道本质是不可靠的无线信道固件传输必须自带完整性校验。ble_ota_rawprofileREADME在esp_ble_conn_mgr之上实现了完整的命令与数据协议。5.1 服务与特征profile 复用了 BLE OTA Service UUID0x8018以及特征0x8020~0x8023。典型分工见 esp_ble_ota_raw.cUUID方向作用0x8020RECV_FW主机 → 设备固件数据包写入0x8022COMMAND双向主机发送命令设备通过 Notify 回 ACK0x8021 / 0x8023 及 DIS 服务—辅助/设备信息服务协议流程对应 ble_ota_raw/README.md 的 OTA Data Path主机向COMMAND_CHAR (0x8022)发送命令包profile 校验命令 CRC16 后通过 Notify 回复 ACK主机向RECV_FW_CHAR (0x8020)持续写入固件包profile 逐包校验sector 顺序、包序号、sector CRC16一个 sector4 KB完整且校验通过后调用应用注册的固件回调。5.2 命令协议固定 20 字节 CRC16从 esp_ble_ota_raw.c 可以看到命令处理逻辑命令帧固定20 字节len ! 20直接拒绝命令 ID 为小端uint16_t位于字节 0~10x0001 START0x0002 STOPCRC16 位于字节 18~19对前 18 字节计算采用CRC16-CCITTpoly 0x1021init 0x0000与 BLE OTA 主机工具兼容见源码注释。START 命令的负载字节 2~5小端uint32_t携带固件总长度设备据此校验接收进度同时 START ACK 会在第 6 字节携带 sector 发送窗口第 7 字节保留见源码常量注释。START 的处理流程handle_start_cmd相当严谨若 OTA 已在运行拒绝固件总长度为 0拒绝malloc(4096)分配 sector 缓冲失败则拒绝可选调用esp_ble_ota_raw_set_ota_begin_cb注册的钩子本例中为esp_ota_begin预先打开 Flash OTA 会话钩子返回非ESP_OK则拒绝 START全部通过才置start_ota true并回复 ACCEPT ACK。这种先确认能写、再开始收数据的顺序把分区空间不足等失败提前暴露在握手阶段而不是传输到一半才失败。5.3 sector 级校验与回调触发时机profile 内部维护fw_sector_buf4 KB、cur_sector当前 sector 序号、cur_packet包序号、fw_sector_offset等状态收到数据后先校验序号连续性与 CRC只有完整收到一个 sector 且 CRC 通过才把该 sector 交给应用回调源码中s_recv_fw_cb(...)的调用点位于 CRC 通过之后。这也解释了为什么协议层称之为ble_ota_raw它对上层只暴露一帧帧干净数据把无线传输的脏活全部揽下。5.4 公共 API 一览esp_ble_ota_raw.h 提供的核心接口esp_ble_ota_raw_init()注册 OTA 服务0x8018与 DIS 服务esp_ble_ota_raw_deinit()注销服务并释放运行时状态esp_ble_ota_raw_recv_fw_data_callback()注册 sector 级固件回调需在主机发送 START 前注册esp_ble_ota_raw_get_fw_length()返回 START 命令携带的固件总长度未收到 START 时返回UINT32_MAXesp_ble_ota_raw_set_ota_begin_cb()注册 OTA 会话开启钩子如esp_ota_beginesp_ble_ota_raw_set_sector_send_window_for_ringbuf()按 ring buffer 容量推导发送窗口。从依赖关系看idf_component.yml示例依赖ble_conn_mgr ~1.*、ble_ota_raw ~0.2.*、ble_services ~1.*且均通过override_path指向仓库内的本地组件保证与仓库源码版本一致。六、预期日志如何判断升级是否正常按 README.md 的说明正常升级应依次看到# 1. BLE 初始化日志boot log # 2. ota_task start等待 ring buffer 数据 ota_task start, wait firmware data from ringbuf # 3. 传输过程中逐块打印 recv: ..., recv_total: ... # 4. 全部写完设备自动重启 OTA success, restart in 0.2 seconds传输中的recv: 本块大小, recv_total: 累计来自 OTA 任务每次esp_ota_write前的日志ble_ota_raw.c可用于观察速率与进度当recv_total达到 START 声明的固件总长后任务收尾重启。若看到OTA failed/esp_ota_* failed则说明传输或落盘异常需要结合recv_total与协议层日志定位。七、配合手机 App 实测示例与乐鑫官方 BLE OTA Android App 配套使用README.md 的 Test With Mobile App 章节指向 EspressifApps 的 esp-ble-ota-android 发布页。实测步骤大致为编译烧录示例设备以0x8018服务 UUID 广播设备名可在 menuconfig 修改打开 BLE OTA App扫描并连接目标设备选择与目标芯片匹配的固件 bin 文件发送 START → 传输固件 → 设备校验并写入分区 → 自动重启进入新固件。八、注意事项与踩坑点综合 README.md 的 Notes 与源码实现落地时请重点检查先 START 后发包主机必须先发送带固件总长度的 START 命令且收到 ACCEPT ACK设备才会进入接收状态未 START 就发数据会被协议层按序号/状态异常处理。严格按 sector 顺序传输sector 尾部带 CRC协议层校验包序号连续性、sector 序号与 sector 级 CRC16乱序或丢包会导致校验失败并被拒绝/重传。固件镜像必须匹配目标芯片与分区约束所选 bin 要与芯片架构匹配且大小不得超过分区表本示例每个 OTA 分区 1500 KB否则esp_ota_begin阶段就可能失败。NimBLE 前提ble_conn_mgr当前仅支持 NimBLE需在 sdkconfig 中确认CONFIG_BT_NIMBLE_ENABLEDy。MTU 与窗口默认已配ATT_PREFERRED_MTU498若要压榨吞吐可同步增大 ring buffer 与发送窗口但需权衡 RAM 占用。OTA 进行中不要重复 START协议层会拒绝 start cmd while ota running如需中途放弃主机应发 STOP 命令0x0002设备会中止会话并回 ACCEPT。九、总结ble_ota示例展示了一条工程级 BLE OTA 链路的标准姿势esp_ble_conn_mgr解决 BLE 连接的通用复杂性ble_ota_raw解决无线信道下的协议可靠性与 CRC 校验业务层用 ring buffer 独立任务把数据搬运到 OTA 分区最终由 ESP-IDF 的 A/B 分区机制保证可回退的升级安全。无论你是要在量产产品中加入 BLE 升级能力还是想学习 BLE 大文件传输的工程化设计这个示例都提供了可直接参考的完整范本。【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价