2014-06-22 9 views
17

Ho un'app che ha bisogno di rilevare i dispositivi nelle vicinanze (nel raggio per Bluetooth LE) che eseguono la stessa applicazione e iOS 7.1. Ho considerato due alternative per la rilevazione:Come rilevare i dispositivi nelle vicinanze con Bluetooth LE in iOS 7.1 sia in background che in primo piano?

  1. Avere i dispositivi agire come iBeacons e rilevare iBeacons nella gamma
  2. Uso CoreBluetooth (come nella realizzazione Vicinity here) per creare una periferica BLE, pubblicizzare che e eseguire la scansione del periferiche

sembra che l'opzione 1 è fuori questione perché:

  • si può prendere almeno 15 minuti per iOS a rilevano entrando in una regione faro quando l'applicazione è in esecuzione di sfondo (iOS 7,1)

opzione 2 sembra la strada da percorrere, ma ci sono alcune difficoltà per quanto riguarda l'attuazione:

  • iOS sembra cambiare la periferica UUID nei pacchetti pubblicitari dopo un certo periodo di tempo (circa 15 minuti?). Ciò significa che non è direttamente possibile identificare il dispositivo pubblicitario dal segnale di trasmissione pubblicitaria.

A questo proposito, ho le seguenti domande:

  • Ci sono altri metodi d'esecuzione del rilevamento dispositivo vicino che non ho preso in considerazione?
  • È possibile identificare il dispositivo tramite la pubblicità (o con altri mezzi) in modo che l'opzione 2 funzioni?

risposta

20

ho trovato un modo per fare questo lavoro Nucleo Bluetooth (opzione 2), la procedura è più o meno il seguente:

  • L'applicazione pubblicizza con un identificatore unico dispositivo codificato in CBAdvertisementDataLocalNameKey (quando l'applicazione di trasmissione gira in primo piano) e una caratteristica che fornisce l'identificatore univoco del dispositivo tramite un servizio Bluetooth LE (quando l'applicazione di trasmissione viene eseguita in background)
  • Allo stesso tempo, l'applicazione scansiona altre periferiche con lo stesso servizio.

La pubblicità funziona come segue:

  • Per gli altri dispositivi per essere in grado di identificare questo dispositivo, io uso un UUID univoco per ogni dispositivo (sto usando di Urban Airship [UAUtils deviceID], perché è il identificatore di dispositivo in altre parti del programma, anche - ma si potrebbe anche usare qualsiasi implementazione ID univoca).
  • Quando l'applicazione è in esecuzione in primo piano, posso passare l'ID univoco del dispositivo direttamente nel pacchetto di annunci utilizzando CBAdvertisementDataLocalNameKey.La rappresentazione UUID standard è troppo lungo, per cui uso una forma abbreviata del UUID come segue:

    + (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; 
    } 
    
  • Quando l'applicazione è in esecuzione sullo sfondo, la pubblicità pacchetto viene spogliato e CBAdvertisementDataLocalNameKey non si passa più lungo. Per questo, l'applicazione ha bisogno di pubblicare una caratteristica che fornisce l'identificatore univoco del dispositivo:

    - (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 scansione funziona come segue:

  • si inizia a eseguire la scansione di periferiche con il determinato servizio di UUID come segue (si noti che è necessario specificare l'UUID del servizio, perché altrimenti sfondo dell'acquisizione non riesce a trovare il dispositivo):

    [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:DEVICE_IDENTIFIER_SERVICE_UUID]] 
        options:scanOptions]; 
    
  • Quando un dispositivo viene scoperto a - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI di verificare che se advertisementData[CBAdvertisementDataLocalNameKey] esiste e cercare di riconvertirlo in forma UUID come questo:

    + (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; 
    } 
    
  • Se la conversione non riesce si conosce il dispositivo di trasmissione è in background , ed è necessario connettersi al dispositivo di leggere il caratteristica che fornisce l'identificatore univoco. Per questo è necessario utilizzare [self.central connectPeripheral:peripheral options:nil]; (con peripheral.delegate = self; e realizzare una catena di metodi delegato come segue:?

    - (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); 
        } 
    } 
    
può
+0

una scansione dispositivo e promuovere al tempo stesso –

+0

Sì, sembra così –

+1

Questo post. è stato estremamente utile, grazie! Una nota, in didDiscoverPeripheral se la periferica che viene rilevata è in background al momento della scoperta l'UUID del servizio può essere trovato nell'annuncioData utilizzando CBAdvertisementDataOverflowServiceUUIDsKey invece di CBAdvertisementDataServiceUUIDsKey. – Tron5000