iOS之BLE蓝牙SDK开发我的总结(基础篇)

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

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

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

为了使用方便,能够把扫描的设备、链接的设备、设备的服务、设备的特征分别保存到数组中ide

@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不是惟一的,但在本机上能够做为惟一标识。ui

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

链接失败时执行centralManager:didFailToConnectPeripheral:error:this

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

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

发现服务加密

//发现服务时
- (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);
    }
}
复制代码

发现特征spa

//发现特征时
- (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判断是不是异常断开,通常状况下异常断开时是须要重连的。

从新链接后发现读写数据时没效果了???

缘由就是当设备断开链接后peripheral.servicesnil了,固然service.characteristics也是nil,因此须要在断开链接时把保存这个设备对应的服务和特征所有清除,而后在链接成功时从新过一遍发现服务和发现特征的流程就行了。

回顾我的的ble蓝牙开发过程总结出来基础篇遇到的问题大体就这么多了。本篇文章旨在我的总结和帮助正在作这方面的人理解蓝牙开发中这些东西的概念。

理解了事物,解决这个事物相关的问题就不难了。

相关文章
相关标签/搜索