iOS 蓝牙开发
蓝牙介绍
本文要介绍的CoreBluetooth,专门用于与BLE设备通讯。并且现在很多蓝牙设备都支持4.0,4.0以其低功耗著称,所以一般也叫BLE(Bluetooth Low Energy),所以也是在iOS比较推荐的一种开发方法。
基本概念
image.png- 中心设备(Central):发起连接,用来扫描周围蓝牙硬件的设备;
- 周边设备(Peripheral):被连接的设备;
- 服务(Service):特征和关系的集合,它封装了设备的一部分的行为;
- 特征(Characteristic):可以理解成一个Service模块具体提供哪些服务,特征会有一个value,一般我们向蓝牙设备写入数据、从蓝牙设备读取数据就是这个value;
- UUID:区分不同服务和特征的唯一标识,使用该字端我们可以获取我们想要的服务或者特征。
- 广播:外部设备不停的散播的蓝牙信号,让中心设备可以扫描到,也是我们开发中接收数据的入口。
CoreBluetooth介绍
image.png在CoreBluetooth中有两个主要的部分,Central和Peripheral,CBPeripheralManager 作为周边设备。CBCentralManager作为中心设备。所有可用的iOS设备可以作为周边设备(Peripheral)也可以作为中心设备(Central),但不可以同时既是周边设备也是中心设备。
Central
作为 Central 模式使用的操作步骤 :
- 创建一个 Central Manager;
- 发现周边设备;
- 发现周边设备的服务及特征;
- 读取特征值或对指定特征设置监听;
- 设置可写入的特征值;
- 读取特征的描述Descriptor;
创建了一个 tableView 来扫描到的周边设备的名字、信号强度等;
导入CoreBluetooth
#import <CoreBluetooth/CoreBluetooth.h>
创建一个管理者centralManager,给当前类签订协议<cbcentralmanagerdelegate>:</cbcentralmanagerdelegate>
_centralManager=[[CBCentralManager alloc] initWithDelegate:self queue:nil];
实现 centralManagerDidUpdateState: 监测蓝牙状态变化:
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBCentralManagerStatePoweredOff:
{
NSLog(@"蓝牙功能未开启");
}
break;
case CBCentralManagerStateUnsupported:
{
NSLog(@"不支持蓝牙功能");
}
break;
case CBCentralManagerStatePoweredOn:
{
NSLog(@"蓝牙功能已开启");
}
break;
default:
break;
}
}
扫描周边设备
[_centralManager scanForPeripheralsWithServices:nil options:nil];
实现协议方法获取扫描到的周边设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
__block BOOL isContain = NO;
SABLEInfoObject *bleInfo = [[SABLEInfoObject alloc] init];
bleInfo.name = peripheral.name ?: @"--";
bleInfo.rssi = RSSI;
bleInfo.deviceUUID = peripheral.identifier.UUIDString;
bleInfo.servicesCount = peripheral.services.count;
[self.dataArray enumerateObjectsUsingBlock:^(SABLEInfoObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj.deviceUUID isEqualToString:bleInfo.deviceUUID]) {
[self.dataArray replaceObjectAtIndex:idx withObject:bleInfo];
isContain = YES;
}
}];
if (!isContain) {
[self.dataArray addObject:bleInfo];
[self.peripheralList addObject:peripheral];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kDiscoverPeripheralRefreshNotificationName object:self.dataArray];
}
在创建的 tableView 中实时显示 peripheral 的信号强度、名称、服务个数,选取某一个发起连接:
[_centralManager connectPeripheral:self.peripheralList[index] options:nil];
实现协议方法 centralManager:didConnectPeripheral 在连接成功时去获取当前设备包含的所有服务:
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"%@ Connect Success", peripheral.name);
peripheral.delegate = self;
[peripheral discoverServices:nil];
}
协议方法 didDiscoverServices,遍历服务,查询服务下的特征:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error {
NSLog(@"%@ Discover Services Success", peripheral.name);
[peripheral.services enumerateObjectsUsingBlock:^(CBService * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"Service UUID: %@", obj.UUID.UUIDString);
[peripheral discoverCharacteristics:nil forService:obj];
}];
}
协议方法 didDiscoverCharacteristicsForService,遍历当前服务下的特征,读取特征值或者监测特征值,还可获取特征的Descriptor:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
[service.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"\nservicesUUID: %@\nCharacteristicUUID: %@", service.UUID.UUIDString, obj);
//读取特征
// if ([obj.UUID.UUIDString isEqualToString:@"2A19"]) {
// [peripheral readValueForCharacteristic:obj];
// }
//
// if ([service.UUID.UUIDString isEqualToString:@"180A"]) {
// [peripheral readValueForCharacteristic:obj];
// }
[peripheral readValueForCharacteristic:obj];
if (obj.properties == CBCharacteristicPropertyWrite) {
NSLog(@"writeValue");
[peripheral writeValue:[@"testValue " dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:obj type:CBCharacteristicWriteWithResponse];
}
//监测特征
// [peripheral setNotifyValue:YES forCharacteristic:obj];
// 外设发现特征的描述
[peripheral discoverDescriptorsForCharacteristic:obj];
}];
}
读取或者监测的特征值可在协议方法 didUpdateValueForCharacteristic 查看,如下,读取电量值,2A19为电量值特征的UUID:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
if ([characteristic.UUID.UUIDString isEqualToString:@"2A19"]) {
NSString *hexStr = [SADataConvertManager convertDataToHexStr:characteristic.value];
NSUInteger batteryLevel = [[SADataConvertManager convertHexStrToDecimalString:hexStr] integerValue];
NSLog(@"\nCharacteristicUUID: %@\nCharacteristicValue: %@", characteristic.UUID, [@(batteryLevel).stringValue stringByAppendingString:@"%"]);
}
}
发现的 Descriptor 需要实现协议方法读取:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
[characteristic.descriptors enumerateObjectsUsingBlock:^(CBDescriptor * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"\nCharacteristicUUID: %@\nDescriptor: %@", characteristic.UUID, obj.value);
}];
}
周边设备读取信号强度:
[peripheral readRSSI]; //读取信号强度
实现协议方法 didReadRSSI 获取读取的信号强度:
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error {
NSLog(@"信号强度:%@", RSSI.stringValue);
}
写入特征值:
if (obj.properties == CBCharacteristicPropertyWrite) {
[peripheral writeValue:[@"testValue " dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:obj type:CBCharacteristicWriteWithResponse];
}
Peripheral
作为 Peripheral 模式使用的操作步骤 :
- 创建一个 Peripheral Manager;
- 为外设添加服务;
- 发送广播
创建一个管理者peripheralManager,给当前类签订协议<cbperipheralmanagerdelegate>:</cbperipheralmanagerdelegate>
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
实现 centralManagerDidUpdateState: 获取蓝牙状态变化:
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
switch (peripheral.state) {
case CBPeripheralManagerStatePoweredOn:
{
NSLog(@"蓝牙已打开");
[self setupPeripheral];
}
break;
case CBPeripheralManagerStatePoweredOff:
NSLog(@"蓝牙已关闭");
break;
case CBPeripheralManagerStateUnsupported:
NSLog(@"不支持蓝牙");
break;
default:
break;
}
}
为蓝牙添加服务
- (void)setupPeripheral {
CBMutableCharacteristic *myCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"] properties:CBCharacteristicPropertyRead value:[@"123456789" dataUsingEncoding:NSUTF8StringEncoding] permissions:CBAttributePermissionsReadable];
CBMutableService *myService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"12AB"] primary:YES];
myService.characteristics = @[myCharacteristic];
[_peripheralManager addService:myService];
}
添加服务成功执行代理方法 didAddService :
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
[service.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"\nserviceUUID: %@\nCharacteristicUUID: %@\nCharacteristicValue: %@", service.UUID.UUIDString, obj.UUID.UUIDString, [[NSString alloc] initWithData:obj.value encoding:NSUTF8StringEncoding]);
}];
}
打印结果:
serviceUUID: 12AB
CharacteristicUUID: 71DA3FD1-7E10-41C1-B16F-4430B506CDE7
CharacteristicValue: 123456789
发送广播:
- (void)setupPeripheralAdvertising {
if (!_peripheralManager.isAdvertising) {
CBMutableCharacteristic *myCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"71DA3FD1-7E10-41C1-B16F-4430B506CDE7"] properties:CBCharacteristicPropertyRead value:[@"123456789" dataUsingEncoding:NSUTF8StringEncoding] permissions:CBAttributePermissionsReadable];
CBMutableService *myService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"12AB"] primary:YES];
myService.characteristics = @[myCharacteristic];
[_peripheralManager addService:myService];
[_peripheralManager startAdvertising:@{
CBAdvertisementDataLocalNameKey : @"测试设备",
CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"12AB"]]
}];
[SAAudioSound sa_playPassSound];
}else {
[_peripheralManager stopAdvertising];
[SAAudioSound sa_playWarningSound];
}
}
广播发送开启成功后会走协议方法:peripheralManagerDidStartAdvertising
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(nullable NSError *)error {
NSLog(@"正在广播");
}
当连接到中心设备时便可接收读或写的请求,接收到请求时会走相应的协议方法:
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
}
附:
GATT服务UUID: https://www.bluetooth.com/specifications/gatt/services
GATT特征UUID: https://www.bluetooth.com/specifications/gatt/characteristics