iOS蓝牙开发学习(2)--连接、读写、DFU升级篇
大致了解了coreBluetooth后,接下來開始著手建立連接的事情。連接大致分為以下幾個步驟。
?
重要提示!!!Xcode10 之后,掃描外設出來的值,會變成 ?<length=8 byte=0x00000000> 類似這樣的!!! ?這會導致,原來的mac 掃描識別,直接取值出現問題。請自行轉換!
?
其中需要注意:options的參數,參數有很多,舉例說兩個,其他的參考API
?
大致意思是說,如果是給的YES,那么在初始化CBCentralManager的時候,如果藍牙沒有打開,那么會以alert的形式提示用戶打開藍牙,但是似乎,iOS11以后就不太好用了。
/*!* @const CBCentralManagerOptionRestoreIdentifierKey** @discussion An NSString containing a unique identifier (UID) for the <code>CBCentralManager</code> that is being instantiated. This UID is used* by the system to identify a specific <code>CBCentralManager</code> instance for restoration and, therefore, must remain the same for* subsequent application executions in order for the manager to be restored.** @see initWithDelegate:queue:options:* @seealso centralManager:willRestoreState:**/ CB_EXTERN NSString * const CBCentralManagerOptionRestoreIdentifierKey NS_AVAILABLE(10_13, 7_0);個人理解,就是將你初始化的這個CBCentralManager存儲到系統藍牙隊列里面,如果你的程序gg以后,當系統藍牙喚醒你的app以后會通過?centralManager:willRestoreState: 直接將之前的CBCentralManager還給你,而不會再通過init初始化了,所以要注意哦~ 關于這點,后面具體說。willRestore記得設置代理。
其他的請自行查閱文檔。
初始化成功以后,不是連接,而是檢查系統藍牙的狀態。
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;通過該回調?central.state,獲取CBCentralManager的狀態也就是系統藍牙的狀態,
typedef NS_ENUM(NSInteger, CBManagerState) {CBManagerStateUnknown = 0,CBManagerStateResetting,CBManagerStateUnsupported,CBManagerStateUnauthorized,CBManagerStatePoweredOff,CBManagerStatePoweredOn, } NS_ENUM_AVAILABLE(10_13, 10_0);一共有這幾種狀態,最后兩個是 已關閉、已打開,其他的自行根據api查看。并根據不同的狀態觸發不同的業務邏輯。
當藍牙是打開的時候,進行掃描。
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;需要提到的是,如果需要掃描包含某個或幾個的指定服務的per時,以CBUUID類型為元素的數組,options 中CBCentralManagerScanOptionSolicitedServiceUUIDsKey, 這樣就可以只掃描出包含指定服務的per了。
如果什么都不需要直接填nil 即可。
當有符合條件的per被掃描到后,觸發回調
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;advertisementData:掃描的per所附帶的廣播數據,可根據與廠商的協議,獲取per的mac值,進行匹配連接
RSSI:藍牙信號強度
當掃描到匹配的per后,調用連接函數。注意,連接之前,需要把per作為單例的屬性保存起來,不然會有不可描述的事情等著你。當然,通過查閱apple的api發現,這個函數,是個很強大的函數,它會將你的連接請求,加入到系統的隊列中,如果你的連接丟失,在丟失的回調里,直接再次寫入連接就可以了,如果你app在期間gg了,還記得willrestore那個回調嗎?
個人感覺,options里的幾個參數沒什么意義,直接寫nil即可,如果有興趣,自己也可以測試一下。
建議在此時寫一個延時操作,用來校驗是否連接成功,如果沒有,需要再次連接,或者進行其他邏輯操作。
同時需要把掃描關閉,以減低功耗
?
連接成功/失敗的回調。成功之后,記得設置代理噢~ per的代理。
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral; - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;沒啥好說的,自己看著辦吧
獲取服務與特征。 ?一般針對per的讀寫操作,是針對到具體的特征值,所以我們要將自己之后需要操作的特征值,作為單例管理類的對象存儲起來。再連接成功的回調里,最好是將per的所以服務特征遍歷讀一遍。
- (void)discoverServices:(nullable NSArray<CBUUID *> *)serviceUUIDs;注意啦,這個是per的對象方法,別用central去調用。參數如果給nil就是遍歷,去讀取所有的服務。讀取到的服務,會觸發回調
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error;是不是發現回調里沒有service之類的參數?哈哈,這個時候Peripheral這個對象的屬性services已經是有值得了,直接get就可以得到。
接下來就是獲取特征值了,根據第一篇里面提到的,特征是屬于服務的,所以我們獲取特征的時候,需要提供其所屬的服務,所以遍歷services
類似于獲取特征,給nil就是獲取該服務的所有特征,該方法觸發回調
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error;吶,其實和獲取服務的回調差不多,自己根據廠商的協議,將制定的特征set給單例管理類對應的特征對象的屬性即可。
這個回調里,可以根據業務需求,直接先讀一下或者訂閱下,讀或者訂閱怎么操作。下面接著講。。
讀。?需要注意讀不等同于訂閱,兩者是有區別的,讀,是代碼控制去讀一下,只是讀一下。而 訂閱,在開啟后,會根據固件方的設定,主動發消息過來,所以,這里也從側面證明,藍牙連接,是個長連接。讀一次,觸發一次回調
//讀取指定特征 - (void)readValueForCharacteristic:(CBCharacteristic *)characteristic; //特征讀取回調 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;讀取到的特征通過回調中的?characteristic.value獲得,所獲得的值為NSData類型,下面是解析示例
Byte *byte = (Byte *)[characteristic.value bytes]; NSLog(@"%s", byte); int sate = byte[0]; NSLog(@"%d", sate);根據業務需求自行解析
訂閱(使能),訂閱時需要注意不要重復開啟訂閱或者關閉訂閱。使用場景:動態心跳監測,訂閱指定特征后,開啟心跳監測,該特征值根據廠商協議,一般會每秒更新一次,具體間隔,由固件決定。app只是被動接收。
- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic;同樣的,也是通過didUpdateNotificationStateForCharacteristic回調更新特征的值。這里與讀的區別,是讀一次觸發一次,而訂閱,是會多次觸發,每次收到per發送新的值,就觸發一次。
寫入。寫入,同樣是寫入NSData對象數據,需要注意的是CBCharacteristicWriteType,要嚴格按照協議的約定選擇CBCharacteristicWriteWithResponse或者CBCharacteristicWriteWithoutResponse。
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;如果type是CBCharacteristicWriteWithResponse,則會觸發回調
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;舉例一個寫入數據
+ (NSData *)ZJjiaozhunDataXinlv:(NSInteger)xinlv Gaoya:(NSInteger)gaoya diya:(NSInteger)diya {NSData *dda = [[NSData alloc]init];unsigned char command[4] = {0x82, gaoya, diya, xinlv};dda = [[NSData alloc] initWithBytes:&command length:4];return dda; }?
取消連接
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;連接取消后,會觸發回調
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error連接已經斷開,注意清空單例類的一些屬性值以及對應的代理
意外斷開連接需要重連。實際情況中,可能會因為用戶關閉藍牙或者距離過遠以及新號不穩定等情況,導致連接中段,這個時候 我們就需要重連了。根據官方文檔,在斷開連接的回調里直接調用連接函數即可。
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {[central connectPeripheral:peripheral options:nil]; }?
關于固件升級,是用iOS-DFULibrary第三方庫進行升級,列舉下主要函數
//發送固件包到指定藍牙設備。 這里的url我使用的是本地url,先通過AFNet將固件包下載到本地 - (void)sendFileToWatchWithURL:(NSURL *)url peripheral:(CBPeripheral *)peripheral; //傳輸過程中的回調 - (void)dfuProgressDidChangeFor:(NSInteger)part outOf:(NSInteger)totalParts to:(NSInteger)progress currentSpeedBytesPerSecond:(double)currentSpeedBytesPerSecond avgSpeedBytesPerSecond:(double)avgSpeedBytesPerSecond {// NSLog(@"part: %ld \n outof:%ld \n progress:%ld \n currentSpeedBytesPerSecond:%.2f \n avgSpeedBytesPerSecond:%.2f",part, totalParts, progress,currentSpeedBytesPerSecond,avgSpeedBytesPerSecond);[SVProgressHUD showProgress:progress/100.0 status:@"固件包傳輸中"];} //傳輸失敗回調 - (void)dfuError:(enum DFUError)error didOccurWithMessage:(NSString *)message; //整個固件升級的per狀態變化 - (void)dfuStateDidChangeTo:(enum DFUState)state {switch (state) {case DFUStateConnecting:NSLog(@"連接:服務正在連接到DFU目標");break;case DFUStateStarting:NSLog(@"啟動:DFU服務正在初始化DFU操作");break;case DFUStateEnablingDfuMode:NSLog(@"服務正在將設備切換到DFU模式");break;case DFUStateUploading:NSLog(@"上傳:服務正在上傳固件");break;case DFUStateValidating:NSLog(@"驗證:DFU目標正在驗證固件");break;case DFUStateDisconnecting:NSLog(@"disconnecting:iDevice正在斷開連接或等待斷開連接");break;case DFUStateCompleted:NSLog(@"完成:DFU操作完成并成功");self.isDFU = NO;[self.manager cancelPeripheralConnection:self.connectPeripheral];[self updataSucess];break;case DFUStateAborted:NSLog(@"aborted:DFU操作已中止");[self updataFail];break;default:break;} }也可以加我qq 1003072244 備注信息:藍牙博客。 ? ?
最后,說一點最重要的,手打不易,生活不易,給個面包錢可好?
總結
以上是生活随笔為你收集整理的iOS蓝牙开发学习(2)--连接、读写、DFU升级篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hive安装报错:Unable to i
- 下一篇: 对象属性和类属性