使用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字节的特征长度限制。 你需要移动到订阅特征和处理更新来解决这个问题:
在中央:
-
通过调用
-[CBPeripheral setNotifyValue:forCharacteristic]
(以YES作为通知值)来订阅特性。 -
在
-peripheral:didUpdateValueForCharacteristic:error
,每一个更新将要么追加数据,要么你select使用外围的一面来指示数据结束(我用这个空的NSData
)。 更新您的-peripheral:didUpdateValueForCharacteristic:error
代码,以便:- 如果你开始读取一个值,为传入的字节初始化接收器(例如一个
NSMutableData
)。 - 如果你正在阅读价值的中间,你可以追加到水槽。
- 如果您看到EOD标记,则认为传输已完成。 您可能希望通过在通知值为NO的情况下调用
-[CBPeripheral setNotifyValue:forCharacteristic]
取消订阅此状态下的特征。
- 如果你开始读取一个值,为传入的字节初始化接收器(例如一个
-
-peripheral:didUpdateNotificationStateForCharacteristic:error:
是pipe理初始化和稍后使用读入块的接收器的好地方。 如果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:
上的callback-peripheralManagerIsReadyToUpdateSubscribers:
在继续之前(这是我认为在其他平滑的API中的毛刺之一)。 根据你如何处理这个问题,你可以画一个angular落,因为: -
如果你正在构build和发送你的数据块到外围设备正在使用的同一个队列中,并且做了正确的事情,并且检查了来自
-updateValue:forCharacteristic:onSubscribedCentrals:
的返回值,明显的僵局。 你要么确保在每次调用-updateValue:forCharacteristic:onSubscribedCentrals:
之后产生队列-updateValue:forCharacteristic:onSubscribedCentrals:
在与外设队列不同的队列上执行你的分块循环(-updateValue:forCharacteristic:onSubscribedCentrals:
将确保它的工作在正确的地方完成)。 或者你可以更奇特的; 只是要注意这一点。
为了看到这一点,WWDC 2012高级核心蓝牙video包含一个涵盖大部分内容的示例(共享video)。 但是,它不检查更新的返回值,所以他们完全避免了#4的陷阱。
希望有所帮助。