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(核心端)更新特征值
本章中的样例简单并抽象,你可能需要在实际开发中做出相应更改.更多外设端的进阶主题-包括小贴士/技巧/最佳实践在随后的章节:
在本例中,self设置成代理对象来接收外设端的事件.当你指定队列为nil时,peripheral manager使用主队列来分发事件.
当创建一个peripheral manager, peripheral 调用
如上图所示,一个本地外设的数据库(存储services和characteristics)是一个树状结构.在本地的外设上建立services和characteristics时必须将他们构造成这个树状结构.第一步就是理解services和characteristics是如何标识的.
CBUUID类提供了可让你在开发中快速处理长UUID的工厂方法.例如,你可以简单的使用
使用命令行工具
然后你可以使用这个UUID通过
当创建了一个可变的characteristic后设置他的
Note:如果你为characteristic指定了一个值,这个值被缓存下来并且它的属性和授权被设置为了可读.如果你需要将characteristic设置为可写的,或者在发布service的生命周期中改变characteristic的值,你必须指定值为nil.如此确保在外设收到核心设备的读写请求时,值是动态变化的
现在你创建了一个可变的characteristic,你可以创建可变的service来联结characteristic,调用
在本例中,第二个参数设置成了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数组来联结它们:
当你调用这个方法发布services时,peripheral manager调用
Note:在你向数据库发布一个service以及相关的characteristic后,这个service被缓存下来并且你不能再改变它
在本例中,NSDictionary的唯一键
当你开始在本地外设中广播数据时,peripheral manager调用
Note:数据广播是”努力”的,因为容量有限并且同时会有多个app广播,更多相关信息请查看
一旦你开始广播数据,远程的核心设备可以发现并且与你建立连接.
当一个连接的核心设备请求读取一个你的characteristic, Peripheral manager调用
举例,当你接收到一个简单的要读取characteristic的请求,从代理中获取的
如果characteristic的UUID匹配成功,下一步要确保这个读取请求要读取的index位置不在你的characteristic值的外界.如下例所示,你可一个使用
假如请求的偏移量成功验证,用本地外设端创建的characteristic值覆盖请求中的characteristic属性值,并且要顾及到读取请求的偏移量:
在设置完值之后,需要向远程核心设备声明请求已成功处理.需调用
在每一次
Note:如果characteristic的UUID不匹配,或因其他原因不能完成读取请求,你无须尝试完成请求.相反的你应该立刻调用
处理核心设备发送的写入请求也很直接.当连接的核心设备向你发送一个或多个characteristic读写请求时,peripheral manager调用
尽管上述例子没有演示,你还要确保在写入characteristic时考虑请求的偏移量属性.
正如回应读取请求一样,在每一次
将多个请求作为单个请求来处理,如果任一请求不能完成,你不应该完成其中的任何一个.相反,立刻调用
当一个连接的核心设备订阅了你的若干characteristic.peripheral manager 调用
使用上面的代理方法来向核心设备发送更新值.
下一步,获得更新的characteristic值并且通过
当你调用该方法时,可以通过最后一个参数来指定你想要更新的订阅characteristic值核心设备.上例中,如果你指定为nil,所有订阅的连接核心设备都将收到更新.
Note:使用通知来向订阅的核心设备发送的一个数据包,也就是说,当你更新一个订阅的核心设备,你应该在一个通知中发送全部更新数据,通过调用一次
不是所有的数据都可以通过通知(notification)传输,这取决于characteristic值的大小.如果数据较大,应该在核心端调用
启动一个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:方法接收全部数据
相关文章推荐
- iOS开发技巧(系列十二:UUID和UDID的区别)
- mui如何增加自定义字体icon图标
- 详解 CALayer 和 UIView 的区别和联系
- iOS 5.0 后UIViewController新增:willMoveToParentViewController和didMoveToParentViewController
- easyUI 中datagrid控件demo,包括选中一行能读取到数据
- UIView和CALayer的区别 说的比较清楚了
- iOS-UItableview 分割线(自定义+原生)方法总结
- UI整理-----part7--模态视图 push&pop
- UI整理-----part6--页面间的传值
- Web UI 自动化测试环境搭建 (转载自51测试天地第三十九期上)
- 在keil mdk中的error: #28: expression must have a constant value
- 在非UI线程处理Bitmap
- 最简单的java GUI
- iOS开发第三方存储库YTKKeyValueStore
- 反编译jd_gui 去掉行号
- 安卓通知栏自定义布局提示(NotificationCompat.Builder)
- Easyui Datagrid 如何实现后台交互显示用户数据列表
- NSNumber和NSValue
- EasyUI datagrid 在ie8和360兼容模式兼容性问题
- 系统数据文件和信息 《APUE》 Chapter-6