ios蓝牙开发学习笔记(三)peripheral角色的实现

本文转载自:http://blog.csdn.net/swibyn/article/details/52096475

Performing Common Peripheral Role Tasks

peripheral角色的实现

• Start up a peripheral manager object
• Set up services and characteristics on your local peripheral
• Publish your services and characteristics to your device’s local database
• Advertise your services
• Respond to read and write requests from a connected central
• Send updated characteristic values to subscribed centrals

作为peripheral模式使用的操作步骤
1,创建peripheral manager 对象
2,构建services和characteristics
3,发布services和characteristics 到数据库
4,广播你的服务
5,响应读和写请求
6,发送数据给centrals订阅者

Starting Up a Peripheral Manager

创建外设管理器

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

创建的时候,peripheral manager将调用代理对象的peripheralManagerDidUpdateState:方法

Setting Up Your Services and Characteristics

构建服务和特征值

services 和 characteristic是树形结构组织的

服务和特征值使用uuid标识

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

为services和characteristics创建你自己的UUID

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

构建services和characteritics树

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

propeties和permissions的设置决定了这个characteristic是否可读,是否可写,是否可订阅。上例中我们把它设置成可读。

注意:如果你设置value的值,这个值将被缓存,并且properties和permissions将是只读的。因此,如果你希望value是可写的,或value值根据具体情况呈不同的值时,你必须把它设置成nil。这样才能使它被动态赋值。才能响应peripheral manager的请求。

创建service

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

这里的第二个参数设置为YES,标明这个服务值主要的。主要服务体现了主要的功能,并能够被其它服务引用。次要服务只用来描述其引用的服务的相关的信息。比如,心率监控的主要服务用来显示心率数据,这时次要服务可能就用来显示电池数据。

关联characteristics

myService.characteristics = @[myCharacteristic];

Publishing Your Services and Characteristics

发布服务和特征值

[myPeripheralManager addService:myService];

这里会触发代理消息

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

...

如果有异常,请通过error查询。
注意:服务和特征值一旦发布,不能更改。

Advertising Your Services

广播你的服务

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

例子中参数是dictionary,不是array。并且只有一个key。在这里只支持两个key,CBAdvertisementDataLocalNameKey 和 CBAdvertisementDataServiceUUIDsKey。详情参见Advertisement Data Retrieval Keys in CBCentralManagerDelegate Protocol Reference.

当你开始广播你的服务,peripheral manager就会通知代理peripheralManagerDidStartAdvertising:error:
。如果有异常将不会发出广播,并在代理中可查到异常的原因:

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

当你发出广播后,远程的centrals将可发现并初始化来取得连接。

Responding to Read and Write Requests from a Central

在连接到remote centrals之后,你就可以开始接收读或写请求。
当central有读请求时,peripheral manager调用 peripheralManager:didReceiveReadRequest: 代理方法。并把请求信息通过CBATTRequest传过来。
比如当你收到一个读请求时,CBATTRequest对象的属性可以确保设备的数据库中的特征值能匹配central所标记的那个特征值。代理的方法实现类似如下:

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

如果特征值的UUID匹配,下一步就是确保请求所读的数据不越界

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

如果请求的偏移量没有越界,那么设置请求的值

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

设置好返回值之后,使用respondToRequest:withResult: 方法来返回数据,类似如下:

[myPeripheralManager respondToRequest:request withResult:CBATTErrorSuccess];

每次收到的请求peripheralManager:didReceiveReadRequest: 都应该有相应的返回respondToRequest:withResult: 方法。

注意:假如特征值的UUID不匹配,或是读请求由于某些原因无法完成。你也应该调用respondToRequest:withResult: 方法来返回失败结果。失败结果列表参见CBATTError Constants enumeration in Core Bluetooth Constants Reference

写请求的处理也很简单。当central发送请求要求写数据时,peripheral manager调用peripheralManager:didReceiveWriteRequests:代理方法,数据通过参数CBATTRequest传递给你,每一个代表一次写请求,当写请求可被处理,你就可以设置特征值的值,如下:

myCharacteristic.value = request.value;

这里同样需要注意请求的偏移量问题,虽然上例中未体现。

跟读请求相似,每次都要调用respondToRequest:withResult: 方法来回应消息。也就是说,虽然代理中入参是array,可能包含多个CBATTRequest,但是回应时是单个的CBATTRequest对象。你必须传入array中的第一个对象。如下:

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

注意:把多个请求看成一个对待,如果其中有一个请求无法实现,那么所有的请求就将无法实现。同时,调用respondToRequest:withResult: 方法回应,并提供失败的原因。

Sending Updated Characteristic Values to Subscribed Centrals

通常,centrals可以订阅一个或多个特征值,这在Subscribing to a Characteristic’s Value. 中也有描述。这种情况下如果他们订阅的特征值的值有变化,你应该要能够给他们发消息。

当一个central订阅某个特征值,peripheral manager将通知代理peripheralManager:central:didSubscribeToCharacteristic: 方法

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

下一步就是获取更新的值并发送给central.

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

在你调用这个方法来发送数据给订阅者时,你可以在最后一个参数标明哪个central,如上。如果你写nil,所有连接着的有订阅的centrals都将收到信息,当然有连接但没订阅的会被忽略。

updateValue:forCharacteristic:onSubscribedCentrals: 这个方法返回Boolean值,指明数据是否成功发送。如果底层的队列正在传输数据,这个方法就会返回NO。当传输队列重新变为空闲时,则会调用peripheral manager的代理方法peripheralManagerIsReadyToUpdateSubscribers: ,这时你就可以利用这个代理重新发送数据,而不需要重新调用updateValue:forCharacteristic:onSubscribedCentrals: 方法。

注意:使用通知来发送一个数据包给central订阅者。也就是说,当你需要给订阅者更新数据时,你应该在通知中发送整个数据,通过一次调用updateValue:forCharacteristic:onSubscribedCentrals: 方法来实现。局限于特征值数据大小的限制,并不是所有数据都能用通知来传递。这种情况下,应该由central端通过调用CBPeripheral的readValueForCharacteristic: 方法来获取整个数据。

推荐阅读更多精彩内容