iOS BLE 开发小记[2] 如何实现一个 Local Central

欢迎访问个人博客 muhlenXi,该文章出自个人博客,欢迎转载,转载请注明来源: muhlenxi.com/2017/04/29/…html

导语:

在这一节,你将会学到,如何经过 CoreBluetooth 框架来实现 Local Central 方面的功能和代理方法。数组

在 BLE 通讯中,实现了 Central 规范的设备,可以调用许多经常使用的方法,好比搜索和链接可用的 Peripheral,而后与 Peripheral 提供的数据进行交互。可是实现了 Peripheral 规范的设备一样可以调用许多常见的方法,可是也有一些不一样。好比,发布和广播 Service,对 Central 的读写请求进行响应以及 响应Central 的订阅请求。app

在这个章节中,你将会学习在 Central 端如何使用 Core Bluetooth 框架来执行通用的 BLE 方法。基于代码的示例将会引导和协助你在你的本地设备上实现 Central 的角色。尤为,你将会学到:框架

  • 如何建立一个 Central Manager 对象
  • 如何搜索和链接一个正在广播信息的 Peripheral
  • 链接成功后如何与 Peripheral 的数据进行交互
  • 如何发送读取或写入请求给 Peripheral Service 的 Characteristic
  • 如何订阅一个当数据更新时就会发出通知的 Characteristic

在下个章节,你将会学习如何在你的本地设备上实现 Peripheral 的角色。学习

你也许会发现本章节中的代码有点简单和抽象,在你的真实 App 中,你须要作些恰当的改变。更高级的开发技能能够参考后续的章节。ui

Central 实现详情

在本文中,你的 ViewController 须要遵循 CBCentralManagerDelegateCBPeripheralDelegate 代理协议。spa

建立 Central Manager

由于在 CoreBluetooth 中是经过面向对象的思想用一个 CBCentralManager(中心管理) 对象来表示一个 Local Central 设备,因此在调用该对象的方法以前须要先 allocate(分配)和 initialize(初始化)一个 Central Manager 实例。能够经过 initWithDelegate:queue:options: 方法来初始化一个 Central Manager 对象。代理

myCentralManager =
        [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
复制代码

在示例代码中,设置 Central Manager 的 Delegate 为 self,是为了接收 Central 的事件响应。 参数 dispatch queue(调度队列)设置为 nil,表示 Central Manager 是在 main queue(主队列)中分发响应事件。rest

当你建立一个 Central Manager 对象时,Central Manager 会调用 centralManagerDidUpdateState: 方法来代理回调。所以你必须实现这个代理方法来确保 Central 设备可以使用 BLE 技术,代理方法的详情见 CBCentralManagerDelegate Protocol Referencecode

搜索正在广播数据的 Peripheral 设备

初始化 Central Manger 对象后,第一个任务就是搜索周围的 Peripheral,上一篇曾提到过, Peripheral 经过广播数据的方式来显示它们的存在,你能够经过 scanForPeripheralsWithServices:options: 方法来搜索周围正在广播数据的 Peripheral 设备。

[myCentralManager scanForPeripheralsWithServices:nil options:nil];
复制代码

关于参数说明:若是第一个参数置为 nil 时,Central Manager 会返回全部正在广播数据的 Peripheral 设备。在实际 APP 开发中,经过指定一个包含 CBUUID 对象的数组来获取指定的 Peripheral,其中用一个 UUID(通用惟一识别码)来表示 Peripheral 正在广播的一个服务。关于 CBUUID 对象的详情见 Services and Characteristics Are Identified by UUIDs.

每当 Central Manager 搜索到一个 Peripheral 设备时,就会经过 代理方法 centralManager:didDiscoverPeripheral:advertisementData:RSSI: 进行回调,新发现的 Peripheral 会以 CBPeripheral 对象的方式返回。若是你后面须要链接这个 Peripheral,须要用一个 CBPeripheral 类型的 Strong Reference(强引用)来指向这个对象,这样系统暂时就不会释放这个对象了。

- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI {
 
    NSLog(@"Discovered %@", peripheral.name);
    self.discoveredPeripheral = peripheral;
    // ...
}
复制代码

当你须要链接多个 Peripheral 设备时,须要使用一个 NSArray 来保存这些搜索到的 Peripheral,无论怎样,为了减小电量损耗,增长续航时间,只要搜索到你须要链接的 Peripheral 时,就能够中止搜索了。经过下面的方法能够中止搜索了。

[myCentralManager stopScan];
复制代码

链接刚发现的 Peripheral 设备

能够调用 connectPeripheral:options: 方法来链接你想要链接的 Peripheral 设备。

[myCentralManager connectPeripheral:peripheral options:nil];
复制代码

若是链接成功,Central 会经过 centralManager:didConnectPeripheral: 方法进行代理回调。在你与 Peripheral 进行交互时,你须要设置 Peripheral 的 Delegate 为 self 来确保能收到 Peripheral 的代理回调。

- (void)centralManager:(CBCentralManager *)central
  didConnectPeripheral:(CBPeripheral *)peripheral {
 
    NSLog(@"Peripheral connected");
    peripheral.delegate = self;
    // ...
}
复制代码

搜索刚链接 Peripheral 的 Service

当与 Peripheral 成功创建链接后,你就能够获取 Peripheral 的 Service 数据了,第一步就是搜索 Peripheral 提供的可用 Service。由于 Peripheral 对广播的数据包大小有限制,因此你可能会搜索到除了广播的 Service 以外的 其余 Service,你能够经过 discoverServices: 方法搜索 Peripheral 提供的全部的 Service。

[peripheral discoverServices:nil];
复制代码

提示:在实际开发中,传入的参数通常不为 nil,传入 nil 会返回所有的可用 Service,为了节省电量以及一些没必要要的时间浪费,经过指定一个 Service Array(包含 UUID 对象)为参数,来获取你想要了解的 Service 的信息,详情见 Explore a Peripheral’s Data Wisely.

当搜索到指定的 Service 时,Peripheral 对象会经过 peripheral:didDiscoverServices: 方法进行代理回调。CoreBluetooth 会生成一个数组用来保存 CBService 对象,你指定的 Service 就被包含在这个数组中。你能够经过实现下面这个代理方法来获取 Service 数组。

- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {
 
    for (CBService *service in peripheral.services) {
        NSLog(@"Discovered service %@", service);
        // ...
    }
    // ...
}
复制代码

搜索 Service 的 Characteristic

当你找到指定的 Service 以后,你下一步要作的就是搜索这个 Service 中提供的全部的 Characteristic,经过调用 discoverCharacteristics:forService: 方法来搜索指定的 Service 的 Characteristic。

[peripheral discoverCharacteristics:nil forService:interestingService];
复制代码

提示:在实际开发中,第一个参数通常不传入 nil,由于你须要的 Characteristic 也许是全部 Characteristic 的一部分,为了节省电量以及一些没必要要的时间浪费,一般指定一个 Characteristic Array(包含 UUID 对象)为参数,来获取你想要了解的 Characteristic 的信息。

当搜索到指定的 Characteristic 后, Peripheral 会经过 peripheral:didDiscoverCharacteristicsForService:error: 方法进行代理回调,CoreBluetooth 会建立一个包含 CBCharacteristic 对象的数组用来保存指定的 Characteristic,以下是遍历数组中的 Characteristic。

- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
             error:(NSError *)error {
 
    for (CBCharacteristic *characteristic in service.characteristics) {
        NSLog(@"Discovered characteristic %@", characteristic);
        // ...
    }
    // ...
}
复制代码

检索 Characteristic 的值

读取某个 Characteristic 的值

当你找到 Service 指定的 Characteristic,能够经过 readValueForCharacteristic: 方法来读取这个 Characteristic 的值。

NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
[peripheral readValueForCharacteristic:interestingCharacteristic];
复制代码

当你尝试去读取一个 Characteristic 的值时, Peripheral 会经过 peripheral:didUpdateValueForCharacteristic:error: 代理回调来返回结果,你能够经过 Characteristic 的 value 属性来获得这个值。

- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {
 
    NSData *data = characteristic.value;
    // parse the data as needed
    // ...
}
复制代码

提示:并非全部的 Characteristic 的值都是可读的,决定一个 Characteristic 的值是否可读是经过检查 Characteristic 的 Properties 属性是否包含 CBCharacteristicPropertyRead 常量来判断的。当你尝试去读取一个值不可读的 Characteristic 时,Peripheral 会经过 peripheral:didUpdateValueForCharacteristic:error: 给你返回一个合适的错误。

订阅一个 Characteristic 的值

经过 readValueForCharacteristic: 方法读取一个 Characteristic 的静态值是有效的,可是,对于动态的值,就不是一个有效的方法,由于 Characteristic 的值在实时改变,好比你的心率数据。只有经过订阅才能获取实时的改变值,当你订阅一个 Characteristic 的值时,每当这个值发生改变时,你就会收到一个通知。

经过 setNotifyValue:forCharacteristic: 方法能够订阅指定 Characteristic 的值,该方法的第一个参数须要指定为 YES

[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];
复制代码

当你订阅或取消订阅一个 Characteristic 的值时,Peripheral 会经过调用 peripheral:didUpdateNotificationStateForCharacteristic:error: 方法进行代理回调,若是订阅失败了,你能够经过这个方法获取到发生错误的缘由。

- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error changing notification state: %@",
           [error localizedDescription]);
    }
    // ...
}
复制代码

提示:并非全部的 Characteristic 都提供订阅功能,决定一个 Characteristic 是否能订阅是经过检查 Characteristic 的 properties 属性是否包含 CBCharacteristicPropertyNotify 或者 CBCharacteristicPropertyIndicate 常量来判断的。

写数据到 Characteristic 中

有时须要写入一个数据到 Characteristic 中,好比,若是你的 APP 与数字恒温器进行交互。你或许想要给数字恒温器提供一个值,使得房间的室温能保持在这个温度左右。若是一个 Characteristic 的值是可写的,你能够经过调用 writeValue:forCharacteristic:type: 方法将一个 data 类型(NSData 对象)的值写入到 Characteristic 中。

NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic
        type:CBCharacteristicWriteWithResponse];
复制代码

当你写一个数据到 Characteristic 中时,你能够指定写入类型,上面代码中的写入类型是 CBCharacteristicWriteWithResponse ,此类型时,Peripheral 会经过 peripheral:didWriteValueForCharacteristic:error: 方法来代理回调告知你是否写入数据成功,能够实现下面这个代理方法进行错误处理。

- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error writing characteristic value: %@",
            [error localizedDescription]);
    }
    // ...
}
复制代码

若是你指定写入类型为 CBCharacteristicWriteWithoutResponse 时,不能保证写入操做是否有效的执行了。这时 Peripheral 不会调用任何代理方法,若是想了解 CoreBluetooth 提供的写入类型详情,能够查阅 CBCharacteristicWriteType.

提示:有的 Characteristic 的值可能仅仅是可写的,或者不是可写的。决定 Characteristic 的值是否可写,须要经过查看 Characteristic 的 properties 属性是否包含 CBCharacteristicPropertyWriteWithoutResponse 或者 CBCharacteristicPropertyWrite 常量来判断的。

参考文献

一、Performing Common Central Role Tasks

结束语

欢迎在本文下面留言一块儿交流心得...

相关文章
相关标签/搜索