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 判断是否是 异常断开 ,一般情况下异常断开时是需要重连的。


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

查看所有标签

猜你喜欢:

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

Effective JavaScript

Effective JavaScript

赫尔曼 (David Herman) / 黄博文、喻杨 / 机械工业出版社 / 2014-1-1 / CNY 49.00

Effective 系列丛书经典著作,亚马逊五星级畅销书,Ecma 的JavaScript 标准化委员会著名专家撰写,JavaScript 语言之父、Mozilla CTO —— Brendan Eich 作序鼎力推荐!作者凭借多年标准化委员会工作和实践经验,深刻辨析JavaScript 的内部运作机制、特性、陷阱和编程最佳实践,将它们高度浓缩为极具实践指导意义的 68 条精华建议。 本书共......一起来看看 《Effective JavaScript》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具