以前写过几篇蓝牙相关的文章,但是没有涉及扫描、收发指令这些基础功能的实现。所以打算写一篇尽可能详尽的蓝牙知识汇总,一方面给有需要的同学看,一方面是对自己学习蓝牙的一个总结。 这篇文章的目的:教你实现设备的扫描,连接,数据收发,蓝牙数据解析。如果在实现上面任一功能遇到问题时,欢迎留下你的问题,我将进行补充,对于说法有误的地方也请老司机予以指正。 目录
思维导图 第一次做图,大家凑合着看哈。这张是我总结的蓝牙知识的结构图,下面的内容将围绕这些东西展开进行。 连接设备流程
苹果对蓝牙设备要求 BLE:bluetouch low energy,蓝牙4.0设备因为低功耗,所有也叫作BLE。苹果在iphone4s及之后的手机型号开始支持蓝牙4.0,这也是最常见的蓝牙设备。低于蓝牙4.0协议的设备需要进行MFI认证,关于MFI认证的申请工作可以看这里:关于MFI认证你所必须要知道的事情(http://www.jianshu.com/p/b90b0c45398d ) 在进行操作蓝牙设备前,我们先下载一个蓝牙工具 操作蓝牙设备用什么库 苹果自身有一个操作蓝牙的库 中心和外围设备 central-peripheral 如图所示,电脑、Pad、手机作为中心,心跳监听器作为外设,这种中心外设模式是最常见的。简单理解就是,发起连接的是中心设备(Central),被连接的是外围设备(Peripheral),对应传统的客户机-服务器体系结构。Central能够扫描侦听到,正在播放广告包的外设。 服务与特征 外设可以包含一个或多个服务(CBService),服务是用于实现装置的功能或特征数据相关联的行为集合。 CBService-CBCharacteristic 如何扫描蓝牙 在进行扫描之前我们需要,首先新建一个类作为蓝牙类,例如 dispatch_queue_t centralQueue = dispatch_queue_create(“centralQueue',DISPATCH_QUEUE_SERIAL); NSDictionary *dic = @{CBCentralManagerOptionShowPowerAlertKey : YES, CBCentralManagerOptionRestoreIdentifierKey : @'unique identifier' }; self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue options:dic];
//不重复扫描已发现设备 NSDictionary *option = @{CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:NO],CBCentralManagerOptionShowPowerAlertKey:YES}; [self.centralManager scanForPeripheralsWithServices:nil options:option]; - (void)scanForPeripheralsWithServices:(nullable NSArray 扫面方法, 在初始化的时候我们调用了代理,在CoreBluetooth中有两个代理,
iOS的命名很友好,我们通过名字就能看出,上面那个是关于中心设备的代理方法,下面是关于外设的代理方法。我们这里先研究 这个方法标了 typedef NS_ENUM(NSInteger, CBCentralManagerState{ CBCentralManagerStateUnknown = CBManagerStateUnknown,//未知状态 CBCentralManagerStateResetting = CBManagerStateResetting,//重启状态 CBCentralManagerStateUnsupported = CBManagerStateUnsupported,//不支持 CBCentralManagerStateUnauthorized = CBManagerStateUnauthorized,//未授权 CBCentralManagerStatePoweredOff = CBManagerStatePoweredOff,//蓝牙未开启 CBCentralManagerStatePoweredOn = CBManagerStatePoweredOn,//蓝牙开启 } NS_DEPRECATED(NA, NA, 5_0, 10_0, 'Use CBManagerState instead”); 该枚举在iOS10之后已经废除了,系统推荐使用 typedef NS_ENUM(NSInteger, CBManagerState{ CBManagerStateUnknown = 0, CBManagerStateResetting, CBManagerStateUnsupported, CBManagerStateUnauthorized, CBManagerStatePoweredOff, CBManagerStatePoweredOn, } NS_ENUM_AVAILABLE(NA, 10_0); - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary RSSI:(NSNumber *)RSSI; peripheral是外设类 - (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary 在蓝牙于后台被杀掉时,重连之后会首先调用此方法,可以获取蓝牙恢复时的各种状态 如何连接 在扫面的代理方法中,我们连接外设名是MI的蓝牙设备 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{ NSLog(@'advertisementData:%@,RSSI:%@',advertisementData,RSSI); if([peripheral.name isEqualToString:@'MI']){ [self.centralManager connectPeripheral:peripheral options:nil];//发起连接的命令 self.peripheral = peripheral; } } 连接的状态 连接失败的回调 连接断开的回调 连接成功之后并没有结束,还记得 lightblue 其中
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties{ CBCharacteristicPropertyBroadcast = 0x01, CBCharacteristicPropertyRead = 0x02, CBCharacteristicPropertyWriteWithoutResponse = 0x04,CBCharacteristicPropertyWrite = 0x08, CBCharacteristicPropertyNotify = 0x10, CBCharacteristicPropertyIndicate = 0x20, CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40, CBCharacteristicPropertyExtendedProperties = 0x80, CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100, CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200}; 如何发送并接收数据 通过上面的步骤我们发现 //发现服务的回调 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error; //发现特征的回调 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error; //读数据的回调 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error; //是否写入成功的回调 - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error; 通过这几个方法我们构建一个流程:连接成功->获取指定的服务->获取指定的特征->订阅指定特征值->通过具有写权限的特征值写数据->在 解释一下订阅特征值:特征值具有Notify权限才可以进行订阅,订阅之后该特征值的value发生变化才会回调 //连接成功 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ //连接成功之后寻找服务,传nil会寻找所有服务 [peripheral discoverServices:nil]; } //发现服务的回调 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ if (!error) { for (CBService *service in peripheral.services) { NSLog(@'serviceUUID:%@', service.UUID.UUIDString); if ([service.UUID.UUIDString isEqualToString:ST_SERVICE_UUID]) { //发现特定服务的特征值 [service.peripheral discoverCharacteristics:nil forService:service]; } } } } //发现characteristics,由发现服务调用(上一步),获取读和写的characteristics - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { for (CBCharacteristic *characteristic in service.characteristics) { //有时读写的操作是由一个characteristic完成 if ([characteristic.UUID.UUIDString isEqualToString:ST_CHARACTERISTIC_UUID_READ]) { self.read = characteristic; [self.peripheral setNotifyValue:YES forCharacteristic:self.read]; } else if ([characteristic.UUID.UUIDString isEqualToString:ST_CHARACTERISTIC_UUID_WRITE]) { self.write = characteristic; } } } //是否写入成功的代理 - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ if (error) { NSLog(@'===写入错误:%@',error); }else{ NSLog(@'===写入成功'); } } //数据接收 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if([characteristic.UUID.UUIDString isEqualToString:ST_CHARACTERISTIC_UUID_READ]){ //获取订阅特征回复的数据 NSData *value = characteristic.value; NSLog(@'蓝牙回复:%@',value); } } 比如我们要获取蓝牙电量,由硬件文档查询得知该指令是 - (void)getBattery{ Byte value[3]={0}; value[0]=x1B; value[1]=x99; value[2]=x01; NSData * data = [NSData dataWithBytes:&value length:sizeof(value)]; //发送数据 [self.peripheral writeValue:data forCharacteristic:self.write type:CBCharacteristicWriteWithoutResponse]; } 如果写入成功,我们将会在 如何解析蓝牙数据 如果你顺利完成了上一步的操作,并且看到了蓝牙返回的数据,那么恭喜你,蓝牙的常用操作你已经了解大半了。因为蓝牙的任务大部分就是围绕发送指令,获取指令,将蓝牙数据呈现给用户。上一步我们已经获取了蓝牙指令,但是获取的却是 device-document
对数据解析的流程就是:判断校验和是否正确,是不是一条正确的数据->该条数据是不是我们需要的电量数据,即首字节为 |
|
来自: xiaotianyueye > 《iOS学习文章2》