前言
这几篇文章里Central 和 Peripheral.您可以理解成核心设备和外部设备.
Service
是指实现一个函数或者功能的设备(或者设备的一部分)的数据采集和相关行为的集合。例如,一个心率监听器的Service
可能包含从监听心率传感器采集的心率数据。
而Service
本身由Characteristic
或者其他被包含的Service
所组成。Characteristic
提供了更多有关Peripheral的Service
中的详细内容。例如,刚才描述的心率Service
中可以包含一个用来描述该设备的心率传感器所记录身体位置的Characteristic
或者包含发送测量心率数据的Characteristic
。
这是概述中的描述.个人理解service就是自己定义的数据包.
在低功耗蓝牙交互中,扮演Central的设备需要执行许多常见的任务 - 例如,发现并连接可用Peripheral,与Peripheral提供的数据交互.相反的,Perpheral设备也执行许多常见,但与Central不同的任务 - 例如,发布并广播service,并且响应与之连接的Central的读
,写
和订阅
的请求.
在本章中,你将会学到在Central端如何使用CB框架来执行最常见的蓝牙低功耗任务.如下的基于代码的样例会帮助你在Central端开发你的app,你将学到如何:
1.启动central manager对象
2.搜索并连接发送广播的Perpheral设备
3.建立连接之后与Peripheral设备进行数据交换
4.向Peripheral设备发送一个characteristic读写请求
5.订阅一个characteristic的值(该值更新会发送通知)
在下一章节中,你将学到用你的本地设备作为外设来开发app.
在这一章节中的代码样例是简单抽象的,你需要在实际开发中做出实际更改,更多关于Central端相关话题 - 包括小提示与技巧以及最佳实践方案包含在如下章节中,Core Bluetooth Background Processing for iOS Apps 和 Best Practices for Interacting with a Remote Peripheral Device.
启动Central Manager
CBCentralManager 对象在CoreBluetooth面向对象中代表本地Central设备,你必须在执行蓝牙低功耗交互之前分配内存并初始化它.你可以调用initWithDelegate:queue:options
方法启动,如下:
myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
在本例中,self设置成代理对象来接收Central端事件.通过指定队列为nil,central manager指定使用主队列.
当你创建一个central manager
,central manager
调用centralManagerDidUpdateState:
代理方法.你必须实现该代理方法来确保低功耗蓝牙设备在Central设备中可用.更多关于代理方法的信息
发现广播中的Peripheral设备
Central端的首要执行任务是发现你的app可以连接的peripheral设备.在前面提到的”Central 发现并连接广播中的 Peripheral”一节中,peripheral端通过广播来让其他设备监听到他的存在.你可以调用scanForPeripheralsWithServices:options:
方法:
[myCentralManager scanForPeripheralsWithServices:nil options:nil];
Note:如果第一个参数为nil,central manager 返回所有检测到可用的peripheral.实际上,你应该制定一个CBUUID对象的数组,每一个代表一个peripheral广播的service的通用唯一标识符(UUID).当你制定了service UUIDs的数组,central manager 只返回发送这些services的peripheral,允许你只扫描你感兴趣的设备.UUIDs和CBUUID对象的更多细节在这里.
在调用scanForPeriperalsWithServices:options:
来发现可用peripherals后,central manager每扫描到一个peripheral就会调用centralManager:didDiscoverPeripheral:advertisementDate:RSSI:
.返回的是CBPeripheral对象.如下所示,你可以实现该代理方法来列举发现的peripheral:
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@", peripheral.name);
...
当发现想要连接的peripheral,停止扫描其他设备来节约电量.
[myCentralManager stopScan];
NSLog(@"Scanning stopped");
发现Peripheral后连接它
在找到了你需要连接的设备后,调用connectPeripheral:options:发送连接请求.简单的调用这个方法并指定你想连接的设备,像这样:
[myCentralManager connectPeripheral:peripheral options:nil];
假设连接成功,central manager 调用centralManager:didConnectPeripheral:
代理方法,你可以在这里打印连接情况:
- (void)centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Peripheral connected");
...
在开始和Peripheral交互之前,设置peripheral的代理来确保他会收到正确的回调:
peripheral.delegate = self;
监听连接的peripheral发送的Service
在与peripheral建立连接后就可以探索(explore)
它的数据.探索的第一步就是发现peripheral广播的可用services.因为peripheral广播的数据量有大小限制,你可能发现一个peripheral有更多未广播出来的services.你可以调用CBPeripheral类的discoverServices来发现peripheral提供的所有services:
[peripheral discoverServices:nil];
Note:实际开发中将不会传nil作为参数,因为一个peripheral可能会包含多个你需要的service,发现全部的service可能会浪费电池寿命并浪费时间.更应该倾向于指定已知要用到的UUIDs.
当指定的service被发现后,peripheral(连接的CBPeripheral对象)调用peripheral:didDiscoverServices:
代理.CB会创建一个存储CBService对象(从peripheral设备上发现的)的数组.你可以实现该代理来访问发现的service:
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
NSLog(@"Discovered service %@", service);
...
}
...
发现Service的Characteristics(特征)
如果你找到了需要的service,下一步就是探索该service提供的所有characteristic.调用CBPeripheral类的discoverCharacteristics:forService:
方法获取它所有的特征值,指定相应的service:
NSLog(@"Discovering characteristics for service %@", interestingService);
[peripheral discoverCharacteristics:nil forService:interestingService];
Note:实际开发中不应该传nil,而是特征值的UUIDs.
peripheral在发现指定service的特征值后调用peripheral:didDiscoverCharacteristicsForService:error:
.CB会创建一个CBCharacteristics数组:
- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
error:(NSError *)error {
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"Discovered characteristic %@", characteristic);
...
}
...
接收Characteristic的值(特征值)
一个特征值是一个简单的值:代表一个peripheral的service许多信息.例如健康体温计发送的温度测量特征值可能会包含一个值:摄氏度值.可以立刻读取它或者订阅
它.
立刻读取特征值
在你找到需要的service的特征值后,使用CBPeripheral类中的readValueForCharacteristic:
NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
[peripheral readValueForCharacteristic:interestingCharacteristic];
在你尝试读取特征值时.peripheral调用peripheral:didUpdateValueForCharacteristic:error:
来接收值,如果成功接收该值,你可以通过特性的属性访问它的值:
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
NSData *data = characteristic.value;
// parse the data as needed
...
Note:不是所有的特征值都是可读的.你可以访问CBCharacteristicPropertyRead属性来查看是否可读.如果你尝试读取一个不可读的值,peripheral:didUpdateValueForCharacteristic:error:会返回对应的错误.
订阅特征值
尽管使用readValueForCharacteristic:来读取特征值有时候会很高效,但这并不是接收一个变化值的最有效的方法.对于大多值是变化的特征值 - 例如在任何时间段的心率变化 - 你应该通过订阅方式接受他们.当你订阅一个特征值时,你会在这个值变化时收到peripheral发出的通知.
调用CBPeripheral类的setNotifyValue:forCharacteristic:
订阅你需要的特征值,指定第一个参数为YES:
[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];
当尝试订阅(或解除)一个特征值,peripheral调用 peripheral:didUpdateNotificationStateForCharacteristic:error:
.如果订阅请求失败,通过如下代理可获取错误信息
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state: %@",
[error localizedDescription]);
}
...
Note:不是所有的特性(characteristic)可配置为订阅.你可以通过访问他的私有变量Characteristic Properties枚举
在你成功绑定特征值之后,peripheral设备会在特征值改变时通知你.每次特征值改变,peripheral调用peripheral:didUpdateValueForCharacteristic:error:
.然后使用前面说过的立刻读取特征值的方法获取特征值.
写入特征值
在某些情况下,写入特征值是有意义的.例如,如果你的app与蓝牙低功耗数字温度计交互,你可能想向温度计提供一个温度值来设置房间温度,如果一个特征值是可写入的,调用CBPeripheral中的writeValue:forCharacteristic:type:
写入一些data(NSData实例变量):
NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic
type:CBCharacteristicWriteWithResponse];
当尝试写入特征值时,可指定你想执行的写入类型.如上例:写入类型被指定为CBCharacteristicWriteWithResponse
,表明着peripheral会在写入成功之后给你响应.更多写入类型请参考CBCharacteristicWriteType枚举
Peripheral通过peripheral:didWriteValueForCharacteristic:error:
响应CBCharacteristicWriteWithResponse写入类型.如果写入失败,可在这里获取错误信息:
- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error writing characteristic value: %@",
[error localizedDescription]);
}
...