執(zhí)行通用中心角色任務(wù)
在BLE通信中實(shí)現(xiàn)中心角色的設(shè)備執(zhí)行一些通用任務(wù)屎慢,例如:搜索并連接設(shè)備,掃描并且和提供數(shù)據(jù)的周邊交互。對比著看湿弦,實(shí)現(xiàn)周邊角色的設(shè)備也在執(zhí)行一些通用的,但是不同的任務(wù)腾夯,例如:公開并廣告服務(wù)颊埃,響應(yīng)連接的中心設(shè)備的讀寫,訂閱(read,write俯在,subscription)請求竟秫。
在這一章娃惯,你會學(xué)到怎樣通過CoreBluetooth執(zhí)行中心角色這邊的最基本的BLE任務(wù)跷乐。下面的示例代碼可以幫助你在設(shè)備上開發(fā)app實(shí)現(xiàn)這一功能。特別地趾浅,你將學(xué)習(xí)怎樣:
?開始一個中心管理對象
?發(fā)現(xiàn)并且連接廣告中的設(shè)備
?在連接設(shè)備之后掃描數(shù)據(jù)愕提。
?針對設(shè)備服務(wù)的特征發(fā)送讀寫請求
?訂閱支持notified(在數(shù)據(jù)更新時會主動上報)的特征。
在下一章皿哨,你會學(xué)習(xí)怎樣在你的設(shè)備上通過開發(fā)實(shí)現(xiàn)一個周邊角色浅侨。
本章的示例代碼簡單抽象证膨;當(dāng)你把它們遷入你的app中時可能需要做一點(diǎn)修改如输。更多和中心角色相關(guān)的高級課題(像貼士,技巧和最佳練習(xí))在后面的章節(jié)將概括到央勒。iOS應(yīng)用CoreBluetooth的后臺執(zhí)行(Core Bluetooth Background Processing for iOS Apps)和與遠(yuǎn)程設(shè)備通訊的最佳訓(xùn)練(Best Practices for Interacting with a Remote Peripheral Device.)
開始一個中心管理對象
CBCentralManager對象是本地中心設(shè)備的CoreBluetooth面向?qū)ο蟮南笳鞑患谀憧梢詧?zhí)行任何BLE痛信之前你必須分配并且初始化一個中心管理者的實(shí)例。你可以通過調(diào)用CBCentralManager類中的initWithDelegate:queue:options: 方法來實(shí)例化一個中心管理者崔步,就像這樣:
myCentralManager =
[[CBCentralManager alloc] initWithDelegate:self queue:nil options:nil];
在這個例子中稳吮,self作為接受中心角色事件的委托。指定dispatchQueue為nil井濒,中心對象使用main queue派遣中心角色事件灶似。
當(dāng)你創(chuàng)建了中心管理者列林。中心管理者將為它的委托對象調(diào)用centralManagerDidUpdateState:方法。你必須實(shí)現(xiàn)這個委托方法用于驗(yàn)證在這個中心設(shè)備上對BLE的支持是否完備酪惭。不會實(shí)現(xiàn)這個委托方法希痴?請看 CBCentralManagerDelegate 協(xié)議參考(CBCentralManagerDelegate Protocol Reference.)
發(fā)現(xiàn)正在廣告的周邊設(shè)備
你要執(zhí)行的中心這邊的任務(wù)第一任務(wù)就是發(fā)現(xiàn)周圍可以被你連接的周邊設(shè)備。像之前提到的中心發(fā)現(xiàn)并連接廣告中的周邊設(shè)備( Centrals Discover and Connect to Peripherals That Are Advertising),廣告是周邊設(shè)備證明自己存在的基礎(chǔ)方式春感。你可以通過CBCentralManager類中的scanForPeripheralsWithServices:options: 方法發(fā)現(xiàn)周圍正在廣告的周邊設(shè)備润梯。就像這樣:
[myCentralManager scanForPeripheralsWithServices:nil options:nil];
注意:如果第一個參數(shù)你傳了nil,中心管理者會返回所有被發(fā)現(xiàn)的周邊甥厦,不管它們所支持的服務(wù)是什么纺铭。在實(shí)際的app中,你可以指定一個 CBUUID 對象的數(shù)組刀疙,每個CBUUID對象對應(yīng)周邊設(shè)備正在廣告的服務(wù)的唯一表示符(UUID)舶赔。當(dāng)你指定了服務(wù)的UUID,中心管理者將只返回正在廣告這些服務(wù)的周邊谦秧,幫助你只搜索你感興趣的設(shè)備竟纳。
UUIDs,CBUUID的對象和它們對應(yīng)疚鲤,更多相關(guān)討論請看 Services and Characteristics Are Identified by UUIDs.
在你調(diào)用 scanForPeripheralsWithServices:options: 發(fā)現(xiàn)可用的周邊之后锥累,中心管理者會為他的委托對象調(diào)用 centralManager:didDiscoverPeripheral:advertisementData:RSSI:方法返回它所發(fā)現(xiàn)的每一臺周邊。所有周邊被發(fā)現(xiàn)的周邊被 CBPeripheral 對象抽象返回集歇。像下面展示的這樣桶略,你可以實(shí)現(xiàn)這個委托方法來排列被發(fā)現(xiàn)的周邊:
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI {
NSLog(@"Discovered %@", peripheral.name);
...
當(dāng)你發(fā)現(xiàn)了你想要連接的周邊設(shè)備,停止掃描來節(jié)省電量诲宇。
[myCentralManager stopScan];
NSLog(@"Scanning stopped");
連接你所發(fā)現(xiàn)的周邊設(shè)備(Connecting to a Peripheral Device After You’ve Discovered It)
在你發(fā)現(xiàn)了你感興趣的正在廣告服務(wù)的周邊設(shè)備之后际歼,你調(diào)用CBCentralManager中的connectPeripheral:options: 方法去請求連接周邊。簡單調(diào)用這個方法并且指定你想連接的已被發(fā)現(xiàn)的周邊姑蓝,就像這樣:
[myCentralManager connectPeripheral:peripheral options:nil];
假設(shè)連接請求成功了鹅心,中心管理者將為它的委托對象調(diào)用 centralManager:didConnectPeripheral:,你可以通過實(shí)現(xiàn)這個方法來打印信息確定連接成功纺荧。想下面寫的這樣:
- (void)centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@"Peripheral connected");
...
在你和周邊通信之前旭愧,你應(yīng)該設(shè)置周邊的委托以確保它可以收到合適的回調(diào),像這樣:
peripheral.delegate = self;
發(fā)現(xiàn)你連接的周邊的服務(wù)(Discovering the Services of a Peripheral That You’re Connected To)
和周邊建立了連接之后宙暇,你可以開始探測它的數(shù)據(jù)输枯,探索周邊提供的第一步是發(fā)現(xiàn)它支持的服務(wù),因?yàn)檫@里對周邊的數(shù)據(jù)量有大小的限制客给,你可以發(fā)現(xiàn)一個周邊廣告好幾個服務(wù)(在他的廣告包里)用押。你可以通過調(diào)用CBPeripheral類中的discoverServices: 方法發(fā)現(xiàn)周邊的所有服務(wù),像這樣:
[peripheral discoverServices:nil];
注意:在實(shí)際的app中靶剑,你不太可能會發(fā)nil作為參數(shù)蜻拨,因?yàn)檫@樣會返回周邊上支持的所有的服務(wù)池充。由于一個周邊上可能含有很多你不太感興趣的服務(wù),搜索它們只是浪費(fèi)電池生命和搜索的時間缎讼。大多數(shù)情況下收夸,你可以指定搜索你所感興趣并且早就知道的服務(wù)的UUIDs,像明智地探索周邊數(shù)據(jù)(Explore a Peripheral’s Data Wisely)里介紹的那樣血崭。
當(dāng)指定的服務(wù)被發(fā)現(xiàn)時卧惜,周邊(你連接的那個周邊對象)會為他的委托對象調(diào)用peripheral:didDiscoverServices: 方法。Core Bluetooth創(chuàng)建了一個CBService對象的數(shù)組-每一個對象對應(yīng)周邊上發(fā)現(xiàn)的服務(wù)夹纫。就像下面展示的這樣咽瓷,你可以實(shí)現(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ù)的特征(Discovering the Characteristics of a Service)
當(dāng)你發(fā)現(xiàn)了你感興趣的服務(wù)之后舰讹,下一步就是探索這個周邊的所有特征查看它提供了啥茅姜。發(fā)現(xiàn)服務(wù)的特征很簡單,指定對應(yīng)的服務(wù)月匣,調(diào)用CBPeriphral的discoverCharacteristics:forService:方法就好了钻洒,就像這樣:
NSLog(@"Discovering characteristics for service %@", interestingService);
[peripheral discoverCharacteristics:nil forService:interestingService];
注意:在實(shí)際開發(fā)中,你不太可能在第一個參數(shù)傳入nil锄开,這樣做的話會返回這個服務(wù)的所有特征素标,因?yàn)橹苓叺姆?wù)可能保護(hù)一些你不感興趣的特征,發(fā)現(xiàn)它們在電量和時間上都是浪費(fèi)萍悴,更多條件下头遭,你應(yīng)該指定搜索你早就知道的感性趣的特征的UUIDs。
在指定服務(wù)的特征被發(fā)現(xiàn)時退腥,周邊會為它的委托對象調(diào)用 peripheral:didDiscoverCharacteristicsForService:error:方法任岸。CoreBluetooth創(chuàng)造了一個CBCharacteristic 對象的數(shù)組再榄,數(shù)組中每一個對象對應(yīng)相應(yīng)的特征狡刘。下面的例子告訴你該如何實(shí)現(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);
...
}
...
獲取特征的值 (Retrieving the Value of a Characteristic)
特征包含一個關(guān)于服務(wù)的更多信息的簡單值困鸥。舉例:一個健康體溫計(jì)服務(wù)的溫度測量值的特征可能表示了一個攝氏溫度值嗅蔬。你可以接受這個特征值通過訂閱它或者立馬解讀它。
讀特征值 (Reading the Value of a Characteristic)
在找到你感興趣的服務(wù)的特征之后疾就,你可以通過調(diào)用CBPeripheral的readValueForCharacteristic:方法并且指定特定的特征來閱讀特征值澜术。像這樣:
NSLog(@"Reading value for characteristic %@", interestingCharacteristic);
[peripheral readValueForCharacteristic:interestingCharacteristic];
當(dāng)你準(zhǔn)備讀一個值時,周邊為它的委托對象調(diào)用peripheral:didUpdateValueForCharacteristic:error: 方法來接受這個值猬腰,如果這個值被成功接收鸟废,你可以通過這個值的屬性接收它,像這樣:
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
NSData *data = characteristic.value;
// parse the data as needed
...
注意:并不是所有特征都有可讀有效值姑荷,你可以通過查看constant常數(shù)來判斷特征值是否可讀盒延,詳細(xì)信息在 CBCharacteristic類文檔(CBCharacteristic Class Reference)中缩擂,如果你想讀一個不可讀的值, peripheral:didUpdateValueForCharacteristic:error:會返回對應(yīng)的錯誤添寺。
訂閱特征值(Subscribing to a Characteristic’s Value)
通過使用 readValueForCharacteristic: 方法去讀一些特征的值可以解決提供一些有效方案胯盯,但是對于經(jīng)常改變的值這個方法便不是很有效—-舉例:你的心率隨時變化—-你可以通過訂閱的方式實(shí)時獲取它們,在你訂閱了特征值之后计露,周邊的特征值在變化時會通過通知的方式讓你收到它博脑。
你可以通過CBPeriphral類中的 setNotifyValue:forCharacteristic:方法訂閱你感興趣的特征值。指定第一個參數(shù)為YES就好票罐,像這樣:
[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];
當(dāng)你試圖訂閱(或者取消訂閱)某一特征時叉趣,周邊會為它的委托對象調(diào)用peripheral:didUpdateNotificationStateForCharacteristic:error: 方法。如果由于一些原因訂閱請求失敗了该押,你可以通過實(shí)現(xiàn)這個委托方法來確定失敗原因君账,像下面的例子一樣:
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state: %@",
[error localizedDescription]);
}
...
注意:并不是所有的特征的配置都允許你訂閱它,你可以通過特征屬性的枚舉來判斷它的設(shè)置沈善。詳細(xì)信息在CBCharacteristic 類相關(guān)( CBCharacteristic Class Reference.)中乡数。
在你成功訂閱特征值之后,周邊設(shè)備為你廣播這個值只要它發(fā)生了變化闻牡,每次值變化時净赴,周邊為它的委托對象調(diào)用peripheral:didUpdateValueForCharacteristic:error:方法。為了接收這個更新的值罩润,你可以像上面在讀特征值介紹過的樣子實(shí)現(xiàn)這個方法玖翅。
寫特征值(Writing the Value of a Characteristic)
在一些情形下球匕,需要給特征值寫一些信息安券,舉例:如果你的app和一個BLE的數(shù)字恒溫計(jì)工作,你可能需要為房間溫度設(shè)一個數(shù)值珊泳。如果這個特征是可寫的严沥,你可以寫一些數(shù)據(jù)(NSData實(shí)例)猜极,通過調(diào)用CBPeripheral的 writeValue:forCharacteristic:type: 類,像這樣:
NSLog(@"Writing value for characteristic %@", interestingCharacteristic);
[peripheral writeValue:dataToWrite forCharacteristic:interestingCharacteristic
type:CBCharacteristicWriteWithResponse];
當(dāng)你試圖在特征寫入值時消玄,指定你要執(zhí)行哪種寫的操作跟伏。在上面的例子中,寫操作類型是CBCharacteristicWriteWithResponse翩瓜,這表示周邊會讓你知道什么時候?qū)懗晒α耸馨狻8郈oreBluetooth框架支持的寫操作,請查閱CBPeripheral Class Reference.中的CBCharacteristicWriteType 枚舉值兔跌。
周邊會通過調(diào)用委托對象的peripheral:didWriteValueForCharacteristic:error: 方法反饋指定為CBCharacteristicWriteWithResponse的寫操作勘高。如果因?yàn)橐粚懺驅(qū)懭胧。憧梢酝ㄟ^實(shí)現(xiàn)這個方法接收信息,像下面的例子:
- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error writing characteristic value: %@",
[error localizedDescription]);
}
...
注意:特征可能只允許某些類型的寫操作在他們的值上執(zhí)行华望。想查看特征值對寫操作的支持詳情层亿,你可以查看Characteristic Properties 枚舉的相關(guān)屬性。詳細(xì)信息在 CBCharacteristic Class Reference.中立美。
執(zhí)行通用周邊角色任務(wù)(Performing Common Peripheral Role Tasks)