iOS 蓝牙初窥

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;

同样的,serviceUUIDsnil表示所有服务

查找某一服务下的特征
- (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. 读取特征的值的时候,在没有可读类型的特征的时候,会报错。
  2. 在还未连接到外围设备的情况下,更新设备信号强度。

解决方法:

  • 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

你可能感兴趣的:(iOS 蓝牙初窥)