# iOS-JL_OTA **Repository Path**: Jieli-Tech/iOS-JL_OTA ## Basic Information - **Project Name**: iOS-JL_OTA - **Description**: No description available - **Primary Language**: Objective-C - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 9 - **Forks**: 7 - **Created**: 2021-05-27 - **Last Updated**: 2025-09-09 ## Categories & Tags **Categories**: ios-modules **Tags**: None ## README [toc] # iOS杰理蓝牙OTA开发说明 - 数传:AC695X/AC608N/AC897/AD697N/AD698N/AC630N/AC632N 手表:AC695X/JL701N/AC707N 音箱:JL701N/AC897/AD697N/AD698N/700N 对应固件 **SDK** 版本:支持 **RCSP** 协议 **OTA** 设备 - APP开发环境:iOS平台,iOS 12.0以上,Xcode 14.0以上 - 对应于苹果商店上的APP: **【杰理OTA】** - 源码连接: https://github.com/Jieli-Tech/iOS-JL_OTA - 杰理OTA对外开发文档:https://doc.zh-jieli.com/Apps/iOS/ota/zh-cn/master/index.html ## 声明 1. 本项⽬所参考、使⽤技术必须全部来源于公知技术信息,或⾃主创新设计。 2. 本项⽬不得使⽤任何未经授权的第三⽅知识产权的技术信息。 3. 如个⼈使⽤未经授权的第三⽅知识产权的技术信息,造成的经济损失和法律后果由个⼈承担。 ## SDK 版本 | 版本 | 日期 | 编辑 | 修改内容 | | ------ | ---------- | -------- | ------------------------------------------------------------ | | V2.3.1 | 2024/12/12 | EzioChan | 1. 分离日志打印库为独立运行模块;
2. 增加所有命令的超时检测;
3. 增加OTA升级的错误回调;
4. 增加OTA对象的对象管理容错; | | V2.1.0 | 2023/3/28 | 陈冠杰 | 1. 性能优化
1.1 分离OTA模块为独立运行模块
1.2 分离设备认证配对业务为独立库
1.3 分离广播包解析模块为独立库 | | V2.0.0 | 2021/10/14 | 凌 煊峰 | 1. 支持BLE单备份升级
2.支持BLE双备份升级
3.支持从浏览器传输OTA升级文件到APP
4.支持第三方电脑软件导入OTA升级文件
5.可选择BLE广播包过滤
6. 可选择BLE握手连接 | ## APP版本 | 版本 | 日期 | 编辑 | 修改内容 | | ---- | -------------- | ------- | ---------------------------------------------- | | V3.5.0 | 2023 年 12 月 13 日 | EzioChan | 适配新 SDK 2.3.1 | |V3.3.0 | 2023年03月23日|陈冠杰 | 适配新SDK2.1.0 | | V3.2.0 | 2023年1月11日 | 陈冠杰 | 重构UI页面,整理项目架构,新增自动化测试/广播音箱模块 | | v2.0.0 | 2021年10月14日 | 凌 煊峰 | 蓝牙库新增根据ble地址对升级设备的回连;重写ota demo | | v1.5.0 | 2021年09月08日 | 冯 洪鹏 | 优化自定义蓝牙SDK的接入方式 | | v1.2 | 2020年12月09日 | 冯 洪鹏 | 更新文档 | | v1.1 | 2020年04月20日 | 冯 洪鹏 | 增加升级的错误回调 | | v1.0 | 2019年09月09日 | 冯 洪鹏 | OTA升级功能 | ## 概述 本文档描述了如何使用已有SDK:JL_BLEKit.framework、JL_OTALib.framework、JL_AdvParse.framework、JL_HashPair.framework等一系列库实现iOS端协助设备端实现OTA升级的过程。本文会随着版本迭代而更新信息以及相关接入时的变化,开发者在使用上述开发库时可优先查看本文。 # 开发环境准备 - 对应的芯片类型:AC692x,BD29 - APP开发环境:iOS平台,iOS 12.0以上,Xcode 13.4以上 - 对应于苹果商店上的APP: **【杰理OTA】** - 源码连接: https://github.com/Jieli-Tech/iOS-JL_OTA - 杰理OTA对外开发文档:https://doc.zh-jieli.com/Apps/iOS/ota/zh-cn/master/index.html ## 注意事项 - `JL_BLEKit.framework` 依赖 `JLLogHelper.framework`,需要在导入 JL_BLEKit.framework 的时候需要同时导入这个库。 - 其他的SDK库诸如: `JL_OTALib.framework、 JL_HashPair.framework`等等,需要在导入的时候需要同时导入 `JLLogHelper.framework`。 JLLogHelper.framework 是默认开启了日志打印和存储的,如果开发者不需要记录,可以通过对应的接口关闭日志打印和存储。具体操作可以参考以下: ```objc [JLLogManager clearLog]; // 清空日志 [JLLogManager setLog:false IsMore:false Level:JLLOG_COMPLETE]; // 关闭日志打印 [JLLogManager saveLogAsFile:false]; // 关闭日志存储 [JLLogManager logWithTimestamp:false]; // 关闭日志打印时间 //BetaBuild 内容 JLLogManager.saveLog(asFile: true) JLLogManager.setLog(true, isMore: false, level: .COMPLETE) JLLogManager.log(withTimestamp: true) let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/abc.txt" // 重置保存的路径 JLLogManager.redirectLogPath(path) JLLogManager.clearLog() //回调所有的日志打印内容 JLLogManager.collectLog { str in print(str) } JLLogManager.logSomething("abcd") ``` ## 备注 1. 随着对OTA库的抽离,以后的版本将会放弃使用臃肿的JL_BLEKit.framework进行OTA升级,此SDK后续将不再提供JL_BLEKit.framework的使用示例,更多的转向开发者自定义蓝牙管理,SDK内容专注于数据解析和流程控制。 2. 由于苹果公司发布的xcode开发工具**V14.0**版本之后,**不再支持 iOS10.0设备以及armv7架构**,我们也在计划淘汰掉这一部分的设备,请各位开发者及时升级Xcode,谢谢! 实现OTA升级必须导入的库: > *JL_OTALib.framework*——OTA升级业务库 > > *JL_AdvParse.framework*——杰理蓝牙设备广播包解析业务库 > > *JL_HashPair.framework*——设备认证业务库 > > JLLogHelper.framework——日志打印收集库 可选择导入的库: > *JL_BLEKit.framework* 备注:当开发者想要使用杰理集成的蓝牙库(连接、其他通讯业务)时,需要导入当前SDK使用 添加以下权限: > *Privacy - Bluetooth Peripheral Usage Description* > > *Privacy - Bluetooth Always Usage Description* # SDK使用的两种方式 - 自定义蓝牙连接方式(对应BleManager文件夹); 备注:**自定义蓝牙连接中所有BLE的操作都自行实现,SDK只负责对OTA数据包解析。** - 使用JL_BLEKit.framework蓝牙连接方式(对应SDKBleManager文件夹)。 工程中已通过**BleHandle**文件夹内的**JLBleHandler**类进行了区分,具体可以参考实际源码。开发普通OTA升级流程只需要参考**Views**文件夹中的**NormalUpdate**文件内容即可。 ## 1. 自定义蓝牙连接方式 ### 1.1. JL_OTALib工作时序 ```mermaid sequenceDiagram participant 设备 participant APP participant SDK APP ->> 设备:连接设备BLE APP ->> 设备:找到AE00服务,发起特征值查找 设备 ->> APP:写特征:AE01,读特征:AE02 APP->>设备:订阅AE02特征值 APP->>SDK:[ota cmdTargetFeature]请求设备状态 SDK->>APP:代理otaDataSend:(即将被发送的数据)Data APP->>设备:通过写特征:AE01 (发送即将被发送的数据)data 设备->>APP:设备响应APP,AE02回复内容 APP->>SDK:[ota cmdOtaDataReceive:data]收到设备的回复 SDK->>APP:代理otaFeatureResult:状态信息 APP->>APP:业务逻辑判断是否升级 APP->>SDK:[ota cmdOTAData:data]开始升级 SDK->>APP:代理otaDataSend:(即将被发送的数据)Data APP->>设备:通过写特征:AE01 (发送即将被发送的数据)data 设备->>APP:设备响应APP,AE02回复内容 APP->>SDK:[ota cmdOtaDataReceive:data]收到设备的回复 SDK->>APP:代理otaFeatureResult:状态信息 APP->>APP:处理状态信息 loop 开始传输 设备->>APP:发起请求数据 AE02(循环开始) APP->>SDK:[ota cmdOtaDataReceive:data]收到设备的回复 SDK->>APP:代理otaDataSend:(即将被发送的数据)Data APP->>设备:通过写特征:AE01 (发送即将被发送的数据)data SDK->>APP:代理otaFeatureResult:状态信息\n(完成时,循环结束) end ``` **参考Demo:「JL_OTA项目的BleManager文件夹」** **支持的功能:** - BLE设备握手连接; - 获取设备信息; - OTA升级能实现; **用到的库和相关类:** 用到的库: > *JL_OTALib.framework*——OTA升级业务库 > > *JL_AdvParse.framework*——杰理蓝牙设备广播包解析业务库 > > *JL_HashPair.framework*——设备认证业务库 > > JLLogHelper.framework——日志打印收集库 相关类说明: **JL_OTAManager:**OTA升级业务管理类 | 平台 | 接口 | 功能 | | ---- | ---------------- | ---------------------------------------------- | | iOS | cmdTargetFeature | 获取设备信息(是否需要升级/强制升级) | | iOS | cmdOtaDataReceive: | 设备端发过来的数据解析接口 | | iOS | cmdOTAData:Result: | 请求进入loader状态,需要携带升数据bfu | | iOS | cmdOTAData:Result: | 进行数据传输(进入loader,重连后需要再次调用) | | iOS | cmdRebootDevice | 重启设备命令 | **JLHashHandler:**设备认证业务类 **JL_AdvParse:**广播包内容解析类 **BLE特征参数** - **【服务号】**:AE00 - **【写】特征值**:AE01 - **【读 】特征值**:AE02 ### 1.2. 初始化SDK ```objective-c @interface JLBleManager() @property (strong, nonatomic) JLHashHandler *pairHash; @end - (instancetype)init { self = [super init]; if (self) { _otaManager = [[JL_OTAManager alloc] init]; _otaManager.delegate = self; self.pairHash = [[JLHashHandler alloc] init]; self.pairHash.delegate = self; [JL_OTAManager logSDKVersion]; [JLHashHandler sdkVersion]; } return self; } ``` ### 1.3. BLE设备特征回调 ```objective-c #pragma mark 设备特征回调 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error { if (error) { NSLog(@"Err: Discovered Characteristics fail."); return; } if ([service.UUID.UUIDString isEqual:FLT_BLE_SERVICE]) { for (CBCharacteristic *character in service.characteristics) { /*--- RCSP ---*/ if ([character.UUID.UUIDString isEqual:FLT_BLE_RCSP_W]) { NSLog(@"BLE Get Rcsp(Write) Channel ---> %@",character.UUID.UUIDString); self.mRcspWrite = character; } if ([character.UUID.UUIDString isEqual:FLT_BLE_RCSP_R]) { NSLog(@"BLE Get Rcsp(Read) Channel ---> %@",character.UUID.UUIDString); self.mRcspRead = character; [peripheral setNotifyValue:YES forCharacteristic:character]; if(self.mRcspRead.properties == CBCharacteristicPropertyRead){ [peripheral readValueForCharacteristic:character]; NSLog(@"BLE Rcsp(Read) Read Value For Characteristic."); } } } } } ``` ### 1.4. BLE更新通知特征的状态 ```objective-c #pragma mark 更新通知特征的状态 - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error { if (error) { NSLog(@"Err: Update NotificationState For Characteristic fail."); return; } if ([characteristic.service.UUID.UUIDString isEqual:FLT_BLE_SERVICE] && [characteristic.UUID.UUIDString isEqual:FLT_BLE_RCSP_R] && characteristic.isNotifying == YES) { __weak typeof(self) weakSelf = self; self.bleMtu = [peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithoutResponse]; NSLog(@"BLE ---> MTU:%lu",(unsigned long)self.bleMtu); if (self.isPaired == YES) { [_pairHash hashResetPair]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ //设备认证 [self->_pairHash bluetoothPairingKey:self.pairKey Result:^(BOOL ret) { if(ret){ weakSelf.lastUUID = peripheral.identifier.UUIDString; weakSelf.lastBleMacAddress = nil; [[NSNotificationCenter defaultCenter] postNotificationName:kFLT_BLE_PAIRED object:peripheral]; [weakSelf.otaManager noteEntityConnected]; weakSelf.pairStatus = YES; }else{ NSLog(@"JL_Assist Err: bluetooth pairing fail."); [weakSelf.bleManager cancelPeripheralConnection:peripheral]; } }]; }); }else{ self.lastUUID = peripheral.identifier.UUIDString; self.lastBleMacAddress = nil; [[NSNotificationCenter defaultCenter] postNotificationName:kFLT_BLE_PAIRED object:peripheral]; [self.otaManager noteEntityConnected]; } } self.isConnected = YES; } ``` ### 1.5. BLE设备返回的数据 设备端返回的数据需要放入到SDK中解析,其中**设备认证部分**和**通讯部分**的处理是分开的 ```objective-c #pragma mark 设备返回的数据 GET - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (error) { NSLog(@"Err: receive data fail."); return; } if(_isPaired == YES && _pairStatus == NO){ //收到设备的认证交互数据 [_pairHash inputPairData:characteristic.value]; }else{ //收到的设备数据,正常通讯数据 [_otaManager cmdOtaDataReceive:characteristic.value]; } } ``` ### 1.6. BLE设备断开连接 ```objective-c #pragma mark 设备断开连接 - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error { NSLog(@"BLE Disconnect ---> Device %@ error:%d", peripheral.name, (int)error.code); [_otaManager noteEntityDisconnected]; self.isConnected = NO; self.pairStatus = NO; /*--- UI刷新,设备断开 ---*/ [[NSNotificationCenter defaultCenter] postNotificationName:kFLT_BLE_DISCONNECTED object:peripheral]; } ``` ### 1.7. 手机蓝牙状态更新 ```objective-c //外部蓝牙,手机蓝牙状态回调处,实现以下: #pragma mark - 蓝牙初始化 Callback - (void)centralManagerDidUpdateState:(CBCentralManager *)central { _mBleManagerState = central.state; if (_mBleManagerState != CBManagerStatePoweredOn) { self.mBlePeripheral = nil; self.blePeripheralArr = [NSMutableArray array]; } } ``` ### 1.8. 获取设备信息 BLE连接且配对后必须执行一次 ```objective-c [_otaManager cmdTargetFeature]; ``` ### 1.9. 固件OTA升级 ```objective-c /** * ota升级 * @param otaFilePath ota升级文件路径 */ - (void)otaFuncWithFilePath:(NSString *)otaFilePath { NSLog(@"current otaFilePath ---> %@", otaFilePath); self.selectedOtaFilePath = otaFilePath; NSData *otaData = [[NSData alloc] initWithContentsOfFile:otaFilePath]; [_otaManager cmdOTAData:otaData Result:^(JL_OTAResult result, float progress) { for (id objc in self.delegates) { if([objc respondsToSelector:@selector(otaProgressWithOtaResult:withProgress:)]){ [objc otaProgressWithOtaResult:result withProgress:progress]; } } }]; } ``` ### 1.10. 取消固件OTA升级 ```objective-c - (void)otaFuncCancel:(CANCEL_CALLBACK _Nonnull)result{ [_otaManager cmdOTACancelResult:^(uint8_t status, uint8_t sn, NSData * _Nullable data) { result(status); }]; } ``` ### 1.11. OTAManager 的delegate回调处理 ```objective-c //MARK: - ota manager delegate callback ///取消升级回调 -(void)otaCancel{ //TODO: 取消OTA升级回调 } ///升级状态进度回调 -(void)otaUpgradeResult:(JL_OTAResult)result Progress:(float)progress{ //TODO: 设备升级过程回调,包括进度状态 } ///即将发送给设备的数据 -(void)otaDataSend:(NSData *)data{ //TODO: 开发者需要在这里要把数据发送到设备 [self writeDataByCbp:data]; } ///设备信息回调 -(void)otaFeatureResult:(JL_OTAManager *)manager{ NSLog(@"getDeviceInfo:%d",__LINE__); if (manager.otaStatus == JL_OtaStatusForce) { NSLog(@"---> 进入强制升级."); if (self.selectedOtaFilePath) { [self otaFuncWithFilePath:self.selectedOtaFilePath]; } else { _getCallback(true); } return; } else { if (manager.otaHeadset == JL_OtaHeadsetYES) { NSLog(@"---> 进入强制升级: OTA另一只耳机."); if (self.selectedOtaFilePath) { [self otaFuncWithFilePath:self.selectedOtaFilePath]; } else { _getCallback(true); } return; } } NSLog(@"---> 设备正常使用..."); dispatch_async(dispatch_get_main_queue(), ^{ /*--- 获取公共信息 ---*/ [self->_otaManager cmdSystemFunction]; self->_getCallback(false); }); } ``` ### 1.12. 设备认证的delegate回调 ```objective-c //MARK: - Hash pair delegate callback ///即将发送到设备的认证数据 -(void)hashOnPairOutputData:(NSData *)data{ //TODO: 开发者需要在这里要把数据发送到设备 [self writeDataByCbp:data]; } ``` ### 1.13. 数据发送 - iOS设备与固件通讯之间的最大MTU需要在连上后获取 ```objective-c self.bleMtu = [peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithoutResponse]; ``` - 发送到设备的数据需要进行分包发送 代码参考如下: ```objective-c //MARK: - data send manager /// 需要分包发送 /// - Parameter data: 数据 -(void)writeDataByCbp:(NSData *)data{ // NSLog(@"%s:data:%@",__func__,data); if (_mBlePeripheral && self.mRcspWrite) { if (data.length > 0 ) { NSInteger len = data.length; while (len>0) { if (len <= _bleMtu) { NSData *mtuData = [data subdataWithRange:NSMakeRange(0, data.length)]; [_mBlePeripheral writeValue:mtuData forCharacteristic:self.mRcspWrite type:CBCharacteristicWriteWithoutResponse]; len -= data.length; }else{ NSData *mtuData = [data subdataWithRange:NSMakeRange(0, _bleMtu)]; [_mBlePeripheral writeValue:mtuData forCharacteristic:self.mRcspWrite type:CBCharacteristicWriteWithoutResponse]; len -= _bleMtu; data = [data subdataWithRange:NSMakeRange(_bleMtu, len)]; } } } }else{ //需要先赋值写特征的内容 NSLog(@"need to init"); } } ``` ## 2. 使用JL_BLEKit.framework蓝牙连接方式 以下实现方法兼容于JL_BLEKit.framework的老版本:**V1.7.0/1.6.3/1.6.4** 区别于旧版SDK,新版JL_BLEKit.framework依赖于其他的SDK,所以在导入时应包含以下: > *JL_OTALib.framework*——OTA升级业务库 > > *JL_AdvParse.framework*——杰理蓝牙设备广播包解析业务库 > > *JL_HashPair.framework*——设备认证业务库 ### 2.1. SDK外部蓝牙管理 外部蓝牙管理,在使用JL_BLEKit.framework的同时,在蓝牙管理部分交由**外边(开发者自定义蓝牙)统筹管理**,可保障使用时的多样化。 **参考Demo:「 JL_OTA项目的 BleByAssist文件夹」** **支持的功能**: - BLE设备握手连接; - 获取设备信息; - OTA升级能实现; - 注意:相对于**2.2.2**中描述的所有BLE操作都需自行实现; **会用到的类**: - **JL_Assist**:部署SDK类;(必须) - **JL_ManagerM**:命令处理中心,所有的命令操作都集中于此;(必须) - **JLModel_Device**:设备信息存储的数据模型;(必须) **BLE参数**: - **【服务号】**:AE00 - **【写】特征值**:AE01 - **【读 】特征值**:AE02 #### 2.1.1. 初始化SDK ```objective-c /*--- JLSDK ADD ---*/ _mAssist = [[JL_Assist alloc] init]; _mAssist.mNeedPaired = _isPaired; //是否需要握手配对 /*--- 自定义配对码(16个字节配对码) ---*/ //char pairkey[16] = {0x01,0x02,0x03,0x04, // 0x01,0x02,0x03,0x04, // 0x01,0x02,0x03,0x04, // 0x01,0x02,0x03,0x04}; //NSData *pairData = [NSData dataWithBytes:pairkey length:16]; _mAssist.mPairKey = nil; //配对秘钥(或者自定义配对码pairData) _mAssist.mService = FLT_BLE_SERVICE; //服务号 _mAssist.mRcsp_W = FLT_BLE_RCSP_W; //特征「写」 _mAssist.mRcsp_R = FLT_BLE_RCSP_R; //特征「读」 ``` #### 2.1.2. BLE设备特征回调 ```objective-c #pragma mark - 设备特征回调 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error { if (error) { NSLog(@"Err: Discovered Characteristics fail."); return; } /*--- JLSDK ADD ---*/ [self.mAssist assistDiscoverCharacteristicsForService:service Peripheral:peripheral]; } ``` #### 2.1.3. BLE更新通知特征的状态 ```objective-c #pragma mark - 更新通知特征的状态 - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error { if (error) { NSLog(@"Err: Update NotificationState For Characteristic fail."); return; } /*--- JLSDK ADD ---*/ __weak typeof(self) weakSelf = self; [self.mAssist assistUpdateCharacteristic:characteristic Peripheral:peripheral Result:^(BOOL isPaired) { if (isPaired == YES) { weakSelf.lastUUID = peripheral.identifier.UUIDString; weakSelf.lastBleMacAddress = nil; weakSelf.mBlePeripheral = peripheral; /*--- UI配对成功 ---*/ [JL_Tools post:kFLT_BLE_PAIRED Object:peripheral]; } else { [weakSelf.bleManager cancelPeripheralConnection:peripheral]; } }]; } ``` #### 2.1.4. BLE设备返回的数据 ```objective-c #pragma mark - 设备返回的数据 GET - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (error) { NSLog(@"Err: receive data fail."); return; } /*--- JLSDK ADD ---*/ [self.mAssist assistUpdateValueForCharacteristic:characteristic]; } ``` #### 2.1.5. BLE设备断开连接 ```objective-c #pragma mark - 设备断开连接 - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error { NSLog(@"BLE Disconnect ---> Device %@ error:%d", peripheral.name, (int)error.code); self.mBlePeripheral = nil; /*--- JLSDK ADD ---*/ [self.mAssist assistDisconnectPeripheral:peripheral]; /*--- UI刷新,设备断开 ---*/ [JL_Tools post:kFLT_BLE_DISCONNECTED Object:peripheral]; } ``` #### 2.1.6. 手机蓝牙状态更新 ```objective-c //外部蓝牙,手机蓝牙状态回调处,实现以下: #pragma mark - 蓝牙初始化 Callback - (void)centralManagerDidUpdateState:(CBCentralManager *)central { _mBleManagerState = central.state; /*--- JLSDK ADD ---*/ [self.mAssist assistUpdateState:central.state]; if (_mBleManagerState != CBManagerStatePoweredOn) { self.mBlePeripheral = nil; self.blePeripheralArr = [NSMutableArray array]; } } ``` #### 2.1.7. 获取设备信息 **BLE连接且配对后必须执行一次** ```objective-c [self.mAssist.mCmdManager cmdTargetFeatureResult:^(NSArray * _Nullable array) { JL_CMDStatus st = [array[0] intValue]; if (st == JL_CMDStatusSuccess) { JLModel_Device *model = [weakSelf.mAssist.mCmdManager outputDeviceModel]; JL_OtaStatus upSt = model.otaStatus; if (upSt == JL_OtaStatusForce) { NSLog(@"---> 进入强制升级."); if (weakSelf.selectedOtaFilePath) { [weakSelf otaFuncWithFilePath:weakSelf.selectedOtaFilePath]; } else { callback(true); } return; } else { if (model.otaHeadset == JL_OtaHeadsetYES) { NSLog(@"---> 进入强制升级: OTA另一只耳机."); if (weakSelf.selectedOtaFilePath) { [weakSelf otaFuncWithFilePath:weakSelf.selectedOtaFilePath]; } else { callback(true); } return; } } NSLog(@"---> 设备正常使用..."); [JL_Tools mainTask:^{ /*--- 获取公共信息 ---*/ [weakSelf.mAssist.mCmdManager cmdGetSystemInfo:JL_FunctionCodeCOMMON Result:nil]; }]; } else { NSLog(@"---> ERROR:设备信息获取错误!"); } }]; ``` #### 2.1.8. 固件OTA升级 ```objective-c //升级流程:连接设备-->获取设备信息-->是否强制升级-->(是)则必须调用该API去OTA升级; // |_______>(否)则可以正常使用APP; // 设置代理 @interface JLUpdateViewController () - (void)viewDidLoad { [super viewDidLoad]; // 设置ota升级过程状态回调代理 [JLBleManager sharedInstance].otaDelegate = self; } /** * 选择文件后,点击启动OTA升级 */ - (IBAction)updateBtnFunc:(id)sender { if (![JLBleManager sharedInstance].mBlePeripheral) { self.updateSeekLabel.text = @""; [DFUITools showText:@"请先连接设备" onView:self.view delay:1.0]; return; } /*--- 获取设备信息 ---*/ [[JLBleManager sharedInstance] otaFuncWithFilePath:_selectFilePath]; } #pragma mark - JLBleManagerOtaDelegate /** * ota升级过程状态回调 */ - (void)otaProgressWithOtaResult:(JL_OTAResult)result withProgress:(float)progress { if (result == JL_OTAResultUpgrading || result == JL_OTAResultPreparing) { if (result == JL_OTAResultPreparing) self.updateLabel.text = @"校验文件中"; if (result == JL_OTAResultUpgrading) self.updateLabel.text = @"正在升级"; } else if (result == JL_OTAResultPrepared) { NSLog(@"---> 检验文件【完成】"); } else if (result == JL_OTAResultReconnect) { NSLog(@"---> OTA正在回连设备... %@", [JLBleManager sharedInstance].mBlePeripheral.name); [[JLBleManager sharedInstance] connectPeripheralWithUUID:[JLBleManager sharedInstance].lastUUID]; [self otaTimeClose];//关闭超时检测 } else if (result == JL_OTAResultReconnectWithMacAddr) { NSLog(@"---> OTA正在通过Mac Addr方式回连设备... %@", [JLBleManager sharedInstance].mBlePeripheral.name); JLModel_Device *model = [[JLBleManager sharedInstance].mAssist.mCmdManager outputDeviceModel]; [JLBleManager sharedInstance].lastBleMacAddress = model.bleAddr; [[JLBleManager sharedInstance] startScanBLE]; [self otaTimeClose];//关闭超时检测 } else if (result == JL_OTAResultSuccess) { NSLog(@"--->升级成功."); } else if (result == JL_OTAResultReboot) { NSLog(@"--->设备重启."); } else { // 其余错误码详细 Command+点击JL_OTAResult 查看说明 NSLog(@"ota update result: %d", result); } } ``` ### 2.2. SDK内部蓝牙管理 以下实现方法基于JL_BLEKit.framework的内部集成蓝牙连接方法,开发者无需管理蓝牙对象,只专注升级业务即可。 **参考Demo:「 JL_OTA项目的 SDKBleManager文件夹」** **1. 支持的功能**: - BLE设备的扫描、连接、断开、收发数据、回连功能; - BLE设备过滤; - BLE设备握手连接; - BLE连接服务和特征值设置; - 获取设备信息; - OTA升级能实现; **2. 会用到的类**: - **JL_BLEUsage** :可设置BLE过滤、握手、参数; ​ 查看蓝牙状态; - **JL_Entity**:BLE设备的模型类,记录设备的相关信息(如名字、UUID、UID、PID等); - **JL_BLEMultiple**:BLE扫描、连接、断开、回连; - **JL_ManagerM**:获取设备信息、OTA操作; #### 2.2.1. 初始化SDK ```objective-c self.mBleMultiple = [[JL_BLEMultiple alloc] init]; self.mBleMultiple.BLE_FILTER_ENABLE = YES; // 过滤非杰理蓝牙设备 // self.mBleMultiple.filterKey = nil; // 一般情况赋值nil即可 self.mBleMultiple.BLE_PAIR_ENABLE = YES; /*--- 自定义配对码(16个字节配对码) ---*/ //char pairkey[16] = {0x01,0x02,0x03,0x04, // 0x01,0x02,0x03,0x04, // 0x01,0x02,0x03,0x04, // 0x01,0x02,0x03,0x04}; //NSData *pairData = [NSData dataWithBytes:pairkey length:16]; // self.mBleMultiple.pairKey = nil; // 配对秘钥(或者自定义配对码pairData) self.mBleMultiple.BLE_TIMEOUT = 7; ``` #### 2.2.2. 扫描设备 ```objective-c /*--- 搜索蓝牙设备 ---*/ [[JL_RunSDK sharedInstance].mBleMultiple scanStart]; // 监听通知【kJL_BLE_M_FOUND】【kJL_BLE_M_FOUND_SINGLE】回调设备数组 [JL_Tools add:kJL_BLE_M_FOUND Action:@selector(reloadTableView) Own:self]; [JL_Tools add:kJL_BLE_M_FOUND_SINGLE Action:@selector(reloadTableView) Own:self]; // 获取设备数组 - (void)reloadTableView { self.btEnityList = [JL_RunSDK sharedInstance].mBleMultiple.blePeripheralArr; if ([JL_RunSDK sharedInstance].mBleEntityM && ![self.btEnityList containsObject:[JL_RunSDK sharedInstance].mBleEntityM]) { [self.btEnityList insertObject:[JL_RunSDK sharedInstance].mBleEntityM atIndex:0]; } [self.subTableView reloadData]; } ``` #### 2.2.3. 连接和断开设备 ```objective-c //API通过【JL_BLEMultiple】使用 /** 连接设备 @param entity 蓝牙设备类 */ -(void)connectEntity:(JL_EntityM*)entity Result:(JL_EntityM_STATUS_BK)result; /** 断开连接 */ -(void)disconnectEntity:(JL_EntityM*)entity Result:(JL_EntityM_STATUS_BK)result; /** * BLE状态通知 */ extern NSString *kJL_BLE_M_FOUND; //发现设备 extern NSString *kJL_BLE_M_FOUND_SINGLE; //发现单个设备 extern NSString *kJL_BLE_M_ENTITY_CONNECTED; //连接有更新 extern NSString *kJL_BLE_M_ENTITY_DISCONNECTED; //断开连接 extern NSString *kJL_BLE_M_ON; //BLE开启 extern NSString *kJL_BLE_M_OFF; //BLE关闭 extern NSString *kJL_BLE_M_EDR_CHANGE; //经典蓝牙输出通道变化 ``` #### 2.2.4. 获取设备信息(必须) ```objective-c [[JL_RunSDK sharedInstance] getDeviceInfo:^(BOOL needForcedUpgrade) { if (needForcedUpgrade) { NSLog(@"设备需要强制升级,请到升级界面选择ota升级文件进行升级!"); [self startLoadingView:@"设备需要强制升级,请到升级界面选择ota升级文件进行升级!" Delay:1.0]; } }]; ``` #### 2.2.5. 固件OTA升级 ```objective-c //升级流程:连接设备-->获取设备信息-->是否强制升级-->(是)则必须调用该API去OTA升级; // |_______>(否)则可以正常使用APP; // 设置代理 @interface JLUpdateViewController () // 设置ota升级过程状态回调代理 [JL_RunSDK sharedInstance].otaDelegate = self; /** * 选择文件后,点击启动OTA升级 */ - (IBAction)updateBtnFunc:(id)sender { if (![JL_RunSDK sharedInstance].mBleEntityM) { self.updateSeekLabel.text = @""; [DFUITools showText:@"请先连接设备" onView:self.view delay:1.0]; return; } /*--- 获取设备信息 ---*/ [[JL_RunSDK sharedInstance] otaFuncWithFilePath:_selectFilePath]; } #pragma mark - JL_RunSDKOtaDelegate /** * ota升级过程状态回调 */ - (void)otaProgressWithOtaResult:(JL_OTAResult)result withProgress:(float)progress { if (result == JL_OTAResultUpgrading || result == JL_OTAResultPreparing) { if (result == JL_OTAResultPreparing) self.updateLabel.text = @"校验文件中"; if (result == JL_OTAResultUpgrading) self.updateLabel.text = @"正在升级"; } else if (result == JL_OTAResultPrepared) { NSLog(@"---> 检验文件【完成】"); } else if (result == JL_OTAResultReconnect) { NSLog(@"---> OTA正在回连设备... %@", [JL_RunSDK sharedInstance].mBleEntityM.mPeripheral.name); } else if (result == JL_OTAResultReconnectWithMacAddr) { NSLog(@"---> OTA正在通过Mac Addr方式回连设备... %@", [JL_RunSDK sharedInstance].mBleEntityM.mPeripheral.name); } else if (result == JL_OTAResultSuccess) { NSLog(@"--->升级成功."); } else if (result == JL_OTAResultReboot) { NSLog(@"--->设备重启."); } else { // 其余错误码详细 Command+点击JL_OTAResult 查看说明 NSLog(@"ota update result: %d", result); } } ```