歡迎訪問我的博客 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 需要遵循 CBCentralManagerDelegate
和 CBPeripheralDelegate
代理協(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é)束語
歡迎在本文下面留言一起交流心得...