iOS-Performing Common Peripheral Role Tasks(API Reference)the fourth part

7 篇文章 0 订阅

Performing Common Peripheral Role Tasks


上一个章节,你学习了怎么执行最普通的蓝牙低能量任务从central 端。在这个章节,你学习在peripheral端怎么执行最普通的蓝牙低能昂任务。下面的代码演示你开发你的app实现peripheral角色在你本地的设备。规制,你将学习什么:

  • 开启一个peripheral管理者对象
  • 在你的本地的peripheral 搭建services和characteristic
  • 发布你的services和characteristics到你的设备的本地数据库
  • 广播你的service
  • 响应来自一个连接的central 读取和写入请求
    代码例子你会发现在这个章节是简单和抽象的,你可能需要进行适当的更改在你的实际的app中。更高级的实现central 角色的相关主题–包括提示,技巧,和最佳的练习在之后的章节。

Starting Up a Peripheral Manager


第一步在你的本地设备alloc和init一个peripheral manager实例(代表CBPeripheralManager对象)实现peripheral角色。开启你的peripheral管理者通过调用CBPeripheralManager类的initWithDelegate:queue:options:,像这样:

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

在这个例子中,self是作为delegate,接收任何的peripheral角色的事件。当你指定dispathc queue为nil,peripheral管理者分配peripheral角色事件使用main queue。

当你创建一个peripheral管理者,central管理者调用delegate中的peripheralManagerDidUpdateState::方法。你必须实现这个delegate方法去确保是支持蓝牙低能量和在central设备中是可获得的。关于更多怎么实现delegate方法的信息,看CBPeripheralManagerDelegate Protocol Reference
.


Setting Up Your Services and Characteristics


如图1-7所示,一个本地的peripheral的数据库services和characteristics是组织树状的方法 你必须组织他们在这个树状方式设置本地peripheral的services和characteristics。你的执行这些任务的第一步是了解services和识别characteristics。


Services and Characteristics Are Identified by UUIDs


peripheral的services和characteristics的标识是128位蓝牙指定UUIDs,代表Core Bluetooth framework中的CBUUID对象。虽然并不是所有的UUIDs识别service或者预定义的characteristic通过Special Interest Group(SIG),蓝牙SIG定义和发布一些常用的uuid,为方便缩短16位。例如,蓝牙SIG有预定义的标识一个心率services的16位UUID为180 d。UUID是缩短从它的等效128位的UUID,0000180 d - 0000 - 1000 - 8000 - 00805 - f9b34fb,基于蓝牙UUID定义在蓝牙4.0规范中,卷3 F部分,3.2.1节。

当开发你的app的时候,CBUUID提供了工厂方法使用它处理长的UUIDs更容易。例如,在你的代码中而不是通过字符串表示的心率services的128位UUID,您可以简单地使用UUIDWithString方法创建一个CBUUID对象从service的预定义的16位UUID,像这样:

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

当你从一个预定义的16位创建一个CBUUID对象时,Core Bluetooth预填充剩余的128位UUID和蓝牙基本的UUID。


Create Your Own UUIDs for Custom Services and Characteristics


你可能通过预定义的蓝牙UUID没有识别services和characteristics。如果你这样做,你需要生成自己的128位uuid来识别它们
使用命令行uuidgen轻松的创建128位UUID。首先打开一个终端窗口。接下来,为每个service和characteristic,您需要识别UUID,在命令行中输入uuidgen,接收一个惟一的ASCII字符串形式的128位值的连字符,如以下例子:

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

你你可以使用这个UUID创建一个CBUUID对象通过UUIDWithString方法,像这样:

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

Build Your Tree of Services and Characteristics


你你在你的services和characteristics有UUID之后,您可以创建可变的services和characteristics和组织在上述树状方式。例如,如果你有characteristic的UUID,您可以创建一个可变characteristics通过调用
CBMutableCharacteristic 类的
initWithType:properties:value:permissions:方法,像这样:

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

你当你创建了一个可变的characteristic,你设置它的属性,值,和权限。属性和权限设置好后,除此之外,characteristic的值是否可读的还是可写的,以及连接central是否可以订阅characteristic的值。在这个例子中,characteristic的值被设置为被连接central可读。更多信息支持的属性和权限范围可变的characteristic,看到CBMutableCharacteristic Class Reference

Note:如果你指定为characteristic指定一个值,这个值是缓存的和它的属性和权限是设置成可读的。因此,如果你需要一个可以写的characteristic的值,或者如果你期望值变化期间characteristic属于发布的service的生命周期,您必须指定值是nil。下面这种方法保证了所请求的值是动态和peripheral管理者只要peripheral收到一个读或写请求连接central。

现在您已经创建了一个可变的characteristic,您可以创建一个可变的service关联characteristic。要做到这一点,调用
CBMutableService 类的
initWithType:primary:方法:

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

在这个例子中,第二个参数设置为YES,表明service是主要与次要的。主要service描述设备的主要功能和可以包含另一个service(引用)。次要服务描述服务相关的只有在上下文中引用的另一个服务。例如,心率监视器的主要service可能暴露心率监测器的心率传感器的数据,而第二个servuce可能暴露传感器的电池数据。
你创建一个服务之后,你可以设置这个service的charactertics的数组,像这样:

myService.characteristics = @[myCharacteristic];


Publishing Your Services and Characteristics


你已经构建树状services和characteristics之后,下一步是实现peripheral角色,在你的本地设备发布他们到设备的services和characteristics数据。这是是非常容易的使用Core Bluetooth framework。你调用CBPeripheralManager类的addService:方法,像这样:

[myPeripheralManager addService:myService];

当你调用这个方法发布你的services的时候,peripheral管理者调用它的代理对象方法peripheralManager:didAddService:error:。如果一个错误发生和你的services不能发布,实现这个代理方法获取错误的原因,以下例子演示:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
            didAddService:(CBService *)service
                    error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error publishing service: %@", [error localizedDescription]);
    }
    ...

>Note:发布一个service和任何它相关的characterictics到peripheral的数据库,service是缓存的,你可以不再对其进行更改。


Advertising Your Services


当你已经发布你的services和characteristics到你的设备的services和characteristics数据库,你准备开始广播他们到任何可能监听到的central.下面的例子,你可以广播一些services通过调用CBPeripheralManagerstartADvertising:方法,通过在一个字典中实例化广播数据:

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

在这个例子中,字典中的key是唯一的, CBAdvertisementDataServiceUUIDsKey
,期望一个代表你想广播的services的UUID的CBUUID对象数组作为一个值,你可以去CBCentralManagerDelegate Protocol Reference的Advertisement Data Retrieval Keys的常量描述中寻找广播数据key的细节。只有两个key支持peripheral管理者对象:
CBAdvertisementDataLocalNameKeyCBAdvertisementDataServiceUUIDsKey
当你在你的本地peripheral开始广播一些数据,peripheral管理者调用他的代理对象方法:peripheralManagerDidStartAdvertising:error:
如果发生一个错误和你的service是不能广播的,实现这个他的代理方法访问这个错误的原因,像这样:

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
                                       error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error advertising: %@", [error localizedDescription]);
    }
    ...

Note:数据的广播是“best effort”的基础上完成的,因为空间有限,同时可能会有多个app的广播。有关更多信息,请参见startAdvertising:的讨论:方法在CBPeripheralManager Class Reference
广播的行为也影响了当你的应用程序在后台。在下一章中讨论过这个问题,核心蓝牙iOS应用程序的后台处理。

一旦你开始广播数据,远程central能发现和初始化一个连接和你。


Responding to Read and Write Requests from a Central


你连接一个或者多个远程central之后,你可能从它们那里开始接受读取或者写入请求。当你这样做确保在一个适当的方法响应那些请求。下面的例子描述了怎样处理这样的请求。

当一个连接的central请求读取你的characteristics中的一个值,peripheral管理者调用它的代理对象方法:peripheralManager:didReceiveReadRequest:,代理方法将该请求以CBATTRequest对象的形式传递给你,它有许多的属性,您可以使用它们来完成请求。

例如:当你接受一个简单的请求读取一个characteristic的值,你接收的代理方法中的CBATTRequest参数的属性可用于确保在你的设备数据库的characteristic匹配远程central指定的读取请求,像这样:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
    didReceiveReadRequest:(CBATTRequest *)request {
 
    if ([request.characteristic.UUID isEqual:myCharacteristic.UUID]) {
        ...

如果characteristic的UUID匹配,下一步是确保读取请求不要求读取索引的范围以外的位置的characteristic的值。如以下示例所示,您可以使用一个CBATTRequest对象的偏移属性,以确保外的读取请求不是试图读取合适的范围以外:

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

假设请求的偏移量是正确的,现在设置请求的characteristic属性的值(默认的值为nil)到你在你的本地peripheral创建的characteristic的值,考虑到读取请求的偏移量:

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

设置值后,响应远程central表示请求成功完成。通过调用CBPeripheralManager类的respondToRequest:withResult:方法,通过请求(其值更新)和请求的结果,是这样的:

[myPeripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    ...

调用respondToRequest:withResult:方法每次都会调用代理方法这里写链接内容peripheralManager:didReceiveReadRequest:

Note:如果characteristic的UUID没有匹配,或者如果读取因为任何的其他原因没有完成,你将不会完成这个请求。反而,你将立即调用respondToRequest:withResult:方法和提供一个失败的原因。对可能结果的列表你可以指定,看 Core Bluetooth Constants Reference.CBATTError Constants

处理写请求从一个连接central也很简单。连接central发送写的一个或多个characteristic的值请求时,peripheral管理者调用它的的代理对象方法 peripheralManager:didReceiveWriteRequests:。同一时间,代理方法包含一个或多个CBATTRequest对象数组的形式向你发送请求,每个代表一个写请求。确保写请求后完成后,,您可以编写characteristic的价值,像这样:

  myCharacteristic.value = request.value;

虽然上面的例子不能证明这一点,一定要考虑到请求的偏移量属性,当你写入你的characteristic的值的时候。
就像你响应一个读取请求,调用respondToRequest:withResult:方法同一时间会调用代理方法 peripheralManager:didReceiveWriteRequests:respondToRequest:withResult: 的第一个参数希望一个单一的CBATTRequest对象,即使你已经从peripheralManager:didReceiveWriteRequests: 代理方法接收了一个包含多个CBATTRequest对象的数组。你应该通过数组第一个请求,像这样:

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

Note:>对待多个请求作为单个请求,如果任何个人请求不能完成,你不应该满足其中任何一个。相反,立即调用respondToRequest:withResult:方法并提供结果,指出失败的原因。


Sending Updated Characteristic Values to Subscribed Centrals


通常,连接central将订阅一个或者多个你的characteristic值,作为在 Subscribing to a Characteristic’s Value的描述,当他们这样做,你是负责发送通知,当他们订阅characteristic的值改变。下面的例子描述。
当一个连接的central订阅你的characteristic的其中一个值的时候,periperhal管理者调用它的代理对象方法 peripheralManager:central:didSubscribeToCharacteristic:

- (void)peripheralManager:(CBPeripheralManager *)peripheral
                  central:(CBCentral *)central
didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
 
    NSLog(@"Central subscribed to characteristic %@", characteristic);
    ...

使用上面的代理方法作为开始返送central更新值的提示。

接下来,得到characteristic的更新的值,和发送它到central 通过调用
CBPeripheralManager类的
updateValue:forCharacteristic:onSubscribedCentrals:

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

当你调用这个方法发送更新的characteristic的值到订阅的centrals,你可以指定哪个central更新最后一个参数。在上面的例子中,如果你指定为nil,所有的连接和订阅的centrald是更新的(包括任何连接的central但没有订阅的)

updateValue:forCharacteristic:onSubscribedCentrals: 方法返回一个Boolean值声明发送订阅是否成功。如果底层的队列用于发送更新后的值是完成的,该方法返回NO。periperhal管理者然后调用它的的代理对象方法 peripheralManagerIsReadyToUpdateSubscribers:,当更多的空间传输队列是可获得的。然后您可以实现这个delegate方法来重新发送这个值,再使用
updateValue:forCharacteristic:onSubscribedCentrals:方法。

Note:使用通知发送一个单一的数据包到订阅的central.当您更新订阅的central,你应该发送整个更新值在单一的通知,通过调用
updateValue:forCharacteristic:onSubscribedCentrals:方法一次。
根据characterisric的值的大小,并不是所有的数据可以传输通过通知。如果发生这种情况,这种情况应该在central端通过调用CBPeripheral类的readValueForCharacteristic:方法c处理,检索整个价值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有头发的猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值