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

https://developer.apple.com/library/prerelease/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/PerformingCommonPeripheralRoleTasks/PerformingCommonPeripheralRoleTasks.html#//apple_ref/doc/uid/TP40013257-CH4-SW1

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

作为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];
  • 1

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

Setting Up Your Services and Characteristics

构建服务和特征值

services 和 characteristic是树形结构组织的

Services and Characteristics Are Identified by UUIDs

服务和特征值使用uuid标识

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

Create Your Own UUIDs for Custom Services and Characteristics

为services和characteristics创建你自己的UUID

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

Build Your Tree of Services and Characteristics

构建services和characteritics树

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

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

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

创建service

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

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

关联characteristics

myService.characteristics = @[myCharacteristic];
  • 1

Publishing Your Services and Characteristics

发布服务和特征值

[myPeripheralManager addService:myService];
  • 1

这里会触发代理消息

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

...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

Advertising Your Services

广播你的服务

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

例子中参数是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]);
}
...
  • 1
  • 2
  • 3
  • 4
  • 5

当你发出广播后,远程的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]) {
 ...
  • 1
  • 2
  • 3
  • 4

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

if (request.offset > myCharacteristic.value.length) {
    [myPeripheralManager respondToRequest:request
    withResult:CBATTErrorInvalidOffset];
    return;
}
  • 1
  • 2
  • 3
  • 4
  • 5

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

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

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

[myPeripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
  • 1

每次收到的请求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;
  • 1

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

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

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

注意:把多个请求看成一个对待,如果其中有一个请求无法实现,那么所有的请求就将无法实现。同时,调用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);
。。。
  • 1
  • 2
  • 3

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

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

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

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

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

你可能感兴趣的:(iOS蓝牙)