CoreBluetooth的部分翻译
执行常见的Central任务
充当Central的角色并且遵循BLE通讯的设备可以执行一系列通用任务— — 比如, 发现并连接可用的Peripherals, 探索Peripheral提供的数据并与之进行交互. 充当Peripheral的角色的设备也能执行一些列的任务, 但是任务内容正好跟Central不同, 甚至相反——比如, 发布并广告services, 相应外界的读写操作, 已经从已连接的Central那订阅请求
你将在这一章节里学会去如何使用 Core Bluetooth framework 来执行一些在Central端最常见的BLE Task. 后文中的基于代码的实例将会帮助你在你的app中实现本地的Central端. 具体来说, 你将学会:
- 启动一个Central manager 对象
- 查找和连接到一个正在广告的peripheral设备
- 搜索已连接上的peripheral设备上的数据
- 给peripheral的某个service发送对应的characteristic的value值的读写请求
- 订阅一个characteristic的value, 当value有更新的时候进行提醒
在下一个章节, 你将会学会如何去在开发你的app的时候去实现本地的peripheral端
你在这一章节找到的示例代码简单又抽象; 在并入你自己的app之前你可能需要先对它们进行一些修改. 更多关于实现Central的一些话题—包括tips, tricks, 以及 best practices — 都会在后面的 Core Bluetooth Background Processing for iOS Apps 和 Best Practices for Interacting with a Remote Peripheral Device中讲到
启动一个Central Manager
initWithDelegate: queue: options:
queue: 如果是nil, 那么默认加到主队列
创建一个Central manager的时候, Central manager就会去调用自己代理中的方法:
centralManagerDidUpdateState:
该方法必须实现, 这样才能确保当前设备是否能支持BLE
关于更多代理的内容: CBCentralManagerDelegate Protocol Reference
查找正在广告的peripheral设备
scanForPeripheralsWithServieces: options:
如果给第一个参数设定为 nil, 那么调用该方法的 central manager就不管是否支持services, 会返回探测到的所有peripheral设备
在一个真实的app中, 一般都是给第一个参数传一个包含特定的CBUUID对象的数组, 每个UUID都代表一个正在广告的peripheral设备.
调用了 scanForPeripheralsWithServieces: options: 以后, central manager每发现一个peripheral就会去调用其代理中的 centralManager: didDiscoverPeripheral: advertisementData: RSSI: 方法. 每个被探测到的peripheral设备都会以 CBPeripheral 对象被返回. 下面的代理方法实现以后你将会得到一个探测到的peripheral设备的列表:
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@", peripheral.name);
...
当你发现了你需要找到的peripheral设备, 停止扫描其它设备来节省电量
[myCentralManager stopScan];
NSLog(@"Scanning stopped");
连接一个已探测到的peripheral设备
调用CBCentralManager类中的 connectPeripheral: option: 方法来发送一个连接请求给你探测到的特定peripheral设备
[myCentralManager connectPeripheral:peripheral options:nil];
假设你现在成功的连接上了, 那么central manager就会调用代理中的方法: centralManger: didConnectPeripheral , 这样你就可以直观的知道连接已经成功的建立
- (void)centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Peripheral connected");
...
在和peripheral交互之前, 先设定peripheral的代理一遍能正常的接收到callback
peripheral.delegate = self;
获取连接上的peripheral的Services
与一个peripheral建立了连接以后, 你就可以去探索这个设备提供的数据了. 探索的第一步是检索这个peripheral中所有可用的的services. 一个peripheral 可以广告的数据是有大小限制的, 所以你可能会发现你连接上的peripheral可以检索到的services 是要多于这个peripheral 的广告数据. 你可以通过实现CBPeripheral 类中的方法: discoverServices: 来获取一个peripheral提供的所有services, 像这样
[peripheral discoverServices:nil];
注意: 一般情况下不会在一个真正的app 中传一个 nil 来作为参数的, 因为这样返回的是这个peripheral 的所有service . 一个peripheral 可能包含的 services 数量远多于你想要得到的service (的数量), 检索全部的services 很可能会损耗电池寿命以及浪费一些不必要的时间. 确定好那些你感兴趣的 services 对应的 UUID, Explore a Peripheral’s Data Wisely 中有提到这些
找到了指定的 services 以后, peripheral (你连接的 CBPeripheral 对象)就会去调用代理中的 peripheral: didDiscoverServices: 方法. Core Bluetooth 会创建一个元素全是 CBService 对象的数组——当中的每一个service都是从peripheral中找到的. 你可以想下面一样实现这个代理方法来获取这个包含了已找到的services的数组:
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
NSLog(@"Discovered service %@", service);
...
}
...
检索一个Service中的Characteristics
现在假设你已经成功的从已连接上的peripheral中检索到了你想要的(或者说你感兴趣的)service, 那么下一步就是如何检索到这个service中你想要的(你感兴趣的)characteristics. 检索一个service中的所有characteristics只要简单的调用CBPeripheral 类中的 discoverCharacteristics: forService:
NSLog(@"Discovering characteristics for service %@", interestingService);
[peripheral discoverCharacteristics:nil forService:interestingService];
注意: 在一个真正的app中, 一般情况下不会传一个 nil 作为第一个参数的值, 因为这样就会返回一个peripheral的service的所有 characteristics. 除了你感兴趣的那几个 characteristics, 一个peripheral的service肯定还有一些其他的东西, 把所有的characteristic 全找出来既损耗电池寿命也浪费时间. 所以在发掘characteristics 的时候, 最好指定一下你感兴趣的那几个characteristics 的UUID
当找到了指定的 services 中的 characteristics 时, peripheral 就会调用代理方法: peripheral: didDiscoverCharacteristicsForServices: error: , Core Bluetooth 会创建一个元素全是 CBCharacteristic 对象的数组—— 每一个都是找到的 characteristic. 下面的这个例子简单告诉了你如何去展示你找到的characteristics:
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
error:(NSError *)error {
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"Discovered characteristic %@", characteristic);
...
}
...
检索一个Characteristic 的值
一个characteristic 包含了一个表示peripheral 的service 的详细信息. 例如, 一个体温检测 service 的 温度测量 characteristic 就有可能包含的是一个代表摄氏温度的值. 你可以通过直接读取一个characteristic 或者是订阅它来检索它的值
读取characteristic的值
当你找到了你感兴趣的service 的characteristic以后, 你通过调用CBPeripheral 类的 readValueForCharacteristic: 方法来读取 characteristic 的值, 记得指定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 都会有一个可读的值. 你可以通过 CBCharacteristicPropertyRead 这个常量来确定一个 characteristic的值的可读性, 详见 CBCharacteristic Class Reference. 如果你想读取一个不可读的value, 那么代理方法 peripheral: didUpdateValueForCharacteristic: error: 会返回一个对应的error
订阅一个Characteristic的值
虽然通过使用 readValueForCharacteristic: 方法可以在一些情况下读取到 characteristic的值, 但是当值放生改变时, 这种方法并不是最有效的那种. 对于大多数的数值改变——比如: 任意给定时刻你的心跳速率——你应该通过订阅来检索他们. 订阅了一个 characteristic 的 Value, 那么只要这个Value 发生改变的时候, 你就会收到来自 peripheral 发过来的通知
你可以调用 CBPeripheral 类的 setNotifyValue: forCharacteristic: 方法来订阅你感兴趣的characteristic 的 Value. 指定第一个参数的值为 YES:
[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];
当你试图去订阅(或者解除订阅)一个 characteristic 的 Value 的时候, peripheral 就会调用代理方法: peripheral: didUpdateNotificationStateForCharacteristic: error: . 如果订阅请求失败了, 你就可以实现这个方法来获取失败的原因( error 类型), 比如下面的例子:
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state: %@",
[error localizedDescription]);
}
...
注意: 并不是所有的characteristic 都会允许值被订阅. 你可以通过枚举: Characteristic Properties 中获取到相关的常量来确定一个 characteristic 的值能否被订阅
当你成功的订阅了一个characteristic 的 Value 以后, 值一旦放生改变, peripheral 设备就会通知你的 app. 值每改变一次, peripheral就会调用代理方法 peripheral: didUpdateValueForCharacteristic: error: . 想要检索更新后的值, 你可以实现上面 读取Characteristic 的 Value 中提到的那个方法
写入一个Characteristic 的 Value
在某些情况下, 给一个characteristic 写入值其实是有意义的. 例如, 如果你的app 同一个支持 BLE的电子恒温器进行交互的话, 你希望给恒稳定设定一个你房间温度的值. 如果一个 characteristic 的值是可写入的, 你可以通过调用 CBPeripheral 类的 writeValue: forCharacteristic: type: 方法把一些数据(NSData的实例对象) 写入进去, 就像:
NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic
type:CBCharacteristicWriteWithResponse];
当你尝试去写入一个characteristic的值的时候, 你需要指定一下你写入的类型, 上面的这个例子中, 写入类型被指定为 CBCharacteristicWriteWithResponse, 这代表 peripheral 允许你的app 获知写入是否成功. 如果想知道 Core Bluetooth 框架提供的其他的写入类型的信息, 参看 CBPeripheral Class Reference 中的 CBCharacteristicWriteType 枚举
peripheral 调用代理方法中的 peripheral:didWriteValueForCharacteristic:error: 并指定CBCharacteristicWriteWithResponse 来响应写入操作. 如果写入失败, 你可以实现下面的代理方法来获取错误:
- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error writing characteristic value: %@",
[error localizedDescription]);
}
...
注意: Characteristics 可能只会允许特定的类型写入来对其值进行写入操作. 想确定当前类型是否被准许对当前 characteristic 的值进行写入, 参阅 CBCharacteristic Class Reference 中的 Characteristic Properties 枚举中的相关属性.