使用CoreBluetooth读取长的特征值

我有一个包含图像数据的特征值。 在外设我设置这样的值:

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

我的理解是,当这个值被请求,didRecieveRequestcallback将被称为:

 -(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的文档。 在didDiscoverCharacteristiccallback的中央侧,我有以下代码:

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

然后又调用didUpdateValueCorCharacteristiccallback:

 - (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被调用,但是当我尝试重新构造数组时,它被破坏,因为并不是所有的数据都被正确传输。 我期望didRecieveReadRequestcallback被调用多次,每次不同的偏移量。 然而它只被调用一次。

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

我猜你正在碰到512字节的特征长度限制。 你需要移动到订阅特征和处理更新来解决这个问题:

在中央:

  1. 通过调用-[CBPeripheral setNotifyValue:forCharacteristic] (以YES作为通知值)来订阅特性。

  2. -peripheral:didUpdateValueForCharacteristic:error ,每一个更新将要么追加数据,要么你select使用外围的一面来指示数据结束(我用这个空的NSData )。 更新您的-peripheral:didUpdateValueForCharacteristic:error代码,以便:

    • 如果你开始读取一个值,为传入的字节初始化接收器(例如一个NSMutableData )。
    • 如果你正在阅读价值的中间,你可以追加到水槽。
    • 如果您看到EOD标记,则认为传输已完成。 您可能希望通过在通知值为NO的情况下调用-[CBPeripheral setNotifyValue:forCharacteristic]取消订阅此状态下的特征。
  3. -peripheral:didUpdateNotificationStateForCharacteristic:error:是pipe理初始化和稍后使用读入块的接收器的好地方。 如果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:上的callback-peripheralManagerIsReadyToUpdateSubscribers:在继续之前(这是我认为在其他平滑的API中的毛刺之一)。 根据你如何处理这个问题,你可以画一个angular落,因为:

  5. 如果你正在构build和发送你的数据块到外围设备正在使用的同一个队列中,并且做了正确的事情,并且检查了来自-updateValue:forCharacteristic:onSubscribedCentrals:的返回值,明显的僵局。 你要么确保在每次调用-updateValue:forCharacteristic:onSubscribedCentrals:之后产生队列-updateValue:forCharacteristic:onSubscribedCentrals:在与外设队列不同的队列上执行你的分块循环( -updateValue:forCharacteristic:onSubscribedCentrals:将确保它的工作在正确的地方完成)。 或者你可以更奇特的; 只是要注意这一点。

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

希望有所帮助。