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秒左右吧,大家可以验证下,譬如 连续连接/断开几次试一下。。。),所以如果您有连续连接再断开的业务,那您就要注意了,可以手动延迟加标志量来处理。

坑...

等大伙来继续填了。。。

你可能感兴趣的:(IOS关于蓝牙实践)