Core Bluetooth的简单介绍
Core Bluetooth是基于蓝牙4.0的,它抽象了一组协议用于与蓝牙低功耗设备通讯。由于Core Bluetooth隐藏了蓝牙底层的细节,它使得开发者能够很容易地实现与蓝牙低功耗设备的通讯。
中心和外设设备以及它们的角色在蓝牙通讯中
外设是拥有设备想要的数据。中心是获取外设服务的数据来完成一些特别任务。
中心发现和连接正在广告的外设
外设通过发送广告包来表明自己的存在。中心通过监听和扫描正在广告的外设来获取外设的基本信息,广告包通常包含一些外设的基本数据,例如外设的名称、主要功能。如果中心监听到感兴趣的外设,中心可以连接它。
外设的数据结构
外设的数据结构由服务组成,服务由特征和一些服务数据组成。特征提供比服务更详细的外设数据,中心可以向特征请求读、写、订阅请求。
中心、外设和外设的数据表现
本地中心和远程外设
本地中心在Core Bluetooth用CBCentralManager代表,远程外设用CBPeripheral代表。
远程外设的数据用CBService和CBCharacteristic表示
本地外设和远程中心
本地外设在Core Bluetooth用CBPeripheralManager表示,远程中心用CBCentral表示。
本地外设的数据用CBMutableService和CBMutableCharacteristic表示
执行通用中心角色的任务
1. 创建中心管理者。
2. 发现和连接正在广告的外设设备。
3. 探索外设的数据。
4. 向特征的值发送读、写请求。
5. 订阅特征的值。
创建中心管理者
myCentralManager =
[[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
在这个例子中,self设置为delegate,获取中心角色相关的事件。queue设置为nil,中心管理者使用主队列分发事件。
当你创建中心管理者对象后,中心管理者会调用delegate的centralManagerDidUpdateState:方法。你必须实现这个方法来确保设备支持和可用蓝牙低功耗技术。
发现和连接正在广告的外设设备
[myCentralManager scanForPeripheralsWithServices:nil options:nil];
在这个例子中,设置第一个参数为nil,中心管理者会扫描所有发现的外设。你可以设置一组服务的UUID数组,服务的UUID用CBUUID表示,它是用来标识服务的,是服务的唯一标识。如果设置了UUID数组,中心管理者会扫描这个UUID数组的服务。
每次发现一个外设,中心管理者会调用delegate的centralManager:didDiscoverPeripheral:advertisementData:RSSI:方法。如果要连接这个外设必须强引用它。
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@", peripheral.name);
self.discoveredPeripheral = peripheral;
...
[myCentralManager stopScan];
连接发现的外设
[myCentralManager connectPeripheral:peripheral options:nil];
当连接成功后,中心管理者会调用delegate的centralManager:didConnectPeripheral:方法。在你交互外设之前必须设置外设的delegate以便接收外设事件。
- (void)centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Peripheral connected");
peripheral.delegate = self;
...
发现已连接外设的服务
由于广告包的大小限制,你可能需要发现外设服务来获取更多的服务信息。
[peripheral discoverServices:nil];
在这里,第一个参数设置外nil,表示获取所有外设的服务。由于获取所有服务可能会对电池寿命不利和浪费不必要的时间,你可以提供一组服务的UUID(CBUUID)来获取你感兴趣的服务。
当外设的服务被发现,外设会调用delegate的peripheral:didDiscoverServices:方法。
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
NSLog(@"Discovered service %@", service);
...
}
...
发现服务的特征
NSLog(@"Discovering characteristics for service %@", interestingService);
[peripheral discoverCharacteristics:nil forService:interestingService];
在这里,指定第一个参数为nil,代表发现所有的特征。由于获取所有特征可能会对电池寿命不利和浪费不必要的时间,你可以提供一组特征的UUID(CBUUID)来获取你感兴趣的特征。
当服务的特征被发现后,外设会调用delegate的peripheral:didDiscoverCharacteristicsForServices:error:方法。
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
error:(NSError *)error {
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"Discovered characteristic %@", characteristic);
...
}
...
获取特征的值
获取特征的值有两种方式,读取特征的值和订阅特征的值。读取特征值一般用于静态特征值,订阅特征值一般用于动态特征值,也就是值会随时间发生变化。
读取特征的值
NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
[peripheral readValueForCharacteristic:interestingCharacteristic];
当你成功读取特征的值或者失败,外设会调用delegate的peripheral:didUpdateValueForCharacteristic:error:方法。
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
NSData *data = characteristic.value;
// parse the data as needed
...
并不是所有的特征都是可读的,你可以检查特征的properties属性是否包含CBCharacteristicPropertyRead常量来确定特征可读。
订阅特征的值
[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];
当你订阅或者取消订阅特征的值,外设会调用delegate的peripheral:didUpdateNotificationStateForCharacteristic:error:方法。
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state: %@",
[error localizedDescription]);
}
...
并不是所有的特征都是可订阅的,你可以检查特征的properties属性是否包含CBCharacteristicPropertyNotify或者CBCharacteristicPropertyIndicate常量来确定特征可订阅。
当你成功订阅特征的值,一旦值发生变化,外设设备就会通知你。通过外设的delegate的peripheral:didUpdateValueForCharacteristic:error:方法获取特征的值(你可以实现这个方法和读取特征值一样)。
写特征的值
NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic
type:CBCharacteristicWriteWithResponse];
你可以设置写类型为CBCharacteristicWithResponse或者CBCharacteristicWithoutResponse,如果设置有响应,你可以通过外设的delegate的peripheral:didWriteValueForCharacteristic:error:方法知道写请求是否成功。
- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error writing characteristic value: %@",
[error localizedDescription]);
}
...
并不是所有的特征都是可写的,你可以检查特征的properties属性是否包含CBCharacteristicPropertyWrite或者CBCharacteristicPropertyWriteWithoutResponse常量来确定特征可写。
执行通用外设角色的任务
1. 创建外设管理者。
2. 建立本地外设的服务和特征。
3. 发布服务和特征。
4. 广告服务。
5. 响应连接中心的读、写请求。
6. 发送更新的特征值给订阅中心。
创建外设管理者
myPeripheralManager =
[[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];
在这个例子中,self设置为delegate,获取外设角色相关的事件。queue设置为nil,外设管理者使用主队列分发事件。
当你创建外设管理者对象后,外设管理者会调用delegate的peripheralManagerDidUpdateState:方法。你必须实现这个方法来确保设备支持和可用蓝牙低功耗技术。
建立本地外设的服务和特征
服务和特征都是用UUID标识的
外设的服务和特征都是用蓝牙128bit的UUID标识。在Core Bluetooth框架中,这个标识用CBUUID来表示。虽然并不是所有的UUID被Bluetooth Special Interest Group (SIG)预定义的。SIG预定义了大部分通用蓝牙UUID标识,这些UUID标识可以缩减成16bit表示,例如表示心率服务的180D。它等价于0000180D-0000-1000-8000-00805F9B34FB,这个标识是蓝牙4.0定义的。
CBUUID可以使用上述的16bit标识来创建UUID,剩下的128bit的UUID会被Core Bluetooth填充。
CBUUID *heartRateServiceUUID = [CBUUID UUIDWithString: @"180D"]
创建你自己自定义服务和特征的UUID
你可能会有一些不是SIG预定义的服务和特征,你可以使用终端的命令行工具uuidgen来生成一个128bit的UUID。
$ uuidgen
71DA3FD1-7E10-41C1-B16F-4430B506CDE7
之后,你可以使用这个UUID来创建CBUUID。
CBUUID *myCustomServiceUUID =
[CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"];
构建你的服务和特征树
在你创建服务和特征的CBUUID后,你可以创建CBMutableService和CBMutableCharacteristic。
myCharacteristic =
[[CBMutableCharacteristic alloc] initWithType:myCharacteristicUUID
properties:CBCharacteristicPropertyRead
value:myValue permissions:CBAttributePermissionsReadable];
在你创建CBMutableCharacteristic时,你可以指定properties、value、permission。properties和permission可以指定为可读或者可写又或者可订阅。如果你指定了value的值,这个值会被缓存而且properties和permission会被设置为可读。因此,如果值是可变的或者是可写的,你必须设置value为nil。
myService = [[CBMutableService alloc] initWithType:myServiceUUID primary:YES];
在这个例子中,服务被设置为主要服务,代表外设的主要功能和能被其他服务关联,你也可以设置为次要服务,代表被关联服务上下文的相关次要数据。例如主要的心率服务可能会暴露心率数据,次要的心率服务可能暴露心率传感器在身体的位置。
创建完服务后,你可以关联特征到这个服务。
myService.characteristics = @[myCharacteristic];
发布服务和特征。
在你构建完服务和特征树后,你可以发布它们。
[myPeripheralManager addService:myService];
在你发布服务和特征后,外设管理者会调用delegate的peripheralManager:didAddService:error:方法,你可以获取相关错误从这个方法。
- (void)peripheralManager:(CBPeripheralManager *)peripheral
didAddService:(CBService *)service
error:(NSError *)error {
if (error) {
NSLog(@"Error publishing service: %@", [error localizedDescription]);
}
...
注意:在你发布服务和特征到外设的数据库后,它们会被缓存而且你不能再改变。
广告服务
[myPeripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey :
@[myFirstService.UUID, mySecondService.UUID] }];
当你广告本地外设的数据给正在监听的远程中心后,外设管理者会调用delegate的peripheralManagerDidStartAdvertising:error:方法。
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
error:(NSError *)error {
if (error) {
NSLog(@"Error advertising: %@", [error localizedDescription]);
}
...
一旦你广告服务,远程中心可能发现并连接本地外设。
响应远程外设的读、写请求
当你收到远程中心的读请求,外设管理者会调用delegate的peripheralManager:didReceiveReadRequest:方法。
- (void)peripheralManager:(CBPeripheralManager *)peripheral
didReceiveReadRequest:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqual:myCharacteristic.UUID]) {
...
如果UUID匹配,确保读请求的offset不会超过值的范围。
if (request.offset > myCharacteristic.value.length) {
[myPeripheralManager respondToRequest:request
withResult:CBATTErrorInvalidOffset];
return;
}
如果offset匹配,设置请求的值。
request.value = [myCharacteristic.value
subdataWithRange:NSMakeRange(request.offset,
myCharacteristic.value.length - request.offset)];
如果成功设置了值,响应请求并设置成功result,否则设置失败result。
[myPeripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
注意:不管怎样,一定要响应请求,并设置相应的result。
当你收到远程中心的写请求,外设管理者会调用delegate的peripheralManager:didReceiveWriteRequests:方法。
- (void)peripheralManager:(CBPeripheralManager *)peripheral
didReceiveWriteRequests:(NSArray<CBATTRequest *> *)request {
...
遍历每个请求,检查每个请求是否满足(UUID是否满足,写限权等),如果不满足,马上响应并提供对应失败result。如果满足,设置特征的值。
myCharacteristic.value = request.value;
响应请求时,使用第一个请求,尽管有多个写请求。
[myPeripheralManager respondToRequest:[requests objectAtIndex:0]
withResult:CBATTErrorSuccess];
发送更新的特征值给订阅中心
当你收到远程中心的订阅请求,外设管理者会调用delegate的peripheralManager:central:didSubscribeToCharacteristic:方法。
- (void)peripheralManager:(CBPeripheralManager *)peripheral
central:(CBCentral *)central
didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"Central subscribed to characteristic %@", characteristic);
...
发送更新的值。
NSData *updatedValue = // fetch the characteristic's new value
BOOL didSendValue = [myPeripheralManager updateValue:updatedValue
forCharacteristic:characteristic onSubscribedCentrals:nil];
在这里,设置最后一个参数为nil,代表发送所有订阅的远程中心。你也可以指定一组远程中心。如果底层的派发队列满了,这个方法会返回NO,你可以在外设管理者的delegate的peripheralManagerIsReadyToUpdateSubscribers:方法来重新发送更新值。
注意:你应该只调用一次updateValue:forCharacteristic:onSubscribedCentrals:方法,来发送更新值。尽管由于特征值的大小,可能不能一次性发送更新的值。这种情况下,只能让中心那边处理。通过调用CBPeripheral的readValueForCharacteristic:方法来获取完整的值。
Core Bluetooth后台执行
Core Bluetooth的后台执行分中心角色和外设角色的后台执行。
仅是前台APP
仅是前台APP在进入后台的时候,会停止执行相关蓝牙任务,也不会被系统唤醒来处理相关蓝牙任务直到进入前台。
在中心那边,仅是前台APP在后台时,不能扫描和发现正在广告的外设。在外设那边,不能广告和被连接中心获取动态特征的值。
如果你在后台由于某种原因与连接外设断开连接,你的APP不会立马知道直到进入前台。
充分利用外设连接选项
仅是前台APP在进入后台时,会把相关蓝牙事件进栈到队列中,回到前台后再恢复它们。Core Bluetooth提供一些连接选项在某些事件发生时提示用户是否允许该事件返回前台处理。
你可以在调用CBCentralManager的connectPeripheral:options:方法时使用这些选项。
• CBConnectPeripheralOptionNotifyOnConnectionKey 当APP暂停时,展示成功连接外设提示。
• CBConnectPeripheralOptionNotifyOnDisconnectionKey 当APP暂停时,展示断开连接外设提示。
• CBConnectPeripheralOptionNotifyOnNotificationKey 当APP暂停时,展示收到外设通知提示。
Core Bluetooth后台执行模式
在info.plist文件提供UIBackgroundModes键和一组包含下面字符串的数组。
• bluetooth-central 使用Core Bluetooth框架与蓝牙低功耗设备通讯。
• bluetooth-peripheral 使用Core Bluetooth框架分享数据。
bluetooth-central后台执行模式
如果APP支持bluetooth-central后台执行模式,Core Bluetooth框架允许你在后台发现和连接外设以及探索和交互外设的数据,而且当CBCentralManagerDelegate和CBPeripheralDelegate方法被触发时,系统会唤醒APP来处理这些事件。
记住,即便实现后台模式,后台操作也会与前台不同。
• CBCentralManagerScanOptionAllowDuplicatesKey扫描选项会被避免,接收同一个设备的多个广告包会合并成一个发现事件。
• 后台扫描外设的时间间隔会提高,造成可能会花更多的时间来发现一个外设。
bluetooth-peripheral后台执行模式
如果APP支持bluetooth-peripheral后台执行模式,Core Bluetooth框架允许你在后台被系统唤醒来处理读、写、订阅请求以及广告外设数据包。
记住,即便实现后台模式,后台操作也会与前台不同。
• CBAdvertisementDataLocalNameKey广告本地名会被避免,本地外设名不会被广告。
• 所有在CBAdvertisementDataServiceUUIDsKey下的服务UUID会被放置在特殊的“溢出”区域,远程中心只有明确地扫描它们才能发现该外设。
• 发送广告包的频率会下降。
明智地使用后台处理模式
虽然支持后台处理,但是你应该尽可能处理后台任务。因为蓝牙相关任务会使用设备的无线电广播,这会对设备的电池寿命不利,所以,尽可能减少后台处理蓝牙任务的工作量。APP被系统唤醒来处理蓝牙相关事务也要尽量快速地处理以便能再次进入暂停状态。
这里有一些使用Core Bluetooth后台处理的指导。
• APP应该以会话为基础并提供一个图形界面让用户选择开始和停止分发一些蓝牙事件(连接设备、断开连接、读值、写值)。
• 在被系统唤醒之前,只有10秒钟来完成后台任务。所以尽可能快地完成后台任务以便APP再次回到暂停状态,如果花太多时间可能会被系统中断甚至停止运行。
• 在有不明原因的额外任务能被在后台处理时,APP不应该被系统唤醒来处理该事件。
执行后台长期操作
一些APP可能需要长期的后台操作。例如,与蓝牙低功耗门锁交互的APP可能需要在用户回家时自动开门,在用户离开家时自动关门。尽管在APP没有停止运行的情况下,可以调用CBCentralManager的connectPeripheral:options:方法来重新连接门锁,但是在用户离开家几天后回来,APP是不能重新连接门锁的,因为此时APP已经停止运行了。在这种情形下,APP需要执行长期的后台操作。
状态保存和恢复
Core Bluetooth提供在APP停止运行时保存中心和外设管理者状态并继续执行它们的任务的特性。一旦其中一个任务被完成,系统会重新启动APP并进入后台恢复中心和外设管理者的状态以及处理相关事件。例如之前的门锁例子,当用户离开家几天后回来,系统会监听门锁外设并且发现和重新连接它,次时,系统会重新启动APP来处理中心管理者的delegate的centralManager:didConnectPeripheral:回调方法。
Core Bluetooth支持中心角色、外设角色或者同时两者的状态保存和恢复。当APP将要停止运行时,系统会保存这些信息。如果你有多个管理者,你可以选择哪些管理者需要保存和恢复。
CBCentralManager的状态跟踪。
• 中心管理者扫描的服务以及扫描它们的扫描选项。
• 中心管理者已经连接和尝试连接的外设。
• 中心管理者订阅的特征。
CBPeripheralManager的状态跟踪。
• 外设管理者广告的数据。
• 外设管理者发布的服务和特征。
• 订阅特征值的远程中心。
添加状态保存和恢复的支持
1. (必须)实例化管理者时,指定状态保存和恢复特性。
2. (必须)APP被系统重新启动后,重新实例化管理者。
3. (必须)实现恢复状态的代理方法。
4. (可选)更新管理者初始化进程。
指定状态保存和恢复特性
在实例化管理者时,简单地提供一个APP内唯一的恢复标识,就可以指定状态保存和恢复特性。
myCentralManager =
[[CBCentralManager alloc] initWithDelegate:self queue:nil
options:@{ CBCentralManagerOptionRestoreIdentifierKey:
@"myCentralManagerIdentifier" }];
重新实例化管理者
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSArray *centralManagerIdentifiers =
launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey];
...
实现恢复状态的代理方法
- (void)centralManager:(CBCentralManager *)central
willRestoreState:(NSDictionary *)state {
NSArray *peripherals =
state[CBCentralManagerRestoredStatePeripheralsKey];
...
在这里,你可以重新获取管理者被系统跟踪的状态。
更新管理者初始化进程
如果你的APP在探索连接外设的数据时被系统停止运行。在你恢复管理者后,你可能不确定是否这个过程已经完成了多少,而且你可能想要重新在那个地方继续探索。
例如,你在中心管理者的centralManagerDidUpdateState:方法,检查管理者的状态完成情况,如果还没完成继续这个过程。
NSUInteger serviceUUIDIndex =
[peripheral.services indexOfObjectPassingTest:^BOOL(CBService *obj,
NSUInteger index, BOOL *stop) {
return [obj.UUID isEqual:myServiceUUIDString];
}];
if (serviceUUIDIndex == NSNotFound) {
[peripheral discoverServices:@[myServiceUUIDString]];
...
远程外设通讯的最佳实践
记住无线电的使用和电池的损耗
只有在你需要时扫描外设
如果你已经发现所有你需要的外设,你应该调用中心管理者的stopScan方法停止扫描。
只有在你需要时指定CBCentralManagerScanOptionAllowDuplicatesKey
默认情况下,接收同一个外设的多个广告包会合并为一个发现事件。如果你要实时获取远程外设的信号强度(RSSI值),你可以设置这个扫描选项为YES。不管是否是同一外设,每次接收到广告包都会对应一个发现事件。
明智地探索外设数据
你可能不需要全部的外设的服务或者特征,在这种情况下,发现服务时指定服务UUID数组,发现服务的特征时指定特征的UUID数组。
[peripheral discoverServices:@[firstServiceUUID, secondServiceUUID]];
订阅经常发生变化的特征值
尽可能订阅特征的值,特别是特征的值经常变化。
在你获取所有需要的数据时断开外设的连接
你可以在你不需要连接外设时,断开外设的连接来减少无线电的使用。下面的一些情况你应该断开外设的连接:
• 所有订阅的特征值不再发送通知。
• 拥有所有外设的数据。
在这两种情形,你可以取消所有订阅并断开外设的连接。
[myCentralManager cancelPeripheralConnection:peripheral];
注意:断开连接并不保证在物理上与外设断开连接,因为这个外设可能被该设备的其它APP连接。但是在该APP上是断开连接的。
重新连接外设
使用Core Bluetooth框架,这里有3种方式你可以重新连接外设。
• 获取一组已知外设数组-你过去已经发现或者连接的外设。
• 获取一组当前已经被系统连接的外设数组。
• 扫描和发现外设。
获取一组已知的外设数组
第一次发现外设时,系统会生成一个外设的唯一标识UUID(NSUUID)。你可以使用这个标识和CBCentralManager的retrievePeripheralsWithIdentifiers:方法来获取这个外设并重新连接它。
在APP启动后,你可以调用这个方法来获取外设并重新连接它,如果没有找到匹配的外设,返回的外设数组为空数组,你应该尝试其他两种方式来重新连接外设。
knownPeripherals =
[myCentralManager retrievePeripheralsWithIdentifiers:savedIdentifiers];
如果外设数组不为空,让用户在图形界面选择外设并调用CBCentralManager的connectPeripheral:options:方法连接它,如果这个外设可用,会调用delegate的centralManager:didConnectPeripheral:方法表示重新连接成功。
注意:外设设备可能会因为一些原因不可用。例如,外设设备不在中心附近,而且一些蓝牙低功耗设备可能会使用随机的设备地址(会周期地改变)。因此,即使外设设备在中心附近,设备地址可能在系统最近一次发现后发生改变,此时,你尝试连接的外设设备可能不会对应实际的外设设备。如果你由于地址的改变不能重新连接外设设备,你必须使用scanForPeripheralsWithServices:options:方法来重新发现这个外设。
获取一组被系统连接的外设数组
另一种重新连接外设的方法是检查这个外设是否被系统连接(例如,被该设备的其他APP连接)。你可以调用CBCentralManager的retrieveConnectedPeripheralsWithServices:方法获取系统正在连接的外设设备。
你可以设置一组服务的UUID数组来获取指定的外设设备。假设你获取了你想要连接的外设,你也要在本地调用CBCentralManager的connectPeripheral:options:方法重新连接它并探索和交互这个外设数据。当连接被建立,会调用delegate的centralManager:didConnectPeripheral:方法表示重新连接成功。
建立本地外设设备的最佳实践
广告的注意事项
广告数据大小的限制
当你广告数据,需要考虑广告什么和对应数据的大小。虽然广告数据可以包含各种信息,但是只能广告本地设备名和包含的服务的UUID数组,也就是说,你只能指定CBAdvertisementDataLocalNameKey和CBAdvertisementDataServiceUUIDsKey,否则会获取错误。
在前台时,你只有28byte的常规空间和10byte的“溢出”空间,在常规空间不够使用时,本地设备名会被放置到“溢出”空间。所有的由于常规空间不够而被放置到“溢出”空间的服务的UUID数组,只能被IOS设备明确地扫描它们才能发现该外设。当在后台时,本地设备名不会被广告,所有服务的UUID数组被放置在“溢出”空间。
只有在你需要时才广告数据
当你想要其他设备连接本地外设时,才广告外设数据。当远程中心连接成功后,它们不再需要广告数据包而是会探索和交互本地外设的数据。当你不再需要和其他设备进行任何蓝牙低功耗事务,为了减少无线电广播的使用和提高APP性能以及节约电池的使用,停止广告外设数据。
[myPeripheralManager stopAdvertising];
让用户去决定什么时候广告
什么时候广告可能只有用户自己知道,这可能没有意义当附近没有任何蓝牙低功耗设备还要广告外设数据包。当你的APP经常察觉不到附近的设备时,提供图形界面让用户决定什么时候广告。
设置你的特征
当你创建CBMutableCharacteristic时,你可能需要设置特征的value、properties、permission。
接下来提供执行下面两种任务的指导:
• 允许连接外设订阅特征的值。
• 防止没有匹配的远程中心来获取特征的值。
创建支持通知的特征
myCharacteristic = [[CBMutableCharacteristic alloc]
initWithType:myCharacteristicUUID
properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyNotify
value:nil permissions:CBAttributePermissionsReadable];
请求配对连接来获取敏感数据
emailCharacteristic = [[CBMutableCharacteristic alloc]
initWithType:emailCharacteristicUUID
properties:CBCharacteristicPropertyRead
| CBCharacteristicPropertyNotifyEncryptionRequired
value:nil permissions:CBAttributePermissionsReadEncryptionRequired];
在这个例子中,这个特征的值只能被信任的中心获取。当远程中心想要读取或者订阅这个特征的值,Core Bluetooth会配对本地外设和远程中心来建立安全连接。
例如,如果中心和外设都是iOS设备,两个设备都会接收到一个alert提示设备是否需要配对。中心设备会提示输入文本以便外设设备配对。
完成配对后,外设会认为配对中心是信任设备并允许它获取加密的特征值。