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

基础用法先说明一下,自己创建一个叫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"

@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.搜索中心设备周边的外设:

在上面代理方法的CBCentralManagerStatePoweredOncase下写搜索方法,可以直接把系统方法写到该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扫描选项不利于电池的寿命及程序性能。因此,只在需要的时候使用这个选项以完成特定的任务。
    原文如下:
  • A Boolean value that specifies whether the scan should run without duplicate filtering.
    The value for this key is an NSNumber object. If true, filtering is disabled and a discovery event is generated each time the central receives an advertising packet from the peripheral. Disabling this filtering can have an adverse effect on battery life and should be used only if necessary. If false, multiple discoveries of the same peripheral are coalesced into a single discovery event. If the key is not specified, the default value is false.
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];
        }
     }
  }
}

不知不觉写了这么多,还是分开上下部说吧...
iOS关于蓝牙框架BLE的开发--基础用法(下)

  • 操作特征码后的回调——订阅/读取回调与失败回调
  • 操作特征码后的回调——写入回调与失败回调)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,561评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,218评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,162评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,470评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,550评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,806评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,951评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,712评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,166评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,510评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,643评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,306评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,930评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,745评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,983评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,351评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,509评论 2 348

推荐阅读更多精彩内容

  • 在写这个博客之前,空余时间抽看了近一个月的文档和Demo,系统给的解释很详细,接口也比较实用,唯独有一点,对于设备...
    木易林1阅读 3,349评论 3 4
  • 蓝牙简介 蓝牙( Bluetooth® ):是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离...
    Chefil阅读 2,035评论 2 19
  • 首先进一则广告: 蓝牙技术联盟(Bluetooth SIG)2010年7月7日宣布,正式采纳蓝牙4.0核心规范(B...
    L泽阅读 1,436评论 3 4
  • 备注:下面说到的内容都基于蓝牙4.0标准以上,主要以实践为主。 ~ CoreBluetooth.framework...
    未_漆小七阅读 1,606评论 1 8
  • 星期一 星期二 星期三 一个礼拜总有那么几天,太过情绪化了吧,今晚干了什么呢?就是下课回来的路上一直在考虑要不要去...
    涂小鸦阅读 230评论 0 0