蓝牙

随着智能家具、智能穿戴等的兴起,蓝牙开发应用越来越广泛,有关蓝牙方面的问题,今天就给大家进行详细的讲解,想要了解蓝牙在iOS开发中的具体实现,首先我们必须知道什么是蓝牙?

蓝牙概念:

随着蓝牙低功耗技术BLE(Bluetooth Low Energy)的发展,蓝牙技术正在一步步成熟,如今的大部分移动设备都配备有蓝牙4.0,相比之前的蓝牙技术耗电量大大降低。从ios的发展史也不难看出苹果目前对蓝牙技术也是越来越关注,例如苹果于2013年9月发布的iOS7就配备了iBeacon技术,这项技术完全基于蓝牙传输。但是众所周知苹果的设备对于权限要求也是比较高的,因此在iOS中并不能像Android一样随意使用蓝牙进行文件传输(除非你已经越狱)。知道什么是蓝牙之后,那么在iOS中进行蓝牙传输应用开发常用的框架有哪几种呢?

蓝牙在开发中的框架

GameKit.framework:iOS7之前的蓝牙通讯框架,从iOS7开始过期,但是目前多数应用还是基于此框架。

MultipeerConnectivity.framework:iOS7开始引入的新的蓝牙通讯开发框架,用于取代GameKit。

CoreBluetooth.framework:功能强大的蓝牙开发框架,要求设备必须支持蓝牙4.0。

因为GameKit.framework和MultipeerConnectivity.framework框架都只能在iOS设备之间进行数据传输,这个框架最大的特点就是完全基于BLE4.0标准并且支持非iOS设备。当前BLE应用相当广泛,不再仅仅是两个设备之间的数据传输,它还有很多其他应用市场,例如室内定位、无线支付、智能家居等等,这也使得CoreBluetooth成为当前最热门的蓝牙技术。

今天我们只讲目前使用最多的一种蓝牙框架CoreBluetooth.framework,在做蓝牙开发之前,最好先了解一些概念:

外围设备和中央设备在CoreBluetooth中使用CBPeripheralManager和CBCentralManager表示。

CBPeripheralManager:外围设备通常用于发布服务、生成数据、保存数据。外围设备发布并广播服务,告诉周围的中央设备它的可用服务和特征。

CBCentralManager:中央设备使用外围设备的数据。中央设备扫描到外围设备后会就会试图建立连接,一旦连接成功就可以使用这些服务和特征。

外围设备和中央设备之间交互的桥梁是服务(CBService)和特征(CBCharacteristic),二者都有一个唯一的标识UUID(CBUUID类型)来唯一确定一个服务或者特征,每个服务可以拥有多个特征,下面是他们之间的关系:

服务(services):蓝牙外设对外广播的必定会有一个服务,可能也有多个,服务下面包含着一些特征,服务可以理解成一个模块的窗口;

特征(characteristic):存在于服务下面的,一个服务下面也可以存在多个特征,特征可以理解成具体实现功能的窗口,一般特征都会有value,也就是特征值,特征是与外界交互的最小单位;

UUID:可以理解成蓝牙上的唯一标识符(硬件上肯定不是这个意思,但是这样理解便于我们开发),为了区分不同的服务和特征,或者给服务和特征取名字,我们就用UUID来代表服务和特征。

蓝牙连接可以大致分为以下几个步骤

1.创建一个Central Manage中心设备管理器

2.搜索外围设备

3.连接外围设备

4.获得外围设备的服务

5.获得服务的特征

6.从外围设备读数据

7.给外围设备发送数据

首先我们先导入系统的BLE的框架 #import <CoreBluetooth/CoreBluetooth.h>

必须遵守2个协议<CBCentralManagerDelegate, CBPeripheralDelegate>

/*中心管理者*/

@property(strong,nonatomic)CBCentralManager *CM;

/*连接到的外设*/

@property (nonatomic, strong)CBPeripheral *peripheral;

//1.创建一个Central Manage中心设备管理器并设置当前控制器视图为代理

_CM = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

#pragma mark - CBCentralManager代理方法

//2.中心服务器状态更新后

- (void)centralManagerDidUpdateState:(CBCentralManager *)central {

NSString *message = nil;

switch (central.state) {

case CBManagerStateUnknown:

message = @"该设备蓝牙状态未知,请检查系统设置";

break;

case CBManagerStateResetting:

message = @"该设备不支持蓝牙功能,请检查系统设置";

break;

case CBManagerStateUnsupported:

message = @"该设备不支持蓝牙功能,请检查系统设置";

break;

case CBManagerStateUnauthorized:

message = @"该设备蓝牙未授权,请检查系统设置";

break;

case CBManagerStatePoweredOff:

message = @"该设备尚未打开蓝牙,请在设置中打开";

break;

case CBManagerStatePoweredOn:

message = @"蓝牙已经成功开启";

//开始扫描外围设备

[self startScanPeripheralsWithServices];

break;

default:

message = @"该设备不支持蓝牙功能或者该设备尚未打开蓝牙,请检查系统设置";

break;

}

if(message != nil && message.length != 0) {

[MBProgressHUD showSuccess:message toView:nil];

}

}

#pragma mark - 扫描外围设备单独写一个方法

//扫描外围设备方法

- (void)startScanPeripheralsWithServices {

//NO表示不会重复搜索已经搜索到的设备

NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];

//当得知蓝牙为开启状态时,直接搜索周围的蓝牙设备

//开始搜索蓝牙

[self.CM scanForPeripheralsWithServices:nil options:options];

}

//2.发现外设后调用的方法

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {

/**

* 第一个参数:中心管理者

* 第二个参数:外围设备

* 第三个参数:外围设备携带的数据

* 第四个参数:外围设备发出的蓝牙信号强度

**/

//在此处对我们的advertisementData(外围设备携带的广播数据)进行一些处理

//把广播包advertisementData里的参数kCBAdvDataManufacturerData数据拼接Mac地址

NSString *facturerDataString = [[[[advertisementData[@"kCBAdvDataManufacturerData"] description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];

NSString *macAddress = [facturerDataString substringFromIndex:4];

/**

** 需要对连接到的外围设备进行过滤(外围设备的名称或者Mac地址或者能区分的标示UUID都可以)

** 1.信号强度(40以上才连接,80以上连接)

** 2.通过设备名(设备字符串前缀是 OBand)

** 我们的过滤规则自定义:譬如:有******前缀并且信号强度大于**

** if ([peripheral.name hasPrefix:@"Tv221u-169E665D"])

** 通过打印,我们知道RSSI一般是带-的

**/

//此处设置需要的过滤规则

if ([macAddress isEqualToString:@"04a3169e665d"]) {

[self.CM stopScan];

//通常通过过滤,我们会得到一些外设,然后将外围设备储存到我们的可变数组中

//将符合要求的设备进行数据持久化,以便下面连接的时候使用

self.peripheral = peripheral;

//发现完之后就是进行连接

[self.CM connectPeripheral:self.peripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];

}

}

//3.连接外围设备

//中心管理者连接外围设备成功

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {

/**

* 第一个参数:中心管理者

* 第二个参数:外围设备

**/

//连接成功后,设置外围设备的代理

self.peripheral.delegate = self;

//中心管理者连接外围设备成功后停止扫以达到节省电量的目的

[self.CM stopScan];

//外围设备发现服务,传nil代表不过滤

//这里会触发外围设备的代理方法

//- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error

[self.peripheral discoverServices:nil];

}

//外围设备连接失败

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {

[MBProgressHUD showSuccess:@"外围设备连接失败" toView:nil];

}

//丢失连接

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {

[MBProgressHUD showSuccess:@"外围设备丢失连接" toView:nil];

}

//4.获得外围设备的服务

#pragma mark - CBPeripheral Delegate

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error  {

if (error) {

[MBProgressHUD showSuccess:@"搜索服务时发生错误" toView:nil];

DLog(@"搜索服务%@时发生错误:%@", peripheral.name, [error localizedDescription]);

return;

}

if ([peripheral isEqual:self.peripheral]) {

//便利获得的外围设备的服务

for (CBService *services in peripheral.services) {

[peripheral discoverCharacteristics:nil forService:services];

}

}

}

//5.获得服务的特征值

//发现外围设备服务里的特征值的时候调用的代理方法(这个是比较重要的方法,你在这里可以通过事先知道的UUID找到你需要的特征值、订阅特征,或者这里写入数据给特征也可以)

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error {

if (error) {

[MBProgressHUD showSuccess:@"搜索特征时发生错误" toView:nil];

DLog(@"搜索特征%@时发生错误:%@", service.UUID, [error localizedDescription]);

return;

}

if ([peripheral isEqual:self.peripheral]) {

//新建特征值数组

NSArray *characteristics = [service characteristics];

CBCharacteristic *characteristic;

 if ([[service UUID] isEqual:[CBUUID UUIDWithString:@"aaaaaaaaaa"]]) {

for (characteristic in characteristics) {

//发现特征

if ([[characteristic UUID] isEqual:[CBUUID UUIDWithString:@"bbbbbbbbbbb"]]) {

//记录特征

_cbchar = characteristic;

}

}

}

}

}

//订阅的特征值发生改变会调用此方法

- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {

NSData *data = characteristic.value;

NSString *characteristicValue = [[[[characteristic.value description] stringByReplacingOccurrencesOfString:@"<" withString:@""] stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];

//根据蓝牙响应的数据去判断向特征下的蓝牙发送命令

if ([str isEqualToString:@"c c c c c c c c c c"]) {

if ([_cbchar.UUID isEqual:[CBUUID UUIDWithString:@"FFE9"]]) {

NSMutableData *data = [[NSMutableData alloc] initWithCapacity:0];

int8_t byte0 = 0x7B;

[data appendBytes:&byte0 length:sizeof(byte0)];

int8_t byte1 = 0x00;

[data appendBytes:&byte1 length:sizeof(byte1)];

int8_t byte2 = 0x00;

[data appendBytes:&byte2 length:sizeof(byte2)];

int8_t byte3 = 0x01;

[data appendBytes:&byte3 length:sizeof(byte3)];

int8_t byte4 = 0x00;

[data appendBytes:&byte4 length:sizeof(byte4)];

int8_t byte5 = 0xFF;

[data appendBytes:&byte5 length:sizeof(byte5)];

int8_t byte6 = 0x7D;

[data appendBytes:&byte6 length:sizeof(byte6)];

//发送命令

[_peripheral writeValue:data forCharacteristic:_cbchar type:CBCharacteristicWriteWithResponse];

}

}else if ([str isEqualToString:@"dddddddddddd"]) {

if ([_cbchar.UUID isEqual:[CBUUID UUIDWithString:@"1111111111"]]) {

NSMutableData *data = [[NSMutableData alloc] initWithCapacity:0];

int8_t byte0 = 0x7B;

[data appendBytes:&byte0 length:sizeof(byte0)];

int8_t byte1 = 0x00;

[data appendBytes:&byte1 length:sizeof(byte1)];

int8_t byte2 = 0x00;

[data appendBytes:&byte2 length:sizeof(byte2)];

int8_t byte3 = 0x01;

[data appendBytes:&byte3 length:sizeof(byte3)];

int8_t byte4 = 0x01;

[data appendBytes:&byte4 length:sizeof(byte4)];

int8_t byte5 = 0xFE;

[data appendBytes:&byte5 length:sizeof(byte5)];

int8_t byte6 = 0x7D;

[data appendBytes:&byte6 length:sizeof(byte6)];

//发送命令

[_peripheral writeValue:data forCharacteristic:_cbchar type:CBCharacteristicWriteWithResponse];

}

}

}

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {

if (error) {

DLog(@"Error writing characteristic value: %@",

[error localizedDescription]);

}else {

DLog(@"发送数据成功");

[peripheral readValueForCharacteristic:characteristic];

}

}

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {

if (error) {

DLog(@"更新特征值数据: %@ 错误: %@", characteristic.UUID,[error localizedDescription]);

}

//发送命令

if ([characteristicValue isEqualToString:@"123456789"]) {

//状态

}else if ([characteristicValue isEqualToString:@"987654321"]) {

//状态

}

}

补充说明:一个设备可以拥有不同的服务,一个服务下面可以对应不同的特征值 总体思路就是去找某一个服务下的某一个特征值,发送不同命令去操作 具体命令看硬件那边,iOS 不同安卓,mac地址一开始不像安卓那样直接可以获取,想要获取就要把地址放到广播包advertisementData里

如果有不明白的可以去看苹果官方API,解释的很清楚

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

推荐阅读更多精彩内容