您的位置:首页 > 产品设计 > UI/UE

CoreBluetooth第二节:Performing Common Peripheral Role Task(执行常见的外设端任务)

2016-02-23 14:34 447 查看
本文官方文档链接

启动一个Peripheral Manager

建立Services和Characteristics
Services和Characteristics通过UUIDs区分

为自定义的Services和Characteristics创建自己的UUIDs

创建Services和characteristics的树形结构

发布你的Service和Characteristic

广播Service

响应核心设备发来的读写请求

向订阅的核心设备发送更新的characteristic

在前一章节中你掌握了执行常见的蓝牙低功耗核心端的任务.在本章中,你要学习执行常见的蓝牙低功耗外设端的任务.基于代码的例子会帮助你在本地设备中扮演外设端.特别的,你将掌握如何:

启动一个peripheral manager对象

在本地外设中建立services 和characteristics

将services和characteristics发送到设备的本地数据库

广播services

响应与自身建立连接的central(核心端)的读写请求

向订阅自己的central(核心端)更新特征值

本章中的样例简单并抽象,你可能需要在实际开发中做出相应更改.更多外设端的进阶主题-包括小贴士/技巧/最佳实践在随后的章节:
Core Bluetooth Background Processing for iOS Apps
Best Practices for Setting Up Your Local Device as a Peripheral
.

启动一个Peripheral Manager

第一步就是分配内存并初始化一个peripheral manager实例(CBPeripheralManager对象)调用CBPeripheralManager类的
initWithDelegate:queue:options
方法:

myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];


在本例中,self设置成代理对象来接收外设端的事件.当你指定队列为nil时,peripheral manager使用主队列来分发事件.

当创建一个peripheral manager, peripheral 调用
peripheralManagerDidUpdateState
代理方法.为了确保本地外设支持蓝牙低功耗并可用,你必须实现该代理.关于实现该代理方法的更多信息,请点击CBPeripheralManagerDelegate Protocol Reference

建立Services和Characteristics



如上图所示,一个本地外设的数据库(存储services和characteristics)是一个树状结构.在本地的外设上建立services和characteristics时必须将他们构造成这个树状结构.第一步就是理解services和characteristics是如何标识的.

Services和Characteristics通过UUIDs区分

外设的services和characteristics通过128位特定的蓝牙UUID区分,在Core BlueTooth框架中用作CBUUID对象.尽管SIG并未预定义用来区分service和characteristic的UUID,SIG定义并发布了一些常见的便于使用的16位的UUID.举个栗子,SIG将心率设备的标识符定义为16位的
180D
.这个UUID是由和它等价的128位UUID缩短而成,
0000180D-0000-1000-8000-00805F9B34FB
,基于基本的UUID定义为蓝牙4.0规范,频道3,part F, Section3.2.1

CBUUID类提供了可让你在开发中快速处理长UUID的工厂方法.例如,你可以简单的使用
UUIDWithString
来从系统预定义的16位UUID创建CBUUID对象,从而取代128位心率service的UUID:

CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString: @"180D"];


为自定义的Services和Characteristics创建自己的UUIDs

你可能会有一些没有预定义UUID的services和characteristic.如果这么做,你需要为它们生成自己的128位UUID.

使用命令行工具
uuidgen
可以快速的生成128位UUID.首先,开启一个终端窗口,下一步为每一个需要标识的service和characteristic生成为一个128位使用-连接的ASCII值:

$ uuidgen
71DA3FD1-7E10-41C1-B16F-4430B506CDE7


然后你可以使用这个UUID通过
UUIDWithString
创建一个CBUUID对象:

CBUUID *myCustomServiceUUID = [CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"];


创建Services和characteristics的树形结构

在你拥有了services和characteristic的UUIDs之后,你可以创建可变的service和characteristics并且将它们构成树形.举例,如果你有一个characteristic的UUID,你可以通过
initWithType:properties:value:permissions
创建可变的characteristic:

myCharacteristic =
[[CBMutableCharacteristic alloc] initWithType:myCharacteristicUUID
properties:CBCharacteristicPropertyRead
value:myValue permissions:CBAttributePermissionsReadable];


当创建了一个可变的characteristic后设置他的
属性
,
授权
.除了其他事项外,
属性
授权
决定了characteristic value(特征值)是可读的还是可写的,以及与本设备建立连接的central(核心)设备是否可以订阅该characteristic.在本例中,特征值被设置成可以被连接的核心设备读取.更多关于可变characteristic相关的可用属性和授权请戳CNMutableCharacteristic Class Reference

Note:如果你为characteristic指定了一个值,这个值被缓存下来并且它的属性和授权被设置为了可读.如果你需要将characteristic设置为可写的,或者在发布service的生命周期中改变characteristic的值,你必须指定值为nil.如此确保在外设收到核心设备的读写请求时,值是动态变化的

现在你创建了一个可变的characteristic,你可以创建可变的service来联结characteristic,调用
CBMutableService
类的
initWithType:primary:


myService = [[CBMutableService alloc] initWithType:myServiceUUID primary:YES];


在本例中,第二个参数设置成了Yes,意味着service是主要的.一个主service描述一个设备的主要功能并可以被其他service引用.次service描述了一个只在引用它的其他service的上下文中相关(翻译的好像不太对.A secondary service describes a service that is relevant only in the context of another service that has referenced it).举个栗子,心率检测器的主service可能从心率传感器的传输心率数据,而次service可能传输传感器的电量数据.

在创建了一个service后,你可以设置service的characteristic数组来联结它们:

myService.characteristics = @[myCharacteristic];


发布你的Service和Characteristic

在你创建好树形的services和characteristics后,下一步是将他们发布到本地设备的services和characteristics数据库中.使用CB可以简单地实现,你可以调用
CBPeripheralManager
类的
addService:


[myPeripheralManager addService:myService];


当你调用这个方法发布services时,peripheral manager调用
peripheralManager:didAddService:error:
代理函数.如果错误发生了并且services不能被发出,实现该代理来捕获错误信息:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
didAddService:(CBService *)service
error:(NSError *)error {

if (error) {
NSLog(@"Error publishing service: %@", [error localizedDescription]);
}
...


Note:在你向数据库发布一个service以及相关的characteristic后,这个service被缓存下来并且你不能再改变它

广播Service

当你将services和characteristics发布到设备的service和characteristic数据库中后,你已经准备将他们广播给一些正在监听的central设备.如下例所示,你可以调用
CBPeripheralManager
类中的
startAdvertising:
,传入一个NSDictionary类型的广播数据.

[myPeripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[myFirstService.UUID, mySecondService.UUID] }];


在本例中,NSDictionary的唯一键
CBAdvertisementDataServiceUUIDsKey
,对应需要一个存放CBUUID对象的数组(代表着你想广播的service的UUIDs).其他你可能用到的NSDictionary中的键在这里.也就是说,只有两个键是支持peripheral manager对象的:
CBAdvertisementDataLocalNameKey
CBAdvertisementDataServiceUUIDsKey.


当你开始在本地外设中广播数据时,peripheral manager调用
peripheralManagerDidStartAdvertising:error:
代理方法.如果发生错误可以在代理中捕获错误信息:

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {

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


Note:数据广播是”努力”的,因为容量有限并且同时会有多个app广播,更多相关信息请查看
startAdvertiseing:
,广播的行为也会因程序在后台受到影响,在下一章中将讨论该问题,
Core Bluetooth Background Processing for iOS Apps


一旦你开始广播数据,远程的核心设备可以发现并且与你建立连接.

响应核心设备发来的读写请求

在你连接了一个或多个远程核心设备后,你可能开始接收他们的读写请求.当你这么做时,确保用适当的方式来响应这些请求.下例描述了如何处理这些请求.

当一个连接的核心设备请求读取一个你的characteristic, Peripheral manager调用
peripheralManager:didReceiveReadRequest:
代理方法.这个代理方法会以CBATTRequest对象向你传递核心端传来的请求(拥有许多你可以用来实现请求的属性)

举例,当你接收到一个简单的要读取characteristic的请求,从代理中获取的
CBATTRequest
对象可以用来确定核心端指定要读的characteristic和你本地设备数据库中的characteristic相匹配.你可以实现这个代理:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
didReceiveReadRequest:(CBATTRequest *)request {

if ([request.characteristic.UUID isEqual:myCharacteristic.UUID]) {
...


如果characteristic的UUID匹配成功,下一步要确保这个读取请求要读取的index位置不在你的characteristic值的外界.如下例所示,你可一个使用
CBATTRequest
offset
属性来保证读取请求不是在读取属性外界的数据.

if (request.offset > myCharacteristic.value.length) {
[myPeripheralManager respondToRequest:request
withResult:CBATTErrorInvalidOffset];
return;
}


假如请求的偏移量成功验证,用本地外设端创建的characteristic值覆盖请求中的characteristic属性值,并且要顾及到读取请求的偏移量:

request.value = [myCharacteristic.value
subdataWithRange:NSMakeRange(request.offset,
myCharacteristic.value.length - request.offset)];


在设置完值之后,需要向远程核心设备声明请求已成功处理.需调用
CBPeripheralManager
respondToRequest:withResult:
方法,将你更新的request和请求处理的结果一并返回:

[myPeripheralManager respondToRequest:request withResult:CBATTErrorSuccess];


在每一次
peripheralManager:didReceiveReadRequest:
被调用后你都需要调用一次
respondToRequest:withResult:


Note:如果characteristic的UUID不匹配,或因其他原因不能完成读取请求,你无须尝试完成请求.相反的你应该立刻调用
respondToRequest:withResult:
并且提供一个表明失败原因的result.对于你可能需要指定的结果,查看
CBATTError Constants


处理核心设备发送的写入请求也很直接.当连接的核心设备向你发送一个或多个characteristic读写请求时,peripheral manager调用
peripheralManager:didReceiveWriteRequests:
代理.此时代理方法会以一个包含多个
CBRequest
对象的数组向你传输请求,其中每一个代表一个写入请求.在你确认完成了一个写入请求后,你可以写characteristic的值:

myCharacteristic.value = request.value;


尽管上述例子没有演示,你还要确保在写入characteristic时考虑请求的偏移量属性.

正如回应读取请求一样,在每一次
peripheralManager:didReceiveWriteRequest:
被调用后你都需要调用一次
respondToRequest:withResult:
,也就是说第一个参数需要一个
CBATTRequest
对象,即使你从
peripheralManager:didReceiveWriteRequests:
收到了多于一个Request的数组.你应该传入数组的第一个请求:

[myPeripheralManager respondToRequest:[requests objectAtIndex:0]
withResult:CBATTErrorSuccess];


将多个请求作为单个请求来处理,如果任一请求不能完成,你不应该完成其中的任何一个.相反,立刻调用
respondToRequest:withResult:
报错

向订阅的核心设备发送更新的characteristic

通常情况下,连接的核心设备会订阅你的一个或多个characteristic值,如上一节的
订阅characteristic值
.当订阅characteristic值发生改变,你有责任发出通知.下例将指导你如何去做.

当一个连接的核心设备订阅了你的若干characteristic.peripheral manager 调用
peripheralManager:central:didSubscribeToCharacteristic:


- (void)peripheralManager:(CBPeripheralManager *)peripheral
central:(CBCentral *)central
didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {

NSLog(@"Central subscribed to characteristic %@", characteristic);
...


使用上面的代理方法来向核心设备发送更新值.

下一步,获得更新的characteristic值并且通过
CBPeripheralManager
updateValue:forCharacteristic:onSubscribedCentrals:
方法发送.

NSData *updatedValue = // fetch the characteristic's new value
BOOL didSendValue = [myPeripheralManager updateValue:updatedValue
forCharacteristic:characteristic onSubscribedCentrals:nil];


当你调用该方法时,可以通过最后一个参数来指定你想要更新的订阅characteristic值核心设备.上例中,如果你指定为nil,所有订阅的连接核心设备都将收到更新.

updateValue:forCharacteristic:onSubscribedCentrals:
返回一个布尔值(意味向订阅的核心设备发送更新成功).如果用来传输更新数据的底层队列满了,返回
NO
.Perpheral manager 会在传输队列有空间时调用
peripheralManagerIsReadyToUpdateSubscribers:
,可以声明本方法来重发值,依旧使用
updateValue:forCharacteristic:onSubscribedCentrals:


Note:使用通知来向订阅的核心设备发送的一个数据包,也就是说,当你更新一个订阅的核心设备,你应该在一个通知中发送全部更新数据,通过调用一次
updateValue:forCharacteristic:onSubscribedCentrals:
.

不是所有的数据都可以通过通知(notification)传输,这取决于characteristic值的大小.如果数据较大,应该在核心端调用
CBPeripheral
readValueForCharacteristic:
方法接收全部数据
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: