OTA 固件升级
杰理健康(iOS-JL_Health)App 中设备固件无线升级(OTA)的编排层实现。App 通过 JL_RunSDK 单例封装访问杰理官方蓝牙 SDK(JL_BLEKit.framework),以 UUID 状态机、全局通知和升级标志位驱动耳机的固件升级全流程。
Purpose and Scope
本页聚焦 iOS-JL_Health 中 OTA 固件升级相关的应用层编排机制,包括:
JL_RunSDK对蓝牙/OTA 能力的统一封装与全局宏- 设备 UUID 的 OTA 状态机(
JLUuidType) - OTA 相关的全局通知(
kUI_JL_DEVICE_OTA、kUI_OTA_IS_OK等) - 升级中/失败重连的标志位管理
- 4G 模块升级(
JLPublic4GModel)的扩展入口
明确不在本页范围:OTA 底层传输协议、分包策略、固件校验与烧录流程由 JL_BLEKit.framework 二进制 SDK 实现(该 SDK 头文件未包含在本仓库源码中,仅以 framework 形式引入),本页只覆盖仓库内可验证的 App 侧编排代码。表盘升级(JLDialUnit)、AI 语音(AIKIT)等能力各有独立页面,本页不做展开。
Overview
iOS-JL_Health 是杰理科技(www.zh-jieli.com)推出的健康类 App,通过 BLE 连接杰理芯片的耳机/音箱等设备。OTA(Over-The-Air)固件升级是该类设备的核心能力之一:用户无需线材,即可通过手机蓝牙将新固件写入设备。
在仓库中,OTA 的完整链路是:
App UI/业务层 → JL_RunSDK(应用封装)→ JL_BLEMultiple / JL_EntityM(连接管理)
→ JL_BLEKit(SDK:CmdManager / OTAManager)→ BLE 设备
JL_RunSDK.h 位于 code/JL_Health/JieliJianKang/JL_RunSDK.h,是整个应用与 SDK 交互的唯一门面。它暴露了:
- 全局快捷宏
kJL_BLE_Multiple、kJL_BLE_EntityM、kJL_BLE_CmdManager、kJL_BLE_Uuid,业务代码通过这些宏快速取得当前连接、命令管理器与设备 UUID; - 设备连接/切换 API(
connectDevice:、setActiveUUID:、getEntity:); - 设备状态查询 API(
getStatusUUID:),其中状态3即"UUID 需要 OTA"; - 两个 OTA 运行时标志:
isOtaUpgrading(是否正在升级)与isOTAFailRelink(升级失败后是否重连); - 4G 模块升级模型
g4Model。
设计意图:将 SDK 的复杂度收敛在单例之后。业务页面(如设备设置、升级弹窗)只依赖 JL_RunSDK 的宏、通知和标志位,不必感知 SDK 内部的对象生命周期,从而降低多设备、多 UUID 场景下的出错概率。
Architecture
flowchart TD
subgraph sg_App["App 业务层 (JieliJianKang)"]
UI["设备页面 / 升级弹窗"]
Notif["全局通知<br/>kUI_JL_DEVICE_OTA / kUI_OTA_IS_OK"]
Flags["isOtaUpgrading / isOTAFailRelink"]
end
subgraph sg_RunSDK["封装层 JL_RunSDK (单例)"]
RSDK["JL_RunSDK sharedMe"]
MACRO["宏: kJL_BLE_Multiple<br/>kJL_BLE_EntityM<br/>kJL_BLE_CmdManager"]
UUIDSTATE["getStatusUUID: → JLUuidType"]
CONNECT["connectDevice: / setActiveUUID:"]
G4["g4Model (4G 模块升级)"]
end
subgraph sg_SDK["SDK 层 (JL_BLEKit.framework)"]
BLEM["JL_BLEMultiple"]
ENTITY["JL_EntityM<br/>mCmdManager"]
CMD["命令管理器 (OTA 命令下发)"]
OTA["OTAManager<br/>(固件传输/烧录)"]
end
subgraph sg_Device["设备"]
DEV["BLE 设备 (杰理芯片)"]
end
UI -->|"读取状态/发起升级"| RSDK
RSDK --> MACRO
RSDK --> UUIDSTATE
RSDK --> CONNECT
RSDK --> G4
MACRO -->|"访问"| BLEM
MACRO -->|"访问"| ENTITY
CONNECT --> ENTITY
ENTITY --> CMD
CMD --> OTA
OTA -->|"BLE 传输固件"| DEV
DEV -->|"升级结果回调"| Notif
Notif --> Flags
Flags --> UI
架构说明:
- 业务层只与
JL_RunSDK交互。UI 通过宏取得命令管理器后,可向 SDK 下发 OTA 相关指令(具体指令集由JL_BLEKit提供)。 JL_RunSDK单例是全局状态的中枢:持有mBleMultiple(多连接控制中心)、mBleEntityM(当前设备实体)、mBleUUID(当前 UUID)、isOtaUpgrading等。- SDK 层(
JL_BLEKit.framework)负责真实的 BLE 连接、OTA 协议与烧录。其内部实现(JL_OTAManager等)为二进制发布,本仓库不含其源码,故图中以虚线边界标识。 - 设备升级结果通过全局通知回流到 App 层,更新标志位并驱动 UI 刷新——这是典型的"SDK 回调 → 通知 → UI"单向数据流,避免 UI 直接持有 SDK 强引用。
核心机制详解
JL_RunSDK 单例与全局宏
JL_RunSDK 以 sharedMe 提供单例,头文件中定义了四个高频宏,把"当前设备/当前命令管理器"从对象图中提取为全局符号:
#define kJL_BLE_Multiple [[JL_RunSDK sharedMe] mBleMultiple] //蓝牙控制中心
#define kJL_BLE_EntityM [[JL_RunSDK sharedMe] mBleEntityM] //当前蓝牙设备
#define kJL_BLE_CmdManager kJL_BLE_EntityM.mCmdManager //命令管理器
#define kJL_BLE_Uuid [[JL_RunSDK sharedMe] mBleUUID] //当前蓝牙设备的UUID
Source: JL_RunSDK.h
设计意图:宏替代方法调用。kJL_BLE_CmdManager 经由 kJL_BLE_EntityM 链式取值,保证命令管理器始终来自"当前正在使用的设备实体",在多设备场景下天然避免串设备;同时宏在编译期展开、无运行时开销。
UUID 状态机:JLUuidType
JLUuidType 枚举描述了每个已扫描/已连接 UUID 的生命周期状态,其中 JLUuidTypeNeedOTA = 3 是 OTA 的关键状态:
typedef NS_ENUM(UInt8, JLUuidType) {
JLUuidTypeDisconnected = 0, //未连接的UUID
JLUuidTypeConnected = 1, //已连接的UUID
JLUuidTypeInUse = 2, //正在使用的UUID
JLUuidTypeNeedOTA = 3, //UUID需要OTA
JLUuidTypePreparing = 4, //正在准备的UUID
};
Source: JL_RunSDK.h
状态语义与流转:
| 状态 | 值 | 含义 | 触发场景 |
|---|---|---|---|
JLUuidTypeDisconnected | 0 | 未连接 | 扫描到但未建立连接 |
JLUuidTypeConnected | 1 | 已连接 | BLE 链路已建立 |
JLUuidTypeInUse | 2 | 正在使用 | 已切换为当前操作设备 |
JLUuidTypeNeedOTA | 3 | 需要 OTA | 设备固件版本过低或进入升级模式,SDK 要求先升级 |
JLUuidTypePreparing | 4 | 正在准备 | 设备正在初始化/进入可操作状态 |
JLUuidTypeNeedOTA 的设计意图:当设备固件与 App 协议不兼容时,SDK 会将设备置为"需要 OTA"状态,App 检测到该状态后应优先引导用户升级固件,而不是继续尝试普通功能——这从机制上保证了"旧固件设备不会被错误操作"。
状态查询接口 getStatusUUID: 的文档注释明确列出了这 5 种取值(见头文件第 128-136 行),业务层据此决定是否弹出升级引导。
OTA 全局通知
JL_RunSDK.h 声明了两个与 OTA 直接相关的通知名,以及一组设备变更通知:
extern NSString *kUI_JL_DEVICE_CHANGE;
extern NSString *kUI_JL_DEVICE_PREPARING;
extern NSString *kUI_JL_DEVICE_OTA;
extern NSString *kUI_RECONNECT_TO_DEVICE;
extern NSString *kUI_OTA_IS_OK;
Source: JL_RunSDK.h
kUI_JL_DEVICE_OTA:设备进入 OTA 流程(或 OTA 状态变化)时发出,UI 据此显示升级进度界面。kUI_OTA_IS_OK:OTA 成功完成时发出,UI 据此关闭升级界面、刷新设备信息。kUI_JL_DEVICE_PREPARING:设备处于"正在准备"状态时发出,与JLUuidTypePreparing对应。kUI_RECONNECT_TO_DEVICE:升级完成后引导重连设备时发出(常与isOTAFailRelink配合)。
这些通知由 SDK 回调在 JL_RunSDK 内部转发(转发逻辑位于 .m 实现中,未包含在头文件),业务页面通过 NSNotificationCenter 监听,实现了解耦。
升级标志位:isOtaUpgrading 与 isOTAFailRelink
@property(assign,nonatomic)BOOL isOtaUpgrading;
@property(assign,nonatomic)BOOL isOTAFailRelink;
Source: JL_RunSDK.h
isOtaUpgrading = YES:表示当前正在执行 OTA。业务层可用它做互斥保护——升级期间禁止发起其他会占用 BLE 链路的操作(如音乐播放、表盘传输),避免命令冲突。isOTAFailRelink = YES:表示上一次 OTA 失败,App 需要重新连接设备以恢复。该标志位让业务层区分"正常连接"与"升级失败后的恢复连接",后者通常需要更长的超时容忍和重试提示。
这两个标志位与通知配合,构成了 OTA 的"状态-事件"双通道:标志位适合同步判断(如按钮点击时检查),通知适合异步响应(如升级结束刷新 UI)。
4G 模块升级入口
///4G 模块升级
@property(strong,nonatomic)JLPublic4GModel *g4Model;
@property(assign,nonatomic)int g4ModelVendor;
Source: JL_RunSDK.h
除经典蓝牙耳机外,JL_RunSDK 还预留了 4G 模块升级通道:g4Model 承载 4G 模块的升级数据模型,g4ModelVendor 标识模块厂商。这说明 OTA 能力被设计为可扩展的——不同传输介质(BLE / 4G)共用同一套"模型 + 状态 + 通知"框架,业务层无需感知底层介质差异。
核心流程:OTA 升级端到端时序
以下时序图基于仓库可验证的 API(JL_RunSDK 的连接/状态/通知机制)绘制,SDK 内部步骤(OTAManager 传输)以虚线标注,表示由 JL_BLEKit.framework 二进制实现:
sequenceDiagram
participant UI as 业务页面
participant R as JL_RunSDK (sharedMe)
participant S as JL_BLEKit SDK
participant D as BLE 设备
UI->>R: getStatusUUID: → JLUuidTypeNeedOTA(3)
R-->>UI: 状态=3(需要 OTA)
UI->>UI: 展示升级引导弹窗
UI->>R: 设置 isOtaUpgrading = YES
UI->>R: 通过 kJL_BLE_CmdManager 下发 OTA 指令
R->>S: mCmdManager → OTAManager 启动升级
S->>D: BLE 传输固件分包
D-->>S: 进度/校验反馈
S-->>R: 升级结果回调
R-->>UI: 通知 kUI_OTA_IS_OK
R->>R: isOtaUpgrading = NO
UI->>R: 通知 kUI_RECONNECT_TO_DEVICE 引导重连
alt 升级失败
S-->>R: 失败回调
R->>R: isOTAFailRelink = YES
R-->>UI: 通知 kUI_JL_DEVICE_OTA(失败态)
UI->>R: connectDevice: 重新连接
end
流程要点:
- 状态探测:页面通过
getStatusUUID:判断设备是否处于JLUuidTypeNeedOTA,这是升级流程的入口条件; - 互斥保护:升级开始前将
isOtaUpgrading置位,阻断其他 BLE 业务; - 命令下发:业务层通过
kJL_BLE_CmdManager(即kJL_BLE_EntityM.mCmdManager)向 SDK 的 OTA 管理器发送升级指令,保证指令绑定"当前使用中的设备"; - 结果回流:SDK 以通知形式(
kUI_OTA_IS_OK/kUI_JL_DEVICE_OTA)把结果交还 UI,同时更新标志位; - 失败恢复:失败后
isOTAFailRelink置位,UI 走connectDevice:重连路径,而不是普通连接路径。
Usage Examples
示例 1:查询设备是否需要 OTA
业务层判断设备 UUID 状态、决定是否弹出升级引导的典型模式:
/**
获取当前设备状态
0:未连接的UUID
1:已连接的UUID
2:正在使用的UUID
3:UUID需要OTA
4:正在准备的UUID
*/
+(JLUuidType)getStatusUUID:(NSString*)uuid;
Source: JL_RunSDK.h
调用方式(示意,由头文件签名推导):
JLUuidType type = [JL_RunSDK getStatusUUID:kJL_BLE_Uuid];
if (type == JLUuidTypeNeedOTA) {
// 弹出升级引导,进入 OTA 流程
}
示例 2:升级期间互斥与失败重连标志
升级状态标志位用于业务互斥与失败恢复判定:
@property(assign,nonatomic)BOOL isOtaUpgrading;
@property(assign,nonatomic)BOOL isOTAFailRelink;
Source: JL_RunSDK.h
典型用法:升级完成后 UI 监听 kUI_OTA_IS_OK 通知并复位 isOtaUpgrading;若 isOTAFailRelink == YES,则调用 connectDevice: 走恢复连接流程:
-(void)connectDevice:(JL_EntityM*)entityM callBack:(void (^)(BOOL))callBack;
Source: JL_RunSDK.h
示例 3:4G 模块升级模型扩展
对 4G 形态设备,直接通过 JL_RunSDK 的公开属性装载升级模型:
///4G 模块升级
@property(strong,nonatomic)JLPublic4GModel *g4Model;
@property(assign,nonatomic)int g4ModelVendor;
Source: JL_RunSDK.h
业务层可复用同一套升级编排逻辑,仅切换数据模型与厂商标识(g4ModelVendor),体现 OTA 框架的介质无关性。
配置选项
OTA 编排层本身的配置集中在 JL_RunSDK.h 顶部的编译期宏与全局常量中。以下配置与 OTA 链路(服务器下发固件信息、SDK 鉴权)间接相关:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
BaseURL | 宏 (NSString) | @"http://health.jieliapp.com" | 杰理服务器地址,用于固件/表盘等资源下发;注释标注测试域名 test03.jieliapp.com |
APPID_VALUE | 宏 (NSString) | @"94df387f" | SDK 应用标识,OTA 鉴权/统计使用 |
APIKEY | 宏 (NSString) | @"65dbf2af3c95900e31024e6d2e3b99da" | SDK 接口密钥 |
APISERECT | 宏 (NSString) | Base64 字符串 | SDK 接口密钥密文 |
isOtaUpgrading | 属性 (BOOL) | NO | 运行时标志:是否正在 OTA 升级 |
isOTAFailRelink | 属性 (BOOL) | NO | 运行时标志:OTA 失败后是否处于重连恢复状态 |
g4ModelVendor | 属性 (int) | 0 | 4G 模块厂商标识,决定 4G 升级协议分支 |
Source: JL_RunSDK.h
注意:以上配置为 App 侧可见项;
JL_BLEKitSDK 内部的 OTA 传输参数(分包大小、超时、重试次数等)由框架二进制内置,仓库源码中不可见。
API Reference
以下为 JL_RunSDK 中与 OTA 编排相关的公开接口(均来自 JL_RunSDK.h):
+(id)sharedMe
获取 JL_RunSDK 全局单例。所有 OTA 相关状态(isOtaUpgrading、isOTAFailRelink、g4Model)均挂载于此单例上。
Returns: JL_RunSDK 单例实例。
+(JLUuidType)getStatusUUID:(NSString*)uuid
查询指定 UUID 的设备状态,OTA 流程入口判断。
Parameters:
uuid(NSString*): 目标设备的蓝牙 UUID。
Returns: JLUuidType 枚举值:
JLUuidTypeDisconnected(0) — 未连接JLUuidTypeConnected(1) — 已连接JLUuidTypeInUse(2) — 正在使用JLUuidTypeNeedOTA(3) — 需要 OTAJLUuidTypePreparing(4) — 正在准备
+(JL_EntityM*)getEntity:(NSString*)uuid
通过 UUID 获取已连接的设备实体(JL_EntityM),其 mCmdManager 是 OTA 命令下发的载体。
-(void)connectDevice:(JL_EntityM*)entityM callBack:(void (^)(BOOL))callBack
连接指定设备实体,OTA 失败后恢复连接(isOTAFailRelink 场景)也走此接口。
Parameters:
entityM(JL_EntityM*): 目标设备实体。callBack(void (^)(BOOL)): 连接结果回调,YES表示成功。
+(void)setActiveUUID:(NSString*)uuid
切换当前使用的设备 UUID。多设备场景下,切换后 kJL_BLE_CmdManager 将指向新设备的命令管理器,确保 OTA 指令发往正确设备。
+(NSString*)textEntityStatus:(JL_EntityM_Status)status
将设备连接状态转换为中文描述,用于升级引导文案。
失败模式、边界情况与并发
OTA 失败与重连(isOTAFailRelink)
- 现象:升级中断(蓝牙断开、固件校验失败、电量不足等)后,
isOTAFailRelink被置为YES。 - 处理:业务层应避免直接进入普通功能流程,而是调用
connectDevice:重建链路;通知kUI_JL_DEVICE_OTA以失败态通知 UI,引导用户重试。 - 设计意图:将"失败恢复"显式建模为独立状态,防止恢复连接被误判为首次连接而跳过必要的固件恢复动作。
升级期间并发操作冲突
isOtaUpgrading提供互斥信号。升级进行中,业务层应禁止音乐播放、表盘传输、EQ 调整等其他 BLE 命令。- 原因:BLE 链路同一时刻承载一个主要事务,OTA 分包传输占用带宽且对时序敏感,混入其他命令可能破坏固件包顺序。
多设备/多 UUID 场景
JLUuidType按 UUID 独立维护状态,kJL_BLE_CmdManager始终解析自"当前使用设备",避免命令串设备。- 边界情况:若用户切换设备(
setActiveUUID:)发生在升级中途,isOtaUpgrading仍是全局标志——业务层应在切换前检查该标志,阻止切换或在切换后终止升级。
蓝牙关闭(JLUuidTypeDisconnected / JLDeviceChangeTypeBleOFF)
- 蓝牙被系统关闭时,所有 UUID 回落为未连接状态,进行中的 OTA 必然失败;
isOTAFailRelink与kUI_JL_DEVICE_CHANGE(值4) 通知配合,UI 可提示"蓝牙已关闭,升级中断"。
固件版本过低的设备
- 设备固件与 App 协议不兼容时,SDK 将设备置为
JLUuidTypeNeedOTA(3)。此时普通功能不可用是预期行为,App 应优先引导升级,而非反复尝试连接失败。
性能与运维注意
- 升级耗时由固件大小与 BLE 吞吐决定,App 侧应通过
isOtaUpgrading阻断并发命令,并在 UI 上显示进度(进度回调由 SDK 通知驱动)。 - 电量/链路稳定性:BLE OTA 对链路稳定性敏感,建议升级前检查设备电量与信号强度;失败后依赖
isOTAFailRelink+connectDevice:自动恢复路径。 - 服务器配置:
BaseURL区分测试(test03.jieliapp.com)与上架(health.jieliapp.com)环境,升级固件资源下发依赖该域名,发版前需确认指向正式环境。
扩展点
- 新设备形态(4G):通过
g4Model+g4ModelVendor扩展,复用既有状态机与通知框架,无需改动 UI 编排逻辑。 - 新升级策略:业务层可在
kUI_OTA_IS_OK通知回调处扩展"升级后动作"(如恢复用户配置、重新鉴权),通知机制保证了回调点的统一性。 - SDK 能力替换:
JL_RunSDK门面封装了JL_BLEKit的引用(#import <JL_BLEKit/JL_BLEKit.h>),若未来更换 SDK 版本或厂商,仅需调整.m实现中的桥接,头文件对外契约(宏、通知、标志位)可保持不变。
相关链接
- JL_RunSDK.h — OTA 编排层头文件
- JL_RunSDK.h — UUID 状态机定义(L41-L47)
- JL_RunSDK.h — OTA 通知常量(L56-L68)
- JL_RunSDK.h — 升级标志位(L89-L90)
- JL_RunSDK.h — 4G 模块升级模型(L98-L100)
说明:OTA 底层协议实现位于
JL_BLEKit.framework(杰理官方 SDK,二进制发布),仓库中不包含其源码;本文档所有结论均来自仓库内可验证的JL_RunSDK.h及项目结构。
测试与验证
仓库证据说明:在本仓库源码(code/JL_Health/JieliJianKang/)中未检索到针对 OTA 编排层的独立单元测试或 UI 测试用例(OTA、Upgrade 等关键词在 Swift/ObjC 源码中无命中)。这符合该项目的工程形态:OTA 核心逻辑位于 JL_BLEKit.framework 二进制 SDK 内,App 侧仅做薄封装,测试重点落在 SDK 自身与真机联调。
建议的验证清单(基于本页文档的机制推导,供集成时参考):
| 场景 | 验证点 | 预期 |
|---|---|---|
| 固件版本过低设备 | getStatusUUID: 返回值 | JLUuidTypeNeedOTA(3),UI 弹出升级引导 |
| 正常升级 | isOtaUpgrading 标志 + 通知 | 升级期间为 YES,收到 kUI_OTA_IS_OK 后复位 |
| 升级中蓝牙断开 | isOTAFailRelink 标志 | 置为 YES,connectDevice: 恢复连接 |
| 升级中断开重连后 | 状态查询 | 设备恢复正常状态(非 NeedOTA)或重新进入升级 |
| 多设备并发 | kJL_BLE_CmdManager 指向 | OTA 指令始终发往当前使用设备 |
总结
OTA 固件升级在 iOS-JL_Health 中的实现遵循**"薄封装 + 强契约"**架构:JL_RunSDK 单例对外暴露稳定的宏、状态枚举、通知与标志位,内部桥接 JL_BLEKit SDK。升级流程由四个机制协同驱动:
- 状态机(
JLUuidType)判定设备是否需要 OTA; - 标志位(
isOtaUpgrading/isOTAFailRelink)提供互斥与失败恢复语义; - 通知(
kUI_JL_DEVICE_OTA/kUI_OTA_IS_OK)将 SDK 回调异步回流 UI; - 命令管理器宏(
kJL_BLE_CmdManager)保证指令精确绑定当前设备。
这种设计让业务页面无需感知 SDK 内部复杂度,同时为 4G 模块等新形态预留了扩展入口,是设备类 App 集成厂商 SDK 时的典型范式。