iOS 蓝牙初窥

作者: 单眼皮00 | 来源:发表于2016-05-26 10:17 被阅读946次

    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;
    

    同样的,serviceUUIDsnil表示所有服务

    查找某一服务下的特征
    - (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. 读取特征的值的时候,在没有可读类型的特征的时候,会报错。
    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<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)}];
    

    这样就可以在回调方法中拿到实时的信号强度了。

    好了,就说这么多了,后续再有遇到问题,会继续更新的...

    Demo地址

    代码写的不好,还望见谅 0.0

    相关文章

      网友评论

        本文标题:iOS 蓝牙初窥

        本文链接:https://www.haomeiwen.com/subject/gwtxdttx.html