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 *)serviceUUIDs options:(nullable NSDictionary *)options;
第一个参数传nil
表示返回所有发现的设备。
第二个参数是一个字典,可以传nil
。
对应的回调方法是
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)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 *)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 *)options;
- 发现设备服务、特征、描述
查找服务
- (void)discoverServices:(nullable NSArray *)serviceUUIDs;
同样的,serviceUUIDs
传nil
表示所有服务
查找某一服务下的特征
- (void)discoverCharacteristics:(nullable NSArray *)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 *)advertisementData RSSI:(NSNumber *)RSSI;
中能拿到RSSI,但是却没有实时更新。经过几番摸索,在扫描外设的方法中看到了希望
- (void)scanForPeripheralsWithServices:(nullable NSArray *)serviceUUIDs options:(nullable NSDictionary *)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)}];
这样就可以在回调方法中拿到实时的信号强度了。
好了,就说这么多了,后续再有遇到问题,会继续更新的...
Demo地址
代码写的不好,还望见谅 0.0