CoreBluetooth框架使用及工作原理介绍
1、准备工作:
新建一个BLE工程之后首先要做的事情就是导入CoreBluetooth库,我们在TARGETS——>Build Phases——>Link Binary With Libraries中搜索添加,如下图所示:
接下来就是在需要使用的时候引入头文件
#import <CoreBluetooth/CoreBluetooth.h>
,并且声明代理:
#import "ZQBluetooth.h"
#import "instructDefine.h"
#import <CoreBluetooth/CoreBluetooth.h>
@interface ZQBluetooth ()
<CBCentralManagerDelegate,
CBPeripheralDelegate>
@property (nonatomic, weak)id<ZQBluetoothDelegate> delegate;
@property (nonatomic,strong) CBCentralManager* centralManager;
@property (nonatomic,strong) CBPeripheral* linkPeripheral; //链接的外设
@property (nonatomic,strong) CBCharacteristic* characteristicTx; //特征Tx
@property (nonatomic,strong) CBCharacteristic* characteristicRx; //特征Rx
@property (nonatomic,strong) CBUUID* uuidService;
@property (nonatomic,strong) CBUUID* uuidCharTx;
@property (nonatomic,strong) CBUUID* uuidCharRx;
@end
可以看到我定义了一些宏,这就是具体你需要连接的外围设备中的服务与特征的UUID了,你的硬件工程师会给你相应的说明文档。
characteristicTx、characteristicRx是其中一个服务的两个特征值,我们就是通过这些特征值和设备做交互的,Tx是读取设备发送给我的数据的,Rx是向设备写入数据发送数据的。
uuidService、uuidCharTx、uuidCharRx分别是服务UUID、Tx特性UUID和Rx特性UUID。
2、创建中心设备管理员对象扫描外设
//初始化 CentralManager
- (void)initBluetoothCentralManager {
self.uuidService = [CBUUID UUIDWithString:SERVICE_UUID];
self.uuidCharTx = [CBUUID UUIDWithString:CHAR_TX_UUID];
self.uuidCharRx = [CBUUID UUIDWithString:CHAR_RX_UUID];
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
//MARK: ---- CBCentralManagerDelegate(中心)
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBManagerStateUnknown: //未知的
break;
case CBManagerStateResetting: //重置
break;
case CBManagerStateUnsupported: //不支持
break;
case CBManagerStateUnauthorized: //未授权
break;
case CBManagerStatePoweredOff: //断开
break;
case CBManagerStatePoweredOn: //链接
{
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[self scanPeripheralWithServices:nil options:options];
}
break;
default:
break;
}
}
[self.centralManager stopScan]; //停止扫描
一般情况下我们会将扫描到的设备添加到一个数组里边,然后建一个TableView来显示我们搜索到的设备对象。
CoreBluetooth有一个搜索到设备之后回调的代理方法,在这个方法中我们可以得到搜索到外设信息,每搜索到一个设备就会调用一次该方法:
/**
已经搜索到外设
@param central 中心设备
@param peripheral 搜索到的外设
@param advertisementData 广告和扫描响应数据
@param RSSI 获取到与设备的距离
*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
NSValue *value_isConnectable = [advertisementData objectForKey:@"kCBAdvDataIsConnectable"];
const char *connectable = [[value_isConnectable description] cStringUsingEncoding:NSUTF8StringEncoding];
NSString *isConnectable = [NSString stringWithFormat:@"%s",connectable];
NSValue *value_localName = [advertisementData objectForKey:@"kCBAdvDataLocalName"];
const char *name = [[value_localName description] cStringUsingEncoding:NSUTF8StringEncoding];
NSString *localName = [NSString stringWithFormat:@"%s",name];
if ([isConnectable boolValue] == YES && [self isNull:localName] == NO && [self isNull:peripheral.name] == NO) {
NSLog(@"搜索到外设==%@==广播数据==%@距离==%.2f",peripheral,advertisementData,[self bluetoothDistanceByRSSI:RSSI]);
NSData *data = advertisementData[@"kCBAdvDataManufacturerData"];
NSLog(@"=========%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
if (_peripherals.count > 0) {
BOOL contains = [_peripherals containsObject:peripheral];
if (!contains) {
[self.peripherals addObject:peripheral];
if (self.delegate && [self.delegate respondsToSelector:@selector(scanPeripheral:)]) {
[self.delegate scanPeripheral:peripheral];
}
}
}
else {
[self.peripherals addObject:peripheral];
if (self.delegate && [self.delegate respondsToSelector:@selector(scanPeripheral:)]) {
[self.delegate scanPeripheral:peripheral];
}
}
}
}
3、连接外设 or 断开外设
当我们做完第二步,获取到了外设信息之后,当然是要开始连接设备了,如果我们使用了TableView,我们可以在cell的点击方法里里调用下边的方法:
//链接外设
- (void)connectPeripheral:(CBPeripheral *)peripheral {
[_centralManager connectPeripheral:peripheral options:nil];// 连接外设 传入你搜索到的目标外设对象
peripheral.delegate = self; //设置外设的代理
}
//断开外设
- (void)disConnectPeripheral:(CBPeripheral *)peripheral {
[_centralManager cancelPeripheralConnection:peripheral];//断开连接
}
连接设备的回调方法:
/**
已经链接成功
@param central 中心设备
@param peripheral 连接的外设
*/
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
_linkPeripheral = peripheral;
if (self.delegate && [self.delegate respondsToSelector:@selector(scanSuccess:)]) {
[self.delegate scanSuccess:peripheral];
}
[self discoverService];
}
/**
链接外设失败
@param central 中心设备
@param peripheral 链接的外设
@param error 失败原因
*/
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
if (self.delegate && [self.delegate respondsToSelector:@selector(scanFailure:)]) {
[self.delegate scanFailure:error];
}
}
4、发现服务与特征
成功的连接到外设之后,当然是要看看它的内部有什么服务和特征了,这样我们才能顺利的与之进行数据的交互。
发现服务: 我们连接成功的时候已经调用了这个方法来发现服务。
//发现服务
- (void)discoverService {
[_linkPeripheral discoverServices:@[[CBUUID UUIDWithString:OTA_SERVICE_UUID]]];
}
我们可以看到这个这个方法的参数是传入一个数组,因为一个外设中可能有很多个服务,但是对于我们有使用价值的可能就呢么一个或者两个,我们可以传入相应的UUID来过滤,比如我现在只传入了一个UUID那么我发现的服务就只有这一个了,如果为nil则发现所有服务。 如果成功的发现了其中的服务系统自动回调:
/**
发现服务
@param peripheral 外设
@param error 发现服务报错
*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error {
if (error) {
NSLog(@"发现服务 error:%@",[error localizedDescription]);
}
//连接成功就遍历这些服务 来发现每个服务中的特征
else {
for (CBService *service in peripheral.services) {
NSLog(@"发现服务 UUID: %@", service.UUID);
if ([service.UUID isEqual:[CBUUID UUIDWithString:SERVICE_UUID]]) {
//调用此方法来发现该服务中的特征值
[peripheral discoverCharacteristics:nil forService:service];
}
}
}
}
发现特征:发现服务之后就该扫描到该服务中的特征了,发现特征值之后系统自动回调下边的方法 ,每发现一个服务都会调用一次该方法:
/**
发现特征回调
@param peripheral 外设
@param service 服务
@param error 错误
*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error {
if (!error) {
//如果成功,我们可以自己写一个方法,查看特征信息等操作。
[self didFindAllCharacteristic:service.characteristics forService:service];
}
else {
NSLog(@"%@",error);
}
}
自己写一个方法来查看所有的特征值:
- (void)didFindAllCharacteristic:(NSArray<CBCharacteristic *> *)characteristics forService:(CBService *)service {
self.characteristicTx = NULL;
self.characteristicRx = NULL;
for (CBCharacteristic *cha in characteristics) {
switch (cha.properties) {
case CBCharacteristicPropertyBroadcast: //广播
break;
case CBCharacteristicPropertyRead: //读
NSLog(@"%@------------读",cha);
break;
case CBCharacteristicPropertyWriteWithoutResponse: //写-没有响应
break;
case CBCharacteristicPropertyWrite: //写
NSLog(@"%@------------写",cha);
break;
case CBCharacteristicPropertyNotify: //通知
NSLog(@"%@------------订阅",cha);
break;
case CBCharacteristicPropertyIndicate: //声明
break;
case CBCharacteristicPropertyAuthenticatedSignedWrites: //通过验证的
break;
case CBCharacteristicPropertyExtendedProperties: //拓展
break;
case CBCharacteristicPropertyNotifyEncryptionRequired: //需要加密的通知
break;
case CBCharacteristicPropertyIndicateEncryptionRequired://需要加密的申明
break;
default:
NSLog(@"%@------------other",cha);
break;
}
if ([[cha UUID] isEqual:_uuidCharTx]) {
_characteristicTx = cha;
//判断相应的UUID来给我们声明的属性赋值
//这个特征只有写入的属性,所有不能为其注册通知
}
if ([[cha UUID] isEqual:_uuidCharRx]) {
_characteristicRx = cha;
//订阅通知
[self.linkPeripheral setNotifyValue:YES forCharacteristic:cha];
//这里要订阅通知,因为这个特征拥有 读 和 订阅 属性,这里注册之后设备通过这个特性值传送给APP的数据就可以收到了。
}
}
}
我们还可以通过这一个方法监听到通知的状态:
//监听到通知的状态
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
if (!error) {
NSLog(@"注册成功!");
}
else {
NSLog(@"注册失败!:%@",error);
}
}
获取到服务与特征之后,就可以开始和设备进行数据的交互了。而整个蓝牙开发过程最复杂的地方,也是最重要的地方就是,数据的交互了。下篇文章再讲
网友评论