提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、coreBluetooth框架是什么?
- 二、主要组件与功能
- 三、使用步骤
- 1.初始化 CBCentralManager 并设置代理。
- 2.检查并请求蓝牙权限。
- 3.调用 scanForPeripherals(withServices:options:) 方法开始扫描周围BLE设备,可以根据需要指定特定服务的UUID数组。
- 4.在 centralManager(_:didDiscover:advertisementData:rssi:) 代理方法中处理发现的外设及其广播数据。
- 5.连接感兴趣的外设,调用 connect(to:options:) 方法。
- 6.在连接成功后,进一步查询外设中的服务列表和每个服务内的特征列表。
- 7.对特征执行读取、写入操作或设置订阅通知,实现双向数据传输。
- 四、后台模式处理
- 总结
前言
BLE(Bluetooth Low Energy),也称为蓝牙低功耗技术,是蓝牙标准的一个子集,设计用于支持低功耗、短距离无线通信的设备和应用。它主要用于电池供电的小型设备,并在保持连接时消耗极低的能量,适合于物联网(IoT)设备、穿戴式设备、健康监测器、智能家居设备等应用场景。
一、coreBluetooth框架是什么?
CoreBluetooth是Apple在iOS和macOS操作系统中提供的一个框架,用于支持蓝牙低功耗(Bluetooth Low Energy, BLE)技术的开发. BLE 协议栈在物理层和链路层之上使用了 Generic Attribute Profile (GATT) 来定义设备如何通过服务(Services)、特性(Characteristics)和描述符(Descriptors)进行数据交换
二、主要组件与功能
1.CBCentralManager(中心设备角色)
CBCentralManager 是开发者用来初始化中心设备的角色,它可以扫描、连接到周围的BLE外设,并与之交互。需要遵守 CBCentralManagerDelegate 协议来接收状态更新(如蓝牙状态改变、发现新设备等)以及处理连接事件。
2.CBPeripheral(外设角色)
CBPeripheral 表示一个被发现或已连接的外部BLE设备,它有服务(Service)和特征(Characteristic)的概念。开发者需要遵守 CBPeripheralDelegate 协议来获取特定外设的服务及特征读写的结果。
3.服务(Service)
在BLE设备上定义的一组相关特征集合,每个服务有一个独特的UUID标识符。
4.特征(Characteristic)
特征包含具体的数据或者描述信息,可以设置为可读、可写、可订阅通知(notify)等属性,也通过UUID唯一识别。特征数据是实际通信的内容载体,例如传感器读数、设备配置参数等。
三、使用步骤
1.初始化 CBCentralManager 并设置代理。
-(id)init{
self=[super init];
if (self) {
dispatch_queue_t searchQueue = dispatch_queue_create("com.myProject.manager", DISPATCH_QUEUE_SERIAL);
manager = [[CBCentralManager alloc]initWithDelegate:self queue:searchQueue];
}
return self;
}
2.检查并请求蓝牙权限。
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
//当你创建并初始化一个CBCentralManager实例后,系统会立即更新蓝牙状态,并通过这个代理方法告知你的应用当前蓝牙服务是否可用。
}
3.调用 scanForPeripherals(withServices:options:) 方法开始扫描周围BLE设备,可以根据需要指定特定服务的UUID数组。
[manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"0xFFD0"]] options:nil];
特别注意的是,当应用处于后台,必须指定特定服务的UUID数组才能扫描到外设
4.在 centralManager(_:didDiscover:advertisementData:rssi:) 代理方法中处理发现的外设及其广播数据。
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI{
//advertisementData 广播数据
//RSSI 信号
//如果广播数据中包含设备名字,则peripheral.name可以获取到预设的设备名
}
其中,advertisementData可包含的字段:
kCBAdvDataIsConnectable 是否可被连接
kCBAdvDataManufacturerData 制造商特定数据,比如设备类型、固件版本、状态信息、MAC地址或其他特定于应用的数据等
5.连接感兴趣的外设,调用 connect(to:options:) 方法。
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI{
[manager connectPeripheral:peripheral options:nil];
}
通常需要将peripheral保存在一个全局可访问的位置。这是因为从扫描到连接的过程中,可能会有多次回调,并且在连接成功后还需要进一步与该外设进行交互,如读取服务和特征、写入数据或订阅通知等
6.在连接成功后,进一步查询外设中的服务列表和每个服务内的特征列表。
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
peripheraldelegate = self;
[peripheral discoverServices:nil];
}
连接成功,查询设备的服务列表
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
NSLog(@"--------发现服务,扫描服务-----------");
if (error) {
NSLog(@"Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
return;
}
[curPeripheral.services enumerateObjectsUsingBlock:^(CBService * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[curPeripheral discoverCharacteristics:nil forService:obj];
//暂时缺少服务ID判定(因为当前项目 设备-服务-特征值均唯一)
}];
}
枚举服务列表,查询服务下的特征值
7.对特征执行读取、写入操作或设置订阅通知,实现双向数据传输。
订阅通知:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
[curService.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
CBCharacteristic * character = (CBCharacteristic*)obj;
CBUUID *charactUUID = character.UUID;
if([charactUUID isEqual:[CBUUID UUIDWithString:@"0xFFD1"]]){
[curPeripheral setNotifyValue:YES forCharacteristic:curCharacteristic];
}
}
订阅状态监听:
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if(error){
NSLog(@"订阅失败 原因:%@",error);
}else{
NSMutableDictionary *peripheralInfo = [NSMutableDictionary dictionaryWithDictionary:[connectedPeripherals objectForKey:peripheral.identifier]];
CBPeripheral *curPeripheral = [peripheralInfo objectForKey:@"peripheral"];
NSLog(@"特征值订阅:");
[self obtainPeripheralPowerWithPeripheral:curPeripheral];//读取蓝牙设备电池电量(0xD4)]
}
}
特征值写入:
-(void)writeCharacteristic:(CBPeripheral *)peripheral
characteristic:(CBCharacteristic *)characteristic
value:(NSData *)value
{
//只有 characteristic.properties 有write的权限才可以写
if((characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) != 0){
[peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
}else{
[peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
特征值变更的监听:(当成功订阅通知,而且特征值发生改变的时候或者主动调用readValueForCharacteristic方法就会触发)
/*!
* @method peripheral:didUpdateValueForCharacteristic:error:
*
* @param peripheral The peripheral providing this information.
* @param characteristic A <code>CBCharacteristic</code> object.
* @param error If an error occurred, the cause of the failure.
*
* @discussion This method is invoked after a @link readValueForCharacteristic: @/link call, or upon receipt of a notification/indication.
*/
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
特征值的读写应该处理好逻辑,避免循环读写过度的损耗电量
四、后台模式处理
1. 在项目的Info.plist文件中添加以下键值对以请求后台模式权限:
添加键 UIBackgroundModes 为数组类型,并在其下增加一个字符串元素,内容为 “bluetooth-central”(对于中心设备角色)或者 “bluetooth-peripheral”(对于外设设备角色)。
2.前台和后台扫描的确有不同的工作模式和限制:
前台扫描: 当应用处于前台时,CBCentralManager的scanForPeripheralsWithServices:options:方法可以接受serviceUUIDs参数为nil。这意味着你的中心设备(central)会广播搜索所有外设(peripheral),并监听到任何发送广播数据的蓝牙外设。
后台扫描: 为了节省电池电量以及确保用户隐私,在iOS系统中,如果应用程序希望在后台持续扫描蓝牙外设,则必须指定至少一个服务UUID数组。这是因为后台扫描受到更严格的资源和权限控制,系统不允许无目标的大范围后台扫描以减少功耗和避免不必要的网络活动。因此,当你在后台执行扫描时,通常需要提供一个包含你感兴趣的特定服务UUID的数组,这样系统才会为你找到具有这些服务的外设。
总结
本文仅仅简单介绍了coreBluetooth框架的应用和前后台模式下的差异,如有错误,欢迎指出。