iOS BLE 開發(fā)小記[2] - 如何實現(xiàn)一個 Local Central

歡迎訪問我的博客 muhlenXi襟企,該文章出自我的博客,歡迎轉(zhuǎn)載曙求,轉(zhuǎn)載請注明來源: http://muhlenxi.com/2017/04/29/iOS-Bluetooth-Low-Energy-Develop-Chapter2吠式。

導(dǎo)語:

在這一節(jié),你將會學(xué)到,如何通過 CoreBluetooth 框架來實現(xiàn) Local Central 方面的功能和代理方法糕簿。

在 BLE 通信中,實現(xiàn)了 Central 規(guī)范的設(shè)備狡孔,能夠調(diào)用許多常用的方法懂诗,比如搜索和連接可用的 Peripheral,然后與 Peripheral 提供的數(shù)據(jù)進(jìn)行交互苗膝。但是實現(xiàn)了 Peripheral 規(guī)范的設(shè)備同樣能夠調(diào)用許多常見的方法殃恒,但是也有一些不同。比如辱揭,發(fā)布和廣播 Service离唐,對 Central 的讀寫請求進(jìn)行響應(yīng)以及 響應(yīng)Central 的訂閱請求。

在這個章節(jié)中问窃,你將會學(xué)習(xí)在 Central 端如何使用 Core Bluetooth 框架來執(zhí)行通用的 BLE 方法亥鬓。基于代碼的示例將會引導(dǎo)和協(xié)助你在你的本地設(shè)備上實現(xiàn) Central 的角色域庇。尤其嵌戈,你將會學(xué)到:

  • 如何創(chuàng)建一個 Central Manager 對象
  • 如何搜索和連接一個正在廣播信息的 Peripheral
  • 連接成功后如何與 Peripheral 的數(shù)據(jù)進(jìn)行交互
  • 如何發(fā)送讀取或?qū)懭胝埱蠼o Peripheral Service 的 Characteristic
  • 如何訂閱一個當(dāng)數(shù)據(jù)更新時就會發(fā)出通知的 Characteristic

在下個章節(jié),你將會學(xué)習(xí)如何在你的本地設(shè)備上實現(xiàn) Peripheral 的角色较剃。

你也許會發(fā)現(xiàn)本章節(jié)中的代碼有點簡單和抽象咕别,在你的真實 App 中,你需要做些恰當(dāng)?shù)母淖冃囱ā8呒壍拈_發(fā)技能可以參考后續(xù)的章節(jié)惰拱。

Central 實現(xiàn)詳情

在本文中,你的 ViewController 需要遵循 CBCentralManagerDelegateCBPeripheralDelegate 代理協(xié)議。

創(chuàng)建 Central Manager

因為在 CoreBluetooth 中是通過面向?qū)ο蟮乃枷胗靡粋€ CBCentralManager(中心管理) 對象來表示一個 Local Central 設(shè)備偿短,所以在調(diào)用該對象的方法之前需要先 allocate(分配)和 initialize(初始化)一個 Central Manager 實例欣孤。可以通過 initWithDelegate:queue:options: 方法來初始化一個 Central Manager 對象昔逗。

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

在示例代碼中降传,設(shè)置 Central Manager 的 Delegate 為 self,是為了接收 Central 的事件響應(yīng)勾怒。 參數(shù) dispatch queue(調(diào)度隊列)設(shè)置為 nil婆排,表示 Central Manager 是在 main queue(主隊列)中分發(fā)響應(yīng)事件。

當(dāng)你創(chuàng)建一個 Central Manager 對象時笔链,Central Manager 會調(diào)用 centralManagerDidUpdateState: 方法來代理回調(diào)段只。因此你必須實現(xiàn)這個代理方法來確保 Central 設(shè)備能夠使用 BLE 技術(shù),代理方法的詳情見 CBCentralManagerDelegate Protocol Reference

搜索正在廣播數(shù)據(jù)的 Peripheral 設(shè)備

初始化 Central Manger 對象后鉴扫,第一個任務(wù)就是搜索周圍的 Peripheral赞枕,上一篇曾提到過, Peripheral 通過廣播數(shù)據(jù)的方式來顯示它們的存在坪创,你可以通過 scanForPeripheralsWithServices:options: 方法來搜索周圍正在廣播數(shù)據(jù)的 Peripheral 設(shè)備炕婶。

[myCentralManager scanForPeripheralsWithServices:nil options:nil];

關(guān)于參數(shù)說明:如果第一個參數(shù)置為 nil 時,Central Manager 會返回所有正在廣播數(shù)據(jù)的 Peripheral 設(shè)備莱预。在實際 APP 開發(fā)中柠掂,通過指定一個包含 CBUUID 對象的數(shù)組來獲取指定的 Peripheral,其中用一個 UUID(通用唯一識別碼)來表示 Peripheral 正在廣播的一個服務(wù)依沮。關(guān)于 CBUUID 對象的詳情見 Services and Characteristics Are Identified by UUIDs.

每當(dāng) Central Manager 搜索到一個 Peripheral 設(shè)備時陪踩,就會通過 代理方法 centralManager:didDiscoverPeripheral:advertisementData:RSSI: 進(jìn)行回調(diào),新發(fā)現(xiàn)的 Peripheral 會以 CBPeripheral 對象的方式返回悉抵。如果你后面需要連接這個 Peripheral,需要用一個 CBPeripheral 類型的 Strong Reference(強(qiáng)引用)來指向這個對象摘完,這樣系統(tǒng)暫時就不會釋放這個對象了姥饰。

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

當(dāng)你需要連接多個 Peripheral 設(shè)備時,需要使用一個 NSArray 來保存這些搜索到的 Peripheral孝治,不管怎樣列粪,為了減少電量損耗,增加續(xù)航時間谈飒,只要搜索到你需要連接的 Peripheral 時岂座,就可以停止搜索了。通過下面的方法可以停止搜索了杭措。

[myCentralManager stopScan];

連接剛發(fā)現(xiàn)的 Peripheral 設(shè)備

可以調(diào)用 connectPeripheral:options: 方法來連接你想要連接的 Peripheral 設(shè)備费什。

[myCentralManager connectPeripheral:peripheral options:nil];

如果連接成功,Central 會通過 centralManager:didConnectPeripheral: 方法進(jìn)行代理回調(diào)手素。在你與 Peripheral 進(jìn)行交互時鸳址,你需要設(shè)置 Peripheral 的 Delegate 為 self 來確保能收到 Peripheral 的代理回調(diào)瘩蚪。

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

搜索剛連接 Peripheral 的 Service

當(dāng)與 Peripheral 成功建立連接后,你就可以獲取 Peripheral 的 Service 數(shù)據(jù)了稿黍,第一步就是搜索 Peripheral 提供的可用 Service疹瘦。因為 Peripheral 對廣播的數(shù)據(jù)包大小有限制,所以你可能會搜索到除了廣播的 Service 之外的 其他 Service巡球,你可以通過 discoverServices: 方法搜索 Peripheral 提供的所有的 Service言沐。

[peripheral discoverServices:nil];

提示:在實際開發(fā)中,傳入的參數(shù)一般不為 nil酣栈,傳入 nil 會返回全部的可用 Service险胰,為了節(jié)省電量以及一些不必要的時間浪費,通過指定一個 Service Array(包含 UUID 對象)為參數(shù)钉嘹,來獲取你想要了解的 Service 的信息鸯乃,詳情見 Explore a Peripheral’s Data Wisely.

當(dāng)搜索到指定的 Service 時,Peripheral 對象會通過 peripheral:didDiscoverServices: 方法進(jìn)行代理回調(diào)跋涣。CoreBluetooth 會生成一個數(shù)組用來保存 CBService 對象缨睡,你指定的 Service 就被包含在這個數(shù)組中。你可以通過實現(xiàn)下面這個代理方法來獲取 Service 數(shù)組陈辱。

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

搜索 Service 的 Characteristic

當(dāng)你找到指定的 Service 之后奖年,你下一步要做的就是搜索這個 Service 中提供的所有的 Characteristic,通過調(diào)用 discoverCharacteristics:forService: 方法來搜索指定的 Service 的 Characteristic沛贪。

[peripheral discoverCharacteristics:nil forService:interestingService];

提示:在實際開發(fā)中陋守,第一個參數(shù)一般不傳入 nil,因為你需要的 Characteristic 也許是所有 Characteristic 的一部分利赋,為了節(jié)省電量以及一些不必要的時間浪費水评,通常指定一個 Characteristic Array(包含 UUID 對象)為參數(shù),來獲取你想要了解的 Characteristic 的信息媚送。

當(dāng)搜索到指定的 Characteristic 后中燥, Peripheral 會通過 peripheral:didDiscoverCharacteristicsForService:error: 方法進(jìn)行代理回調(diào),CoreBluetooth 會創(chuàng)建一個包含 CBCharacteristic 對象的數(shù)組用來保存指定的 Characteristic塘偎,如下是遍歷數(shù)組中的 Characteristic疗涉。

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

檢索 Characteristic 的值

讀取某個 Characteristic 的值

當(dāng)你找到 Service 指定的 Characteristic,可以通過 readValueForCharacteristic: 方法來讀取這個 Characteristic 的值吟秩。

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

當(dāng)你嘗試去讀取一個 Characteristic 的值時咱扣, Peripheral 會通過 peripheral:didUpdateValueForCharacteristic:error: 代理回調(diào)來返回結(jié)果,你可以通過 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 常量來判斷的。當(dāng)你嘗試去讀取一個值不可讀的 Characteristic 時,Peripheral 會通過 peripheral:didUpdateValueForCharacteristic:error: 給你返回一個合適的錯誤祭往。

訂閱一個 Characteristic 的值

通過 readValueForCharacteristic: 方法讀取一個 Characteristic 的靜態(tài)值是有效的伦意,但是,對于動態(tài)的值硼补,就不是一個有效的方法驮肉,因為 Characteristic 的值在實時改變,比如你的心率數(shù)據(jù)已骇。只有通過訂閱才能獲取實時的改變值离钝,當(dāng)你訂閱一個 Characteristic 的值時同辣,每當(dāng)這個值發(fā)生改變時北启,你就會收到一個通知工碾。

通過 ?setNotifyValue:forCharacteristic: 方法可以訂閱指定 Characteristic 的值揉燃,該方法的第一個參數(shù)需要指定為 YES

[peripheral setNotifyValue:YES forCharacteristic:interestingCharacteristic];

當(dāng)你訂閱或取消訂閱一個 Characteristic 的值時搅方,Peripheral 會通過調(diào)用 peripheral:didUpdateNotificationStateForCharacteristic:error: 方法進(jìn)行代理回調(diào)流译,如果訂閱失敗了妓肢,你可以通過這個方法獲取到發(fā)生錯誤的原因辛藻。

- (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 常量來判斷的。

寫數(shù)據(jù)到 Characteristic 中

有時需要寫入一個數(shù)據(jù)到 Characteristic 中吱肌,比如痘拆,如果你的 APP 與數(shù)字恒溫器進(jìn)行交互。你或許想要給數(shù)字恒溫器提供一個值氮墨,使得房間的室溫能保持在這個溫度左右纺蛆。如果一個 Characteristic 的值是可寫的,你可以通過調(diào)用 writeValue:forCharacteristic:type: 方法將一個 data 類型(NSData 對象)的值寫入到 Characteristic 中规揪。

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

當(dāng)你寫一個數(shù)據(jù)到 Characteristic 中時桥氏,你可以指定寫入類型,上面代碼中的寫入類型是 CBCharacteristicWriteWithResponse 猛铅,此類型時识颊,Peripheral 會通過 peripheral:didWriteValueForCharacteristic:error: 方法來代理回調(diào)告知你是否寫入數(shù)據(jù)成功,可以實現(xiàn)下面這個代理方法進(jìn)行錯誤處理奕坟。

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

如果你指定寫入類型為 CBCharacteristicWriteWithoutResponse 時,不能保證寫入操作是否有效的執(zhí)行了清笨。這時 Peripheral 不會調(diào)用任何代理方法月杉,如果想了解 CoreBluetooth 提供的寫入類型詳情,可以查閱 CBCharacteristicWriteType.

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

參考文獻(xiàn)

1腌歉、Performing Common Central Role Tasks

結(jié)束語

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛙酪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子翘盖,更是在濱河造成了極大的恐慌桂塞,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馍驯,死亡現(xiàn)場離奇詭異阁危,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)汰瘫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門狂打,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人混弥,你說我怎么就攤上這事趴乡。” “怎么了蝗拿?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵晾捏,是天一觀的道長。 經(jīng)常有香客問我蛹磺,道長粟瞬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任萤捆,我火速辦了婚禮裙品,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俗或。我一直安慰自己市怎,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布辛慰。 她就那樣靜靜地躺著区匠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帅腌。 梳的紋絲不亂的頭發(fā)上驰弄,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音速客,去河邊找鬼戚篙。 笑死,一個胖子當(dāng)著我的面吹牛溺职,可吹牛的內(nèi)容都是我干的岔擂。 我是一名探鬼主播位喂,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼乱灵!你這毒婦竟也來了塑崖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤痛倚,失蹤者是張志新(化名)和其女友劉穎规婆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體状原,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡聋呢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了颠区。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片削锰。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖毕莱,靈堂內(nèi)的尸體忽然破棺而出器贩,到底是詐尸還是另有隱情,我是刑警寧澤朋截,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布蛹稍,位于F島的核電站,受9級特大地震影響部服,放射性物質(zhì)發(fā)生泄漏唆姐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一廓八、第九天 我趴在偏房一處隱蔽的房頂上張望奉芦。 院中可真熱鬧,春花似錦剧蹂、人聲如沸声功。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽先巴。三九已至,卻和暖如春冒冬,著一層夾襖步出監(jiān)牢的瞬間伸蚯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工简烤, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留剂邮,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓乐埠,卻偏偏與公主長得像抗斤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丈咐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容