iOS之BLE藍牙SDK開發我的總結(基礎篇)

最近一段時間一直在作公司的BLE藍牙SDK,sdk主要負責外設和手機的鏈接以及數據通訊。過程當中遇到了一些比較有價值的問題,如今總結記錄下。數組

藍牙開發使用系統框架#import <CoreBluetooth/CoreBluetooth.h> 使用[[CBCentralManager alloc] initWithDelegate:self queue:nil]初始化CBCentralManager對象。(設置CBCentralManagerDelegate爲self,nil表示在主線程) 初始化成功後系統會自動檢測藍牙狀態。實現centralManagerDidUpdateState:代理方法可實時獲取藍牙狀態。當central.stateCBManagerStatePoweredOn時表示可用。bash

初始化完成可以使用[self.centralManager scanForPeripheralsWithServices:nil options:nil]方法掃描周圍設備(Services表示只掃描具備某些ServiceUUID的設備,nil爲掃描所有)。 當掃描到設備時會執行代理方法centralManager:didDiscoverPeripheral:advertisementData:RSSI:返回每個掃描到的設備及設備的相關信息。框架

爲了使用方便,能夠把掃描的設備、鏈接的設備、設備的服務、設備的特徵分別保存到數組中ide

@property(nonatomic, strong) NSMutableArray<CBPeripheral *> *peripheralArr;             //掃描到的設備數組
@property(nonatomic, strong) NSMutableArray<CBPeripheral *> *currentPeripheralArr;      //當前鏈接的全部設備
@property(nonatomic, strong) NSMutableArray<CBService *> *serviceArr;                   //當前鏈接設備的服務
@property(nonatomic, strong) NSMutableArray<CBCharacteristic *> *characteristicArr;     //當前鏈接的設備的全部特徵
複製代碼

保存到數組中的設備可經過UUID來進行區分。從 iOS7以後蘋果不提供外設的mac地址,外設的惟一標識換成了由mac封裝加密後的UUID,須要注意的是不一樣的手機獲取同一個外設的UUID是不一樣的,因此在不一樣手機之間UUID不是惟一的,但在本機上能夠做爲惟一標識。ui

//經過設備對象鏈接設備
[self.centralManager connectPeripheral:peripheral options:nil];
複製代碼

鏈接失敗時執行centralManager:didFailToConnectPeripheral:error:this

//鏈接設備成功時調用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    //設置代理
    peripheral.delegate = self;
    //獲取設備的服務,傳nil表明獲取全部的服務
    [peripheral discoverServices:nil];
}
複製代碼

ble藍牙主要有 設備-->服務-->特徵 3層。分別都是一對多的關係。它們都有個惟一標識UUID,設備UUID,服務UUID,特徵UUID。atom

發現服務加密

//發現服務時
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if(!error)
    {
        //遍歷peripheral.services數組
        for (CBService * service in peripheral.services)
        {
            if (![self.serviceArr containsObject:service])
            {
                NSLog(@"設備:%@發現新服務:%@",peripheral.identifier.UUIDString, service.UUID.UUIDString);
                [self.serviceArr addObject:service];
                //發現特徵
                [peripheral discoverCharacteristics:nil forService:service];
            }
        }
    }
    else{
        NSLog(@"發現服務失敗的錯誤信息%@", error);
    }
}
複製代碼

發現特徵spa

//發現特徵時
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if(!error)
    {
        //把特徵保存到數組
        for (CBCharacteristic *charact in service.characteristics)
        {
            if (![self.characteristicArr containsObject:charact])
            {
                NSLog(@"設備:%@的服務:%@發現新特徵:%@",peripheral.identifier.UUIDString, service.UUID.UUIDString, charact.UUID.UUIDString);
                //保存到數組
                [self.characteristicArr addObject:charact];
            }
        }
        /*
            把設備保存到已鏈接的設備數組
            此時的需求是當特徵發現完成時纔算鏈接成功
        */
        [self.currentPeripheralArr addObject:peripheral];
    }
    else{
        NSLog(@"發現特徵失敗的錯誤信息%@", error);
    }
}
複製代碼

至此以上4個數組都已填滿。 手機和藍牙硬件之間的通訊主要是使用藍牙特徵值的讀寫,接下來就是鏈接設備以後對設備的特徵值進行讀、寫、訂閱。線程

寫入特徵值

/*
peripheral是寫入的設備對象,charact是特徵對象,valueData是要寫入的數據
type的取值有CBCharacteristicWriteWithResponse(有回覆)和CBCharacteristicWriteWithoutResponse(無回覆),和硬件的設置有關
*/
[peripheral writeValue:valueData forCharacteristic:charact type:CBCharacteristicWriteWithResponse];
複製代碼

當type是CBCharacteristicWriteWithResponse時 實現peripheral:didWriteValueForCharacteristic:error:代理方法可以獲取寫入結果的回調。

讀取特徵值

//調用此方法去讀取參數特徵的value
[peripheral readValueForCharacteristic:charact];
複製代碼

實現peripheral:didUpdateValueForCharacteristic:error:代理方法 獲取characteristic.value即爲讀取的特徵值。

訂閱特徵

//設置某特徵的Notify爲YES爲訂閱狀態
[peripheral setNotifyValue:YES forCharacteristic:charact];
複製代碼

實現peripheral:didUpdateNotificationStateForCharacteristic:error:代理方法當訂閱狀態發生改變時會執行。 當訂閱設備的某個特徵時,設備端給這個特徵發送notify消息時會調用peripheral:didUpdateValueForCharacteristic:error:代理方法把notify要傳的值發送過來。

有關藍牙基礎的最後一點就是斷開藍牙鏈接了,也是很是重要的一點,因此寫在最後

斷開鏈接很簡單,只須要調用[self.centralManager cancelPeripheralConnection:peripheral]傳入須要斷開鏈接的設備對象就好了。斷開鏈接時會自動調用centralManager:didDisconnectPeripheral:error:代理方法。 按照以前的慣例,當error爲nil時表示斷開成功,error不爲nil時斷開失敗。這種理解時錯誤的。

這個代理方法官方的解釋

/*!
 *  @method centralManager:didDisconnectPeripheral:error:
 *
 *  @param central      The central manager providing this information.
 *  @param peripheral   The <code>CBPeripheral</code> that has disconnected.
 *  @param error        If an error occurred, the cause of the failure.
 *
 *  @discussion         This method is invoked upon the disconnection of a peripheral that was connected by {@link connectPeripheral:options:}. If the disconnection
 *                      was not initiated by {@link cancelPeripheralConnection}, the cause will be detailed in the <i>error</i> parameter. Once this method has been
 *                      called, no more methods will be invoked on <i>peripheral</i>'s <code>CBPeripheralDelegate</code>. * */ - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error; 複製代碼

大體意思理解爲當你調用cancelPeripheralConnection:方法(主動斷開)斷開鏈接時error爲nil ; 沒有調用這個方法(異常斷開)而斷開時error返回的是異常斷開的緣由。也能夠理解爲主動調用斷開鏈接方法必定會斷開。

接下來就是斷開重連的問題了,對藍牙功能進行封裝時確定少不了斷開重連。首先斷開時可經過上面的代理方法的error是否爲nil判斷是不是異常斷開,通常狀況下異常斷開時是須要重連的。

從新鏈接後發現讀寫數據時沒效果了???

緣由就是當設備斷開鏈接後peripheral.servicesnil了,固然service.characteristics也是nil,因此須要在斷開鏈接時把保存這個設備對應的服務和特徵所有清除,而後在鏈接成功時從新過一遍發現服務和發現特徵的流程就行了。

回顧我的的ble藍牙開發過程總結出來基礎篇遇到的問題大體就這麼多了。本篇文章旨在我的總結和幫助正在作這方面的人理解藍牙開發中這些東西的概念。

理解了事物,解決這個事物相關的問題就不難了。

相關文章
相關標籤/搜索