前言
笔者在上篇文章蓝牙基础知识中介绍了蓝牙的基础知识。
在本文中,笔者会记录下 iOS 蓝牙的基本使用相关内容。
蓝牙的基本使用内容笔者主要会围绕如下几部分展开,本文主要会记录1-4的内容。
- 创建蓝牙中心管理者;
- 扫描发现外围设备;
- 连接设备;
- 发现服务,发现特征值;
- 给指定外设的特征添加监听
- 给已连接设备发送数据
- 外设给中心设备通知数据时,监听过指定外设特征值的方法回调。
创建蓝牙中心管理者需要使用Core Bluetooth framework,我们需要在项目中导入 #import <CoreBluetooth/CoreBluetooth.h> 头文件。
使用蓝牙相关功能,我们需要遵守 <CBCentralManagerDelegate, CBPeripheralDelegate> 等代理,并实现相关代理方法。
1. 创建蓝牙中心管理者
我们可以使用如下代码创建一个指定队列和options的蓝牙中心管理者。
目前笔者指定options的作用是,当我们的App没有打开蓝牙的时候,苹果会帮我们弹出没有打开蓝牙,需要跳转到蓝牙设置界面的弹框。
@property (nonatomic, strong) CBCentralManager *centerManager;
dispatch_queue_t queue = dispatch_queue_create("com.qishare.ios.wyw.bluetooth", DISPATCH_QUEUE_CONCURRENT);
NSDictionary *options = @{
CBCentralManagerOptionShowPowerAlertKey: @(YES),
};
_centerManager = [[CBCentralManager alloc] initWithDelegate:self queue:queue options:options];
2. 扫描发现外围设备
我们可以使用如下代码对指定服务的外设进行扫描,并且可以使用 options 参数控制扫描指定服务的设备,并且控制不重复扫描已扫描到的设备。
//! 开始扫描蓝牙设备
- (void)startScanBluetoothDevice {
NSMutableArray <CBUUID *>*mServices = [NSMutableArray arrayWithCapacity:1];
CBUUID *uuid1 = [CBUUID UUIDWithString:kServiceScanUUID];
[mServices addObject:uuid1];
NSArray <CBUUID *>*services = [mServices copy];
// CBCentralManagerScanOptionAllowDuplicatesKey 的值为NO用于不重复扫描已扫描到的设备
NSDictionary *options = @{CBCentralManagerScanOptionAllowDuplicatesKey: @(NO),
CBCentralManagerScanOptionSolicitedServiceUUIDsKey: services,
};
[_centerManager scanForPeripheralsWithServices:services options:options];
}
遵守 CBCentralManagerDelegate 实现如下代理方法可以获取到中心管理者,外设、外设广播的数据(包含外设的广播数据、是否可连接等内容)、蓝牙外设的信号强度。
//! 发现了外围设备的回调
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
dispatch_async(dispatch_get_main_queue(), ^{
QiLog(@"设备名称BLE:%@", peripheral.name);
QiLog(@"设备identifier:%@", peripheral.identifier);
QiLog(@"设备广播信息:%@", advertisementData);
});
}
这里提一下后续连接蓝牙设备可能遇到的问题。 [CoreBluetooth] API MISUSE: Cancelling connection for unused peripheral <CBPeripheral: 0x2801ad860, identifier = 6F7DF2B5-BBCB-1394-FF2C-E3D4DEE322E3, name = BLE_BROADCASTER, state = connecting>, Did you forget to keep a reference to it?
处理方式是在上述代理方法中强引用一下peripheral。
_blePeripheral = peripheral;
我们这里简单看下停止扫描蓝牙设备的API,这个API的调用时机主要看业务。
停止扫描外围设备
//! 停止扫描蓝牙设备
- (void)stopScanBluetoothDevice {
[_centerManager stopScan];
}
可以谈一下的是这里的 CBUUID。
苹果文档对CBUUID的说明已经表明,CBUUID 是16比特、32比特或128比特的蓝牙UUID。并且16比特、32比特的UUID会被隐式填充为128位的蓝牙Base UUID。
/*!
* @class CBUUID
*
* @discussion
* A 16-bit, 32-bit, or 128-bit Bluetooth UUID.
* 16-bit and 32-bit UUIDs are implicitly pre-filled with the Bluetooth Base UUID.
*
*/
NS_CLASS_AVAILABLE(10_7, 5_0)
CB_EXTERN_CLASS @interface CBUUID : NSObject <NSCopying>
3. 连接设备
连接设备可以使用如下API就可以让指定中心管理者连接蓝牙外设,后边的options的值,笔者目前传入的nil,后续会再更新文章,阐述options的参数用法。
[_centerManager connectPeripheral:_blePeripheral options:nil];
连接设备成功或失败的回调
//! 连接上了某外设
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
QiLog(@"设备连接成功 %@", peripheral.name);
}
//! 连接某外设失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
QiLog(@"连接失败,错误信息:%@", error);
}
断开连接设备
if (peripheral.state == CBPeripheralStateConnected) {
[_centerManager cancelPeripheralConnection:_blePeripheral];
}
断开蓝牙设备的结果可从如下代理方法中确定。
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
4. 发现服务 发现特征值
当我们连接了设备之后,我们就可以使用如下API发现设备的服务信息、发现设备的特征值信息。
NSMutableArray <CBUUID *>*mServices = [NSMutableArray arrayWithCapacity:1];
CBUUID *uuid1 = [CBUUID UUIDWithString:kServiceScanUUID];
[mServices addObject:uuid1];
NSArray <CBUUID *>*services = [mServices copy];
[_blePeripheral discoverServices:services];
发现服务、发现特征值的回调方法
我们在遵守了 CBPeripheralDelegate 并且实现了如下代理方法后,便可以指定发现指定服务、指定特征值的蓝牙外设,然后,我可以使用一些 API 如 - (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic;
开启外设实例对象的指定特征值的监听,使得特定外设的特征值更新的时候,我们 App 层面可以感知到。
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
if (!error && peripheral.services) {
for (CBService *service in peripheral.services) {
NSArray<CBUUID *> *characteristics = nil;
characteristics = @[[CBUUID UUIDWithString:characteristicUUIDWrite], [CBUUID UUIDWithString:characteristicUUIDIndicate]];
[peripheral discoverCharacteristics:characteristics forService:service];
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
}
}
当我们扫描完设备的服务特征并监听特定设备的特征值后,就可以进行后续的给蓝牙外设备写入数据并且感知蓝牙外设是否收数据成功,及更多交互的内容。后续的内容,笔者将在下一篇文章中记录。