Подтвердить что ты не робот

Чтение длинных значений характеристик с помощью CoreBluetooth

У меня есть характеристическое значение, которое содержит данные для изображения. В периферийном я устанавливаю значение, подобное этому:

_photoUUID = [CBUUID UUIDWithString:bPhotoCharacteristicUUID];
_photoCharacteristic = [[CBMutableCharacteristic alloc] initWithType:_photoUUID
                                                          properties:CBCharacteristicPropertyRead
                                                               value:Nil
                                                         permissions:CBAttributePermissionsReadable];

Я понимаю, что при запросе этого значения будет вызван обратный вызов didRecieveRequest:

-(void) peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {

    if ([request.characteristic.UUID isEqual:_photoUUID]) {
        if (request.offset > request.characteristic.value.length) {
            [_peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
            return;
        }
        else {
            // Get the photos
            if (request.offset == 0) {
                _photoData = [NSKeyedArchiver archivedDataWithRootObject:_myProfile.photosImmutable];
            }

            request.value = [_photoData subdataWithRange:NSMakeRange(request.offset, request.characteristic.value.length - request.offset)];
            [_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
        }
    }
}

Это в значительной степени связано с документацией Apple. На центральной стороне в обратном вызове didDiscoverCharacteristic у меня есть следующий код:

if ([characteristic.UUID isEqual:_photoUUID]) {
    _photoCharacteristic = characteristic;
    [peripheral readValueForCharacteristic:characteristic];
}

Что, в свою очередь, вызывает обратный вызов didUpdateValueCorCharacteristic:

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    NSLog(@"updated value for characteristic");

    if ([characteristic.UUID isEqual:_photoUUID]) {
        NSArray * photos = [NSKeyedUnarchiver unarchiveObjectWithData:characteristic.value];
    }
}

Все вызовы вызываются, но когда я пытаюсь перестроить массив, он поврежден, потому что не все данные переданы правильно. Я ожидаю, что callback didRecieveReadRequest будет вызываться несколько раз с различным смещением каждый раз. Однако он называется только один раз.

Мне было интересно, знает ли кто-нибудь, что я делаю неправильно?

4b9b3361

Ответ 1

Я предполагаю, что вы натыкаетесь на ограничение по 512 байт на характерную длину. Вам нужно перейти к подпискам на характеристики и обработку обновлений, чтобы обойти это:

В центре:

  • Подпишитесь на характеристику, вызвав -[CBPeripheral setNotifyValue:forCharacteristic] (с YES в качестве значения уведомления).

  • В -peripheral:didUpdateValueForCharacteristic:error каждое обновление будет либо представлять собой данные для добавления, либо то, что вы решите использовать на периферийной стороне, чтобы указать конец данных (для этого я использую пустой NSData). Обновите код -peripheral:didUpdateValueForCharacteristic:error, чтобы:

    • Если вы начинаете читать значение, инициализируйте приемник для входящих байтов (например, NSMutableData).
    • Если вы находитесь в середине чтения значения, вы добавляете его в раковину.
    • Если вы видите маркер EOD, вы считаете, что передача завершена. Вы можете отказаться от подписки на характеристику в этом состоянии, вызвав -[CBPeripheral setNotifyValue:forCharacteristic] со значением уведомления NO.
  • -peripheral:didUpdateNotificationStateForCharacteristic:error: - хорошее место для управления инициализацией и последующим использованием раковины, в которую вы читаете куски. Если characteristic.isNotifying обновлено до YES, у вас есть новая подписка; если он обновлен до NO, тогда вы закончите чтение. На данный момент вы можете использовать NSKeyedUnarchiver для разблокировки данных.

На периферии:

  • В -[CBMutableCharacteristic initWithType:properties:value:permissions] убедитесь, что значение properties содержит CBCharacteristicPropertyNotify.

  • Используйте -peripheralManager:central:didSubscribeToCharacteristic:, чтобы начать отправку ваших данных, а не -peripheral:didReceiveReadRequest:result:.

  • При разбиении ваших данных убедитесь, что размер вашего куска не больше central.maximumUpdateValueLength. На iOS7, между iPad 3 и iPhone 5, я обычно видел 132 байта. Если вы отправляете несколько центров, используйте наименьшее общее значение.

  • Вам нужно проверить код возврата -updateValue:forCharacteristic:onSubscribedCentrals; если базовая очередь поддерживает резервную копию, это вернет NO, и вам придется дождаться обратного вызова на -peripheralManagerIsReadyToUpdateSubscribers:, прежде чем продолжить (это, по-моему, одно из заусенцев в противном случае, гладкий API). В зависимости от того, как вы справляетесь с этим, вы можете рисовать себя в угол, потому что:

  • Если вы создаете и отправляете свои куски в той же очереди, что периферийное устройство использует для своих операций, И, делая правильную вещь и проверяя возвращаемое значение из -updateValue:forCharacteristic:onSubscribedCentrals:, легко поддерживать себя в неочевидный тупик. Вы либо захотите удостовериться, что вы получаете очередь после каждого вызова -updateValue:forCharacteristic:onSubscribedCentrals:, выполняете цикл chunking в другой очереди, чем в периферийной очереди (-updateValue:forCharacteristic:onSubscribedCentrals: будет следить за тем, чтобы его работа была выполнена в нужном месте). Или вы могли бы стать фаворитом; просто помните об этом.

Чтобы увидеть это в действии, в видеообъявлении WWDC 2012 Advanced Core Bluetooth содержится пример (общий доступ к VCards), который охватывает большую часть этого. Однако он не проверяет возвращаемое значение для обновления, поэтому они избегают ошибок в # 4 вообще.

Надеюсь, что это поможет.