开源软件名称:iOS-JL_Bluetooth
开源软件地址:https://gitee.com/Jieli-Tech/iOS-JL_Bluetooth
开源软件介绍:
iOS杰理蓝牙SDK接入基础[toc] - 对应的芯片类型:AC692x,AC693x,AC695x,BD29
- APP开发环境:iOS平台,iOS 10.0以上,Xcode 11.0以上
- 基于「杰理蓝牙控制库SDK v1.5.0 (多设备版)」 开发
声明本项⽬所参考、使⽤技术必须全部来源于公知技术信息,或⾃主创新设计。 本项⽬不得使⽤任何未经授权的第三⽅知识产权的技术信息。 如个⼈使⽤未经授权的第三⽅知识产权的技术信息,造成的经济损失和法律后果由个⼈承担。
版本版本 | 日期 | 编辑 | 修改内容 |
---|
v1.4 | 2020年12月17日 | 冯 洪鹏 | 1、手表表盘操作(表盘OTA); | v1.3 | 2020年12月11日 | 冯 洪鹏 | 1、重新整理代码框架; 2、增加多设备管理; | v1.2 | 2020年12月09日 | 冯 洪鹏 | 更新文档 | v1.1 | 2020年04月20日 | 冯 洪鹏 | 增加升级的错误回调 | v1.0 | 2019年09月09日 | 冯 洪鹏 | OTA升级功能 |
概述本文档是为了后续开发者更加便捷移植杰理蓝牙控制功能而创建。 1、导入JL_BLEKit.framework1、将「 JL_BLEKit.framework 」导入Xcode工程,添加两个权限: - Privacy - Bluetooth Peripheral Usage Description
- Privacy - Bluetooth Always Usage Description
2、SDK具体使用的两种方式: 第一种,使用自定义的蓝牙连接API进行OTA:所有BLE的操作都自行实现,SDK只负责对OTA数据包解析 从而实现OTA功能。 第二种,使用SDK内的蓝牙连接API进行OTA:完全使用SDK。 3、针对【杰理之家APP】的开发文档,请看《杰理蓝牙控制库_IOS_SDK开发说明》描述杰理之家内部所有功 能的实现。 4、本工程可Build出的ipa需要iPhone手机系统版本在iOS10.0以上。 2、使用自定义的蓝牙API接入SDK参考Demo:「OTA_Update_M(外部BLE) 」 1、支持的功能: - BLE设备握手连接;
- 获取设备信息;
- OTA升级能实现;
- 注意:所有BLE扫描、连接、断开、重连等操作都需自行实现。
2、接触到的类: - JL_BLEAction:实现BLE设备握手连接;(可选,需设备支持)
- JL_Handle:RCSP数据格式分包器;(必须)
- JL_ManagerM:命令处理中心,所有的命令操作都集中于此;(必须)
- JLModel_Device:设备信息存储的数据模型;(必须)
3、BLE参数: - 【服务号】:AE00
- 【写】特征值:AE01
- 【读 】特征值:AE02
2.1、初始化SDK//主要是3个类,分别是配对器、数据分包器、命令中心类/*--- 配对器 ---*/bleAction = [[JL_BLEAction alloc] init];bleAction.delegate = self; /*--- 数据结构分包 ---*/bleHandle = [[JL_Handle alloc] init];bleHandle.delegate = self; /*--- 命令处理中心 ---*/self.mCmdManager = [[JL_ManagerM alloc] init];self.mCmdManager.delegate = self; 2.2、BLE握手连接(可选)/** 蓝牙设备配对 @param pKey 配对码(默认传nil) @param bk 配对回调YES:成功 NO:失败 */-(void)bluetoothPairingKey:(NSData*)pKey Result:(ATC_Block)bk //外部蓝牙更新通知特征的状态的回调处实现,以下:#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.isNotifying) { if ([characteristic.UUID.UUIDString containsString:@"AE02"]) { QCY_BLE_LEN_45 = [peripheral maximumWriteValueLengthForType:CBCharacteristicWriteWithoutResponse]; NSLog(@"BLE ---> MTU:%lu",(unsigned long)QCY_BLE_LEN_45); NSData *pairKey = nil;//使用内置默认 [bleAction bluetoothPairingKey:pairKey Result:^(BOOL ret) { if (ret) { self.mBlePeripheral = peripheral; self.lastUUID = peripheral.identifier.UUIDString; self.mBleName = peripheral.name; [JL_Tools setUser:self.lastUUID forKey:kUUID_BLE_LAST]; /*--- 存储名字和UUID ---*/ [self.mCmdManager setBleUuid:self.lastUUID]; [self.mCmdManager setBleName:self.mBleName]; /*--- 告之SDK,设备连接成功 ---*/ [JL_Tools post:kJL_BLE_M_ENTITY_CONNECTED Object:peripheral]; }else{ NSLog(@"Err: bluetooth pairing fail."); [self->bleManager cancelPeripheralConnection:peripheral]; } }]; /* //不需要握手可以这样处理。 self.lastUUID = peripheral.identifier.UUIDString; self.mBleName = peripheral.name; [JL_Tools setUser:self.lastUUID forKey:kUUID_BLE_LAST]; //存储名字和UUID [self.mCmdManager setBleUuid:self.lastUUID]; [self.mCmdManager setBleName:self.mBleName]; //告之SDK,设备连接成功 [JL_Tools post:kJL_BLE_M_ENTITY_CONNECTED Object:peripheral]; */ } }} 2.3、BLE设备返回的数据传入SDK//外部蓝牙数据接收处,实现以下:#pragma mark - 设备返回的数据 GET- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ if (error) { NSLog(@"Err: receive data fail."); return; } NSData *data = characteristic.value; if (data.length <= 0) { return; } /*--- 【命令】通道返回的数据 ---*/ if ([characteristic.UUID.UUIDString containsString:QCY_BLE_RCSP_R]) { /*--- 配对输入 ---*/ [bleAction inputPairData:data]; /*--- RCSP数据结构分包 ---*/ [bleHandle inputHandleData:data]; }} 2.4、实现SDK的Delete方法#pragma mark -【JL_BLEActionDelegate】配对数据输出-(void)onPairOutputData:(NSData *)data{ [self writeRcspData:data];//使用外部蓝牙发数API}#pragma mark -【JL_HandleDelegate】RCSP数据包-(void)onHandleOutputPKG:(JL_PKG *)pkg{ [self.mCmdManager inputPKG:pkg];}#pragma mark -【JL_ManagerMDelegate】RCSP数据包-(void)onManagerSendPackage:(JL_PKG *)pkg{ NSData *data = [bleHandle sendPackage:pkg WithName:self.mBleName]; [self writeRcspData:data];//使用外部蓝牙发数API} 2.5、设备断开的处理//外部蓝牙设备断开连接处,实现以下:#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.mCmdManager cmdFlashActionDisconnect]; /*--- 告之SDK,设备已断开 ---*/ [JL_Tools post:kJL_BLE_M_ENTITY_DISCONNECTED Object:peripheral];} 2.6、手机蓝牙状态传入SDK//外部蓝牙,手机蓝牙状态回调处,实现以下:#pragma mark - 蓝牙初始化 Callback- (void)centralManagerDidUpdateState:(CBCentralManager *)central{ NSInteger st = central.state; if (st != CBManagerStatePoweredOn) { /*--- 表盘清除回调 ---*/ [self.mCmdManager cmdFlashActionDisconnect]; /*--- 告之SDK,手机已关闭蓝牙 ---*/ [JL_Tools post:kJL_BLE_M_OFF Object:@(0)]; }} 2.7、功能实现2.7.1、获取设备信息 [bt_ble.mCmdManager cmdTargetFeatureResult:^(NSArray * _Nullable array) { JL_CMDStatus st = [array[0] intValue]; if (st == JL_CMDStatusSuccess) { JLModel_Device *model = [self->bt_ble.mCmdManager outputDeviceModel]; JL_OtaStatus upSt = model.otaStatus; if (upSt == JL_OtaStatusForce) { NSLog(@"---> 进入强制升级."); [self noteOtaUpdate:nil]; return; }else{ if (model.otaHeadset == JL_OtaHeadsetYES) { NSLog(@"---> 进入强制升级: OTA另一只耳机."); [self noteOtaUpdate:nil]; return; } } NSLog(@"---> 设备正常使用..."); }else{ NSLog(@"---> ERROR:设备信息获取错误!"); } }]; 2.7.2、固件OTA升级 //升级流程:连接设备-->获取设备信息-->是否强制升级-->(是)则必须调用该API去OTA升级; // |_______>(否)则可以正常使用APP; NSData *otaData = [[NSData alloc] initWithContentsOfFile:@"OTA升级文件路径"]; [bt_ble.mCmdManager cmdOTAData:otaData Result:^(JL_OTAResult result, float progress) { if (result == JL_OTAResultSuccess) { NSLog(@"--->升级成功."); } if (result == JL_OTAResultFail) { NSLog(@"--->OTA升级失败"); } if (result == JL_OTAResultDataIsNull) { NSLog(@"--->OTA升级数据为空!"); } if (result == JL_OTAResultCommandFail) { NSLog(@"--->OTA指令失败!"); } if (result == JL_OTAResultSeekFail) { NSLog(@"--->OTA标示偏移查找失败!"); } if (result == JL_OTAResultInfoFail) { NSLog(@"--->OTA升级固件信息错误!"); } if (result == JL_OTAResultLowPower) { NSLog(@"--->OTA升级设备电压低!"); } if (result == JL_OTAResultEnterFail) { NSLog(@"--->未能进入OTA升级模式!"); } if (result == JL_OTAResultUnknown) { NSLog(@"--->OTA未知错误!"); } if (result == JL_OTAResultFailSameVersion) { NSLog(@"--->相同版本!"); } if (result == JL_OTAResultFailTWSDisconnect) { NSLog(@"--->TWS耳机未连接"); } if (result == JL_OTAResultFailNotInBin) { NSLog(@"--->耳机未在充电仓"); } if (result == JL_OTAResultPreparing || result == JL_OTAResultUpgrading) { if (result == JL_OTAResultUpgrading) NSLog(@"---> 正在升级:%.1f",progress*100.0f); if (result == JL_OTAResultPreparing) NSLog(@"---> 检验文件:%.1f",progress*100.0f); } if (result == JL_OTAResultPrepared) { NSLog(@"---> 检验文件【完成】"); } if (result == JL_OTAResultReconnect) { NSLog(@"---> OTA正在回连设备... %@",self->bt_ble.mBleName); [self->bt_ble connectPeripheralWithUUID:self->bt_ble.lastUUID];//自行实现回连 } }]; 2.7.3、⼿表切换表盘(OTA功能)- 详情看【FAT_Demo】
- 导⼊【FATApi】【FATTool】⽂件内代码;
STEP.1、设置命令中⼼类 JL_RunSDK *bleSDK = [JL_RunSDK sharedMe]; bt_ble = bleSDK.bt_ble; /*--- 设置命令处理中心 ---*/ [FatsObject makeCmdManager:bt_ble.mCmdManager]; STEP.2、获取外挂Flash的信息//调⽤该API需要⽤异步,[JL_Tools subTask:^{}]为异步代码块 [JL_Tools subTask:^{ JL_ManagerM *mCmdManager = self->bleSDK.mBleEntityM.mCmdManager; [mCmdManager cmdGetFlashInfoResult:^(JLModel_Flash * _Nullable model) { [JL_Tools mainTask:^{ //mFlashSize; //flash大小 //mFatfsSize; //FAT系统大小 //mFlashType; //系统类型 0:FAT //mFlashStatus; //系统当前状态,0x00正常,0x01异常 //mFlashVersion; //Flash版本 //mFlashMtu; //发包窗口大小 //mFlashCluster; //扇区大小 [DFUITools showText:@"FATFS信息已更新" onView:self delay:1.0]; }]; }]; }]; STEP.3、初始化本地FATFS系统//调⽤该API需要⽤异步 [JL_Tools subTask:^{ JLModel_Device *model = [self->bleSDK.mBleEntityM.mCmdManager outputDeviceModel]; uint32_t flashSize = model.flashInfo.mFlashSize;//外挂Flash剩余空间 uint32_t fatsSize = model.flashInfo.mFatfsSize; //统⼤⼩ BOOL isOk = [FatsObject makeFlashSize:flashSize FatsSize:fatsSize]; [JL_Tools mainTask:^{ NSString *txt = @"FATFS Mount OK !"; if (isOk == NO) txt = @"FATFS Mount Fail~"; [DFUITools showText:txt onView:self delay:1.0]; }]; }]; 读取表盘(⽂件)名字//调⽤该API需要⽤异步 [JL_Tools subTask:^{ self->dataArray = [FatsObject makeListPath:@"/"]; NSLog(@"Fats List ---> %@",self->dataArray); [JL_Tools mainTask:^{ [self->subTableView reloadData]; }]; }]; 新增表盘(⽂件) NSString *pathMatch = [DFFile find:@"watch6.zip"]; NSData *data = [NSData dataWithContentsOfFile:pathMatch]; NSLog(@"-->创建的文件大小:%lld",(long long)data.length); NSString *path = @"/watch6"; [JL_Tools subTask:^{#if IS_FROM_BLE /*--- 开始写文件 ---*/ __block uint8_t mFlag_0 = 0; NSLog(@"--->Fats Insert stat."); [mCmdManager cmdInsertFlashPath:path Size:(uint32_t)data.length Flag:0x01 Result:^(uint8_t flag) { mFlag_0 = flag; }]; if (mFlag_0 != 0) { NSLog(@"--->Fats Insert Fail."); [JL_Tools mainTask:^{ [self setLoadingText:@"创建文件失败!" Delay:0.5]; }]; return; }#endif BOOL isOk = [FatsObject makeCreateFile:path Content:data Result:^(float progress) { [JL_Tools mainTask:^{ //NSLog(@"---> Progress: %.1f",progress*100.0f); NSString *txt = [NSString stringWithFormat:@"正在升级:%.1f%%",progress*100.0f]; self->progressLabel.text = txt; self->progressView.progress = progress; }]; }]; NSLog(@"Fats Add ---> %d",isOk); [JL_Tools mainTask:^{ if (isOk) { [self setLoadingText:@"创建文件成功!" Delay:0.5];#if IS_FROM_BLE [JL_Tools subTask:^{ /*--- 结束写文件 ---*/ NSLog(@"--->Fats Insert end."); [mCmdManager cmdInsertFlashPath:nil Size:0 Flag:0x00 Result:nil]; }];#endif [JL_Tools delay:1.0 Task:^{ self->progressView.hidden = YES; self->progressLabel.hidden = YES; self->progressView.progress = 0.0f; /*--- 读取列表 ---*/ [wSelf btn_List:nil]; }]; }else{ [self setLoadingText:@"创建文件失败!" Delay:0.5]; } }]; }]; 删除表盘(⽂件) [self startLoadingView:@"删除文件..." Delay:20.0]; NSString *path = [N |
请发表评论