IOS关于蓝牙实践

备注:下面说到的内容都基于蓝牙4.0标准以上,主要以实践为主。

~ CoreBluetooth.framework 蓝牙4.0以低功耗著称,也称BLE。

~ Ios蓝牙设备主要是通过设备的服务(server)特征 (Characteristic)来区别以及展示设备功能。


~当然蓝牙设备还分两种角色【 外设、中心设备】:

中心设备:手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用;

外设:蓝牙设备,比如智能手环之类的东西, 会由硬件工程师开发好,并定义好设备提供的服务,每个服务对于的特征,每个特征的属性(只读,只写,通知等等).


下面主要展示中心设备去扫描连接外设,并且通信的过程:

业务:

*蓝牙设备管理器 CBCentralManager *centralManager;

*特征1 CBCharacteristic *rxCharacteristic;//读特征

*特征2 CBCharacteristic *txCharacteristic;//写特征

*搜索到要连接的设备蓝牙 CBPeripheral *peripheral;


连接外设的代码实现流程:

1. 建立中心角色 (CBCentralManager)

2. 中心设备蓝牙状态更新 (centralManagerDidUpdateState)

2. 扫描外设(discover)

3. 连接外设(connect)

4. 扫描外设中的服务和特征(discover)

 .4.1 获取外设的services

.4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值

5. 与外设做数据交互(explore and interact)

6. 订阅Characteristic的通知

7. 断开连接(disconnect)


干货来了:


//创建中心设备管理器并设置当前控制器视图为代理

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

#pragma mark - CBCentralManager代理方法

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

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

switch (central.state) {

case CBPeripheralManagerStatePoweredOn:

LDLog(@"BLE已打开.");

//扫描外围设备

[self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@“服务UUID”]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];

break;

default:

LDLog(@"此设备不支持BLE或未打开蓝牙功能,无法作为外围设备.");

break;

}}


/**

*  发现外围设备

*  @param central          中心设备

*  @param peripheral        外围设备

*  @param advertisementData 特征数据

*  @param RSSI              信号质量(信号强度)

*/

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

LDLog(@"发现外围设备...");

if(!peripheral){return;}

NSData *data = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"];//广播报数据,根据硬件协议进行解析得到自己想要的数据

.......

//停止扫描

[self.centralManager stopScan];

self.peripheral = peripheral;

LDLog(@"开始连接外围设备...");

self.centralManager.delegate = self;

[self.centralManager connectPeripheral:self.peripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];//这里以通知模式为例子(订阅模式...***文章后面会详细说)

}

//连接到外围设备

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

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

LDLog(@"连接外围设备成功!");

//设置外围设备的代理为当前视图控制器

peripheral.delegate=self;

//外围设备开始寻找服务

[peripheral discoverServices:@[[CBUUID UUIDWithString:@"服务UUID"]]];

}}

//连接外围设备失败

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

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

LDLog(@"连接外围设备失败!");

}}

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

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

LDLog(@"断开外围连接设备!");

}}

#pragma mark - CBPeripheral 代理方法

//外围设备寻找到服务后

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

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

LDLog(@"已发现可用服务...");

if(error){

LDLog(@"外围设备寻找服务过程中发生错误,错误信息:%@",error.localizedDescription);

}

//遍历查找到的服务

CBUUID *serviceUUID=[CBUUID UUIDWithString:@“服务”];

CBUUID *txCharacteristicUUID=[CBUUID UUIDWithString:@“特征1”];

CBUUID *rxCharacteristicUUID=[CBUUID UUIDWithString:@“特征2”];

for (CBService *service in peripheral.services) {

if([service.UUID isEqual:serviceUUID]){

//外围设备查找指定服务中的特征

[peripheral discoverCharacteristics:@[txCharacteristicUUID,rxCharacteristicUUID] forService:service];

}}}}


//外围设备寻找到特征后

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

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

LDLog(@"已发现可用特征...");

if (error) {

LDLog(@"外围设备寻找特征过程中发生错误,错误信息:%@",error.localizedDescription);

return;

}

//遍历服务中的特征

CBUUID *serviceUUID = [CBUUID UUIDWithString:@“服务”];

CBUUID *txCharacteristicUUID = [CBUUID UUIDWithString:@“特征1”];

CBUUID *rxCharacteristicUUID = [CBUUID UUIDWithString:@“特征2”];

if ([service.UUID isEqual:serviceUUID]) {

for (CBCharacteristic *characteristic in service.characteristics) {

if ([characteristic.UUID isEqual:txCharacteristicUUID]) {

self.txCharacteristic = characteristic;

}

else if ([characteristic.UUID isEqual:rxCharacteristicUUID]){

self.rxCharacteristic = characteristic;

[peripheral setNotifyValue:YES forCharacteristic:self.rxCharacteristic];

//通知

/*找到特征后设置外围设备为已通知状态(订阅特征):

*1.调用此方法会触发代理方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error

*2.调用此方法会触发外围设备的订阅代理方法

*/

//读取

//                [peripheral readValueForCharacteristic:characteristic];

//                    if(characteristic.value){

//                    NSString *value=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];

//                    LDLog(@"读取到特征值:%@",value);

//                }

}}}}}


//特征值被更新后

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

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

LDLog(@"收到特征更新通知...");

if (error) {

LDLog(@"更新通知状态时发生错误,错误信息:%@",error.localizedDescription);

}

//给特征值设置新的值

CBUUID *txCharacteristicUUID=[CBUUID UUIDWithString:@“特征1”];

CBUUID *rxCharacteristicUUID=[CBUUID UUIDWithString:@“特征2”];

if ([characteristic.UUID isEqual:rxCharacteristicUUID]) {

if (characteristic.isNotifying) {

[self XXX]; // 这里是你特征值设置完状态更新后,你可以做一些你的操作...具体根据您的协议以及需求

if (characteristic.properties==CBCharacteristicPropertyNotify) {

LDLog(@"已订阅特征通知.");

return;

}

else if (characteristic.properties ==CBCharacteristicPropertyRead) {

//从外围设备读取新值,调用此方法会触发代理方法

[peripheral readValueForCharacteristic:characteristic];

}}

else{

LDLog(@"停止已停止.");

//取消连接

[self.centralManager cancelPeripheralConnection:peripheral];

}}

else if ([characteristic.UUID isEqual:txCharacteristicUUID]){

}}}


//更新特征值后(调用readValueForCharacteristic:方法或者外围设备在订阅后更新特征值都会调用此代理方法)

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

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

if (error) {

LDLog(@"更新特征值时发生错误,错误信息:%@",error.localizedDescription);

return;

}

if (characteristic.value) {

NSString *value=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];

LDLog(@"读取到特征值:%@",value);

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

NSData *value=[characteristic value];

if(!value){return ;}

//解析蓝牙数据

NSString *sig = ...解析收到的蓝牙通知信息;

...再然后干你想干的事情

}}else{

LDLog(@"未发现特征值.");

}}}

//向外设写数据的结果回调

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

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

if (error) {

LDLog(@"更新特征值时发生错误,错误信息:%@",error.localizedDescription);

return;

}

else{

NSString *result  =[[ NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];

LDLog(@"写数据成功:%@",error.localizedDescription);

}}}


//停止扫描并断开连接

-(void)disconnectPeripheral:(CBCentralManager *)centralManager

peripheral:(CBPeripheral *)peripheral{

//停止扫描

[self.centralManager stopScan];

//断开连接

[self.centralManager cancelPeripheralConnection:self.peripheral];

}


好了,现在要开启吐槽模式了:

首先,蓝牙开发这里有些小坑,当然大神是不会在意的~~~

坑1 :

如果您的APP需要在后台也能接收到外设发过来的数据,好吧,你需要在项目info.plist配置下,支持蓝牙后台数据交互;

坑2 :

虽然蓝牙只是短距离数据交互方式,但是也是一种长连接通信模式,当然那种连接上外设,干完事情立马断开的就不要往下看了...当APP后台切换前台的时候,这个时候很有必要查看一下您的蓝牙通信状态,首先手机蓝牙是否开启状态(用户喜欢动不动关闭蓝牙,觉得省电...),再检查蓝牙连接是否被关闭,最后根据我的辛酸史,最好向外设发一条数据,看一看外设是否有回应,这样子就完美了。

坑3: 关于蓝牙 数据读取模式的理解,咱们大Ios是在太人性化了...

其实蓝牙数据读取模式有三种:Notify ;Indicate ;Read;了解过安卓的肯定了解。。。但是伟大的ios系统把Notify 、Indicate 给合并了,貌似是个好事情~~~所以当您的蓝牙硬件工程师很牛掰的给你说 Notify模式,Indicate模式您可以一句话,我搞定了。

坑4:蓝牙的重连机制

前面提到了长连接,那么就有断开的情况,看清楚了这里说的‘断开’是指正常连接状态下,业务没有断开连接,因为其他因素(手机离外设距离远/手机或者外设蓝牙突然出问题了等。。)断开了连接,那么可以做一个自动重连的功能,怎么做呢,很简单接收到判断蓝牙断开就马上再次connect,,,对应的 要实现一个方法,那就是取消自动车重连的方法,并且还要考虑多台设备连接的情况,这就是看业务了~~~。

坑5:关于ios蓝牙主动调用断开问题

呵呵,安卓的蓝牙那可是 说断开就断开,一句话给开发者的感觉就是秒断;但是 ios不好意思了,连续断开的话10次有6次都会延迟(5秒左右吧,大家可以验证下,譬如 连续连接/断开几次试一下。。。),所以如果您有连续连接再断开的业务,那您就要注意了,可以手动延迟加标志量来处理。

坑...

等大伙来继续填了。。。

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

推荐阅读更多精彩内容

  • 本文主要以蓝牙4.0做介绍,因为现在iOS能用的蓝牙也就是只仅仅4.0的设备 用的库就是core bluetoot...
    暮雨飞烟阅读 752评论 0 2
  • 随着智能家具、智能穿戴等的兴起,蓝牙开发应用越来越广泛,有关蓝牙方面的问题,今天就给大家进行详细的讲解,想要了解蓝...
    知易行难880721阅读 578评论 0 0
  • 这是我第二遍追这部剧了,真是看一遍被气一遍啊,剧中的小夫妻林磊和叶筱羽在相识五六个月结婚两年后,林磊的前女友回来了...
    悠冉的情感观阅读 586评论 0 0
  • 今天参加宝宝学校的中秋活动。活动主题很棒,过程安排井井有条的。每一个宝宝,每一个家长都有参与。 好开心的和宝宝们做...
    弘毅某阅读 85评论 1 2
  • 水日常系列。 今天下午的计量经济学也是这个学期的最后一节课,一个星期后我的期末考试就全部结束,大三上也就这样结束了...
    裴紊阅读 143评论 0 0