執(zhí)行普通中央角色任務(wù)
在藍牙低功耗交互中實現(xiàn)了中央角色的設(shè)備執(zhí)行一些普通任務(wù)--例如,發(fā)現(xiàn)和連接有效的外設(shè),探索并與外設(shè)提供的數(shù)據(jù)進行交互。與此形成鮮明對比的是,實現(xiàn)外設(shè)角色的設(shè)備執(zhí)行一些普通但不同的任務(wù)—例如,發(fā)布和廣播服務(wù),響應(yīng)從連接的中央讀寫和訂閱請求。
在這章中,你將會學習到怎樣使用Core Bluetooth框架來從中央端執(zhí)行大多數(shù)的藍牙低功耗任務(wù)。下面一個基本的代碼示例將會有助于開發(fā)你的應(yīng)用程序來在你的本地設(shè)備上實現(xiàn)中央角色。
*啟動一個中央管理者對象
*發(fā)現(xiàn)和連接正在廣播的外設(shè)
*連接外設(shè)之后探索其中數(shù)據(jù)
*給外設(shè)的服務(wù)的一個特征發(fā)送讀寫請求
*訂閱一個值在更新時發(fā)出通知的特征
在下一章節(jié),你將會學習到怎樣開發(fā)你的應(yīng)用程序在你的本地設(shè)備上實現(xiàn)外設(shè)角色。
在這個章節(jié)你發(fā)現(xiàn)的代碼示例是簡單的和抽象的;你可能需要進行適當?shù)男薷模瑢⑵浼{入你的真實世界的應(yīng)用。更高級的主題是與實現(xiàn)中央角色有關(guān)--包括提示,技巧和好的練習--在后面的章節(jié)會提到。Core Bluetooth Background Processing for iOS Apps和 Best Practices for Interacting with a Remote Peripheral Device.
啟動一個中央管理者
一個CBCentralManager
對象是在Core Bluetooth中對象定向表示為本地中央設(shè)備,你必須在你執(zhí)行一些藍牙低功耗事務(wù)時分配內(nèi)存和初始化一個中央管理者實例,你可以通過調(diào)用CBCentralManager
類中的initWithDelegate:queue:options:
方法來啟動你的中央管理者,例如:
myCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
在這個例子中,self
是作為接受中央角色事件的代理,通過指定派遣隊列是nil
,中央管理者會在主隊列中處理中央角色事件。
當你創(chuàng)建了中央管理者,中央管理者會調(diào)用它的代理對象centralManagerDidUpdateState:
方法。你必須實現(xiàn)這個代理方法來確保設(shè)備支持藍牙低功耗且中央設(shè)備可以有效使用。關(guān)于怎樣實現(xiàn)這個代理方法更多的信息,請看CBCentralManagerDelegate Protocol Reference
發(fā)現(xiàn)正在廣播的外設(shè)
中央端的其中的一個任務(wù)是你可能需要執(zhí)行發(fā)現(xiàn)一些你的應(yīng)用程序能夠有效連接的外設(shè)。在Centrals Discover and Connect to Peripheral That Are Advertising早有提及,廣播是外設(shè)被知道的主要方式。你可以通過調(diào)用CBCentralManager
類中的scanForPeripheralsWithService:options:
方法來發(fā)現(xiàn)一些正在廣播的外設(shè)。
[myCentralManager scanForPeripheralsWithService:nil options:nil];
注意:如果你給第一個參數(shù)指定為
nil
,中央管理者會返回所有發(fā)現(xiàn)的外設(shè),不管它們支持的服務(wù)。在一個真實的應(yīng)用程序中,你將更可能指定一個CBUUID
對象的數(shù)組,這個對象表示正在廣播的外設(shè)的一個服務(wù)的普通唯一標識(UUID
),當你指定了服務(wù)UUIDs
數(shù)組,中央管理者會返回只廣播這些服務(wù)的外設(shè),允許你只掃描你可能感興趣的外設(shè)。
UUIDs
,和CBUUID
對象來表示它們,在Services and Characteristic Are Identified by UUIDs有更詳細的討論。
在你調(diào)用scanForPeripheralsWithServices:options:
方法來發(fā)現(xiàn)可用的外設(shè)后,中央管理者在每次發(fā)現(xiàn)外設(shè)時會調(diào)用它的代理對象的centralManager:didDiscoverPeripheral:advertisementData:RSSI:
方法。任何一個被發(fā)現(xiàn)的外設(shè)都會返回一個CBPeripheral
對象。如以下所示,你可以實現(xiàn)這個代理方法來列出任何被發(fā)現(xiàn)的外設(shè)。
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@", peripheral.name);
...
}
當你已經(jīng)發(fā)現(xiàn)了你感興趣想要連接的外設(shè),為了保存手機能量停止掃描其他的外設(shè)。
[myCentralManager stopScan];
NSLog(@"Scanning stopped");
當你已經(jīng)發(fā)現(xiàn)了外設(shè)之后連接外設(shè)
當你已經(jīng)發(fā)現(xiàn)了正在廣播你感興趣的服務(wù)的外設(shè),你可以通過調(diào)用CBCentralManager
類的connectPeripheral:options:
方法來請求連接外設(shè)。用簡單地調(diào)用這個方法并指定你想連接的已經(jīng)發(fā)現(xiàn)的外設(shè)。例如:
[myCentralManager connectPeripheral:peripheral options:nil];
假設(shè)連接成功,中央管理者會調(diào)用它代理對象的centralManager:didConnectPeripheral:
方法,你可以在這里面實現(xiàn)打印建立連接的信息,如下所示:
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Peripheral connected");
...
}
在你與該外設(shè)交互之前,你應(yīng)該設(shè)置外設(shè)的代理來確保它能接受到適當?shù)幕卣{(diào),例如:
peripheral.delegate = self;
發(fā)現(xiàn)你已經(jīng)連接外設(shè)的服務(wù)
在你已經(jīng)與外設(shè)建立連接之后,你可以開始探索它的數(shù)據(jù)。在探索的第一步外設(shè)必須提供正在被發(fā)現(xiàn)的有效服務(wù)。因為外設(shè)能廣播有大小限制的數(shù)據(jù)量,你可能會發(fā)現(xiàn)一個外設(shè)有更多的廣播服務(wù)(在它的廣播包中),你可以通過調(diào)用CBPeripheral
類中的discoverServices:
方法來發(fā)現(xiàn)外設(shè)提供的所有服務(wù)。如下:
[peripheral discoverServices:nil];
注意:在真實的應(yīng)用程序中,你不需要將參數(shù)設(shè)置成
nil
;這樣做會返回外設(shè)的所有有效服務(wù)。因為一個外設(shè)可能包含很多你感興趣的服務(wù),發(fā)現(xiàn)它們的所有會浪費電池的壽命同時也沒有必要。更多的情況下,你將指定你已經(jīng)知道你所感興趣的服務(wù)的UUIDs
,在Explore a Peripheral's Data Wisely可以看。
當一個指定的服務(wù)被發(fā)現(xiàn),外設(shè)(你已經(jīng)連接的CBPeripheral
對象)會調(diào)用它的代理對象的peripheral:didDiscoverServices:
方法。Core Bluetooth創(chuàng)建了一個數(shù)組包含CBService
對象--代表著一個在外設(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ù)的特征
假設(shè)你已經(jīng)發(fā)現(xiàn)了你感興趣的服務(wù),接下來的一步是探索外設(shè)已經(jīng)提供的正在被發(fā)現(xiàn)的服務(wù)的特征。通過調(diào)用CBPeripheral
類的discoverCharacteristics:forService:
方法很容易發(fā)現(xiàn)服務(wù)的所有特征,指定合適的服務(wù),如下:
NSLog(@"Discovering characteristics for service %@", interestingService);
[peripheral discoverCharacteristics:nil forService:interestingService];
注意:在真實的應(yīng)用程序中,你可能不需要設(shè)置第一個參數(shù)為空,這樣做會返回一個外設(shè)服務(wù)的所有特征。因為一個外設(shè)服務(wù)可能包含你感興趣的更多的特征,發(fā)現(xiàn)它們的所有會浪費電池的壽命,而且也會花費不必要的時間。所以在發(fā)現(xiàn)特征的時候,你可以指定你感興趣的已知特征的UUIDs。
當指定服務(wù)的特征被發(fā)現(xiàn)了外設(shè)會調(diào)用它的代理對象的peripheral:didDiscoverCharacteristicsForService:error:
方法,Core Bluetooth創(chuàng)建一個數(shù)組保存CBCharacteristic
對象--一個被發(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ù)信息的值。例如,一個健康溫度計的一個溫度測量特征可能有一個值來表明一個攝氏溫度。你可以通過正確地讀取或訂閱它來得到這個特征值。
讀取特征值
在你發(fā)現(xiàn)了一個你感興趣的服務(wù)的特征之后,你可以通過CBPeripheral
類的readValueForCharacteristic:
方法,指定一個合適的特征,來讀取特征值。就像:
NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
[peripheral readValueForCharacteristic:interestingCharacteristic];
當你想要讀取一個特征值時,外設(shè)會調(diào)用它的代理對象的peripheral:didUpdateValueForCharacteristic:error:
方法來得到這個值。如果這個值是成功的獲得,你可以通過這個特征的值屬性來訪問它。就像:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSData *data = characteristic.value;
// parse the data as needed
...
}
注意:不是所有的特征都保證有一個值是可讀的。你可以通過訪問
CBCharacteristicPropertyRead
常量屬性來決定特征值是否可讀,具體請看CBCharacteristic Class Reference.如果你嘗試讀取不可讀的特征值,代理方法peripheral:didUpdateValueForCharacteristic:error:
將會返回一個錯誤的信息。
訂閱特征值
通過使用readValueForCharacteristic:
方法來讀取特征值在一些情況下是有效的,但它并不是一個高效的方式來獲取一個改變的特征值。對于大多數(shù)改變的特征值--例如在每一個給定的時間的心率值--你應(yīng)該通過訂閱它們來獲取它們的值,當你訂閱了一個特征值,當外設(shè)的特征值改變的時候你會接受到一個通知。你可以通過調(diào)用CBPeripheral
類的setNotifyValue:forCharacteristic:
方法并指定第一個參數(shù)為YES來訂閱一個你感興趣的特征值。就像:
[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];
當你想要訂閱(或取消訂閱)一個特征值時,外設(shè)會調(diào)用它的代理對象的peripheral:didUpdateNotificationStateForCharacteristic:error:
方法,如果因為一些原因而訂閱失敗,你可以實現(xiàn)這個代理方法來訪問錯誤的原因,如下所示:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state : %@", [error localizedDescription]);
}
...
}
注意:并不是所有的特征都配置了允許你訂閱它們的值。你可以通過訪問在
Characteristic Properties
枚舉中有關(guān)的常量來決定這個特征是否已經(jīng)配置來,具體請看CBCharacteristic Class Reference
當你成功的訂閱了一個特征值以后,在該特征值改變時外設(shè)會通知你的應(yīng)用程序。每次值的改變,外設(shè)會調(diào)用它的代理對象的peripheral:didUpdateValueForCharacteristic:error:
方法。為了獲得這個更新的值,你可以用同樣的方式實現(xiàn)這個方法在Reading the Value of a Characteristic的描述中.
給特征值寫入數(shù)據(jù)
在一些情況下,需要給一些特征寫一些有意義的值。例如,如果你的應(yīng)用程序與藍牙低功耗數(shù)字恒溫控制器交互,你可能會想要提供給恒溫控制器一些值來設(shè)置房間的溫度。如果一個特征值是可寫的,你可以通過調(diào)用CBPeripheral
類的writeValue:forCharacteristic:type:
方法來寫入一些數(shù)據(jù)(NSData類型的實例).就像:
NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic type:CBCharacteristicWriteWithResponse];
當你想要給一個特征寫入值,你需要指定你想要執(zhí)行的寫入類型。在上面的例子中,指定的類型是CBCharacteristicWriteWithResponse
,這表明外設(shè)讓你的應(yīng)用程序知道是否寫入成功,想要知道Core Bluetooth框架中提供的寫入類型更多信息,請看在CBPeripheral Class Reference中的CBCharacteristicWriteType
枚舉。
外設(shè)通過調(diào)用它的代理對象的peripheral:didWriteValueForCharacteristic:error:
方法會響應(yīng)指定為CBCharacteristicWriteWithResponse
類型的寫入請求。如果因為一些原因?qū)懭胧。憧梢詫崿F(xiàn)這個代理方法來訪問失敗的原因,如下例所示:
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"Error writing characteristic value: %@", [error localizedDescription]);
}
...
}
注意:特征可能只允許某些類型可寫入值。為了確定哪些特征值允許被寫入,你可以訪問
Characteristic Properties
枚舉中的相關(guān)屬性,詳細的請看CBCharacteristic Class Reference;
---翻譯的文檔地址:Performing Common Central Role Tasks