iOS之BLE蓝牙SDK开发个人总结(基础篇)

栏目: IOS · 发布时间: 5年前

内容简介:最近一段时间一直在做公司的BLE蓝牙SDK,sdk主要负责外设和手机的连接以及数据通信。过程中遇到了一些比较有价值的问题,现在总结记录下。蓝牙开发使用系统框架初始化完成可使用

最近一段时间一直在做公司的BLE蓝牙SDK,sdk主要负责外设和手机的连接以及数据通信。过程中遇到了一些比较有价值的问题,现在总结记录下。

蓝牙开发使用系统框架 #import <CoreBluetooth/CoreBluetooth.h> 使用 [[CBCentralManager alloc] initWithDelegate:self queue:nil] 初始化 CBCentralManager 对象。(设置 CBCentralManagerDelegate 为self,nil表示在主线程) 初始化成功后系统会自动检测蓝牙状态。实现 centralManagerDidUpdateState: 代理方法可实时获取蓝牙状态。当 central.stateCBManagerStatePoweredOn 时表示可用。

初始化完成可使用 [self.centralManager scanForPeripheralsWithServices:nil options:nil] 方法扫描周围设备(Services表示只扫描具有某些ServiceUUID的设备,nil为扫描全部)。 当扫描到设备时会执行代理方法 centralManager:didDiscoverPeripheral:advertisementData:RSSI: 返回每一个扫描到的设备及设备的相关信息。

为了使用方便,可以把扫描的设备、连接的设备、设备的服务、设备的特征分别保存到数组中

@property(nonatomic, strong) NSMutableArray<CBPeripheral *> *peripheralArr;             //扫描到的设备数组
@property(nonatomic, strong) NSMutableArray<CBPeripheral *> *currentPeripheralArr;      //当前连接的所有设备
@property(nonatomic, strong) NSMutableArray<CBService *> *serviceArr;                   //当前连接设备的服务
@property(nonatomic, strong) NSMutableArray<CBCharacteristic *> *characteristicArr;     //当前连接的设备的所有特征
复制代码

保存到数组中的设备可通过 UUID 来进行区分。从 iOS7 之后苹果不提供外设的 mac 地址,外设的唯一标识换成了由 mac 封装加密后的 UUID ,需要注意的是不同的手机获取同一个外设的 UUID 是不同的,所以在不同手机之间 UUID 不是唯一的,但在本机上可以作为唯一标识。

//通过设备对象连接设备
[self.centralManager connectPeripheral:peripheral options:nil];
复制代码

连接失败时执行 centralManager:didFailToConnectPeripheral:error:

//连接设备成功时调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    //设置代理
    peripheral.delegate = self;
    //获取设备的服务,传nil代表获取所有的服务
    [peripheral discoverServices:nil];
}
复制代码

ble蓝牙主要有 设备-->服务-->特征 3层。分别都是一对多的关系。它们都有个唯一标识UUID,设备UUID,服务UUID,特征UUID。

发现服务

//发现服务时
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if(!error)
    {
        //遍历peripheral.services数组
        for (CBService * service in peripheral.services)
        {
            if (![self.serviceArr containsObject:service])
            {
                NSLog(@"设备:%@发现新服务:%@",peripheral.identifier.UUIDString, service.UUID.UUIDString);
                [self.serviceArr addObject:service];
                //发现特征
                [peripheral discoverCharacteristics:nil forService:service];
            }
        }
    }
    else{
        NSLog(@"发现服务失败的错误信息%@", error);
    }
}
复制代码

发现特征

//发现特征时
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if(!error)
    {
        //把特征保存到数组
        for (CBCharacteristic *charact in service.characteristics)
        {
            if (![self.characteristicArr containsObject:charact])
            {
                NSLog(@"设备:%@的服务:%@发现新特征:%@",peripheral.identifier.UUIDString, service.UUID.UUIDString, charact.UUID.UUIDString);
                //保存到数组
                [self.characteristicArr addObject:charact];
            }
        }
        /*
            把设备保存到已连接的设备数组
            此时的需求是当特征发现完成时才算连接成功
        */
        [self.currentPeripheralArr addObject:peripheral];
    }
    else{
        NSLog(@"发现特征失败的错误信息%@", error);
    }
}
复制代码

至此以上4个数组都已填满。 手机和蓝牙硬件之间的通信主要是使用蓝牙特征值的读写,接下来就是连接设备之后对设备的特征值进行读、写、订阅。

写入特征值

/*
peripheral是写入的设备对象,charact是特征对象,valueData是要写入的数据
type的取值有CBCharacteristicWriteWithResponse(有回复)和CBCharacteristicWriteWithoutResponse(无回复),和硬件的设置有关
*/
[peripheral writeValue:valueData forCharacteristic:charact type:CBCharacteristicWriteWithResponse];
复制代码

当type是 CBCharacteristicWriteWithResponse 时 实现 peripheral:didWriteValueForCharacteristic:error: 代理方法能够获取写入结果的回调。

读取特征值

//调用此方法去读取参数特征的value
[peripheral readValueForCharacteristic:charact];
复制代码

实现 peripheral:didUpdateValueForCharacteristic:error: 代理方法 获取 characteristic.value 即为读取的特征值。

订阅特征

//设置某特征的Notify为YES为订阅状态
[peripheral setNotifyValue:YES forCharacteristic:charact];
复制代码

实现 peripheral:didUpdateNotificationStateForCharacteristic:error: 代理方法当订阅状态发生改变时会执行。 当订阅设备的某个特征时,设备端给这个特征发送notify消息时会调用 peripheral:didUpdateValueForCharacteristic:error: 代理方法把notify要传的值发送过来。

有关蓝牙基础的最后一点就是断开蓝牙连接了,也是非常重要的一点,所以写在最后

断开连接很简单,只需要调用 [self.centralManager cancelPeripheralConnection:peripheral] 传入需要断开连接的设备对象就行了。断开连接时会自动调用 centralManager:didDisconnectPeripheral:error: 代理方法。 按照之前的惯例,当error为nil时表示断开成功,error不为nil时断开失败。这种理解时错误的。

这个代理方法官方的解释

/*!
 *  @method centralManager:didDisconnectPeripheral:error:
 *
 *  @param central      The central manager providing this information.
 *  @param peripheral   The <code>CBPeripheral</code> that has disconnected.
 *  @param error        If an error occurred, the cause of the failure.
 *
 *  @discussion         This method is invoked upon the disconnection of a peripheral that was connected by {@link connectPeripheral:options:}. If the disconnection
 *                      was not initiated by {@link cancelPeripheralConnection}, the cause will be detailed in the <i>error</i> parameter. Once this method has been
 *                      called, no more methods will be invoked on <i>peripheral</i>'s <code>CBPeripheralDelegate</code>.
 *
 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
复制代码

大致意思理解为当你调用 cancelPeripheralConnection: 方法( 主动断开 )断开连接时error为nil ; 没有调用这个方法( 异常断开 )而断开时error返回的是异常断开的原因。也可以理解为主动调用断开连接方法一定会断开。

接下来就是断开重连的问题了,对蓝牙功能进行封装时肯定少不了断开重连。首先断开时可通过上面的代理方法的 error是否为nil 判断是否是 异常断开 ,一般情况下异常断开时是需要重连的。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

如何不在网上虚度人生

如何不在网上虚度人生

[美] 肯尼思·戈德史密斯 / 刘畅 / 北京联合出版公司 / 2017-9 / 39.80元

我们平时上网多大程度上是浪费时间,多大程度是在学习、关心社会、激发创造力?我们真能彻底断网,逃离社交网络吗? 手机把都市人变成一群电子僵尸,是福是祸? 浏览记录就是我们将来的回忆录吗?文件归档属于一种现代民间艺术? 不自拍、P图、发朋友圈,我还是我吗? 美国知名概念艺术家戈德史密斯认为:上网绝不是浪费时间,而是一种创造性的活动。在本书中他以跨学科角度、散文式语言进行论证,涉及大众传播学、计算......一起来看看 《如何不在网上虚度人生》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具