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

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

内容简介:最近一段时间一直在做公司的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 判断是否是 异常断开 ,一般情况下异常断开时是需要重连的。


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

查看所有标签

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

深入浅出Node.js

深入浅出Node.js

朴灵 / 人民邮电出版社 / 2013-12-1 / CNY 69.00

本书从不同的视角介绍了 Node 内在的特点和结构。由首章Node 介绍为索引,涉及Node 的各个方面,主要内容包含模块机制的揭示、异步I/O 实现原理的展现、异步编程的探讨、内存控制的介绍、二进制数据Buffer 的细节、Node 中的网络编程基础、Node 中的Web 开发、进程间的消息传递、Node 测试以及通过Node 构建产品需要的注意事项。最后的附录介绍了Node 的安装、调试、编码......一起来看看 《深入浅出Node.js》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

SHA 加密
SHA 加密

SHA 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具