iOS 蓝牙开发

iOS 蓝牙开发_第1张图片
Bluetooth.png

蓝牙介绍

本文要介绍的CoreBluetooth,专门用于与BLE设备通讯。并且现在很多蓝牙设备都支持4.0,4.0以其低功耗著称,所以一般也叫BLE(Bluetooth Low Energy),所以也是在iOS比较推荐的一种开发方法。

基本概念

iOS 蓝牙开发_第2张图片
image.png
  • 中心设备(Central):发起连接,用来扫描周围蓝牙硬件的设备;
  • 周边设备(Peripheral):被连接的设备;
  • 服务(Service):特征和关系的集合,它封装了设备的一部分的行为;
  • 特征(Characteristic):可以理解成一个Service模块具体提供哪些服务,特征会有一个value,一般我们向蓝牙设备写入数据、从蓝牙设备读取数据就是这个value;
  • UUID:区分不同服务和特征的唯一标识,使用该字端我们可以获取我们想要的服务或者特征。
  • 广播:外部设备不停的散播的蓝牙信号,让中心设备可以扫描到,也是我们开发中接收数据的入口。

CoreBluetooth介绍

iOS 蓝牙开发_第3张图片
image.png

在CoreBluetooth中有两个主要的部分,Central和Peripheral,CBPeripheralManager 作为周边设备。CBCentralManager作为中心设备。所有可用的iOS设备可以作为周边设备(Peripheral)也可以作为中心设备(Central),但不可以同时既是周边设备也是中心设备。

Central

作为 Central 模式使用的操作步骤 :

  1. 创建一个 Central Manager;
  2. 发现周边设备;
  3. 发现周边设备的服务及特征;
  4. 读取特征值或对指定特征设置监听;
  5. 设置可写入的特征值;
  6. 读取特征的描述Descriptor;

创建了一个 tableView 来扫描到的周边设备的名字、信号强度等;

导入CoreBluetooth

#import 

创建一个管理者centralManager,给当前类签订协议

_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 *)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 模式使用的操作步骤 :

  1. 创建一个 Peripheral Manager;
  2. 为外设添加服务;
  3. 发送广播

创建一个管理者peripheralManager,给当前类签订协议

_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 *)requests {

}

附:

GATT服务UUID: https://www.bluetooth.com/specifications/gatt/services

GATT特征UUID: https://www.bluetooth.com/specifications/gatt/characteristics

你可能感兴趣的:(iOS 蓝牙开发)