iOS藍牙連接
因為項目需要,這幾天接觸了一下藍牙相關API,自己寫了一個Demo 傳送門來調試,當然AppStore的LightBlue也是不錯的。
好了,閑話不多說,來談談我這幾天遇到的問題和最后是怎么解決的。
首先說一下CoreBlueTooth
藍牙框架的一些常用的API。
相關類和協議
- CBCentralManager
- CBPeripheral
- CBCharacteristic
- CBCentralManagerDelegate
- CBPeripheralDelegate
API介紹
- 中心設備狀態改變
這個是CBCentralManager
必須實現的方法
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
以下API只有在設備藍牙開啟時才可以,所以你可以像這樣...
-(void)centralManagerDidUpdateState:(CBCentralManager *)central
{
if(central.state != CBCentralManagerStatePoweredOn)
{
// ****
}
}
- 掃描設備
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;
第一個參數傳nil
表示返回所有發現的設備。
第二個參數是一個字典,可以傳nil
。
對應的回調方法是
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;
RSSI
表示設備連接的信號強度
注意:
這里發現的外圍設備,如果后面需要使用它,必須要對它進行保留
,官方是這樣說的:
A discovered peripheral must
be retained in order to use it; otherwise, it is assumed to not be of interest and will be cleaned up by the central manager
取消掃描
- (void)stopScan;
- 連接設備
- (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary<NSString *, id> *)options;
對應的回調方法:
連接成功:
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
連接失敗:
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
取消連接
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;
- (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary<NSString *, id> *)options;
- 發現設備服務、特征、描述
查找服務
- (void)discoverServices:(nullable NSArray<CBUUID *> *)serviceUUIDs;
同樣的,serviceUUIDs
傳nil
表示所有服務
查找某一服務下的特征
- (void)discoverCharacteristics:(nullable NSArray<CBUUID *> *)characteristicUUIDs forService:(CBService *)service;
查找某一特征下的描述
- (void)discoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic;
對應的回調函數:
發現服務
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error;
發現特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error;
發現描述
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
- 特征值變化通知
- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic;
回調
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
如果開啟通知,當連接設備的當前特征的值發生變化時,就會調用函數:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
調用下面的方法也是可以的,但是如果該特性不可讀,也就是characteristic.properties & CBCharacteristicPropertyRead
,那么在回調函數里面會產生一個錯誤
- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic;
- 讀值和寫值
讀值:
讀取特征的值
- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic;
回調
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
讀取特征描述的值
- (void)readValueForDescriptor:(CBDescriptor *)descriptor;
回調
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error
寫值:
寫特征的值
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type
回調
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
寫特征描述的值
- (void)writeValue:(NSData *)data forDescriptor:(CBDescriptor *)descriptor
回調
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error
寫值之前確保特征是可寫的,如果寫值的類型是CBCharacteristicWriteWithResponse
的話,才會調用回調函數。
- 設備信號強度
外圍設備有一個RSSI
屬性,但是現在被棄用了,改用回調函數代替了。
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error NS_AVAILABLE(NA, 8_0);
好了API大致的介紹了一下,還有一些沒有介紹到的,大家可以自己去了解一下。
遇到的問題
- 讀取特征的值的時候,在沒有可讀類型的特征的時候,會報錯。
- 在還未連接到外圍設備的情況下,更新設備信號強度。
解決方法:
- 1.原來是自己開啟了廣播,同時又調用了讀值的方法,雖然這兩種方式都會調用回調函數,但是特征并不可讀。0..0
This method is invoked after a @link readValueForCharacteristic: @/link call, or upon receipt of a notification/indication.
忽略了一個or
- 2.本意是想模仿LightBlue App信號強度的更新,在Log中看到設備的信號強度在沒有連接的情況下就在更新了。但是
readRSSI
方法明確表明是要在已連接
狀態下才能讀取,回調方法也才會響應。在委托方法:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;
中能拿到RSSI,但是卻沒有實時更新。經過幾番摸索,在掃描外設的方法中看到了希望
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;
第二個參數options
之前傳的是nil
,在CBCentralManagerScanOptionAllowDuplicatesKey
這個Key中看到了這樣一段話
This can be useful in specific situations, such as making a connection based on a peripheral's RSSI, but may have an adverse affect on battery-life and application performance
問題得到解決,修改后的掃描方法
[_centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @(YES)}];
這樣就可以在回調方法中拿到實時的信號強度了。
好了,就說這么多了,后續再有遇到問題,會繼續更新的...
代碼寫的不好,還望見諒 0.0