执行普通中央角色任务
在蓝牙低功耗交互中实现了中央角色的设备执行一些普通任务--例如,发现和连接有效的外设,探索并与外设提供的数据进行交互。与此形成鲜明对比的是,实现外设角色的设备执行一些普通但不同的任务—例如,发布和广播服务,响应从连接的中央读写和订阅请求。
在这章中,你将会学习到怎样使用Core Bluetooth框架来从中央端执行大多数的蓝牙低功耗任务。下面一个基本的代码示例将会有助于开发你的应用程序来在你的本地设备上实现中央角色。
*启动一个中央管理者对象
*发现和连接正在广播的外设
*连接外设之后探索其中数据
*给外设的服务的一个特征发送读写请求
*订阅一个值在更新时发出通知的特征
在下一章节,你将会学习到怎样开发你的应用程序在你的本地设备上实现外设角色。
在这个章节你发现的代码示例是简单的和抽象的;你可能需要进行适当的修改,将其纳入你的真实世界的应用。更高级的主题是与实现中央角色有关--包括提示,技巧和好的练习--在后面的章节会提到。Core Bluetooth Background Processing for iOS Apps和 Best Practices for Interacting with a Remote Peripheral Device.
启动一个中央管理者
一个CBCentralManager
对象是在Core Bluetooth中对象定向表示为本地中央设备,你必须在你执行一些蓝牙低功耗事务时分配内存和初始化一个中央管理者实例,你可以通过调用CBCentralManager
类中的initWithDelegate:queue:options:
方法来启动你的中央管理者,例如:
myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
在这个例子中,self
是作为接受中央角色事件的代理,通过指定派遣队列是nil
,中央管理者会在主队列中处理中央角色事件。
当你创建了中央管理者,中央管理者会调用它的代理对象centralManagerDidUpdateState:
方法。你必须实现这个代理方法来确保设备支持蓝牙低功耗且中央设备可以有效使用。关于怎样实现这个代理方法更多的信息,请看CBCentralManagerDelegate Protocol Reference
发现正在广播的外设
中央端的其中的一个任务是你可能需要执行发现一些你的应用程序能够有效连接的外设。在Centrals Discover and Connect to Peripheral That Are Advertising早有提及,广播是外设被知道的主要方式。你可以通过调用CBCentralManager
类中的scanForPeripheralsWithService:options:
方法来发现一些正在广播的外设。
[myCentralManager scanForPeripheralsWithService:nil options:nil];
注意:如果你给第一个参数指定为
nil
,中央管理者会返回所有发现的外设,不管它们支持的服务。在一个真实的应用程序中,你将更可能指定一个CBUUID
对象的数组,这个对象表示正在广播的外设的一个服务的普通唯一标识(UUID
),当你指定了服务UUIDs
数组,中央管理者会返回只广播这些服务的外设,允许你只扫描你可能感兴趣的外设。
UUIDs
,和CBUUID
对象来表示它们,在Services and Characteristic Are Identified by UUIDs有更详细的讨论。
在你调用scanForPeripheralsWithServices:options:
方法来发现可用的外设后,中央管理者在每次发现外设时会调用它的代理对象的centralManager:didDiscoverPeripheral:advertisementData:RSSI:
方法。任何一个被发现的外设都会返回一个CBPeripheral
对象。如以下所示,你可以实现这个代理方法来列出任何被发现的外设。
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@", peripheral.name);
...
}
当你已经发现了你感兴趣想要连接的外设,为了保存手机能量停止扫描其他的外设。
[myCentralManager stopScan];
NSLog(@"Scanning stopped");
当你已经发现了外设之后连接外设
当你已经发现了正在广播你感兴趣的服务的外设,你可以通过调用CBCentralManager
类的connectPeripheral:options:
方法来请求连接外设。用简单地调用这个方法并指定你想连接的已经发现的外设。例如:
[myCentralManager connectPeripheral:peripheral options:nil];
假设连接成功,中央管理者会调用它代理对象的centralManager:didConnectPeripheral:
方法,你可以在这里面实现打印建立连接的信息,如下所示:
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Peripheral connected");
...
}
在你与该外设交互之前,你应该设置外设的代理来确保它能接受到适当的回调,例如:
peripheral.delegate = self;
发现你已经连接外设的服务
在你已经与外设建立连接之后,你可以开始探索它的数据。在探索的第一步外设必须提供正在被发现的有效服务。因为外设能广播有大小限制的数据量,你可能会发现一个外设有更多的广播服务(在它的广播包中),你可以通过调用CBPeripheral
类中的discoverServices:
方法来发现外设提供的所有服务。如下:
[peripheral discoverServices:nil];
注意:在真实的应用程序中,你不需要将参数设置成
nil
;这样做会返回外设的所有有效服务。因为一个外设可能包含很多你感兴趣的服务,发现它们的所有会浪费电池的寿命同时也没有必要。更多的情况下,你将指定你已经知道你所感兴趣的服务的UUIDs
,在Explore a Peripheral's Data Wisely可以看。
当一个指定的服务被发现,外设(你已经连接的CBPeripheral
对象)会调用它的代理对象的peripheral:didDiscoverServices:
方法。Core Bluetooth创建了一个数组包含CBService
对象--代表着一个在外设中被发现的服务。如下所示,你可以实现这个代理方法来访问发现服务的数组:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
for(CBService *service in peripheral.services) {
NSLog(@"Discovered service %@", service);
...
}
...
}
发现服务的特征
假设你已经发现了你感兴趣的服务,接下来的一步是探索外设已经提供的正在被发现的服务的特征。通过调用CBPeripheral
类的discoverCharacteristics:forService:
方法很容易发现服务的所有特征,指定合适的服务,如下:
NSLog(@"Discovering characteristics for service %@", interestingService);
[peripheral discoverCharacteristics:nil forService:interestingService];
注意:在真实的应用程序中,你可能不需要设置第一个参数为空,这样做会返回一个外设服务的所有特征。因为一个外设服务可能包含你感兴趣的更多的特征,发现它们的所有会浪费电池的寿命,而且也会花费不必要的时间。所以在发现特征的时候,你可以指定你感兴趣的已知特征的UUIDs。
当指定服务的特征被发现了外设会调用它的代理对象的peripheral:didDiscoverCharacteristicsForService:error:
方法,Core Bluetooth创建一个数组保存CBCharacteristic
对象--一个被发现的特征。下面的例子显示你可以实现这个代理方法来简单的打印每个被发现的特征值的信息:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"Discovered characteristic %@", characteristic);
...
}
...
}
得到特征值
一个特征包含一个代表一个外设服务信息的值。例如,一个健康温度计的一个温度测量特征可能有一个值来表明一个摄氏温度。你可以通过正确地读取或订阅它来得到这个特征值。
读取特征值
在你发现了一个你感兴趣的服务的特征之后,你可以通过CBPeripheral
类的readValueForCharacteristic:
方法,指定一个合适的特征,来读取特征值。就像:
NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
[peripheral readValueForCharacteristic:interestingCharacteristic];
当你想要读取一个特征值时,外设会调用它的代理对象的peripheral:didUpdateValueForCharacteristic:error:
方法来得到这个值。如果这个值是成功的获得,你可以通过这个特征的值属性来访问它。就像:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSData *data = characteristic.value;
// parse the data as needed
...
}
注意:不是所有的特征都保证有一个值是可读的。你可以通过访问
CBCharacteristicPropertyRead
常量属性来决定特征值是否可读,具体请看CBCharacteristic Class Reference.如果你尝试读取不可读的特征值,代理方法peripheral:didUpdateValueForCharacteristic:error:
将会返回一个错误的信息。
订阅特征值
通过使用readValueForCharacteristic:
方法来读取特征值在一些情况下是有效的,但它并不是一个高效的方式来获取一个改变的特征值。对于大多数改变的特征值--例如在每一个给定的时间的心率值--你应该通过订阅它们来获取它们的值,当你订阅了一个特征值,当外设的特征值改变的时候你会接受到一个通知。你可以通过调用CBPeripheral
类的setNotifyValue:forCharacteristic:
方法并指定第一个参数为YES来订阅一个你感兴趣的特征值。就像:
[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];
当你想要订阅(或取消订阅)一个特征值时,外设会调用它的代理对象的peripheral:didUpdateNotificationStateForCharacteristic:error:
方法,如果因为一些原因而订阅失败,你可以实现这个代理方法来访问错误的原因,如下所示:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state : %@", [error localizedDescription]);
}
...
}
注意:并不是所有的特征都配置了允许你订阅它们的值。你可以通过访问在
Characteristic Properties
枚举中有关的常量来决定这个特征是否已经配置来,具体请看CBCharacteristic Class Reference
当你成功的订阅了一个特征值以后,在该特征值改变时外设会通知你的应用程序。每次值的改变,外设会调用它的代理对象的peripheral:didUpdateValueForCharacteristic:error:
方法。为了获得这个更新的值,你可以用同样的方式实现这个方法在Reading the Value of a Characteristic的描述中.
给特征值写入数据
在一些情况下,需要给一些特征写一些有意义的值。例如,如果你的应用程序与蓝牙低功耗数字恒温控制器交互,你可能会想要提供给恒温控制器一些值来设置房间的温度。如果一个特征值是可写的,你可以通过调用CBPeripheral
类的writeValue:forCharacteristic:type:
方法来写入一些数据(NSData类型的实例).就像:
NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic type:CBCharacteristicWriteWithResponse];
当你想要给一个特征写入值,你需要指定你想要执行的写入类型。在上面的例子中,指定的类型是CBCharacteristicWriteWithResponse
,这表明外设让你的应用程序知道是否写入成功,想要知道Core Bluetooth框架中提供的写入类型更多信息,请看在CBPeripheral Class Reference中的CBCharacteristicWriteType
枚举。
外设通过调用它的代理对象的peripheral:didWriteValueForCharacteristic:error:
方法会响应指定为CBCharacteristicWriteWithResponse
类型的写入请求。如果因为一些原因写入失败,你可以实现这个代理方法来访问失败的原因,如下例所示:
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"Error writing characteristic value: %@", [error localizedDescription]);
}
...
}
注意:特征可能只允许某些类型可写入值。为了确定哪些特征值允许被写入,你可以访问
Characteristic Properties
枚举中的相关属性,详细的请看CBCharacteristic Class Reference;
---翻译的文档地址:Performing Common Central Role Tasks