我有一个特征值,其中包含图像数据。在外围设备中,我设置的值是这样的:

_photoUUID = [CBUUID UUIDWithString:bPhotoCharacteristicUUID];
_photoCharacteristic = [[CBMutableCharacteristic alloc] initWithType:_photoUUID
                                                          properties:CBCharacteristicPropertyRead
                                                               value:Nil
                                                         permissions:CBAttributePermissionsReadable];
我的理解是,当请求此值时,将调用didReceiveReadRequest回调:
-(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];
}
依次调用didUpdateValueForCharacteristic回调:
- (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回调被多次调用,每次都有不同的偏移量。但是,它仅被调用一次。
我想知道是否有人知道我在做什么错?

最佳答案

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

在中央:

  • 通过调用-[CBPeripheral setNotifyValue:forCharacteristic](以YES作为通知值)订阅特征。
  • -peripheral:didUpdateValueForCharacteristic:error中,每个更新要么是要追加的数据,要么是您选择在外围设备上用来表示数据结束的数据(为此,我使用了一个空的NSData)。更新您的-peripheral:didUpdateValueForCharacteristic:error代码,以便:
  • 如果您开始读取值,请为接收的字节初始化接收器(例如NSMutableData)。
  • 如果您正在读取一个值,请附加到接收器。
  • 如果看到EOD标记,则认为传输已完成。您可能希望通过在通知状态下使用nott值调用-[CBPeripheral setNotifyValue:forCharacteristic]来取消订阅该特性。
  • -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:之后就产生队列,请在与外设队列不同的队列上执行分块循环(-updateValue:forCharacteristic:onSubscribedCentrals:将确保其工作在正确的位置进行)。否则您可能会变得更幻想;请注意这一点。

  • 为了实际操作,WWDC 2012 Advanced Core蓝牙视频包含一个示例(共享VCard),其中涵盖了大部分内容。但是,它不检查更新的返回值,因此它们完全避免了#4的陷阱。

    希望能有所帮助。

    关于ios - 使用CoreBluetooth读取长特征值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19280429/

    10-10 20:31