• 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.

  • 相关阅读:
    vue.js生成纵向拓扑图
    vue.js生成横向拓扑图
    Vue.js中使用wangEditor富文本编辑器
    文件上传与下载,PDF与Excel的操作
    Element布局实现日历布局
    golang时间转换
    iView学习笔记(四):Form表单操作
    iView学习笔记(三):表格搜索,过滤及隐藏列操作
    iView学习笔记(二):Table行编辑操作
    iView学习笔记(一):Table基本操作(包含前后端示例代码)
  • 原文地址:https://www.cnblogs.com/tangjianfeng/p/12856217.html
Copyright © 2020-2023  润新知