https://developer.apple.com/library/prerelease/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/PerformingCommonPeripheralRoleTasks/PerformingCommonPeripheralRoleTasks.html#//apple_ref/doc/uid/TP40013257-CH4-SW1
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订阅者
创建外设管理器
myPeripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];
创建的时候,peripheral manager将调用代理对象的peripheralManagerDidUpdateState:方法
构建服务和特征值
services 和 characteristic是树形结构组织的
Services and Characteristics Are Identified by UUIDs
服务和特征值使用uuid标识
CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString: @"180D"];
Create Your Own UUIDs for Custom Services and Characteristics
为services和characteristics创建你自己的UUID
CBUUID *myCustomServiceUUID = [CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"];
Build Your Tree of Services and Characteristics
构建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];
发布服务和特征值
[myPeripheralManager addService:myService];
这里会触发代理消息
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
if (error){
NSLog(@"Error publishing service: %@", [error localizedDescription]);
}
...
如果有异常,请通过error查询。
注意:服务和特征值一旦发布,不能更改。
广播你的服务
[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将可发现并初始化来取得连接。
在连接到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: 方法回应,并提供失败的原因。
通常,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: 方法来获取整个数据。