『CoreBluetooth』2. 作為 Central 時(shí)的數(shù)據(jù)讀寫

當(dāng)設(shè)備作為 central 的時(shí)候,需要做一系列常見的操作:搜索并連接周圍 peripheral,處理 peripheral 提供的數(shù)據(jù)。其實(shí)在設(shè)備作為 peripheral 的時(shí)候,依然有一系列的操作,不過和作為 central 時(shí)不同(廢話),例如它會(huì)去發(fā)起廣播,會(huì)在讀寫數(shù)據(jù)時(shí)做出響應(yīng)。

本章內(nèi)容則是介紹設(shè)備在作為 central 時(shí),怎么使用 Core Bluetooth framework 來操作了。大致包含以下幾點(diǎn):

使用CBCentralManager

搜索并連接可用的 peripheral

連接時(shí)候,進(jìn)行數(shù)據(jù)接收

對(duì) characteristic 進(jìn)行數(shù)據(jù)讀寫

當(dāng) characteristic 狀態(tài)更新時(shí),進(jìn)行回調(diào)

之后才會(huì)介紹在設(shè)備作為 Peripheral 時(shí)的相關(guān)操作。

設(shè)備作為 central 相關(guān)文章還有:

CoreBluetooth3 作為 Central 時(shí)的數(shù)據(jù)讀寫(補(bǔ)充)

CoreBluetooth4 作為 Central 時(shí)的數(shù)據(jù)讀寫(最佳實(shí)踐)

CoreBluetooth5 作為 Central 時(shí)的數(shù)據(jù)讀寫(OTA 固件升級(jí)與文件傳輸)

初始化 CBCentralManager

第一步先進(jìn)行初始化,可以使用initWithDelegate:queue:options:方法:

myCentralManager = [[CBCentralManager alloc] initWithDelegate:selfqueue:niloptions:nil];

上面的代碼中,將self設(shè)置為代理,用于接收各種 central 事件。將queue設(shè)置為nil,則表示直接在主線程中運(yùn)行。

初始化 central manager 之后,設(shè)置的代理會(huì)調(diào)用centralManagerDidUpdateState:方法,所以需要去遵循協(xié)議。這個(gè) did update state 的方法,能獲得當(dāng)前設(shè)備是否能作為 central。關(guān)于這個(gè)協(xié)議的實(shí)現(xiàn)和其他方法,接下來會(huì)講到,也可以先看看官方API

搜索當(dāng)前可用的 peripheral

可以使用CBCentralManager的scanForPeripheralsWithServices:options:方法來掃描周圍正在發(fā)出廣播的 Peripheral 設(shè)備。

[myCentralManager scanForPeripheralsWithServices:niloptions:nil];

第一個(gè)參數(shù)為nil,表示所有周圍全部可用的設(shè)備。在實(shí)際應(yīng)用中,你可以傳入一個(gè)CBUUID的數(shù)組(注意,這個(gè) UUID 是 service 的 UUID 數(shù)組,如有不明白可以參考最佳實(shí)踐),表示只搜索當(dāng)前數(shù)組包含的設(shè)備(每個(gè) peripheral 的 service 都有唯一標(biāo)識(shí)——UUID)。所以,如果你傳入了這樣一個(gè)數(shù)組,那么 central manager 則只會(huì)去包含這些 service UUID 的 Peripheral。

CBUUID會(huì)在下一章中講到,因?yàn)檫@是 peripheral 相關(guān)的,和 central 本身關(guān)系不大,如果你是做的硬件對(duì)接,那么可以向硬件同事詢問。當(dāng)然,下一節(jié)的內(nèi)容也能幫助你理解這個(gè)屬性。

在調(diào)用scanForPeripheralsWithServices:options:方法之后,找到可用設(shè)備,系統(tǒng)會(huì)回調(diào)(每找到一個(gè)都會(huì)回調(diào))centralManager:didDiscoverPeripheral:advertisementData:RSSI:。該方法會(huì)已CBPeripheral返回找到的 peripheral,所以你可以使用數(shù)組將找到的 peripheral 存起來。

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

當(dāng)你找到你需要的那個(gè) peripheral 時(shí),可以調(diào)用stop方法來停止搜索。

[myCentralManager stopScan];NSLog(@"Scanning stopped");

連接 peripheral

找到你需要的 peripheral 之后,下一步就是調(diào)用connectPeripheral:options:方法來連接。

[myCentralManager connectPeripheral:peripheral options:nil];

當(dāng)連接成功后,會(huì)回調(diào)方法centralManager:didConnectPeripheral:。在這個(gè)方法中,你可以去記錄當(dāng)前的連接狀態(tài)等數(shù)據(jù)。

- (void)centralManager:(CBCentralManager *)central? didConnectPeripheral:(CBPeripheral *)peripheral {NSLog(@"Peripheral connected");}

不過在進(jìn)行其他操作之前,你應(yīng)該給已連接的這個(gè) peripheral 設(shè)置代理,這樣才能收到 peripheral 的回調(diào)(可以就寫在上面這個(gè)方法中)。

peripheral.delegate =self;

注意去遵循協(xié)議。

搜索 peripheral 的 service

當(dāng)與 peripheral 成功建立連接以后,就可以通信了。第一步是先找到當(dāng)前 peripheral 提供的 service,因?yàn)?service 廣播的數(shù)據(jù)有大小限制(貌似是 31 bytes),所以你實(shí)際找到的 service 的數(shù)量可能要比它廣播時(shí)候說的數(shù)量要多。調(diào)用CBPeripheral的discoverServices:方法可以找到當(dāng)前 peripheral 的所有 service。

[peripheral discoverServices:nil];

在實(shí)際項(xiàng)目中,這個(gè)參數(shù)應(yīng)該不是nil的,因?yàn)閚il表示查找所有可用的Service,但實(shí)際上,你可能只需要其中的某幾個(gè)。搜索全部的操作既耗時(shí)又耗電,所以應(yīng)該提供一個(gè)要搜索的 service 的 UUID 數(shù)組。更加詳細(xì)的內(nèi)容會(huì)在最佳實(shí)踐中講到。

當(dāng)找到特定的 Service 以后,會(huì)回調(diào)的peripheral:didDiscoverServices:方法。Core Bluetooth 提供了CBService類來表示 service,找到以后,它們以數(shù)組的形式存入了當(dāng)前 peripheral 的services屬性中,你可以在當(dāng)前回調(diào)中遍歷這個(gè)屬性。

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

如果是搜索的全部 service 的話,你可以選擇在遍歷的過程中,去對(duì)比 UUID 是不是你要找的那個(gè)。

搜索 service 的 characteristic

找到需要的 service 之后,下一步是找它所提供的 characteristic。如果搜索全部 characteristic,那調(diào)用CBPeripheral的discoverCharacteristics:forService:方法即可。如果是搜索當(dāng)前 service 的 characteristic,那還應(yīng)該傳入相應(yīng)的CBService對(duì)象。

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

同樣是出于節(jié)能的考慮,第一個(gè)參數(shù)在實(shí)際項(xiàng)目中應(yīng)該是 characteristic 的 UUID 數(shù)組。也同樣能在最佳實(shí)踐中介紹。

找到所有 characteristic 之后,回調(diào)peripheral:didDiscoverCharacteristicsForService:error:方法,此時(shí) Core Bluetooth 提供了CBCharacteristic類來表示 characteristic??梢酝ㄟ^以下代碼來遍歷找到的 characteristic :

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

同樣也可以通過添加 UUID 的判斷來找到需要的 characteristic。

讀取 characteristic 數(shù)據(jù)

characteristic 包含了 service 要傳輸?shù)臄?shù)據(jù)。例如溫度設(shè)備中表達(dá)溫度的 characteristic,就可能包含著當(dāng)前溫度值。這時(shí)我們就可以通過讀取 characteristic,來得到里面的數(shù)據(jù)。

讀取 characteristic 數(shù)據(jù)

當(dāng)找到 characteristic 之后,可以通過調(diào)用CBPeripheral的readValueForCharacteristic:方法來進(jìn)行讀取。

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

當(dāng)你調(diào)用上面這方法后,會(huì)回調(diào)peripheral:didUpdateValueForCharacteristic:error:方法,其中包含了要讀取的數(shù)據(jù)。如果讀取正確,可以用以下方式來獲得值:

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

注意,不是所有 characteristic 的值都是可讀的,你可以通過CBCharacteristicPropertyReadoptions 來進(jìn)行判斷(這個(gè)枚舉會(huì)在下一章中介紹到)。如果你嘗試讀取不可讀的數(shù)據(jù),那上面的代理方法會(huì)返回相應(yīng)的 error。

訂閱 Characteristic 數(shù)據(jù)

其實(shí)使用readValueForCharacteristic:方法并不是實(shí)時(shí)的??紤]到很多實(shí)時(shí)的數(shù)據(jù),比如心率這種,那就需要訂閱 characteristic 了。

可以通過調(diào)用CBPeripheral的setNotifyValue:forCharacteristic:方法來實(shí)現(xiàn)訂閱,注意第一個(gè)參數(shù)是YES。

[peripheral setNotifyValue:YESforCharacteristic:interestingCharacteristic];

如果是訂閱,成功與否的回調(diào)是peripheral:didUpdateNotificationStateForCharacteristic:error:,讀取中的錯(cuò)誤會(huì)以 error 形式傳回:

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

當(dāng)然也不是所有 characteristic 都允許訂閱,依然可以通過CBCharacteristicPropertyNoifyoptions 來進(jìn)行判斷。這個(gè)枚舉會(huì)在下一章中介紹到。

當(dāng)訂閱成功以后,那數(shù)據(jù)便會(huì)實(shí)時(shí)的傳回了,數(shù)據(jù)的回調(diào)依然和之前讀取 characteristic 的回調(diào)相同(注意,不是訂閱的那個(gè)回調(diào))peripheral:didUpdateValueForCharacteristic:error:。

向 characteristic 寫數(shù)據(jù)

寫數(shù)據(jù)其實(shí)是一個(gè)很常見的需求,如果 characteristic 可寫,你可以通過CBPeripheral類的writeValue:forCharacteristic:type:方法來向設(shè)備寫入NSData數(shù)據(jù)。

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

關(guān)于寫入數(shù)據(jù)的type,如上面這行代碼,type就是CBCharacteristicWriteWithResponse,表示當(dāng)寫入成功時(shí),要進(jìn)行回調(diào)。更多的類型可以參考CBCharacteristicWriteType枚舉。這個(gè)枚舉會(huì)在下一章中介紹到。

如果寫入成功后要回調(diào),那么回調(diào)方法是peripheral:didWriteValueForCharacteristic:error:。如果寫入失敗,那么會(huì)包含到 error 參數(shù)返回。

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

注意:characteristic 也可能并不支持寫操作,可以通過CBCharacteristic的properties屬性來判斷。這個(gè) option 會(huì)在下一章中介紹到。

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

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

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