使用CoreBluetooth读取长特征值

问题描述 投票:13回答:1

我有一个包含图像数据的特征值。在外围设备中我设置了如下值:

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

调用所有回调但是当我尝试重新构造数组时,它已损坏,因为并非所有数据都被正确传输。我希望didRecieveReadRequest回调可以多次调用,每次都有不同的偏移量。然而它只被召唤一次。

我想知道是否有人知道我做错了什么?

ios objective-c bluetooth core-bluetooth
1个回答
32
投票

我猜你正在突破特征长度的512字节限制。您需要转到订阅特征和处理更新以解决此问题:

在中央:

  1. 通过调用-[CBPeripheral setNotifyValue:forCharacteristic]订阅特征(使用YES作为通知值)。
  2. -peripheral:didUpdateValueForCharacteristic:error中,每次更新都将是要附加的数据,或者您选择在外围端使用以指示数据结束的内容(我使用空的NSData)。更新您的-peripheral:didUpdateValueForCharacteristic:error代码,以便: 如果您开始读取值,请为传入的字节初始化接收器(例如,NSMutableData)。 如果您正在读取值,则会附加到接收器。 如果您看到EOD标记,则认为传输已完成。您可以通过调用通知值为NO的-[CBPeripheral setNotifyValue:forCharacteristic]来取消订阅此状态下的特征。
  3. -peripheral:didUpdateNotificationStateForCharacteristic:error:是管理初始化和以后使用读取块的接收器的好地方。如果characteristic.isNotifying更新为YES,您有新的订阅;如果它更新到NO然后你已经读完了。此时,您可以使用NSKeyedUnarchiver取消归档数据。

在外围:

  1. -[CBMutableCharacteristic initWithType:properties:value:permissions],确保properties值包括CBCharacteristicPropertyNotify
  2. 使用-peripheralManager:central:didSubscribeToCharacteristic:启动数据的分块发送,而不是-peripheral:didReceiveReadRequest:result:
  3. 在分块数据时,请确保您的块大小不大于central.maximumUpdateValueLength。在iOS7上,在iPad 3和iPhone 5之间,我通常看到132个字节。如果您要发送到多个中心,请使用最小公共值。
  4. 你想检查-updateValue:forCharacteristic:onSubscribedCentrals的返回码;如果底层队列备份,这将返回NO,你必须等待-peripheralManagerIsReadyToUpdateSubscribers:上的回调才能继续(我认为这是其他平滑API中的一个毛刺)。根据你如何处理这个问题,你可以把自己画成一个角落,因为:
  5. 如果你在外围设备用于其操作的同一队列上构建和发送你的块,并且做正确的事情并检查-updateValue:forCharacteristic:onSubscribedCentrals:的返回值,那么很容易让自己陷入非明显的死锁状态。您要么确保在每次调用-updateValue:forCharacteristic:onSubscribedCentrals:之后产生队列,请在与外围队列不同的队列上执行组块循环(-updateValue:forCharacteristic:onSubscribedCentrals:将确保其工作在正确的位置完成)。或者你可以变得更加漂亮;请注意这一点。

为了看到这一点,WWDC 2012高级核心蓝牙视频包含一个涵盖大部分内容的示例(共享VCards)。但是,它不会检查更新的返回值,因此它们完全避免了#4中的陷阱。

希望有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.