iOS蓝牙开发梳理:广播端和扫描端实现

               

【前言】

 * 目前项目里有蓝牙支付功能,对于蓝牙开发功能,要求比较高,包括iOS与Android之间的通讯。

 * 今天整理了下iOS蓝牙SDK开发流程中的知识点,总结了这篇文章,希望给各位开发蓝牙功能的同学带来帮助。

【功能目标】

  开发移动设备的蓝牙功能,目的用来实现设备之间数据自由通讯(数据发,收),完成移动服务端和客服端场景交互。

【定义场景】

  1: 广播端:服务端定义,用于被多台扫描设备同时识别并订阅;

  2: 扫描端:客服端定义,用于扫描并订阅广播端设备;

【实现方案】

CoreBluetooth:iOS原生SDK。

导入:  <CoreBluetooth/CoreBluetooth.h>  。

 

开始广播功能:

【第一步、开启广播】

(1):  涉及的类

1:  CBPeripheralManager

外设管理器,管理设备广播状态。

2:  CBUUID

唯一标识,设备的服务,特性和特征描述符。

3: CBMutableService

外设管理器的服务,用于设定服务特征。

4:  CBMutableCharacteristic

服务的特征,用于设定特征描述。

5:  CBMutableDescriptor

特征的描述。

 

(2):  类调用时序图

  

时序图备注: CBPeripheralManager 添加服务是很重要的一步。

 

(3):  CoreBluetooth 原生函数

1:  蓝牙创建,用于权限判断:

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;

2:  外设管理器添加服务完成,回调结果:

(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error;

3:  广播开启完成,回调结果:

-(void)peripheralManagerDidStartAdvertising: (CBPeripheralManager *)peripheral error:(NSError *)error;

 

4广播开启失败,重启

1:   重置广播,目前重试次数:         ADVERTISING_RETRY = 3(注:根据自己需求设置)

2:  重置加载安全模块调用方法:       [self setUpServiceSecurity];   (注:需要根据自己功能需求实现)

3:  重置外设管理器步骤:              1:关闭广播 2:清除设备 3:重置管理器

4:  重置创建服务和特征调用方法:  [self setupServiceAndCharacteristics];(注:需要根据自己功能需求实现)

 

5蓝牙广播,参数配置

1:  广播设备名称:  可通过CBPeripheralManager 函数:

- (void)startAdvertising:(nullable NSDictionary<NSString *, id> *)advertisementData;

进行设置:CBAdvertisementDataLocalNameKey 的值。 

2:  信号强度 :           iOS不支持设置。

3:  广播频率 :           iOS不支持设置。

 

【第二步、广播被订阅】

1:  涉及的类

1:  CBCentral:             发起订阅的扫描设备;

2:  CBCharacteristic:   扫描设备的特性信息;

  

2:  CoreBluetooth原生函数

1:  订阅成功回调,记录订阅扫描设备:

-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic;

 

3:  被取消订阅函数

l  扫描设备取消订阅,移除相应记录的设备信息:

-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic;

 

4:完成订阅

注:当有扫描设备订阅了广播设备后,广播端可以给扫描设备发送数据。

 

【第三步、发送数据】

1:  涉及的类

  DeviceInforModel:            数据容器对象。(注:自定义对象,用来记录外设设备部信息和接,发数据的记录)

-属性-  messageSentData: 发送数据DATA;

-属性- sentStartIndex:       发送数据开始下标;

-属性- diffLength:             每个包最大字节;

 

2:  发送流程图

 

(流程图备注):

1:  Start:                   分包首个数据包字符串标示;

2:  End:                    分包最后一个数据包字符串标示;

3:  sentStartIndex:     每个发送数据包的开始标示,默认等于0;

4:  messageSentData:总数据中按照 sentStartIndex 截取的分包数据;

5:  IsReady:              外围设备已准备好发送特征值更新,有相应的回调函数;

6:  sendMessagePart:进入数据分包流程;

 

3:  CoreBluetooth原生函数

1:  发送:通过通知或指示将更新后的特征值发送给一个或多个中心。

- (BOOL)updateValue:(NSData *)value forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:(nullable NSArray<CBCentral *> *)centrals;

 

2:  发送结果:告诉委托本地外围设备已准备好发送特征值更新。

- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral;

 

【第四步、接收数据】

1:  涉及的类

1:  CBATTRequest:            写入数据的请求。

-属性- lastObject:             目标数据;

      

2:  DeviceInforModel:          数据容器对象。(自定义对象,用来记录外设设备部信息和接,发数据的记录)

-属性-  messageReceivedData:发送数据DATA;

-属性- isStart:                        开头标示;

-属性- startMsg:                     开头数据;

-属性- isEnd:                          结尾标示;

-属性- endMsg:                       结尾数据;

 

2:  接收流程图

 

(流程图备注):

1:  Start:       分包首个数据包字符串标示,此时设置isStart=YES;

2:  End:        分包最后一个数据包字符串标示,此时设置isEnd=YES;

3:  CBATTRequest.lastObject:每个包的数据源;

4:  messageReceivedData:    与历史数据进行合并;

5:  hasPrefix:@"End":这步判断结束标示,有End标示,合并数据,输出完整包;

6:  CBATTRequest:   没有End标示,等待分包数据;

 

3:  CoreBluetooth原生函数

l  中心设备写入数据的时候,回调函数:

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests;

 

【完成:广播功能】

完成上面步骤,此时广播端可以自由发送和接收数据。

 

 

开始扫描功能:

【第一步、开启扫描】

(1):  涉及的类

1:  CBCentralManager:中心设备管理器。

2:  CBUUID:               唯一标识,设备的服务,特性和特征描述符。

3:  CBService:    中心设备管理器的服务,用于扫描指定服务广播。

4:  CBPeripheral:外设管理器,扫描到或连接的广播设备。

5:  RSSI:            外设型号强度值。

 

(2):  类调用时序图

时序图备注:在识别到广播外设 CBPeripheral,并且完成记录后,就可以开启指定外设的连接请求,APP级外设标示通过 CBService 区分。

 

3扫描开启失败

注:蓝牙功能不可用,或者未开启,会导致开启失败。

未发现符合要求的外设时,会继续扫描。

 

4蓝牙扫描,参数配置

1: 扫描设备名称:     iOS不支持设置,

备注:订阅广播端成功后,广播端区别扫描设备通过:central.identifier

2: 扫描频率:          iOS不支持设置。

 

【第二步、连接广播】

(1):  涉及的对象

1:  self. centralManager:中心设备管理器;

2:  self.peripheral:        中心设备管理器连接目标外设;

3:  self.service:                用于查找的服务特征;

4:  service.characteristics:对应服务的特征集合;

 

 2:  CoreBluetooth原生函数

1: 发起连接外设,调用函数:

- (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary<NSString *, id> *)options;

2: 连接成功,回调函数:

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

3: 根据SERVICE_UUID来寻找服务,调用函数:

- (void)discoverServices:(nullable NSArray<CBUUID *> *)serviceUUIDs;

4: 寻找到特定服务,回调函数:

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

5:寻找特定服务特征,回调函数:

- (void)discoverCharacteristics:(nullable NSArray<CBUUID *> *)characteristicUUIDs forService:(CBService *)service;

6:  发现特征,回调函数:

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

7:  读取特征数据,调用函数:

[peripheral readValueForCharacteristic:self.characteristic];

8:  发送订阅通知,调用函数(广播端会收到被订阅消息):

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

9:  连接失败,回调函数:

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

10:  断开连接,回调函数:

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

  

3:  CoreBluetooth连接流程图

 

【第三步、发送数据】

注:扫描端处理发送数据流程,与广播端处理发送一致,详见广播端;

 

【第四步、接收数据】

注:扫描端处理接收数据流程,与广播端处理接收一致,详见广播端;

 

【第五步、断开连接】

1:  self.centralManager  断开指定 外设  self.peripheral;

[self.centralManager cancelPeripheralConnection:self.peripheral];

l  断开连接,回调函数:

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

 

功能补充部分:

- 前面梳理了两端数据发送,接收流程,下面补充一下实现方案:

【一:广播端数据读,写方案】

CBMutableCharacteristic:        广播端主要通过设置 CBMutableCharacteristic 特性值属性实现:

CBCharacteristicPropertyWrite:设置广播允许写入特性值,用于接收数据;

CBCharacteristicPropertyNotify:设置广播允许特征值更新的通知,用于发送数据;

(以下代码,案例)

CBMutableCharacteristic *characteristic = [

                                               [CBMutableCharacteristic alloc]

                                               initWithType:characteristicID

                                               properties:

                                               CBCharacteristicPropertyNotify|     (注:特性支持通知方案)

                                               CBCharacteristicPropertyWrite       (注:特性支持写入方案)

                                               value:nil

                                               permissions:CBAttributePermissionsReadable|

                                               CBAttributePermissionsWriteable];

    CBUUID *UUID_Descriptor = [CBUUID UUIDWithString:DESCRIPTORUUID];    

    // 初始化一个特征的描述

    CBMutableDescriptor *mDescriptor = [[CBMutableDescriptor alloc]initWithType:UUID_Descriptor value:[NSData data]];

    [characteristic setDescriptors:@[mDescriptor]];

    // 特征添加进服务

    service.characteristics = @[characteristic];

    // 服务加入管理

    [self.peripheralManager addService:service];

 

----  写入方案:

1: 通过更新自身特性值实现写入数据,调用函数:

- (BOOL)updateValue:(NSData *)value forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:(nullable NSArray<CBCentral *> *)centrals;

2: 当自身特性值准备更新时,回调函数(写入完成,会通知扫描端):

- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral;

 

----  读取方案:

1: 通过允许写入特性值,回调函数(扫描端写入特性值完成回调):

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests;

  

【第二:扫描端数据读,写方案】

---- 写入方案:

1: 扫描发送数据,调用函数(广播特性值需要支持writeValue: ):

- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;

2: 写入数据完成,回调结果:

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

 

----  读取方案:

1:通过实现外设代理方案:CBPeripheralDelegate

self.peripheral.delegate = self;

2: CBPeripheralDelegate代理回调函数:

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

 

 结语:

蓝牙广播端和扫描端的实现流程,上面梳理完成。

蓝牙数据传输中的加密处理没有包括在里面。

有问题欢迎一起研究讨论,本人QQ号:497609288.

 

posted on 2020-05-09 14:04  tangjianfeng  阅读(3177)  评论(0编辑  收藏  举报