如何在iOS 7.1的后台和前台中使用蓝牙LE检测附近的设备?

时间:2014-06-22 15:29:16

标签: ios iphone bluetooth bluetooth-lowenergy core-bluetooth

我有一个应用需要检测运行相同应用程序和iOS 7.1的附近(蓝牙LE范围内)设备。我已经考虑了两种检测方法:

  1. 让设备充当iBeacons并检测范围内的iBeacons
  2. 使用CoreBluetooth(如在Vicinity实现here中)创建BLE外设,宣传并扫描外围设备
  3. 似乎选项1是不可能的,因为:

    • 当应用程序运行后台时,iOS可能需要至少15分钟才能检测到进入信标区域(iOS 7.1)

    选项2似乎还有待实施,但实施方面存在一些困难:

    • iOS似乎会在一段时间(大约15分钟后)后更改广告包中的外围UUID。这意味着不能直接从广告广播信号中识别广告设备。

    关于这一点,我有以下问题:

    • 有没有其他方法可以实现附近的设备检测?我还没有考虑过?
    • 是否可以通过广告(或通过其他方式)识别设备,以便选项2可以工作?

1 个答案:

答案 0 :(得分:20)

我找到了一种方法让这项工作成为Core Bluetooth(选项2),程序大致如下:

  • 应用程序使用CBAdvertisementDataLocalNameKey中的编码设备唯一标识符(当广播应用程序运行前景时)和通过蓝牙LE服务提供设备唯一标识符的特征(<广播应用程序运行背景)
  • 同时,应用程序使用相同的服务扫描其他外围设备

广告的工作原理如下:

  • 为了能够识别此设备的其他设备,我使用每设备唯一的UUID(我使用Urban Airship&#39; [UAUtils deviceID],因为它是设备程序其他部分的标识符也是 - 但您也可以使用任何唯一的ID实现。)
  • 当应用程序运行前台时,我可以使用CBAdvertisementDataLocalNameKey直接在广告包中传递设备唯一ID 。标准的UUID表示太长了,所以我使用UUID的缩写形式如下:

    + (NSString *)shortenedDeviceID
    {
        NSString *deviceID = [UAUtils deviceID];
    
        NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:deviceID];
        uuid_t uuidBytes;
        [uuid getUUIDBytes:uuidBytes];
    
        NSData *data = [NSData dataWithBytes:uuidBytes length:16];
        NSString *base64 = [data base64EncodedStringWithOptions:0];
        NSString *encoded = [[[base64
                               stringByReplacingOccurrencesOfString:@"/" withString:@"_"]
                              stringByReplacingOccurrencesOfString:@"+" withString:@"-"]
                             stringByReplacingOccurrencesOfString:@"=" withString:@""];
        return encoded;
    }
    
  • 当应用程序运行后台时,广告数据包将被剥离,CBAdvertisementDataLocalNameKey不再传递。为此,应用程序需要发布提供唯一设备标识符的特性

    - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
    {
        if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
            [self startAdvertising];
    
            if (peripheralManager) {
                CBUUID *serviceUUID = [CBUUID UUIDWithString:DEVICE_IDENTIFIER_SERVICE_UUID];
                CBUUID *characteristicUUID = [CBUUID UUIDWithString:DEVICE_IDENTIFIER_CHARACTERISTIC_UUID];
                CBMutableCharacteristic *characteristic =
                [[CBMutableCharacteristic alloc] initWithType:characteristicUUID
                                                   properties:CBCharacteristicPropertyRead
                                                        value:[[MyUtils shortenedDeviceID] dataUsingEncoding:NSUTF8StringEncoding]
                                                  permissions:CBAttributePermissionsReadable];
                CBMutableService *service = [[CBMutableService alloc] initWithType:serviceUUID primary:YES];
                service.characteristics = @[characteristic];
                [peripheralManager addService:service];
            }
        }
    }
    

扫描工作如下:

  • 您开始使用特定服务UUID扫描外围设备,如下所示(请注意您需要指定服务UUID,否则后台扫描无法找到设备):

    [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:DEVICE_IDENTIFIER_SERVICE_UUID]]
        options:scanOptions];
    
  • - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI处发现设备时,您会检查advertisementData[CBAdvertisementDataLocalNameKey]是否存在,并尝试将其转换回UUID表格,如下所示:

    + (NSString *)deviceIDfromShortenedDeviceID:(NSString *)shortenedDeviceID
    {
        if (!shortenedDeviceID)
            return nil;
        NSString *decoded = [[[shortenedDeviceID
                               stringByReplacingOccurrencesOfString:@"_" withString:@"/"]
                              stringByReplacingOccurrencesOfString:@"-" withString:@"+"]
                             stringByAppendingString:@"=="];
    
        NSData *data = [[NSData alloc] initWithBase64EncodedString:decoded options:0];
        if (!data)
            return nil;
    
        NSUUID *uuid = [[NSUUID alloc] initWithUUIDBytes:[data bytes]];
    
        return uuid.UUIDString;
    }
    
  • 如果转换失败,您知道广播设备在后台,您需要连接到设备以读取提供唯一标识符的特征 。为此,您需要使用[self.central connectPeripheral:peripheral options:nil];(使用peripheral.delegate = self;并实现一系列委托方法,如下所示:

    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
    {
        [peripheral discoverServices:@[[CBUUID UUIDWithString:DEVICE_IDENTIFIER_SERVICE_UUID]]];
    }
    
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
    {
        if (!error) {
            for (CBService *service in peripheral.services) {
                if ([service.UUID.UUIDString isEqualToString:DEVICE_IDENTIFIER_SERVICE_UUID]) {
                    NSLog(@"Service found with UUID: %@", service.UUID);
                    [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:DEVICE_IDENTIFIER_CHARACTERISTIC_UUID]] forService:service];
                }
            }
        }
    }
    
    - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
    {
        if (!error) {
            for (CBCharacteristic *characteristic in service.characteristics) {
                if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:DEVICE_IDENTIFIER_CHARACTERISTIC_UUID]]) {
                    [peripheral readValueForCharacteristic:characteristic];
                }
            }
        }
    }
    
    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
    {
        if (!error) {
            NSString *shortenedDeviceID = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
            NSString *deviceId = [MyUtils deviceIDfromShortenedDeviceID:shortenedDeviceID];
            NSLog(@"Got device id: %@", deviceId);
        }
    }