iOS关于蓝牙框架BLE的开发--基础用法(上)

kf111023 2017-05-24 08:10:37
基础用法先说明一下,自己创建一个叫BluetoothManager的单例,在头文件BluetoothManager.h导入系统蓝牙框架,分别创建几个数组,在使用时调用:

#import <CoreBluetooth/CoreBluetooth.h>
@interface BluetoothManager : NSObject
@property (nonatomic, strong) NSMutableArray *findPeripherals; //查找到设备(不包含用户列表里的设备)
@property (nonatomic, strong) NSMutableArray *connectPeripherals; //连接上的设备
@property (nonatomic, strong) CBCentralManager *cbCentralManager;

/**
单例实现方法
*/
+ (BluetoothManager *)share;
@end
在.m文件里面创建单例,并且添加蓝牙模块的协议,并为签订代理:

#import "BluetoothManager.h" <br>
@interface BluetoothManager()<CBCentralManagerDelegate,CBPeripheralDelegate,CBPeripheralManagerDelegate>
@property (nonatomic, strong) NSMutableArray *offlineperipherals;
@property (nonatomic, strong) NSMutableArray *tempLists;
@property (nonatomic, strong) CBPeripheral *peripheral;
@property (nonatomic, strong) CBPeripheralManager *peripheralmanager;
@implementation BlueInfo
@end

@implementation BluetoothManager

//单例生成
+(BluetoothManager *)share {
static BluetoothManager *shareInstance_ = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareInstance_ = [[self alloc] init];
});
return shareInstance_;
}
//初始化中心设备CBCentraManager(管理者)和CBPeripheralManager(外设管理者)
-(id) init {
self = [super init];
if (self) {
_cbCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
_peripheralmanager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
}
return self;
}
-(void) setDelegate:(id<BluetoothManagerDelegate>)delegate
{
if (!_delegate) {
_delegate = delegate;
}
}
下面是中心设备的代理方法,步骤基本要按照下面的走:

1.检查设备蓝牙服务是否打开(可用),关于中心设备的状态central.state有以下6个枚举

- (void) centralManagerDidUpdateState:(CBCentralManager *)central{
BOOL state = false;
NSString *msg = @"";
switch (central.state) {
case CBCentralManagerStatePoweredOn:{
msg = @"Bluetooth is currently powered on";
state = YES;
}
break;
case CBCentralManagerStatePoweredOff:{
msg = @"Bluetooth is currently powered off.";
}
break;
case CBCentralManagerStateUnauthorized:{
msg = @"The application is not authorized to use the Bluetooth Low Energy Central/Client role.";
}
break;
case CBCentralManagerStateUnsupported:{
msg = @"The platform doesn't support the Bluetooth Low Energy Central/Client role.";
}
break;
case CBCentralManagerStateResetting:{
msg = @"The connection with the system service was momentarily lost, update imminent.";
}
break;
case CBCentralManagerStateUnknown:{
msg = @"State unknown, update imminent.";
}
break;
}
}
NSLog(@"%@",msg);
只有在CBCentralManagerStatePoweredOn的状态下,才可以进行下步操作——其他状态,可以根据自身情况做相应的提示高告诉用户;

2.搜索中心设备周边的外设:

在上面代理方法的CBCentralManagerStatePoweredOn的case下写搜索方法,可以直接把系统方法写到该case,此处把系统的搜索方法封装到一个自定义方法,代码更整洁(创建布尔值state,用于标记设备蓝牙成功与否)

- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
BOOL state = false;
NSString *msg = @"";
switch (central.state) {
case CBCentralManagerStatePoweredOn:{
msg = @"Bluetooth is currently powered on";
[self scanDevice];//搜索设备方法
state = YES;
}
break;
// ...... 下面的状态省略......
}
}
下面是搜索设备封装方法:

-(void) scanBlueServes {
NSDictionary *optionsDict = @{CBCentralManagerScanOptionAllowDuplicatesKey:@YES};
[self.cbCentralManager scanForPeripheralsWithServices:nil options:optionsDict];
//CBUUID *uuid = [CBUUID UUIDWithString:@"FFF0"];
//[self.cbCentralManager scanForPeripheralsWithServices:@[uuid] options:optionsDict];

}
方法中,需要注意的是:

注释的那部分代码是用于指定搜索设备特定服务的uuid,nil则无差别搜索;
如果想要改变默认行为,可以指定CBCentralManagerScanOptionAllowDuplicatesKey作为扫描选项。此时,central管理器会在每次收到peripheral端的广告包时都触发一个事件。在某些情况下关闭默认行为很有用处,但记住指定CBCentralManagerScanOptionAllowDuplicatesKey扫描选项不利于电池的寿命及程序性能。因此,只在需要的时候使用这个选项以完成特定的任务。

3.检测到外设后,进入下面的代理方法,连接设备(这里开始加入了一些个人的方法)

当搜索到设备进入这个代理方法:-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI,搜索到多个设备,则此方法多次调用;
由于iOS端禁止获取蓝牙设备Mac地址,所以和硬件工程师协商好,将Mac地址放到指定广播包里,从此方法中advertisementData以NSData传过来,而这个key是苹果指定的,不能乱写;
(余下的除了'central'分别是搜索到的设备:peripheral,设备信号:RSSI(数据类型参考方法)).
我们可通过外设的名字 peripheral.name或者刚刚说的蓝牙Mac地址来过滤设备,这就需要和硬件开发协商了,这里还做了一步处理,将已经被其他设备连接的外设通过连接状态的判断也过滤掉
if (peripheral.state == CBPeripheralStateConnected) {
return;
}
过滤之后,将设备添加到self.findPeripherals数组中传到界面(通过UITableView)展示出来:
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
if (peripheral.state == CBPeripheralStateConnected) {
return;
}
NSData *data = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"];
if ((peripheral.name && ([peripheral.name hasPrefix:@"BBCare"]) || [self checkMacAddress]){
[self.findPeripherals addObject:peripheral];
}
4.通过界面方法,实现蓝牙类方法;

一般情况下,我们(其实是我)会在tableView通过BluetoothManager单例获取到findPeripherals数组,展示到界面,然后在tableview的协议方法:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
里面获取要连接的设备peripheral,传入如下方法实现连接设备:
[[BluetoothManager share].cbCentralManager connectPeripheral:peripheral options:nil];

5.连接后的处理(划重点了!)

连接失败:
有时候,蓝牙搜索到设备时设备还在,点击连接时设备不在附近或没电(就不详细说了),类似这些情况,连接会失败,会进入如下方法,NSLog一下,会打印出具体的失败原因:
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@">>>连接到名称为(%@)的设备-失败,原因:%@",[peripheral name],[error localizedDescription]);
}
连接成功后,签订外设代理方法
别以为连接成功了数据就会自己找你了,并不是,后面还有不少的坑呢.
首先,连接成功后,会进入另外一个代理方法",这里我把连接的设备添加进self.connectperipherals数组以备不时之需,然后该设备需签订peripheral的代理,然后开始一起去了解外设的代理方法吧
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@">>>连接到设备(%@ >> %@)-- 成功",peripheral.name,peripheral.identifier.UUIDString);
if (self.connectperipherals == nil) {
self.connectperipherals = [NSMutableArray array];
}
if (![self.connectperipherals containsObject:peripheral]) {
[self.connectperipherals addObject:peripheral];
}
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
}
发现外设服务CBService
来到这里,就要打开硬件工程师写给你的硬件对接的文档,看看他们给你的设备里面有什么服务了
外设一般都会有几个服务,每个服务都会有几个特征,服务和特征都是用不同的 UUID 来标识,每个特征的 properties 是不同的,就是说有不同的功能属性后面会说到,不清楚的话,可以谷歌一下 "蓝牙","服务","特征码"等关键字;
而peripheral.services就是一个存有所有服务码的数组,forin一下,看看里面有没有你需要的?当然,代码里面的那个特征码的UUID@"FFF1"是我乱写的,大家可以根据自己的硬件去搜索特征码,用到的方法是:
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:@"FFF1"]] forService:service];
//完整代码
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
NSLog(@">>>扫描到服务:%@",peripheral.services);
if (error){
NSLog(@">>>发现服务 %@ 错误: %@", peripheral.name, [error localizedDescription]);
return;
}
for (CBService *service in peripheral.services) {
NSLog(@">>>扫描到服务 = %@",service.UUID);
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:@"FFF1"]] forService:service];
}
}
发现外设服务中的特征码CBCharacteristic
刚也说到,和服务类似,特征码也是以不同的 UUID 来标识,我们要对蓝牙的操作,其实就是对这些特征码搞事情了特征码的属性是一个枚举类型:
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
CBCharacteristicPropertyBroadcast = 0x01,
CBCharacteristicPropertyRead = 0x02,
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
CBCharacteristicPropertyWrite = 0x08,
CBCharacteristicPropertyNotify = 0x10,
CBCharacteristicPropertyIndicate = 0x20,
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
CBCharacteristicPropertyExtendedProperties = 0x80,
CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200
};
在我的项目中用到的分别是:

CBCharacteristicPropertyWrite, //可写入
CBCharacteristicPropertyWriteWithoutResponse, //可写入并带回执
CBCharacteristicPropertyRead, //可读
CBCharacteristicPropertyNotify, //可订阅1
CBCharacteristicPropertyIndicate //可订阅2
会一点英文的同学看看类型后面大概能猜到,第一二个是写入,一个带回执,一个不带;第三个是可读;第四第五个是骚骚不同的两种订阅,具体根据不同的情况使用,其他的好像我也没用到过...不过看后缀的话好像能发现些什么吧?

发现外设的特征码CBCharacteristic代理方法
这也是通过forinservice.characteristics,判断特征码的UUID,对应不同的特征码做不同的操作,比如:
setNotifyValue forCharacteristic //订阅方法`;
writeValue: forCharacteristic: type: //写入方法,其中type类型有无回执
peripheral readValueForCharacteristic: //读取方法
整段的代码如下:

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error) {
NSLog(@"%s,%@",__PRETTY_FUNCTION__,error);
}
else{
for (CBCharacteristic *characteristic in service.characteristics{
NSLog(@"特征码 == %@",characteristic.UUID);
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FFF4"]]){
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FFF3"]]){
[peripheral writeValue:[NSData data] forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
}
}
原文地址http://www.jianshu.com/p/05142fdde025
不知不觉写了这么多,还是分开上下部说吧...
...全文
512 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
kf111023 2017-05-24
  • 打赏
  • 举报
回复
感觉还行吧啊

29,028

社区成员

发帖
与我相关
我的任务
社区描述
主要讨论与iOS相关的软件和技术
社区管理员
  • iOS
  • 大熊猫侯佩
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧