蓝牙连接与 RCSP 协议
本文档深入解析 iOS-JL_Health 项目中杰理(Jieli)BLE 芯片生态的核心通信链路:蓝牙连接管理与 RCSP(杰理私有命令协议)。内容涵盖协议包结构、数据编解码、连接生命周期、命令分发中心以及功能管理器的协作机制,全部基于 JL_BLEKit.framework 公开头文件中的真实声明。
Purpose and Scope
本页面向需要理解或扩展 App 与杰理设备(耳机、音箱、穿戴设备)通信链路的开发者,说明:
- RCSP 协议包(
JL_PKG)的二进制结构、生成与解析规则; JL_Assist蓝牙连接助手如何完成扫描过滤、服务/特征发现、设备认证与数据收发;JL_ManagerM命令中心如何按 OpCode 分发命令,并挂载各功能管理器;JL_BLEAction提供的广播过滤、配对、OTA 回连与数据通知能力;- 与 RCSP 相关的配置项(服务号、特征 UUID、MTU、认证密钥等)。
不覆盖的内容(属于兄弟页面):具体业务功能管理器(如 JL_FileManager 文件管理、JL_OTAManager 固件升级、JL_TwsManager 双耳、JL_AlarmClockManager 闹钟等)的内部实现;外围设备扫描与界面层逻辑;AI 语音助手(AIKIT)相关能力。这些模块仅在本页提及其挂载关系,供读者按图索骥。
Overview
iOS-JL_Health 通过 CoreBluetooth 与杰理芯片设备通信。杰理私有协议 RCSP(参考代码中的 JL_RCSP 类)承载了绝大多数设备控制命令:音量、EQ、ANC、音乐控制、文件传输、OTA 等,全部功能管理器最终都通过统一的 RCSP 命令包与设备交互。
整个链路可抽象为三层:
- BLE 传输层:
JL_Assist负责 CoreBluetooth 生命周期(状态回调、特征发现、MTU 协商、分包发送),把 RCSP 数据包写入 RCSP 写特征,并从读特征(Notify)接收设备回包; - 协议编解码层:
JL_RCSP把上层命令模型(JL_PKG)编码为字节流,或把设备回包解码回JL_PKG; - 命令分发层:
JL_ManagerM维护命令序号mCmdSN、接收inputPKG:入包,按 OpCode 分发给对应功能管理器,并通过NSNotification(如kJL_RCSP_RECEIVE/kJL_RCSP_SEND)广播给业务层。
RCSP 之所以采用"1 字节 OpCode + 参数区"的紧凑设计,是因为 BLE 单包 MTU 通常只有 20~244 字节,协议必须尽可能精简;同时通过 pkgIsCommand 标志位区分命令与数据,通过 pkgNeedResponse 标志位控制是否需要设备应答,保证长数据(如文件传输)能够可靠分包传输。
Architecture
flowchart TD
subgraph sg_App["App 业务层 (JL_Health)"]
UI["业务界面 / 控制器"]
OBS["NSNotification 观察者<br/>(kJL_RCSP_RECEIVE / SEND)"]
end
subgraph sg_Cmd["命令分发层"]
MGR["JL_ManagerM 命令中心"]
DELEGATE["JL_ManagerMDelegate<br/>onManagerSendPackage:"]
FM["功能管理器集合<br/>JL_FileManager / JL_OTAManager /<br/>JL_TwsManager / JL_CallManager / ..."]
end
subgraph sg_Proto["协议编解码层"]
RCSP["JL_RCSP"]
PKG["JL_PKG 数据模型"]
end
subgraph sg_BLE["BLE 传输层 (CoreBluetooth)"]
ASSIST["JL_Assist 连接助手"]
CENTRAL["CBCentralManager / CBPeripheral"]
ACTION["JL_BLEAction<br/>广播过滤 / 配对 / OTA回连"]
end
subgraph sg_Dev["设备端"]
DEV["杰理芯片设备"]
end
UI -->|"发送命令"| MGR
MGR -->|"xmCommandCode: 构建 JL_PKG"| PKG
MGR --> DELEGATE
DELEGATE -->|"包回调"| ASSIST
PKG -->|"rcspMakePackage: 编码"| RCSP
RCSP -->|"NSData 字节流"| ASSIST
ASSIST -->|"写入 mRcspWrite 特征"| CENTRAL
CENTRAL -->|"BLE GATT 写入"| DEV
DEV -->|"Notify 回包"| CENTRAL
CENTRAL -->|"didUpdateValueForCharacteristic"| ASSIST
ASSIST -->|"assistUpdateValueForCharacteristic:"| MGR
MGR -->|"inputPKG: + rcspAnalysisData:"| RCSP
RCSP -->|"解码为 JL_PKG"| MGR
MGR -->|"按 OpCode 分发"| FM
FM --> OBS
OBS --> UI
ACTION --> CENTRAL
架构说明:
- JL_ManagerM 是整个命令体系的中枢:它持有
JL_RCSP的编解码能力、唯一命令序号mCmdSN、以及所有功能管理器实例。业务层通过xmCommandCode:系列方法下发命令,通过inputPKG:接收设备回包; - JL_Assist 是 CoreBluetooth 与上层之间的"适配器":App 只需在系统代理回调里调用
assistUpdateState:、assistDiscoverCharacteristicsForService:Peripheral:等转发方法,即可获得特征发现、认证配对与数据收发的完整处理; - JL_RCSP 无状态、只做编解码(工具类),保证协议逻辑单一、可独立测试;
- JL_BLEAction 以单例形式提供设备过滤、握手配对与 OTA 回连等"连接前"能力,与
JL_Assist的"连接中/连接后"能力互补。
(架构依据:JL_RCSP.h、JL_Assist.h、JL_ManagerM.h、JL_BLEAction.h)
RCSP 协议包结构(JL_PKG)
RCSP 的命令载荷模型是 JL_PKG,其字段定义精确到比特位:
@interface JL_PKG : NSObject
@property(assign,nonatomic) uint16_t pkgIsCommand; //1Bit
@property(assign,nonatomic) uint16_t pkgNeedResponse; //1Bit
@property(assign,nonatomic) uint16_t pkgUnused; //6Bits
@property(assign,nonatomic) uint16_t pkgOpCode; //8Bits
@property(assign,nonatomic) uint16_t pkgLength; //参数长度(本身不计)
@property(strong,nonatomic) NSData *pkgData; //参数数据
@end
Source: JL_RCSP.h
头字段语义
| 字段 | 位宽 | 含义 |
|---|---|---|
pkgIsCommand | 1 bit | 1 = 命令包(需要按 OpCode 处理),0 = 数据包 |
pkgNeedResponse | 1 bit | 是否需要设备回复(仅命令包有意义,与 xmCommandCode:needResponse: 参数对应) |
pkgUnused | 6 bits | 保留位,供协议扩展 |
pkgOpCode | 8 bits | 操作码,对应 JL_OpCode.h 中的命令枚举,如 0x01 系统、0x05 设备信息、0x1A 文件等 |
pkgLength | — | 参数区长度(不包含头部自身) |
pkgData | — | 参数区原始字节 |
这个"1+1+6+8"的头部设计意图是:把 OpCode 固定为单字节,使最常见的控制命令(音量、EQ、播放/暂停)恰好落在 1~2 个 BLE 包内,降低 MTU 压力;6 位保留位则为后续协议版本预留了扩展空间而无需改变包头总长。
JL_RCSP:协议编解码核心
JL_RCSP 是一个纯工具类,提供 6 个静态方法完成"模型 ⇄ 字节流 ⇄ 参数数组"的完整转换:
| 方法 | 方向 | 说明 |
|---|---|---|
rcspMakePackage: | 模型 → 字节流 | 把 JL_PKG 编码为待发送的 NSData(XM 数据包) |
rcspAnalysisData: | 字节流 → 模型 | 把接收到的原始数据解析为 JL_PKG |
rcspMakeParams: | 参数数组 → 字节流 | 把 NSData 元素数组拼装为参数区数据 |
rcspAnalysisParams: | JL_PKG → 参数数组 | 把 pkgData 参数区拆分为数组 |
rcspInfoArrFromData: | 信息数据 → 数组 | 分解 JL_PKG 中的信息数据(LTV 结构) |
rcspInfoFromData2ByteSize: | LTV 数据 → 数组 | 拆分 L 长度为 2 字节的 LTV 数据 |
/**
解析JL数据包
@param data 把数据转成JL_PKG模型。
@return JL_PKG数据模型
*/
+(JL_PKG*)rcspAnalysisData:(NSData*)data;
/**
生成XM数据包
@param pkg 把JL_PKG模型转成data。
@return 数据
*/
+(NSData*)rcspMakePackage:(JL_PKG*)pkg;
/**
分析JL_PKG参数
@param pkg 把JL_PKG的xmData参数部分解析成一列数组。
@return 参数数组
*/
+(NSArray*)rcspAnalysisParams:(JL_PKG*)pkg;
/**
生成JL_PKG参数
@param array 把参数数组变成data(注意:数组元素必须是NSData类型)。
@return 数据
*/
+(NSData*)rcspMakeParams:(NSArray*)array;
Source: JL_RCSP.h
设计要点
- 参数区采用数组模型:
rcspMakeParams:严格要求数组元素是NSData,这使每个参数块可以独立构造(例如先拼操作数、再拼 TLV 子块),避免多层字节拼接的指针错误; - LTV 双模式:
rcspInfoArrFromData:(单字节长度)与rcspInfoFromData2ByteSize:(双字节长度)并存,前者用于短参数(如设备信息字段),后者用于文件/大数据块场景(如固件升级、歌词传输),兼顾紧凑性与长度上限; - 无状态设计:编解码不依赖连接上下文,便于单元测试与在非 BLE 通道(如串口调试)复用。
核心数据流:命令发送与回包接收
sequenceDiagram
participant UI as 业务层
participant MGR as JL_ManagerM
participant RCSP as JL_RCSP
participant AS as JL_Assist
participant CB as CoreBluetooth
participant DEV as 设备
UI->>MGR: xmCommandCode:needResponse:sendData:
activate MGR
MGR->>MGR: 生成命令序号 mCmdSN 并构建 JL_PKG
MGR->>RCSP: rcspMakePackage:(JL_PKG)
RCSP-->>MGR: NSData 字节流
MGR-->>UI: onManagerSendPackage:(JL_PKG) 代理回调
MGR->>AS: assistDidWriteData:(NSData)
activate AS
AS->>CB: 写入 mRcspWrite 特征(按 MTU 分包)
CB->>DEV: GATT Write
deactivate AS
deactivate MGR
DEV-->>CB: Notify 回包
CB-->>AS: didUpdateValueForCharacteristic:
AS->>MGR: assistUpdateValueForCharacteristic:(CBCharacteristic)
activate MGR
MGR->>RCSP: rcspAnalysisData:(value)
RCSP-->>MGR: JL_PKG(含 OpCode 与参数)
MGR->>MGR: inputPKG: 按 OpCode 路由到功能管理器
MGR-->>UI: 发送 kJL_RCSP_RECEIVE 通知(字典含 UUID/对象)
deactivate MGR
步骤解读:
- 业务层调用
JL_ManagerM的命令方法(如xmCommandCode:needResponse:sendData:),管理器自增mCmdSN并构造JL_PKG; JL_RCSP将模型编码为字节流;管理器通过JL_ManagerMDelegate的onManagerSendPackage:把包交给外部(即JL_Assist的写通道),同时广播kJL_RCSP_SEND通知供日志/调试使用;JL_Assist把数据按 MTU 分包写入 RCSP 写特征(mRcspWrite),设备执行命令;- 设备通过 Notify 回包,CoreBluetooth 回调
didUpdateValueForCharacteristic:,App 转发给JL_Assist的assistUpdateValueForCharacteristic:,再进入JL_ManagerM inputPKG:; - 管理器用
rcspAnalysisData:还原JL_PKG,按pkgOpCode路由到对应功能管理器,最终以NSDictionary通知(含kJL_MANAGER_KEY_UUID与kJL_MANAGER_KEY_OBJECT)形式抛给业务层。
(依据:JL_RCSP.h、JL_ManagerM.h、JL_BLEAction.h)
JL_Assist:BLE 连接生命周期
JL_Assist 封装了从蓝牙状态变化到数据收发全过程。它的设计原则是"薄转发":App 在 CoreBluetooth 代理方法中直接调用对应 assist 方法,内部完成状态机推进,业务层无需关心 GATT 细节。
/// Execute in a method 「- (void)centralManagerDidUpdateState:」
-(void)assistUpdateState:(CBManagerState)state;
/// Execute in a method 「- (void)peripheral:didDiscoverServices:」
-(void)assistDiscoverCharacteristicsForService:(CBService*)service
Peripheral:(CBPeripheral*)peripheral;
/// Execute in a method 「- (void)peripheral:didUpdateNotificationStateForCharacteristic:error:」
-(void)assistUpdateCharacteristic:(nonnull CBCharacteristic *)characteristic
Peripheral:(CBPeripheral*)peripheral
Result:(JL_Assist_BK)result;
/// Execute in a method 「- (void)peripheral:didUpdateValueForCharacteristic:error:」
-(void)assistUpdateValueForCharacteristic:(CBCharacteristic *)characteristic;
/// Execute in a method 「- (void)peripheral:didWriteValueForCharacteristic:error:」
/// Execute in a method 「- (void)peripheral:didIsReadyForWrite:error:」
-(void)assistDidReady;
/// Execute in a method 「- (void)centralManager:didDisconnectPeripheral:error:」
-(void)assistDisconnectPeripheral:(CBPeripheral *)peripheral;
Source: JL_Assist.h
连接状态机
stateDiagram-v2
[*] --> Idle: assistUpdateState: poweredOn
Idle --> Discovering: 连接外设 & didDiscoverServices
Discovering --> Authenticating: assistDiscoverCharacteristicsForService:
Authenticating --> Ready: assistUpdateCharacteristic: Result(YES)
Authenticating --> Failed: Result(NO)
Failed --> Idle: assistDisconnectPeripheral:
Ready --> DataFlow: assistUpdateValueForCharacteristic: / assistDidReady
DataFlow --> Ready
Ready --> Idle: 断开 / 蓝牙关闭
Failed --> [*]
- Discovering:
assistDiscoverCharacteristicsForService:Peripheral:内部按mService(服务号)匹配服务,再按mRcsp_W(写特征 UUID)与mRcsp_R(读/Notify 特征 UUID)匹配特征并保存为mRcspWrite/mRcspRead; - Authenticating:
assistUpdateCharacteristic:Peripheral:Result:在订阅 Notify 成功后触发。若mAuthEnable == YES,则用mAuthKey完成设备认证握手,结果通过JL_Assist_BK回调(BOOL isPaired)返回;若未开启认证则直接回调成功; - Ready/DataFlow:
assistDidReady表示写通道就绪(处理didWriteValue与didIsReadyForWrite),assistUpdateValueForCharacteristic:负责把设备 Notify 数据上传给命令中心。
关键属性与配置
| 属性 | 类型 | 默认 | 说明 |
|---|---|---|---|
mService | NSString | — | GATT 服务 UUID(RCSP 服务号),必须配置 |
mRcsp_W | NSString | — | RCSP 写特征 UUID |
mRcsp_R | NSString | — | RCSP 读/Notify 特征 UUID |
mAuthKey | NSData | nil | 设备认证密钥(替代已废弃的 mPairKey) |
mAuthEnable | BOOL | NO | 是否需要设备认证(替代已废弃的 mNeedPaired) |
mLimitMtu | NSInteger | 40 | 在最大 MTU 基础上预留的写入余量 |
mMaxMtu | NSInteger | — | 最大 MTU,建议用 maximumWriteValueLengthForType: 实测刷新 |
mLogData | BOOL | NO | 是否打印裸数据(调试用) |
mCmdManager | JL_ManagerM | — | 命令中心引用,数据上报的终点 |
Source: JL_Assist.h
设计意图:mLimitMtu 默认 40 字节余量,是因为 iOS 上报的 MTU 往往是协商上限,实际写满可能触发系统节流;预留余量并配合 assistDidReady 的流控,可显著降低 didIsReadyForWrite 排队导致的丢包概率。mDelegate(JL_AssistDelegate)若被设置,则所有待发数据都交给外部回调 assistDidWriteData: 发送,便于自定义加密或替换传输通道。
JL_ManagerM:命令中心与功能管理器
JL_ManagerM 是连接建立后所有命令的总入口。它对外暴露只读的设备信息(mBLE_UUID、mBLE_NAME)与命令序号(mCmdSN),并聚合了 20+ 个功能管理器,每个管理器对应一类设备能力:
@property(nonatomic,strong)JL_SmallFileManager *mSmallFileManager;
@property(nonatomic,strong)JL_FileManager *mFileManager;
@property(nonatomic,strong)JL_OTAManager *mOTAManager;
@property(nonatomic,strong)JL_FlashOperateManager *mFlashManager;
@property(nonatomic,strong)JL_CallManager *mCallManager;
@property(nonatomic,strong)JL_AlarmClockManager *mAlarmClockManager;
@property(nonatomic,strong)JL_TwsManager *mTwsManager;
@property(nonatomic,strong)JL_MusicControlManager *mMusicControlManager;
@property(nonatomic,strong)JL_SystemEQ *mSystemEQ;
@property(nonatomic,strong)JL_SystemTime *mSystemTime;
@property(nonatomic,strong)JL_SystemVolume *mSystemVolume;
@property(nonatomic,strong)JL_CustomManager *mCustomManager;
@property(nonatomic,strong)JL_BatchManger *mBatchManger;
Source: JL_ManagerM.h
命令发送 API
/**
发送【命令包】
@param cmdCode 具体要发送的命令
@param needResponse 是否需要回复
@param sendData 具体要发送的数据
@discussion 只有isCommand是YES时needResponse才有意义,即只有命令才需要回复
*/
-(void)xmCommandCode:(uint8_t)cmdCode
needResponse:(BOOL)needResponse
sendData:(NSData*)sendData;
Source: JL_ManagerM.h
管理器的其他关键方法:
setPropertyUpdate:— 控制设备属性是否主动上报;setBleUuid: / setBleName:— 记录当前设备身份;inputPKG:— 命令中心接收JL_PKG的入口(由JL_Assist上报触发),内部解析 OpCode 并路由;noteEntityConnected / noteEntityDisconnected / noteEntityBleOff— 向各功能管理器广播连接/断开/蓝牙关闭事件,使管理器及时复位内部状态(如文件传输中断)。
通知约定
从 JL_ManagerM 发出的所有通知均为字典格式:
/*
* 从JL_ManagerM发出去的通知都是字典,如下所示:
*
* @{ kJL_MANAGER_KEY_UUID :当前设备的UUID,
* kJL_MANAGER_KEY_OBJECT:外抛的对象 }
*/
extern NSString *kJL_MANAGER_KEY_UUID; //KEY --> UUID
extern NSString *kJL_MANAGER_KEY_OBJECT; //KEY --> 对象
Source: JL_ManagerM.h
统一携带 UUID 是为了支持多设备连接(如 TWS 双耳或同时连接多个外设):观察者可以通过 UUID 区分通知来自哪台设备,避免状态串扰。
JL_BLEAction:广播过滤、配对与 OTA 回连
JL_BLEAction 以单例形式提供"连接前"的辅助能力,其职责与 JL_Assist(连接后)互补:
+(id)sharedMe;
/**
过滤其余蓝牙设备
@param key 过滤码
@param advertData 蓝牙广播字典
@return NSDictionary 广播包含信息字典
*/
+(NSDictionary*)bluetoothKey_1:(NSData*)key Filter:(NSDictionary*)advertData;
/**
蓝牙设备配对
@param pKey 配对码
@param bk 配对回调YES:成功 NO:失败
*/
-(void)bluetoothPairingKey:(NSData *__nullable)pKey Result:(ATC_Block)bk;
-(void)inputPairData:(NSData*)rData;
-(void)cancelPair;
Source: JL_BLEAction.h
- 广播过滤:
bluetoothKey_1:Filter:用厂商过滤码匹配kCBAdvDataManufacturerData,返回广播中包含的设备信息字典。这是扫描列表过滤"非杰理设备"的标准入口,配合JL_Assist的mRcspPeripheral完成连接; - 配对/认证前置:
bluetoothPairingKey:Result:发起握手配对(对应设备端的配对码挑战),数据流经inputPairData:注入、cancelPair取消——该流程现已逐步被JL_Assist的mAuthKey/mAuthEnable认证取代(源码中mPairKey/mNeedPaired已标记deprecated); - OTA 回连:固件升级后设备可能更换广播身份,
otaBleMacAddressFromCBAdvDataManufacturerData:可从广播中提取'JLOTA'标识的蓝牙地址,otaBleMacAddress:isEqualToCBAdvDataManufacturerData:判断当前设备是否为升级目标,用于升级中断线后的自动重连; - 数据通知:
kJL_RCSP_RECEIVE(接收)与kJL_RCSP_SEND(发送)是全局裸数据通知,日志系统与抓包工具依赖它们实现协议可视化。
/**
* BLE数据通知
*/
extern NSString *kJL_RCSP_RECEIVE; //Rcsp数据【接收】
extern NSString *kJL_RCSP_SEND; //Rcsp数据【发送】
Source: JL_BLEAction.h
与 RCSP 相关的扩展属性
- TWS / LeAudio 地址:
JLTwsAdv中的rcspUseLeAudioAddress标志指示 RCSP 命令是否使用 LE Audio 地址,供双耳/LE Audio 场景下区分命令寻址方式(见 JLTwsAdv.h); - Flash 系统类型:
JL_FlashSystemType_RCSP = 1表示设备 Flash 采用 RCSP 文件系统(区别于 FATFS),文件管理器的读写路径据此选择(见 JLModel_Flash.h)。
Usage Examples
1. 在 CoreBluetooth 代理中桥接 JL_Assist(连接建立)
以下模式展示了 App 如何把系统蓝牙回调"转发"给 JL_Assist,由后者完成服务/特征发现与认证。这是所有接入方必须遵循的桥接样板:
// 在 CBCentralManagerDelegate / CBPeripheralDelegate 回调中:
- (void)centralManagerDidUpdateState:(CBManagerState)state {
[assist assistUpdateState:state]; // 蓝牙开关状态同步
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
// JL_Assist 按 mService/mRcsp_W/mRcsp_R 匹配并缓存特征
[assist assistDiscoverCharacteristicsForService:service Peripheral:peripheral];
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
// 订阅 Notify 完成后触发认证握手,isPaired 为 YES 表示可开始收发命令
[assist assistUpdateCharacteristic:characteristic
Peripheral:peripheral
Result:^(BOOL isPaired) {
if (isPaired) { /* 连接就绪,可以发命令 */ }
}];
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
[assist assistUpdateValueForCharacteristic:characteristic]; // 设备回包 → 命令中心
}
Source: JL_Assist.h
2. RCSP 数据包编解码(协议层独立使用)
JL_RCSP 的编解码方法不依赖连接,可用于抓包分析、单元测试或自定义通道:
// 接收方向:设备回包 NSData → JL_PKG 模型
JL_PKG *pkg = [JL_RCSP rcspAnalysisData:receivedData];
uint16_t opCode = pkg.pkgOpCode; // 取命令码
NSArray *params = [JL_RCSP rcspAnalysisParams:pkg]; // 参数拆分为数组
// 发送方向:构造 JL_PKG → 编码为字节流
JL_PKG *outPkg = [[JL_PKG alloc] init];
outPkg.pkgIsCommand = 1; // 命令包
outPkg.pkgNeedResponse = 1; // 需要设备回复
outPkg.pkgOpCode = 0x01; // 以 0x01 系统命令为例
outPkg.pkgData = [JL_RCSP rcspMakeParams:@[data1, data2]]; // 参数区
NSData *frame = [JL_RCSP rcspMakePackage:outPkg]; // 得到可发送的 XM 数据包
3. 扫描列表过滤与 OTA 回连
// 过滤:只保留广播中携带杰理过滤码的设备
NSDictionary *info = [JL_BLEAction bluetoothKey_1:filterKey
Filter:advertisementData];
if (info) { /* 该设备是杰理设备,展示/自动连接 */ }
// OTA 回连:升级后从广播包取回设备蓝牙地址
NSString *mac = [JL_BLEAction otaBleMacAddressFromCBAdvDataManufacturerData:advManufacturerData];
BOOL isTarget = [JL_BLEAction otaBleMacAddress:mac
isEqualToCBAdvDataManufacturerData:advManufacturerData];
Source: JL_BLEAction.h
Configuration Options
JL_Assist 是 RCSP 链路的主要配置点,配置必须发生在 assistUpdateState: 与特征发现之前:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mService | NSString | 无 | RCSP GATT 服务号,特征发现时匹配 |
mRcsp_W | NSString | 无 | RCSP 写特征 UUID |
mRcsp_R | NSString | 无 | RCSP 读/Notify 特征 UUID |
mAuthEnable | BOOL | NO | 是否启用设备认证握手 |
mAuthKey | NSData | nil | 设备认证密钥(替代已废弃的 mPairKey) |
mLimitMtu | NSInteger | 40 | 写入 MTU 余量,防止写满触发流控 |
mMaxMtu | NSInteger | 0 | 最大 MTU,建议实测刷新 |
mLogData | BOOL | NO | 是否打印收发裸数据 |
mDelegate | id<JL_AssistDelegate> | nil | 设置后数据改由外部回调发送 |
mBleName | NSString | 无 | 设备名字(日志/展示用) |
Source: JL_Assist.h
API Reference
JL_RCSP(协议编解码工具类)
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
+ rcspAnalysisData: | NSData *data | JL_PKG * | 原始数据解析为包模型 |
+ rcspMakePackage: | JL_PKG *pkg | NSData * | 包模型编码为字节流 |
+ rcspAnalysisParams: | JL_PKG *pkg | NSArray * | 拆分参数区为数组 |
+ rcspMakeParams: | NSArray *array | NSData * | 参数数组拼装为数据(元素必须是 NSData) |
+ rcspInfoArrFromData: | NSData *data | NSArray * | 分解 LTV 信息(L 为 1 字节) |
+ rcspInfoFromData2ByteSize: | NSData *data | NSArray * | 拆分 L 为 2 字节的 LTV 数据 |
JL_Assist(BLE 连接助手)
- assistUpdateState:(CBManagerState)state— 在centralManagerDidUpdateState:中调用,同步蓝牙开关状态;- assistDiscoverCharacteristicsForService:Peripheral:— 在didDiscoverCharacteristicsForService:中调用,匹配并缓存mRcspWrite/mRcspRead;- assistUpdateCharacteristic:Peripheral:Result:— 订阅 Notify 后触发认证;回调JL_Assist_BK(BOOL isPaired)(YES 配对成功 / NO 失败);- assistUpdateValueForCharacteristic:— 设备 Notify 数据上报入口,内部交给mCmdManager;- assistDidReady— 写通道就绪信号(didWriteValueForCharacteristic:/didIsReadyForWrite:中调用);- assistDisconnectPeripheral:— 断开事件处理,复位连接状态。
JL_ManagerM(命令中心)
- xmCommandCode:(uint8_t)cmdCode needResponse:(BOOL)needResponse sendData:(NSData*)sendData— 发送命令包;needResponse仅在命令包场景有效;- inputPKG:(JL_PKG*)pkg— 接收设备回包并路由;- noteEntityConnected / noteEntityDisconnected / noteEntityBleOff— 连接事件广播;- setPropertyUpdate:(BOOL)isUpdate、- setBleUuid:/- setBleName:— 状态设置;- 代理
JL_ManagerMDelegate - onManagerSendPackage:(JL_PKG*)pkg— 命令包外发回调。
JL_BLEAction(单例辅助)
+ sharedMe— 获取单例;+ bluetoothKey_1:Filter:— 过滤码匹配广播,返回设备信息字典;- bluetoothPairingKey:Result:/- inputPairData:/- cancelPair— 握手配对流程;+ otaBleMacAddressFromCBAdvDataManufacturerData:— 提取 OTA 蓝牙地址;+ otaBleMacAddress:isEqualToCBAdvDataManufacturerData:— 判断是否为 OTA 目标设备。
Failure Modes, Edge Cases & Concurrency
连接阶段失败
| 场景 | 现象 | 处理路径 |
|---|---|---|
| 蓝牙未开启/关闭 | centralManagerDidUpdateState: 非 poweredOn | assistUpdateState: 同步状态;JL_ManagerM 收到 noteEntityBleOff 后各管理器复位 |
| 特征发现失败 | 服务/特征 UUID 与 mService/mRcsp_W/mRcsp_R 不匹配 | 不会缓存 mRcspWrite/mRcspRead,后续写数据失败;需核对设备端固件配置 |
| 认证失败 | JL_Assist_BK 回调 NO | 认证握手未通过,禁止进入数据收发;需检查 mAuthKey 与设备端密钥一致 |
| 连接断开 | didDisconnectPeripheral: | assistDisconnectPeripheral: 复位;noteEntityDisconnected 通知各管理器清理进行中的传输(如文件/OTA) |
数据收发边界与并发
- MTU 分包与流控:写入超过
mMaxMtu - mLimitMtu的数据必须分包。assistDidReady(对应didIsReadyForWrite:)是 iOS 的写队列流控信号——大量连续写入时,必须等待该回调再发下一包,否则触发系统级节流甚至丢包。mLimitMtu默认 40 正是为这条边界预留的缓冲; - 命令序号竞态:
mCmdSN由JL_ManagerM统一自增,保证同一时刻发出的命令序号唯一,设备端依赖它做请求-应答匹配。多线程调用命令接口时,序号生成必须在管理器内部串行,业务层不应自行拼装序号; - 多设备通知区分:所有通知字典携带
kJL_MANAGER_KEY_UUID,观察者必须按 UUID 过滤,避免 TWS/多设备场景下的状态串扰; - 命令/数据包歧义:
pkgIsCommand区分命令与数据;长文件传输由pkgLength与分包序号还原,rcspInfoFromData2ByteSize:(2 字节长度)专用于大块 LTV 数据,避免 1 字节长度溢出(最大 255 的限制)。
协议兼容性注意
mPairKey/mNeedPaired已标记deprecated,新代码应使用mAuthKey/mAuthEnable;rcspUseLeAudioAddress(见 JLTwsAdv.h)影响 RCSP 命令的寻址方式,LE Audio 设备与经典 BLE 设备的命令构造不能混用;- Flash 系统类型
JL_FlashSystemType_RCSP(见 JLModel_Flash.h)决定文件读写协议分支,升级固件改变文件系统后必须同步刷新该字段。
Performance & Operational Notes
- 日志:
JL_Assist.mLogData开启后打印裸数据;kJL_RCSP_RECEIVE/kJL_RCSP_SEND通知可挂接协议抓包工具,用于线上问题定位,但高频通知有性能开销,正式包建议关闭或采样; - MTU 优化:
mMaxMtu初始化值不一定准确,源码注释明确建议在连接后通过[peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithoutResponse]实测刷新(见 JL_Assist.h),更大的 MTU 能显著减少文件传输的包数; - 热路径:音频流控(音乐控制、音量)与系统属性上报(
setPropertyUpdate:)是高频路径,功能管理器通过属性下发而非逐条查询,减少空中往返; - OTA 中断恢复:升级断线后使用
JL_BLEAction的广播地址匹配能力自动回连目标设备,避免用户手动重连错设备。
Extension Points
- 自定义传输通道:实现
JL_AssistDelegate的assistDidWriteData:,可接管数据发送(如加密、转发到其他外设),JL_Assist内部不再处理发送; - 新功能管理器:
JL_ManagerM已挂载 20+ 管理器,新增设备能力时可在JL_ManagerM.h增加属性、并在inputPKG:的分发逻辑中扩展 OpCode 路由,复用统一的xmCommandCode:发送链路; - 协议扩展位:
pkgUnused(6 bits)为协议保留,扩展命令码需与设备端固件同步升级,注意与JL_OpCode.h的现有枚举不冲突; - 扫描策略定制:
bluetoothKey_1:Filter:返回的广播信息字典可扩展为扫描列表的展示数据源,自定义过滤逻辑只需在调用前处理advertisementData。