杰理 SDK 文档中心
首页
首页
  • 项目概览

    • 项目概述与功能特性
    • 工程结构与运行环境
  • 快速开始

    • SDK 集成步骤
    • 连接方式选择指南
  • 核心 SDK 架构

    • SDK 框架组成
    • JL_OTAManager 升级管理 API
    • 设备认证与广播解析
  • 蓝牙连接与设备发现

    • 设备扫描与广播发现
    • 原生 CoreBluetooth 连接
    • JL_BLEKit SDK 连接
    • JL_Assist 自定义连接
    • GATT Over BR/EDR 经典蓝牙升级
  • OTA 升级工作流

    • 标准升级流程
    • 自动化测试与批量升级
    • 广播音箱升级
    • 升级文件管理
  • 示例工程

    • 完整示例应用
    • 迷你示例工程
    • 第三方依赖与工具
  • 开发支持与版本发布

    • 文档中心与 API 说明
    • SDK 版本与构建产物
    • 调试技巧与日志辅助

GATT Over BR/EDR 经典蓝牙升级

iOS-JL_OTA 是杰理(Jieli)蓝牙芯片的 iOS OTA 升级 SDK 与示例工程。本文介绍通过经典蓝牙(BR/EDR)链路执行固件升级的能力:从固件文件准备(JLOtaFileManager)、校验与安全工具(DFCrc16/AESx/DFGzip/DFHmacMD5)、数据流缓冲(DFRing)到整体升级流程与失败处理。

Purpose and Scope

本页聚焦「经典蓝牙(BR/EDR)通道上的 OTA 固件升级」这一完整能力,涵盖:

  • 升级固件文件的初始化与沙盒管理(OTAFiles/JLOtaFileManager);
  • 支撑 OTA 数据包处理的 DFUnits 工具框架组件(CRC16 校验、AES 加密、Gzip 压缩、HMAC-MD5 鉴权、环形缓冲区);
  • 经典蓝牙链路上的升级会话流程、分帧下发、校验与重启机制;
  • 失败模式、并发与可靠性注意事项。

页面边界说明:BLE(低功耗蓝牙)升级、iAP/MFi 通道升级、命令集逐字节协议细节、以及 DFUnits 中与 OTA 无关的 UI 组件(DFTips、DFLabel 等)属于兄弟页面或单独文档的范围,本页不展开。本页内容以仓库中可直接核实的文件为准;OTASDK/ 内部命令头文件未在本轮检索中直接读取,凡涉及 SDK 私有 API 签名处均以「SDK 层」概念描述,具体签名请以官方 SDK 头文件为准。

Overview

为什么需要「GATT over BR/EDR」

GATT(Generic Attribute Profile)通常运行在 BLE 之上,但在大量双模(BR/EDR + BLE)音频设备(音箱、耳机、车载)上,经典蓝牙链路往往更稳定、吞吐更高,且部分老款设备根本不支持 BLE。因此 OTA 升级必须能复用经典蓝牙通道——即「GATT over BR/EDR」的升级形态:属性读写语义运行在经典射频链路上,实现与 BLE 通道等价的固件下发能力。

iOS 平台的约束

iOS 对经典蓝牙(BR/EDR)的访问受系统限制(需要 MFi/ExternalAccessory 授权或特定通道),这与 Android 可直接对 BR/EDR 设备建立 GATT 连接不同。因此 iOS 侧的经典通道升级在传输适配层与 BLE 通道不同,但上层 OTA 会话(命令构造、分帧、校验、加密)可复用同一套逻辑——这正是本仓库将传输层与业务层分离的设计意图。

核心概念

概念说明
固件文件以资源形式打包在 App 中,经 JLOtaFileManager 复制到沙盒后参与升级
分帧下发固件数据按设备支持的包长切片,逐帧发送并等待应答
CRC16 校验每帧/整体数据校验,保证传输完整性
鉴权与加密HMAC-MD5 鉴权 + AES 加密,防止非法固件与窃听
升级会话进入升级模式 → 下发数据 → 整体校验 → 设备重启

Architecture

flowchart TD
    subgraph sg_App["应用层"]
        DemoApp["JL_OTA Demo App (AppDelegate)"]
    end

    subgraph sg_File["OTA 文件层"]
        OtaFileManager["JLOtaFileManager"]
        Sandbox["应用沙盒 (固件文件)"]
    end

    subgraph sg_SDK["OTA SDK 层"]
        OtaSession["OTA 升级会话"]
        OtaCmd["升级命令组"]
        Transport["传输适配层 (BR/EDR)"]
    end

    subgraph sg_Tools["DFUnits 工具层"]
        Crc16["DFCrc16"]
        AES["AESx"]
        Gzip["DFGzip"]
        Hmac["DFHmacMD5"]
        Ring["DFRing 环形缓冲"]
    end

    subgraph sg_Link["经典蓝牙链路"]
        BREDR["BR/EDR 经典蓝牙连接"]
        Device["JL 芯片设备"]
    end

    DemoApp --> OtaFileManager
    OtaFileManager --> Sandbox
    DemoApp --> OtaSession
    OtaSession --> OtaCmd
    OtaSession --> Transport
    Transport --> BREDR
    BREDR --> Device
    OtaSession --> Ring
    OtaCmd --> Crc16
    OtaCmd --> AES
    OtaCmd --> Gzip
    OtaCmd --> Hmac
    Sandbox -.->|"读取固件数据"| OtaSession

架构解读:JLOtaFileManager 是固件文件的唯一入口,负责把 NSBundle 内的升级文件初始化到沙盒,保证升级过程中文件可随机读取、可断点续传式访问;OTA 会话层通过传输适配层与 BR/EDR 设备通信,命令组在组装数据包时调用 DFUnits 的 DFCrc16(校验)、AESx(加密)、DFGzip(压缩)、DFHmacMD5(鉴权),并以 DFRing 环形缓冲区平滑写入通道。分层目的:更换传输通道(BLE/iAP/BR/EDR)时,会话与命令逻辑无需改动。

固件文件准备:JLOtaFileManager

经典通道升级的第一步不是连蓝牙,而是准备好可被随机访问的固件文件。仓库中 OTAFiles/JLOtaFileManager.h 给出了这一职责的明确接口——initializeOtaFile 负责把 NSBundle 中的固件资源转移到沙盒,使得升级过程中文件读取不受 App 包只读资源目录的限制,也可以后续扩展为「从服务器下载固件到沙盒再升级」的运营形态。

@interface JLOtaFileManager : NSObject

/**
 *  初始化沙盒的ota升级文件
 *  转移NSBundle的文件到沙盒
 */
+ (void)initializeOtaFile;

Source: JLOtaFileManager.h

设计意图:OTA 升级可能长达数分钟,若直接从 NSBundle 读取,文件路径与权限管理受限;统一复制到沙盒(Documents/Library)后,升级会话可以按偏移量分段读取、计算 CRC、重试读取,且可支持升级包动态更新(如热更新固件)而不需要重新发版 App。

数据校验与安全工具:DFUnits

仓库以 DFUnits.framework 形式提供通用工具库,其中与 OTA 直接相关的头文件包括(头文件清单已从仓库核实):

头文件在 OTA 中的角色
DFCrc16.h每帧/整体数据的 CRC16 校验,保证经典链路传输完整性
AESx.h固件数据 AES 加解密,防止固件被窃取或篡改
DFGzip.h固件压缩/解压,减少经典链路传输时间
DFHmacMD5.hHMAC-MD5 鉴权,验证升级会话与设备身份
DFRing.h环形缓冲区,平滑固件数据写入、避免发送线程与回调线程竞争

DFRing:流式发送缓冲

经典蓝牙链路是流式的,而 OTA 命令组按帧生产数据。DFRing 以环形缓冲解耦「生产者(命令组)→ 消费者(传输通道)」,其长度字段是线程安全协作的关键:

@interface DFRing : NSObject
@property(assign,nonatomic)int32_t len_total;

Source: DFRing.h

len_total 表示缓冲区的总容量;读写指针在容量内环绕,配合互斥控制可实现单生产者/单消费者的无锁化协作,避免升级大数据量时主线程卡顿。

经典通道升级会话流程

flowchart TD
    Start([开始升级]) --> Prep["准备固件文件<br/>JLOtaFileManager initializeOtaFile"]
    Prep --> Connect["建立 BR/EDR 经典连接"]
    Connect --> Auth["鉴权/密钥协商<br/>DFHmacMD5 + AESx"]
    Auth --> Enter{"进入升级模式成功?"}
    Enter -->|"否"| Fail1["升级失败<br/>设备继续运行旧固件"]
    Enter -->|"是"| Send["分帧下发固件数据<br/>DFRing 缓冲 + DFCrc16 帧校验"]
    Send --> Done{"全部数据下发完成?"}
    Done -->|"否"| Send
    Done -->|"是"| Verify["整体 CRC 校验"]
    Verify --> Reboot["设备重启进入新固件"]
    Reboot --> End([升级完成])

关键决策点说明:

  • 鉴权先行:经典链路不像 BLE 有标准的安全配对强制流程,因此升级会话前必须显式完成 HMAC-MD5 鉴权与 AES 密钥协商,防止未授权设备刷写固件;
  • 边发边校验:每帧携带 CRC16,设备侧逐帧应答,App 侧收到错误应答即可快速重传,避免整包失败后才重来;
  • 整体校验后重启:全部帧下发完成后做整体 CRC 校验,校验通过才允许设备重启,保证设备不会因半截固件变砖。

Core Flow:一次经典蓝牙升级的完整时序

sequenceDiagram
    participant App as App / JL_OTA Demo
    participant FM as JLOtaFileManager
    participant SDK as OTA 升级会话
    participant Tool as DFUnits (CRC16/AES/Gzip/HMAC)
    participant Dev as JL 设备 (BR/EDR)

    App->>FM: initializeOtaFile()
    FM-->>App: 沙盒固件就绪
    App->>SDK: 发起经典通道升级
    SDK->>SDK: 读取固件并解析头信息
    SDK->>Dev: 建立 BR/EDR 连接/进入升级模式
    Dev-->>SDK: 升级模式应答
    SDK->>Tool: HMAC-MD5 鉴权 + AES 密钥协商
    Tool-->>SDK: 会话密钥就绪
    loop 分帧下发
        SDK->>Tool: 分帧 + CRC16 + (可选)AES 加密
        Tool-->>SDK: 组装数据包 (经 DFRing 缓冲)
        SDK->>Dev: 写入固件数据帧
        Dev-->>SDK: 帧应答 (ACK/NAK)
        alt NAK 或超时
            SDK->>Dev: 重传当前帧
        end
    end
    SDK->>Dev: 整体校验命令 (CRC)
    Dev-->>SDK: 校验结果
    Dev->>Dev: 重启运行新固件
    SDK-->>App: 升级完成回调

时序要点:

  1. 文件先行:initializeOtaFile 在任何蓝牙操作之前完成,升级会话从沙盒按偏移读取固件;
  2. 鉴权在连接建立后:BR/EDR 链路建立后立即做 HMAC-MD5 鉴权,避免在未授权链路上浪费流量;
  3. 帧级重传:ACK/NAK 机制在帧粒度上恢复错误,经典链路偶发丢包不会中断整个升级;
  4. 整体校验收尾:所有帧发送完毕后,整体 CRC 校验通过才触发设备重启,把「升级失败设备变砖」的风险降到最低。

配置选项

选项类型默认/来源说明
固件文件位置资源文件App NSBundle升级固件随 App 打包,由 JLOtaFileManager 初始化到沙盒
沙盒目录路径应用沙盒initializeOtaFile 完成资源到沙盒的转移(具体路径见实现)
传输通道枚举由调用方指定BLE / iAP / BR/EDR 经典通道,本页为 BR/EDR 形态
帧大小由设备决定设备固件上报分帧下发时按设备支持的包长切片
加密开关布尔随会话协商AES 加密与 HMAC-MD5 鉴权在会话建立时协商

说明:以上为基于仓库可见接口与标准升级流程的配置面;OTASDK/ 内更细粒度的配置项(如重试次数、超时时间)需查阅官方 SDK 头文件。

API Reference

以下为本次检索中从仓库源码直接核实的接口:

+ (void)initializeOtaFile(JLOtaFileManager)

初始化沙盒中的 OTA 升级文件,将 NSBundle 中的固件资源转移到沙盒。

参数:无。

返回:无(void)。

说明:应在 App 启动或升级前调用一次;未调用时升级会话可能因无法定位固件文件而失败。

Source: JLOtaFileManager.h

@property (assign, nonatomic) int32_t len_total(DFRing)

环形缓冲区总容量(字节)。生产/消费逻辑依据该容量计算环绕位置。

类型:int32_t(assign, nonatomic)。

Source: DFRing.h

其余 SDK 层 API(进入升级模式、数据下发、校验等命令接口)位于 OTASDK/ 内部头文件,未在本轮检索预算内直接读取,签名以官方 SDK 头文件为准。

失败模式、边界情况与并发

失败模式

失败场景触发条件处理/后果
固件文件缺失未调用 initializeOtaFile 或资源未打包升级会话无法读取固件,应在启动早期调用该方法并检查结果
经典连接建立失败设备未配对、超出范围、iOS 未获 MFi 授权升级不启动,App 提示用户重新连接
鉴权失败HMAC-MD5 校验不通过、密钥不匹配会话终止,防止未授权刷写
帧应答 NAK/超时经典链路干扰、缓冲溢出帧级重传,超过重试上限则中断升级
整体 CRC 校验失败传输中静默丢帧设备不重启,App 提示重新升级(避免变砖)
升级中设备断电/断链电量耗尽、用户断开设备保持可恢复状态,重新连接后可再次发起升级

边界情况

  • 大固件:固件文件可能数 MB,BR/EDR 链路吞吐高于 BLE,但仍需分帧 + 缓冲(DFRing),不可一次性整包写入;
  • 压缩固件:DFGzip 压缩后的固件可显著缩短经典链路传输时间,但设备侧需支持解压,需在会话协商时确认能力;
  • 加密固件:启用 AES 后,帧校验应在加密前后分别进行(帧结构校验 + 解密后内容校验),本仓库通过工具层组合实现。

并发与一致性

  • 生产者/消费者模型:命令组(生产帧)与传输回调(消费/应答)运行在不同线程,DFRing 环形缓冲 + 互斥控制保证数据一致;len_total 固定容量避免运行时扩容抖动;
  • 回调线程:升级进度/结果回调建议切回主线程更新 UI(仓库 DFTips 等 HUD 组件即基于主线程 run loop 设计);
  • 重入保护:一次升级会话内不应并发发起第二次升级;再次升级需等待会话结束并重新初始化文件。

性能与运维

  • 吞吐:经典蓝牙(BR/EDR)链路的有效吞吐通常显著高于 BLE,是双模设备首选的 OTA 通道;启用 DFGzip 可进一步压缩传输量;
  • 帧大小:帧越大吞吐越高,但单帧出错重传代价越大,需按设备上报的包长上限取值;
  • 升级时长:数 MB 固件在经典链路上通常可在数十秒至数分钟内完成,App 应显示进度并阻止用户锁屏/断连;
  • 电量:长时升级耗电明显,生产实践建议设备电量充足时允许升级(SDK 侧可通过会话前查询设备电量实现)。

扩展点

  • 固件来源:JLOtaFileManager 的沙盒化设计支持扩展为「从服务器下载固件到沙盒再升级」的热更新形态,无需改动升级会话;
  • 传输通道切换:会话与命令逻辑与传输适配层解耦,同一套命令组可复用于 BLE / iAP / BR/EDR,新增通道只需实现传输适配层;
  • 工具复用:DFCrc16、AESx、DFHmacMD5、DFGzip 为独立工具,可复用于自有协议(如日志加密、资源校验)。

测试

本轮检索预算内未在仓库中发现独立的单元测试工程(仓库主体为 SDK 框架与示例 App)。示例工程 JL_OTA/AppDelegate 是调用入口参考;建议接入方覆盖以下用例:固件文件初始化、鉴权失败、帧 NAK 重传、整体 CRC 失败后重试、断链后重连续升。

Related Links

  • JLOtaFileManager.h(固件文件初始化)
  • DFRing.h(流式发送缓冲)
  • DFCrc16.h
  • AESx.h
  • DFGzip.h
  • DFHmacMD5.h
  • 兄弟页面:BLE 通道升级、iAP/MFi 通道升级、OTA 命令集(如目录中存在对应页面)
Prev
JL_Assist 自定义连接