簡體   English   中英

使用 CoreBluetooth 讀取長特征值

[英]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 字節限制。 您需要轉向訂閱特征和處理更新以解決此問題:

在中央:

  1. 通過調用-[CBPeripheral setNotifyValue:forCharacteristic] (以 YES 作為通知值)訂閱特征。

  2. -peripheral:didUpdateValueForCharacteristic:error ,每次更新要么是要追加的數據,要么是您選擇在外設端使用以指示數據結束的內容(為此我使用了一個空的NSData )。 更新您的-peripheral:didUpdateValueForCharacteristic:error代碼,以便:

    • 如果您開始讀取值,請為傳入的字節(例如NSMutableData )初始化接收器。
    • 如果您正在讀取一個值,則將其附加到接收器。
    • 如果您看到 EOD 標記,則認為傳輸已完成。 您可能希望取消訂閱此狀態下的特征,方法是調用-[CBPeripheral setNotifyValue:forCharacteristic] ,通知值為 NO。
  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 高級核心藍牙視頻包含一個示例(共享 VCard),其中涵蓋了大部分內容。 但是,它不會檢查更新的返回值,因此它們完全避免了 #4 中的陷阱。

希望有幫助。

我嘗試了 Cora Middleton 描述的方法,但無法讓它發揮作用。 如果我正確理解她的方法,她會通過更新通知發送所有部分數據。 對我來說,問題似乎是如果這些通知中的值經常在短時間內連續更改,則無法保證中央讀取每個更新。

所以因為這種方法不起作用,我做了以下事情:

  • 我使用一些特性來跟蹤外圍設備的狀態。 此特性將僅包含一些標志,並且會在一個或多個標志更改時發出通知。 用戶在外圍設備上的交互會改變狀態,並且用戶可以在外圍設備上執行一項操作來觸發從連接的中心下載。

  • 要從中央下載的數據被添加到外設的堆棧中。 堆棧中的最后一項是終止符指示符(一個空的NSData對象)

  • 中央登記接收上述狀態特征的通知。 如果設置了某個標志,則觸發下載。

  • 在外設方面,每次收到某個特征的讀取請求時,我從堆棧中取出 1 個項目並返回該項目。

  • 在中央,我添加了從讀取請求返回的所有數據。 如果檢索到空數據值,則我從返回的數據創建一個對象(在我的例子中它是一個 JSON 字符串)。

  • 在外設方面,我也知道在返回空 NSData 對象后下載已完成,因此之后我可以再次更改外設的狀態。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM