[英]Reading long characteristic values using CoreBluetooth
我有一個包含圖像數據的特征值。 在外圍我設置的值是這樣的:
_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
)初始化接收器。-[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:
生成隊列,在與外圍設備隊列不同的隊列上執行分塊循環( -updateValue:forCharacteristic:onSubscribedCentrals:
將確保其工作在正確的地方完成)。 或者你可以變得更狂熱; 請注意這一點。
為了看到這一點,WWDC 2012 高級核心藍牙視頻包含一個示例(共享 VCard),其中涵蓋了大部分內容。 但是,它不會檢查更新的返回值,因此它們完全避免了 #4 中的陷阱。
希望有幫助。
我嘗試了 Cora Middleton 描述的方法,但無法讓它發揮作用。 如果我正確理解她的方法,她會通過更新通知發送所有部分數據。 對我來說,問題似乎是如果這些通知中的值經常在短時間內連續更改,則無法保證中央讀取每個更新。
所以因為這種方法不起作用,我做了以下事情:
我使用一些特性來跟蹤外圍設備的狀態。 此特性將僅包含一些標志,並且會在一個或多個標志更改時發出通知。 用戶在外圍設備上的交互會改變狀態,並且用戶可以在外圍設備上執行一項操作來觸發從連接的中心下載。
要從中央下載的數據被添加到外設的堆棧中。 堆棧中的最后一項是終止符指示符(一個空的NSData
對象)
中央登記接收上述狀態特征的通知。 如果設置了某個標志,則觸發下載。
在外設方面,每次收到某個特征的讀取請求時,我從堆棧中取出 1 個項目並返回該項目。
在中央,我添加了從讀取請求返回的所有數據。 如果檢索到空數據值,則我從返回的數據創建一個對象(在我的例子中它是一個 JSON 字符串)。
在外設方面,我也知道在返回空 NSData 對象后下載已完成,因此之后我可以再次更改外設的狀態。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.