Comment détecter les appareils à proximité avec Bluetooth LE dans iOS 7.1 à la fois en arrière-plan et en avant-plan?

j'ai une application qui a besoin de détecter un périphérique proche (à portée de Bluetooth LE) qui exécute la même application et IOS 7.1. J'ai envisagé deux alternatives pour la détection:

  1. faire en sorte que les dispositifs agissent comme des iBeacons et détectent les iBeacons dans la gamme
  2. utilisation de CoreBluetooth (comme dans la mise en oeuvre de proximité ici) pour créer un périphérique blable, annoncez-le et scannez les périphériques

il semble que l'option 1 ne soit pas la question parce que:

  • Il peut prendre au moins 15 minutes pour iOS pour détecter de la saisie d'un phare de la région lorsque l'application s'exécute en arrière-plan (iOS 7.1)

L'Option 2 semble la voie à suivre, mais il y a quelques difficultés concernant la mise en oeuvre:

  • iOS semble changer l'UUID périphérique dans les paquets de publicité après une certaine période de temps (environ 15 minutes?). Cela signifie qu'il n'est pas directement possible d'identifier les dispositif publicitaire du signal de diffusion de publicité.

a ce sujet, j'ai les questions suivantes:

  • y a-t-il d'autres méthodes de mise en oeuvre de la détection des appareils à proximité dont je n'ai pas tenu compte?
  • est-il possible d'identifier l'appareil par la publicité (ou par d'autres moyens) de sorte que l'option 2 fonctionnerait?
18
demandé sur Markus Rautopuro 2014-06-22 19:29:15

1 réponses

j'ai trouvé un moyen de rendre ce travail Bluetooth Core (option 2), la procédure est à peu près la suivante:

  • l'application se fait de la publicité avec un identifiant unique de périphérique encodé dans CBAdvertisementDataLocalNameKey (lorsque l'application de radiodiffusion court à l'avant-plan) et une caractéristique qui fournit l'identificateur unique de l'appareil par le biais D'un service Bluetooth LE (lorsque l'application de radiodiffusion court à l'arrière-plan)
  • en même temps, la demande scanne d'autres périphériques avec le même service.

La publicité fonctionne de la manière suivante:

  • pour que les autres appareils puissent identifier cet appareil, j'utilise un UUID unique par appareil (J'utilise les[UAUtils deviceID], parce que c'est l'identifiant du périphérique dans d'autres parties du programme, aussi - mais vous pouvez aussi utiliser n'importe quelle implémentation D'ID unique).
  • quand l'application est en avant plan, je peux passer le ID unique de l'appareil directement dans le paquet de publicité en utilisant CBAdvertisementDataLocalNameKey. La représentation UUID standard est trop longue, donc j'utilise une forme raccourcie de L'UUID comme suit:

    + (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;
    }
    
  • lorsque l'application exécute background, le paquet publicitaire est dépouillé et CBAdvertisementDataLocalNameKey n'est plus transmise. Pour cela, l'application doit publier un caractéristique qui fournit le dispositif unique identificateur:

    - (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];
            }
        }
    }
    

la numérisation fonctionne comme suit:

  • vous commencez à scanner les périphériques avec le service UUID comme suit (notez que vous devez spécifier le service UUID, car sinon background scan ne trouve pas le périphérique):

    [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:DEVICE_IDENTIFIER_SERVICE_UUID]]
        options:scanOptions];
    
  • Lorsqu'un périphérique est découvert à - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI vérifier que si advertisementData[CBAdvertisementDataLocalNameKey] existe et essayer de le convertir de nouveau à la forme UUID comme ceci:

    + (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;
    }
    
  • si la conversion échoue, vous savez que le dispositif de diffusion est en arrière-plan, et vous devez se connecter à l'appareil de lire le caractéristique qui fournit l'identifiant unique. Pour cela, vous devez utiliser [self.central connectPeripheral:peripheral options:nil]; (avec peripheral.delegate = self; et de mettre en œuvre une chaîne de délégué méthodes comme suit:

    - (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);
        }
    }
    
20
répondu Markus Rautopuro 2014-06-24 12:13:15