Performing Common Central Role Tasks - Core Bluetooth programming Guide 第2章 中文翻译

Apple 官方文档 Core Bluetooth Programming Guide 的中文翻译
本文翻译的是其中的第二章节 Performing Common Central Role Tasks
一些术语:
Bluetooth low energe(BLE):低功耗蓝牙,全文默认的蓝牙版本
central:中央设备
peripheral:外围设备,[ pəˈrɪfərəl ]重音在第二音节,千万不要重音放在第一音节。会念了之后发现这个单词的发音特别有韵味有没有。
service:服务,peripheral暴露的服务
characteristic:特性,一个service中包含若干特性,[ ˌkærəktəˈrɪstɪk ]同样的需要注意重音。
这些术语将不再做翻译,以避免翻译不同带来的沟通壁垒。
另外一些开发过程中习以为常用英文表达的词,如manager, delegate, framework等也保持不翻译状态,以便于理解为要。
国际惯例,转载请注明出处:http://www.jianshu.com/p/8ca610777de2

// pragma mark - 正文开始

Performing Common Central Role Tasks

执行典型Central角色的任务

在低功耗蓝牙(BLE)通信过程中,实现了central角色的设备通常要执行一系列任务----例如,发现并连接可用的peripheral,探索peripheral提供的数据并与之交互。相对的,实现了peripheral的设备也要执行一系列不同的任务----例如,发布并广播service,并且为连接着的central提供读、写、订阅请求的响应。

这一节中,你将学习到如何用Core Bluetooth Framework执行central端大部分典型任务。随附的代码示例也会帮助你在开发app过程中,在本地设备上实现central角色。具体来说,你将学习到如何:

  • 创建一个central manager对象
  • 发现并连接到正在广播的peripheral
  • 连接成功之后,探索peripheral设备上的数据
  • 发送读、写请求到peripheral service的characteristic值
  • 订阅characteristic,当其变化时收到通知

下一节中,你将学习如何在开发app过程中,在本地设备上实现peripheral角色。
  本节中的代码示例很简单很抽象,把它集成到真实的app中你可能需要做一些额外的工作。更多关于实现central角色的高级话题,包括小贴士、小技巧、最佳实践,会在后续章节中讲到,Core Bluetooth Background Processing for iOS Apps 以及 Best Practices for Interacting with a Remote Peripheral Device.

创建一个central manager对象

由于CBCentralManager对象是Core Bluetooth中本地central设备面向对象的抽象表现形式,你必须在执行任何BLE事务之前分配并初始化一个central manager实例。你可以通过调用CBCentralManager类的initWithDelegate:queue:options:方法创建central manager,如此如此:

    myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];

在这个示例中,self被设置为delegate,以接收任何central角色的事件。如果指定dispatch queue为nil,central manager将在主线程中分发事件。

当你创建一个central manager,它会调用它的delegate对象的centralManagerDidUpdateState:
方法。你必须实现这个delegate方法以保证central设备支持BLE并可以正常使用。关于如何实现这个delegate的更多细节请见CBCentralManagerDelegate Protocol Reference

发现正在广播的peripheral设备

central端首要任务之一是发现有哪些peripheral设备是可用的,可以让你的app连上。正如早先在Centrals Discover and Connect to Peripherals That Are Advertising(译注:上一章节内容,尚未翻译)提到,广播是peripheral使它们自己被发现的主要方式。你可以通过调用CBCentralManager类的scanForPeripheralsWithServices:options:
方法来发现任何正在广播的peripheral设备。

    [myCentralManager scanForPeripheralsWithServices:nil options:nil];

注意:如果第一个参数传的是nil,central manager会返回所有发现的peripheral,忽略它们支持的service。在真实的app中,你很可能会指定一个CBUUID
数组对象,每个元素代表一个正在广播的peripheral service的UUID。若如此做,central manager只会返回广播对应service的peripheral,使你能够只扫描你可能感兴趣的设备。
UUID和代表它们的CBUUID对象,在Services and Characteristics Are Identified by UUIDs中详述。

当你调用scanForPeripheralsWithServices:options:方法发现了哪些设备可用之后,central manager每发现一个peripheral就会调用一次delegate对象的centralManager:didDiscoverPeripheral:advertisementData:RSSI:方法。任何被发现的peripheral以CBPeripheral对象的形式返回。如下所示,你可以实现这个delegate方法列出所有发现的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设备(需要先发现它们)

当你发现一个peripheral正在广播你感兴趣的service,你可以通过调用CBCentralManager类的connectPeripheral:options:方法向peripheral请求一个连接。简单的调用这个方法并指定你想要连接的peripheral,如此如此:

    [myCentralManager connectPeripheral:peripheral options:nil];

如果连接请求成功,central manager会调用delegate对象的centralManager:didConnectPeripheral:
方法,你可以在其中打印出连接成功的日志,如下所示:
- (void)centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral {

    NSLog(@"Peripheral connected");
    ...

当你开始与peripheral交互之前,你需要设置好peripheral的delegate,确保能收到相应的回调,如下:

    peripheral.delegate = self;

在连接上的peripheral上发现service

当你对peripheral建立一个连接之后,你可以开始探索其中的数据。探索的第一步是发现peripheral提供的service。由于peripheral所能广播的数据量有限,你可能会发现peripheral广播的service不全(在广播数据包中)。你可以通过调用CBPeripheral类的discoverServices:
方法发现一个peripheral提供的所有service,如下:

    [peripheral discoverServices:nil];

注意:在真实的app中,你可能不会传nil作为参数,因为这样做会返回peripheral设备上的所有可用service,因为一个peripheral可能包含的service比你感兴趣的多得多,发现它们全部是对电量和时间的浪费。你更有可能会指定已知感兴趣的service的UUID,正如Explore a Peripheral’s Data Wisely这一节所示。(译注:后面第五章,将要翻译)

当指定的service被发现时,peripheral(即你连接到的那个CBPeripheral对象)会调用它的delegate对象上的peripheral:didDiscoverServices:方法。Core Bluetooth创建一个CBService对象数组,一一对应peripheral上发现的service。如下所示,你可以实现这个delegate方法以访问这个发现到的service的数组。

- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {
    for (CBService *service in peripheral.services) {

        NSLog(@"Discovered service %@", service);
        ...
    }
    ...

发现service中的characteristic

假设你已经发现一个感兴趣的设备,探索peripheral所能提供的东西,下一步就是发现所有service提供的characteristic。这个步骤很简单,只需调用CBPeripheral类的discoverCharacteristics:forService:
方法,为之指定一个适当的service参数,如下:

    NSLog(@"Discovering characteristics for service %@", interestingService);
    [peripheral discoverCharacteristics:nil forService:interestingService];

注意:在真实app中,第一个参数你很可能不会传nil,因为这样会返回一个peripheral service的所有characteristic。由于一个peripheral service包含的characteristic比你感兴趣的多的多,发现所有会浪费电量和时间。更可能的是,你会指定一个已知感兴趣的characteristic的UUID。

当characteristic被发现时,peripheral会调用delegate对象的peripheral:didDiscoverCharacteristicsForService:error:方法。Core Bluetooth会创建一个 CBCharacteristic对象的数组,对应每个发现的characteristic。下面的例子展示了如何实现delegate方法,简单的将每个发现的characteristic记录日志。

- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
             error:(NSError *)error {
    for (CBCharacteristic *characteristic in service.characteristics) {

        NSLog(@"Discovered characteristic %@", characteristic);
        ...
    }
    ...

获取characteristic的值

一个characteristic只包含一个值,作为一个service的更多信息的展现。例如,一个体温计service的温度度量characteristic可能包含一个表示摄氏温度的值。你可以通过直接读取获取一个characteristic的值,也可以订阅它。

读取characteristic的值

当你从service中发现一个你感兴趣的characteristic之后,你可以通过调用CBPeripheral类的readValueForCharacteristic:方法读取这个characteristic值,如下:

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

当你尝试读取一个characteristic值的时候,peripheral调用delegate对象的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。若你试图读取一个不可读的值,peripheral:didUpdateValueForCharacteristic:error:delegate方法会返回一个对应的error。

订阅一个characteristic值

虽然使用readValueForCharacteristic:读取characteristic值在某些场景很有效,它不是读取可变值的最有效率的方法。对于大多数可变characteristic值来说----例如你在任意时刻的心率----你应该通过订阅来读取它们。当你订阅了characteristic值,这个值变化时你会从peripheral收到通知。

你可以通过调用CBPeripheral类的setNotifyValue:forCharacteristic:方法,并指定第一个参数为YES,来订阅你感兴趣的characteristic值,如下:

    [peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];

当你尝试订阅或取消订阅一个characteristic值时,peripheral会调用delegate对象的peripheral:didUpdateNotificationStateForCharacteristic:error:方法。若订阅请求出现任何问题,你可以通过实现这个delegate方法拿到错误原因,如下例所示:

- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {

    if (error) {
        NSLog(@"Error changing notification state: %@",
        [error localizedDescription]);
    }
    ...

注意:不是所有characteristic值都被配置为支持订阅。你可以通过访问Characteristic Properties枚举中的相应常量来判定某个characteristic是否支持订阅。

当你成功订阅了某个characteristic值,这个值变化时peripheral设备会通知你。每当这个值发生变化,peripheral调用delegate对象的peripheral:didUpdateValueForCharacteristic:error:方法。要获取这个值,你可以实现与上文Reading the Value of a Characteristic(读取characteristic值)所述相同的方法。

写入characteristic值

在某些场景中,有写入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 framework支持哪些写入类型的更多信息,参见 CBPeripheral Class Reference类的CBCharacteristicWriteType枚举。
peripheral会响应那些指定为CBCharacteristicWriteWithResponse类型的写入请求,通过调用delegate对象的peripheral:didWriteValueForCharacteristic:error:
方法。如果写入有任何问题,你可以实现这个delegate方法拿到错误原因,如下例所示:
- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {

    if (error) {
        NSLog(@"Error writing characteristic value: %@",
        [error localizedDescription]);
    }
    ...

注意:characteristic值可能可允许某些类型的写入,确定哪些类型的写入是允许写入到characteristic值的,可以参照Characteristic Properties枚举的相关属性,详见 CBCharacteristic Class Reference

@end

你可能感兴趣的:(Performing Common Central Role Tasks - Core Bluetooth programming Guide 第2章 中文翻译)