翻譯Core Bluetooth Programming Guide(Performing Common Central Role Tasks)

原文

執(zhí)行常規(guī)的中心角色(Central Role)任務(wù)

在低功耗藍牙(以下簡稱“藍牙”)通信中,充當中心角色的設(shè)備執(zhí)行著幾個常規(guī)任務(wù)——例如,發(fā)現(xiàn)和鏈接有效的外部設(shè)備(以下簡稱“外設(shè)”),探測和交互外設(shè)提供的數(shù)據(jù)。充當外設(shè)角色的設(shè)備也執(zhí)行著常規(guī)但是不同的任務(wù)——例如,發(fā)布和廣告服務(wù),響應(yīng)來自連接中心的讀,寫和訂閱請求。

在這一章中,你將學(xué)會如何使用Core Bluetooth框架去執(zhí)行藍牙任務(wù)的中心部分的大多數(shù)常規(guī)任務(wù)。伴隨基于代碼的例子將幫你開發(fā)應(yīng)用,以實現(xiàn)在你的本地設(shè)備上實現(xiàn)中心角色。主要的,你會學(xué)到:

  • 開啟一個中心管理器對象
  • 發(fā)現(xiàn)和鏈接正在廣告的外設(shè)
  • 連接上外設(shè)之后,探測它上面的數(shù)據(jù)
  • 向外設(shè)服務(wù)中特性的值發(fā)送讀寫請求
  • 訂閱特性的值,以察覺它的更新

下一章,你學(xué)會如何在你的本地設(shè)備上開發(fā)實現(xiàn)了外設(shè)角色的應(yīng)用。

你在本章中找到的代碼例子是簡化和抽象的;你也許需要適當修改才能將它包含到你現(xiàn)實世界的應(yīng)用中。更多相于實現(xiàn)中心角色高級主題——包括建議,技巧和最佳實踐——包含在以后的章節(jié)中,Background Processing for iOS AppsBest Practices for Interacting with a Remote Peripheral Device。

開啟中心管理器(Central Manager)

由于CBCentralManager對象是本地中心設(shè)備的Core Bluetooth面向?qū)ο蟮拇?,在你開始執(zhí)行任何藍牙事務(wù)之前你要開辟和初始化一個中心管理器實例。通過調(diào)用中心管理器的initWithDelegate:queue:options:方法初始化它:

myCentralManager =
        [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];

在這個例子中, self被設(shè)為代理,以接受各種中心角色的事件。通過指定派發(fā)隊列為nil,中心管理器使用主隊列派發(fā)中心角色事件。

當你創(chuàng)建了一個中心管理器,它會調(diào)用其代理對象的方法centralManagerDidUpdateState:。你必須實現(xiàn)這個代理方法以確保中心設(shè)備藍牙的有效性。更多關(guān)于何實現(xiàn)這個代理方法的信息,請見CBCentralManagerDelegate Protocol Reference。

發(fā)現(xiàn)正在廣告的外設(shè)

一旦完成初始化,中心管理器的第一個任務(wù)是發(fā)現(xiàn)外設(shè)。正如在Centrals Discover and Connect to Peripherals That Are Advertising中提到的,外設(shè)以廣告的方式來被察覺。你的應(yīng)用發(fā)現(xiàn)附近正在廣告的外設(shè)的方式是,調(diào)用中心管理器的scanForPeripheralsWithServices:options:方法:

[myCentralManager scanForPeripheralsWithServices:nil options:nil];

注意:如果你指定nil為第一個參數(shù),那么中心管理器返回所有發(fā)現(xiàn)的外設(shè),忽視它們所支持的服務(wù)。在真實的應(yīng)用中,你通常指定的是一個包含CBUUID對象的數(shù)組,其中每一個代表外設(shè)正在廣播的服務(wù)的通用唯一識別碼(UUID)。當你指定了服務(wù)UUID的數(shù)組,中心管理器只返回廣播了那些服務(wù)的外設(shè),讓你只掃描到你可能感興趣的設(shè)備。

UUID,以及它們的代表CBUUID,將會在Services and Characteristics Are Identified by UUIDs詳細討論。

每當中心管理器發(fā)現(xiàn)一個外設(shè),它會調(diào)用其代理的方法centralManager:didDiscoverPeripheral:advertisementData:RSSI:。剛被發(fā)現(xiàn)的外設(shè)以一個CBPeripheral對象返回。如果你打算連接它,持有一個對它的強引用,遮掩系統(tǒng)就不會釋放它。以下例子的場景是,用一個類的屬性維持對一個已發(fā)現(xiàn)的外設(shè)的引用:

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

如果你想連接多個外設(shè),你可以相應(yīng)地持有一個外設(shè)的數(shù)組。任何情況下,一旦你已經(jīng)發(fā)現(xiàn)了所有你想連接的外設(shè),停止對其他設(shè)備的掃描以省電:

[myCentralManager stopScan];

連接你發(fā)現(xiàn)的外設(shè)

發(fā)現(xiàn)你想要的外設(shè)之后,調(diào)用中心管理器的connectPeripheral:options:方法以請求連接到它,傳入你想連接的外設(shè):

[myCentralManager connectPeripheral:peripheral options:nil];

如果連接成功,中心管理器會調(diào)用其代理的centralManager:didConnectPeripheral:方法。在與外設(shè)交互之前,你要設(shè)置它的代理以確保代理能收到準確的回調(diào):

- (void)centralManager:(CBCentralManager *)central
  didConnectPeripheral:(CBPeripheral *)peripheral {
 
    NSLog(@"Peripheral connected");
    peripheral.delegate = self;
    ...

發(fā)現(xiàn)你連接的設(shè)備中的服務(wù)

同一個外設(shè)建立連接之后,你可以探測它的數(shù)據(jù)。探測數(shù)據(jù)的第一步是發(fā)現(xiàn)它有效的服務(wù)。因為一個外設(shè)可以廣告的數(shù)據(jù)大小是有限的,你可能會發(fā)現(xiàn)一個外設(shè)擁有的服務(wù)總數(shù)超過它所廣告的(在它的廣告包中)。你可以調(diào)用方法discoverServices:來發(fā)現(xiàn)外設(shè)提供的所有服務(wù),像這樣:

[peripheral discoverServices:nil];

注意:在現(xiàn)實的應(yīng)用中,你通常不會傳nil作為參數(shù),因為這樣做會返回外設(shè)上所有的服務(wù)。因為一個外設(shè)包含的服務(wù)可能比你想要的多,全部發(fā)現(xiàn)它們會浪費電池壽命和時間。相反的,你通常會指定你感興趣的服務(wù)的UUID,像Explore a Peripheral’s Data Wisely中所展示的一樣。

當指定的設(shè)備被發(fā)現(xiàn)了,外設(shè)(我們連接的CBPeripheral對象)就回調(diào)用其代理的peripheral:didDiscoverServices:方法。Core Bluetooth創(chuàng)建了一個CBService數(shù)組——包含著外設(shè)上發(fā)現(xiàn)的服務(wù)。如下所示,你可以實現(xiàn)這個代理方法來以獲取已發(fā)現(xiàn)服務(wù)的數(shù)組。

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

發(fā)現(xiàn)服務(wù)中的特性

當你發(fā)現(xiàn)了你感興趣的服務(wù),探測外設(shè)的下一步就是要發(fā)現(xiàn)服務(wù)的所有特性。發(fā)現(xiàn)所有的特性只需調(diào)用外設(shè)的discoverCharacteristics:forService:方法,同時傳入合適的服務(wù),像這樣:

NSLog(@"Discovering characteristics for service %@", interestingService);
    [peripheral discoverCharacteristics:nil forService:interestingService];

注意:在現(xiàn)實的應(yīng)用中,你通常不會傳nil作為第一個參數(shù),因為這樣做會返回外設(shè)的服務(wù)中所有的特性。因為一個外設(shè)的服務(wù)所包含的特性可能比你想要的多,全部發(fā)現(xiàn)它們會浪費電池壽命和時間。相反的,你通常會指定你感興趣的特性的UUID。

目標服務(wù)的特性被發(fā)現(xiàn)的時候,外設(shè)就會調(diào)用其代理的peripheral:didDiscoverCharacteristicsForService:error: 方法。Core Bluetooth創(chuàng)建了一個CBCharacteristic數(shù)組——包含了被發(fā)現(xiàn)的特性。以下的例子展示了你如何可以實現(xiàn)這個代理方法來打印被發(fā)現(xiàn)的特性:

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

獲取特性的值

每個特性包含了一個單一的值,描述一個外設(shè)的服務(wù)的信息。例如,一個體溫計服務(wù)的溫度測量值特性可以有一個以攝氏度為單位的溫度值。獲取特性的值可以通過直接讀取或訂閱它。

讀取特性的值

當你已經(jīng)找到你感興趣的特性,要從中讀值可以通過調(diào)用外設(shè)的readValueForCharacteristic:方法,同時傳入適當?shù)奶匦?,像這樣:

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

當你試圖讀取特性的值,外設(shè)會調(diào)用其代理的peripheral:didUpdateValueForCharacteristic:error:方法來獲取。如果成功獲取,你可以通過特性的value屬性來存取它,像這樣:

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

注意:并非所有的特性都是可讀的。通過檢查特性的properties屬性是否含有常量CBCharacteristicPropertyRead,你可以知道它是否可讀。如果你試圖讀取一個不可讀的特性的值,代理方法peripheral:didUpdateValueForCharacteristic:error:將返回一個對應(yīng)的錯誤。

訂閱特性的值

雖然使用readValueForCharacteristic:方法可以有效的讀取特性的靜態(tài)值,但這并不是讀取動態(tài)值最有效的方式。通過訂閱的方式獲取隨時變化的特性值——例如,你的心率。當你訂閱了特性的值,每當它發(fā)生變化,你就會收到通知。

訂閱你感興趣的特性的方法是調(diào)用外設(shè)的setNotifyValue:forCharacteristic:方法,第一個參數(shù)指定為YES,像這樣像這樣:

[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];

當你訂閱(或者取消訂閱)一個特性值,外設(shè)會調(diào)用其代理的setNotifyValue:forCharacteristic:,像這樣:

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

注意:并非所有特性提供訂閱。通過檢查特性的properties屬性是否含有常量CBCharacteristicPropertyNotifyCBCharacteristicPropertyIndicate可以知道它是否提供訂閱。

完成對特性的值的訂閱之后,外設(shè)會通知你的app特性的值的改變。每當只發(fā)生變化,外設(shè)會調(diào)用其代理的peripheral:didUpdateValueForCharacteristic:error:方法。為了獲取更新后的值,你可以按上述Reading the Value of a Characteristic的方法實現(xiàn)。

書寫特性的值

偶爾需要書寫特性值。例如,你的應(yīng)用和一個藍牙數(shù)字恒溫器交互,你可能需要提供恒溫器一個房間的溫度。如果特性的值是可書寫的,你可以使用data(NSData對象)書寫它的值,通過調(diào)用外設(shè)的writeValue:forCharacteristic:type:方法,像這樣:

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

書寫特性的值,你要指定你執(zhí)行書寫的類型。上面的例子中,書寫的類型是CBCharacteristicWriteWithResponse,它讓外設(shè)通知你的應(yīng)用書寫是否成功,通過調(diào)用其代理的peripheral:didWriteValueForCharacteristic:error:方法。你要實現(xiàn)這個代理方法來處理錯誤的情況,如下所示:

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

如果相反你指定的書寫類型是CBCharacteristicWriteWithoutResponse,那么書寫操作以盡力而為的方式執(zhí)行,而且交付過程既沒有保證也沒有報告。外設(shè)不會回調(diào)代理方法。更多關(guān)于Core Bluetooth框架支持的書寫類型的信息,參考CBPeripheral Class Reference中的枚舉BCharacteristicWriteType

注意:一些特性可能只支持某幾種書寫類型,甚至完全不支持。查看特性的書寫類型,要通過檢查它的properties屬性是否具有CBCharacteristicPropertyWriteWithoutResponse或者CBCharacteristicPropertyWrite常量。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容