2.使用自定义的蓝牙连接API进行OTA
2.1. JL_OTALib工作时序
参考Demo:「JL_OTA项目的BleManager文件夹」
支持的功能:
BLE设备握手连接;
获取设备信息;
OTA升级能实现;
用到的库和相关类:
用到的库:
JL_OTALib.framework —-OTA升级业务库
JL_AdvParse.framework——杰理蓝牙设备广播包解析业务库
JL_HashPair.framework——设备认证业务库
重要
在OTA过程中不允许进行其他命令的交互
相关类说明: JL_OTAManager: OTA升级业务管理类
获取设备信息(是否需要升级/强制升级)
cmdTargetFeature
设备端发过来的数据解析接口
cmdOTADataReceive:
请求进入loader状态,需要携带升数据bfu
cmdOTAData:Result:
进行数据传输(进入loader,重连后需要再次调用)
cmdOTAData:Result:
重启设备命令
cmdRebootDevice
JLHashHandler: 设备认证业务类
JL_AdvParse: 广播包内容解析类
BLE特征参数
【服务号】 :AE00
【写】特征值 :AE01
【读 】特征值 :AE02
2.2. 初始化SDK
1@interface JLBleManager() <JL_OTAManagerDelegate,JLHashHandlerDelegate>
2@property (strong, nonatomic) JLHashHandler *pairHash;
3
4@end
5- (instancetype)init {
6 self = [super init];
7 if (self) {
8 _otaManager = [[JL_OTAManager alloc] init];
9 _otaManager.delegate = self;
10 self.pairHash = [[JLHashHandler alloc] init];
11 self.pairHash.delegate = self;
12
13 [JL_OTAManager logSDKVersion];
14 [JLHashHandler sdkVersion];
15 }
16 return self;
17}
2.3. BLE设备特征回调
1#pragma mark 设备特征回调
2- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error {
3 if (error) { NSLog(@"Err: Discovered Characteristics fail."); return; }
4
5 if ([service.UUID.UUIDString isEqual:FLT_BLE_SERVICE]) {
6
7 for (CBCharacteristic *character in service.characteristics) {
8 /*--- RCSP ---*/
9 if ([character.UUID.UUIDString isEqual:FLT_BLE_RCSP_W]) {
10 NSLog(@"BLE Get Rcsp(Write) Channel ---> %@",character.UUID.UUIDString);
11 self.mRcspWrite = character;
12 }
13
14 if ([character.UUID.UUIDString isEqual:FLT_BLE_RCSP_R]) {
15 NSLog(@"BLE Get Rcsp(Read) Channel ---> %@",character.UUID.UUIDString);
16 self.mRcspRead = character;
17 [peripheral setNotifyValue:YES forCharacteristic:character];
18
19 if(self.mRcspRead.properties == CBCharacteristicPropertyRead){
20 [peripheral readValueForCharacteristic:character];
21 NSLog(@"BLE Rcsp(Read) Read Value For Characteristic.");
22 }
23 }
24 }
25 }
26}
2.4. BLE更新通知特征的状态
1#pragma mark 更新通知特征的状态
2- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
3
4 if (error) { NSLog(@"Err: Update NotificationState For Characteristic fail."); return; }
5
6 if ([characteristic.service.UUID.UUIDString isEqual:FLT_BLE_SERVICE] &&
7 [characteristic.UUID.UUIDString isEqual:FLT_BLE_RCSP_R] &&
8 characteristic.isNotifying == YES)
9 {
10
11 __weak typeof(self) weakSelf = self;
12 self.bleMtu = [peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithoutResponse];
13 NSLog(@"BLE ---> MTU:%lu",(unsigned long)self.bleMtu);
14 if (self.isPaired == YES) {
15 [_pairHash hashResetPair];
16 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
17 //设备认证
18 [self->_pairHash bluetoothPairingKey:self.pairKey Result:^(BOOL ret) {
19 if(ret){
20 weakSelf.lastUUID = peripheral.identifier.UUIDString;
21 weakSelf.lastBleMacAddress = nil;
22 [[NSNotificationCenter defaultCenter] postNotificationName:kFLT_BLE_PAIRED object:peripheral];
23 [weakSelf.otaManager noteEntityConnected];
24 weakSelf.pairStatus = YES;
25 }else{
26 NSLog(@"JL_Assist Err: bluetooth pairing fail.");
27 [weakSelf.bleManager cancelPeripheralConnection:peripheral];
28 }
29 }];
30 });
31 }else{
32 self.lastUUID = peripheral.identifier.UUIDString;
33 self.lastBleMacAddress = nil;
34 [[NSNotificationCenter defaultCenter] postNotificationName:kFLT_BLE_PAIRED object:peripheral];
35 [self.otaManager noteEntityConnected];
36 }
37 }
38 self.isConnected = YES;
39}
2.5. BLE设备返回的数据
设备端返回的数据需要放入到SDK中解析,其中 设备认证部分 和 通讯部分 的处理是分开的
#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];
}
}
2.6. BLE设备断开连接
#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];
}
2.7. 手机蓝牙状态更新
1//外部蓝牙,手机蓝牙状态回调处,实现以下:
2#pragma mark - 蓝牙初始化 Callback
3- (void)centralManagerDidUpdateState:(CBCentralManager *)central
4{
5_mBleManagerState = central.state;
6
7 if (_mBleManagerState != CBManagerStatePoweredOn) {
8 self.mBlePeripheral = nil;
9 self.blePeripheralArr = [NSMutableArray array];
10 }
11}
2.8. 获取设备信息
BLE连接且配对后必须执行一次
1 [_otaManager cmdTargetFeature];
2.9. 固件OTA升级
1/**
2* ota升级
3* @param otaFilePath ota升级文件路径
4*/
5- (void)otaFuncWithFilePath:(NSString *)otaFilePath {
6 NSLog(@"current otaFilePath ---> %@", otaFilePath);
7 self.selectedOtaFilePath = otaFilePath;
8 NSData *otaData = [[NSData alloc] initWithContentsOfFile:otaFilePath];
9
10 [_otaManager cmdOTAData:otaData Result:^(JL_OTAResult result, float progress) {
11 for (id<JLBleManagerOtaDelegate> objc in self.delegates) {
12 if([objc respondsToSelector:@selector(otaProgressWithOtaResult:withProgress:)]){
13 [objc otaProgressWithOtaResult:result withProgress:progress];
14 }
15 }
16
17 }];
18}
2.10. 取消固件OTA升级
1- (void)otaFuncCancel:(CANCEL_CALLBACK _Nonnull)result{
2
3 [_otaManager cmdOTACancelResult:^(uint8_t status, uint8_t sn, NSData * _Nullable data) {
4 result(status);
5 }];
6}
2.11. OTAManager 的delegate回调处理
1//MARK: - ota manager delegate callback
2
3///取消升级回调
4-(void)otaCancel{
5 //TODO: 取消OTA升级回调
6}
7
8///升级状态进度回调
9-(void)otaUpgradeResult:(JL_OTAResult)result Progress:(float)progress{
10 //TODO: 设备升级过程回调,包括进度状态
11}
12
13///即将发送给设备的数据
14-(void)otaDataSend:(NSData *)data{
15 //TODO: 开发者需要在这里要把数据发送到设备
16 [self writeDataByCbp:data];
17}
18///设备信息回调
19-(void)otaFeatureResult:(JL_OTAManager *)manager{
20
21 NSLog(@"getDeviceInfo:%d",__LINE__);
22 if (manager.otaStatus == JL_OtaStatusForce) {
23 NSLog(@"---> 进入强制升级.");
24 if (self.selectedOtaFilePath) {
25 [self otaFuncWithFilePath:self.selectedOtaFilePath];
26 } else {
27 _getCallback(true);
28 }
29 return;
30 } else {
31 if (manager.otaHeadset == JL_OtaHeadsetYES) {
32 NSLog(@"---> 进入强制升级: OTA另一只耳机.");
33 if (self.selectedOtaFilePath) {
34 [self otaFuncWithFilePath:self.selectedOtaFilePath];
35 } else {
36 _getCallback(true);
37 }
38 return;
39 }
40 }
41 NSLog(@"---> 设备正常使用...");
42 dispatch_async(dispatch_get_main_queue(), ^{
43 /*--- 获取公共信息 ---*/
44 [self->_otaManager cmdSystemFunction];
45 self->_getCallback(false);
46 });
47
48}
2.12. 设备认证的delegate回调
1//MARK: - Hash pair delegate callback
2///即将发送到设备的认证数据
3-(void)hashOnPairOutputData:(NSData *)data{
4//TODO: 开发者需要在这里要把数据发送到设备
5[self writeDataByCbp:data];
6}
2.13. 数据发送
iOS设备与固件通讯之间的最大MTU需要在连上后获取
1elf.bleMtu = [peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithoutResponse];
发送到设备的数据需要进行分包发送
代码参考如下:
1//MARK: - data send manager
2
3/// 需要分包发送
4/// - Parameter data: 数据
5-(void)writeDataByCbp:(NSData *)data{
6 // NSLog(@"%s:data:%@",__func__,data);
7 if (_mBlePeripheral && self.mRcspWrite) {
8 if (data.length > 0 ) {
9 NSInteger len = data.length;
10 while (len>0) {
11 if (len <= _bleMtu) {
12 NSData *mtuData = [data subdataWithRange:NSMakeRange(0, data.length)];
13 [_mBlePeripheral writeValue:mtuData
14 forCharacteristic:self.mRcspWrite
15 type:CBCharacteristicWriteWithoutResponse];
16 len -= data.length;
17 }else{
18 NSData *mtuData = [data subdataWithRange:NSMakeRange(0, _bleMtu)];
19 [_mBlePeripheral writeValue:mtuData
20 forCharacteristic:self.mRcspWrite
21 type:CBCharacteristicWriteWithoutResponse];
22 len -= _bleMtu;
23 data = [data subdataWithRange:NSMakeRange(_bleMtu, len)];
24 }
25 }
26 }
27 }else{
28 //需要先赋值写特征的内容
29 NSLog(@"need to init");
30 }
31}