内容简介:最近一段时间一直在做公司的BLE蓝牙SDK,sdk主要负责外设和手机的连接以及数据通信。过程中遇到了一些比较有价值的问题,现在总结记录下。蓝牙开发使用系统框架初始化完成可使用
最近一段时间一直在做公司的BLE蓝牙SDK,sdk主要负责外设和手机的连接以及数据通信。过程中遇到了一些比较有价值的问题,现在总结记录下。
蓝牙开发使用系统框架 #import <CoreBluetooth/CoreBluetooth.h>
使用 [[CBCentralManager alloc] initWithDelegate:self queue:nil]
初始化 CBCentralManager
对象。(设置 CBCentralManagerDelegate
为self,nil表示在主线程)
初始化成功后系统会自动检测蓝牙状态。实现 centralManagerDidUpdateState:
代理方法可实时获取蓝牙状态。当 central.state
为 CBManagerStatePoweredOn
时表示可用。
初始化完成可使用 [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
赫尔曼 (David Herman) / 黄博文、喻杨 / 机械工业出版社 / 2014-1-1 / CNY 49.00
Effective 系列丛书经典著作,亚马逊五星级畅销书,Ecma 的JavaScript 标准化委员会著名专家撰写,JavaScript 语言之父、Mozilla CTO —— Brendan Eich 作序鼎力推荐!作者凭借多年标准化委员会工作和实践经验,深刻辨析JavaScript 的内部运作机制、特性、陷阱和编程最佳实践,将它们高度浓缩为极具实践指导意义的 68 条精华建议。 本书共......一起来看看 《Effective JavaScript》 这本书的介绍吧!