iOS BLE 開發小記[2] 如何實現一個 Local Central

歡迎訪問個人博客 muhlenXi,該文章出自個人博客,歡迎轉載,轉載請註明來源: muhlenxi.com/2017/04/29/…html

導語:

在這一節,你將會學到,如何經過 CoreBluetooth 框架來實現 Local Central 方面的功能和代理方法。數組

在 BLE 通訊中,實現了 Central 規範的設備,可以調用許多經常使用的方法,好比搜索和鏈接可用的 Peripheral,而後與 Peripheral 提供的數據進行交互。可是實現了 Peripheral 規範的設備一樣可以調用許多常見的方法,可是也有一些不一樣。好比,發佈和廣播 Service,對 Central 的讀寫請求進行響應以及 響應Central 的訂閱請求。app

在這個章節中,你將會學習在 Central 端如何使用 Core Bluetooth 框架來執行通用的 BLE 方法。基於代碼的示例將會引導和協助你在你的本地設備上實現 Central 的角色。尤爲,你將會學到:框架

  • 如何建立一個 Central Manager 對象
  • 如何搜索和鏈接一個正在廣播信息的 Peripheral
  • 鏈接成功後如何與 Peripheral 的數據進行交互
  • 如何發送讀取或寫入請求給 Peripheral Service 的 Characteristic
  • 如何訂閱一個當數據更新時就會發出通知的 Characteristic

在下個章節,你將會學習如何在你的本地設備上實現 Peripheral 的角色。學習

你也許會發現本章節中的代碼有點簡單和抽象,在你的真實 App 中,你須要作些恰當的改變。更高級的開發技能能夠參考後續的章節。ui

Central 實現詳情

在本文中,你的 ViewController 須要遵循 CBCentralManagerDelegateCBPeripheralDelegate 代理協議。spa

建立 Central Manager

由於在 CoreBluetooth 中是經過面向對象的思想用一個 CBCentralManager(中心管理) 對象來表示一個 Local Central 設備,因此在調用該對象的方法以前須要先 allocate(分配)和 initialize(初始化)一個 Central Manager 實例。能夠經過 initWithDelegate:queue:options: 方法來初始化一個 Central Manager 對象。代理

myCentralManager =
        [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
複製代碼

在示例代碼中,設置 Central Manager 的 Delegate 爲 self,是爲了接收 Central 的事件響應。 參數 dispatch queue(調度隊列)設置爲 nil,表示 Central Manager 是在 main queue(主隊列)中分發響應事件。rest

當你建立一個 Central Manager 對象時,Central Manager 會調用 centralManagerDidUpdateState: 方法來代理回調。所以你必須實現這個代理方法來確保 Central 設備可以使用 BLE 技術,代理方法的詳情見 CBCentralManagerDelegate Protocol Referencecode

搜索正在廣播數據的 Peripheral 設備

初始化 Central Manger 對象後,第一個任務就是搜索周圍的 Peripheral,上一篇曾提到過, Peripheral 經過廣播數據的方式來顯示它們的存在,你能夠經過 scanForPeripheralsWithServices:options: 方法來搜索周圍正在廣播數據的 Peripheral 設備。

[myCentralManager scanForPeripheralsWithServices:nil options:nil];
複製代碼

關於參數說明:若是第一個參數置爲 nil 時,Central Manager 會返回全部正在廣播數據的 Peripheral 設備。在實際 APP 開發中,經過指定一個包含 CBUUID 對象的數組來獲取指定的 Peripheral,其中用一個 UUID(通用惟一識別碼)來表示 Peripheral 正在廣播的一個服務。關於 CBUUID 對象的詳情見 Services and Characteristics Are Identified by UUIDs.

每當 Central Manager 搜索到一個 Peripheral 設備時,就會經過 代理方法 centralManager:didDiscoverPeripheral:advertisementData:RSSI: 進行回調,新發現的 Peripheral 會以 CBPeripheral 對象的方式返回。若是你後面須要鏈接這個 Peripheral,須要用一個 CBPeripheral 類型的 Strong Reference(強引用)來指向這個對象,這樣系統暫時就不會釋放這個對象了。

- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI {
 
    NSLog(@"Discovered %@", peripheral.name);
    self.discoveredPeripheral = peripheral;
    // ...
}
複製代碼

當你須要鏈接多個 Peripheral 設備時,須要使用一個 NSArray 來保存這些搜索到的 Peripheral,無論怎樣,爲了減小電量損耗,增長續航時間,只要搜索到你須要鏈接的 Peripheral 時,就能夠中止搜索了。經過下面的方法能夠中止搜索了。

[myCentralManager stopScan];
複製代碼

鏈接剛發現的 Peripheral 設備

能夠調用 connectPeripheral:options: 方法來鏈接你想要鏈接的 Peripheral 設備。

[myCentralManager connectPeripheral:peripheral options:nil];
複製代碼

若是鏈接成功,Central 會經過 centralManager:didConnectPeripheral: 方法進行代理回調。在你與 Peripheral 進行交互時,你須要設置 Peripheral 的 Delegate 爲 self 來確保能收到 Peripheral 的代理回調。

- (void)centralManager:(CBCentralManager *)central
  didConnectPeripheral:(CBPeripheral *)peripheral {
 
    NSLog(@"Peripheral connected");
    peripheral.delegate = self;
    // ...
}
複製代碼

搜索剛鏈接 Peripheral 的 Service

當與 Peripheral 成功創建鏈接後,你就能夠獲取 Peripheral 的 Service 數據了,第一步就是搜索 Peripheral 提供的可用 Service。由於 Peripheral 對廣播的數據包大小有限制,因此你可能會搜索到除了廣播的 Service 以外的 其餘 Service,你能夠經過 discoverServices: 方法搜索 Peripheral 提供的全部的 Service。

[peripheral discoverServices:nil];
複製代碼

提示:在實際開發中,傳入的參數通常不爲 nil,傳入 nil 會返回所有的可用 Service,爲了節省電量以及一些沒必要要的時間浪費,經過指定一個 Service Array(包含 UUID 對象)爲參數,來獲取你想要了解的 Service 的信息,詳情見 Explore a Peripheral’s Data Wisely.

當搜索到指定的 Service 時,Peripheral 對象會經過 peripheral:didDiscoverServices: 方法進行代理回調。CoreBluetooth 會生成一個數組用來保存 CBService 對象,你指定的 Service 就被包含在這個數組中。你能夠經過實現下面這個代理方法來獲取 Service 數組。

- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverServices:(NSError *)error {
 
    for (CBService *service in peripheral.services) {
        NSLog(@"Discovered service %@", service);
        // ...
    }
    // ...
}
複製代碼

搜索 Service 的 Characteristic

當你找到指定的 Service 以後,你下一步要作的就是搜索這個 Service 中提供的全部的 Characteristic,經過調用 discoverCharacteristics:forService: 方法來搜索指定的 Service 的 Characteristic。

[peripheral discoverCharacteristics:nil forService:interestingService];
複製代碼

提示:在實際開發中,第一個參數通常不傳入 nil,由於你須要的 Characteristic 也許是全部 Characteristic 的一部分,爲了節省電量以及一些沒必要要的時間浪費,一般指定一個 Characteristic Array(包含 UUID 對象)爲參數,來獲取你想要了解的 Characteristic 的信息。

當搜索到指定的 Characteristic 後, Peripheral 會經過 peripheral:didDiscoverCharacteristicsForService:error: 方法進行代理回調,CoreBluetooth 會建立一個包含 CBCharacteristic 對象的數組用來保存指定的 Characteristic,以下是遍歷數組中的 Characteristic。

- (void)peripheral:(CBPeripheral *)peripheral
didDiscoverCharacteristicsForService:(CBService *)service
             error:(NSError *)error {
 
    for (CBCharacteristic *characteristic in service.characteristics) {
        NSLog(@"Discovered characteristic %@", characteristic);
        // ...
    }
    // ...
}
複製代碼

檢索 Characteristic 的值

讀取某個 Characteristic 的值

當你找到 Service 指定的 Characteristic,能夠經過 readValueForCharacteristic: 方法來讀取這個 Characteristic 的值。

NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
[peripheral readValueForCharacteristic:interestingCharacteristic];
複製代碼

當你嘗試去讀取一個 Characteristic 的值時, Peripheral 會經過 peripheral:didUpdateValueForCharacteristic:error: 代理回調來返回結果,你能夠經過 Characteristic 的 value 屬性來獲得這個值。

- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {
 
    NSData *data = characteristic.value;
    // parse the data as needed
    // ...
}
複製代碼

提示:並非全部的 Characteristic 的值都是可讀的,決定一個 Characteristic 的值是否可讀是經過檢查 Characteristic 的 Properties 屬性是否包含 CBCharacteristicPropertyRead 常量來判斷的。當你嘗試去讀取一個值不可讀的 Characteristic 時,Peripheral 會經過 peripheral:didUpdateValueForCharacteristic:error: 給你返回一個合適的錯誤。

訂閱一個 Characteristic 的值

經過 readValueForCharacteristic: 方法讀取一個 Characteristic 的靜態值是有效的,可是,對於動態的值,就不是一個有效的方法,由於 Characteristic 的值在實時改變,好比你的心率數據。只有經過訂閱才能獲取實時的改變值,當你訂閱一個 Characteristic 的值時,每當這個值發生改變時,你就會收到一個通知。

經過 setNotifyValue:forCharacteristic: 方法能夠訂閱指定 Characteristic 的值,該方法的第一個參數須要指定爲 YES

[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];
複製代碼

當你訂閱或取消訂閱一個 Characteristic 的值時,Peripheral 會經過調用 peripheral:didUpdateNotificationStateForCharacteristic:error: 方法進行代理回調,若是訂閱失敗了,你能夠經過這個方法獲取到發生錯誤的緣由。

- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error changing notification state: %@",
           [error localizedDescription]);
    }
    // ...
}
複製代碼

提示:並非全部的 Characteristic 都提供訂閱功能,決定一個 Characteristic 是否能訂閱是經過檢查 Characteristic 的 properties 屬性是否包含 CBCharacteristicPropertyNotify 或者 CBCharacteristicPropertyIndicate 常量來判斷的。

寫數據到 Characteristic 中

有時須要寫入一個數據到 Characteristic 中,好比,若是你的 APP 與數字恆溫器進行交互。你或許想要給數字恆溫器提供一個值,使得房間的室溫能保持在這個溫度左右。若是一個 Characteristic 的值是可寫的,你能夠經過調用 writeValue:forCharacteristic:type: 方法將一個 data 類型(NSData 對象)的值寫入到 Characteristic 中。

NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic
        type:CBCharacteristicWriteWithResponse];
複製代碼

當你寫一個數據到 Characteristic 中時,你能夠指定寫入類型,上面代碼中的寫入類型是 CBCharacteristicWriteWithResponse ,此類型時,Peripheral 會經過 peripheral:didWriteValueForCharacteristic:error: 方法來代理回調告知你是否寫入數據成功,能夠實現下面這個代理方法進行錯誤處理。

- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
             error:(NSError *)error {
 
    if (error) {
        NSLog(@"Error writing characteristic value: %@",
            [error localizedDescription]);
    }
    // ...
}
複製代碼

若是你指定寫入類型爲 CBCharacteristicWriteWithoutResponse 時,不能保證寫入操做是否有效的執行了。這時 Peripheral 不會調用任何代理方法,若是想了解 CoreBluetooth 提供的寫入類型詳情,能夠查閱 CBCharacteristicWriteType.

提示:有的 Characteristic 的值可能僅僅是可寫的,或者不是可寫的。決定 Characteristic 的值是否可寫,須要經過查看 Characteristic 的 properties 屬性是否包含 CBCharacteristicPropertyWriteWithoutResponse 或者 CBCharacteristicPropertyWrite 常量來判斷的。

參考文獻

一、Performing Common Central Role Tasks

結束語

歡迎在本文下面留言一塊兒交流心得...

相關文章
相關標籤/搜索