iOS BLE開(kāi)發(fā)

簡(jiǎn)介

BLE(Bluetooth Low Energy),低功耗藍(lán)牙,使用2.4GHz無(wú)線(xiàn)電頻率。從藍(lán)牙4.0開(kāi)始支持。通常我們的手機(jī)都已支持藍(lán)牙4.0,iPhone從iPhone4s起已經(jīng)支持藍(lán)牙4.0(不是iPhone4s就是iPhone5,具體沒(méi)仔細(xì)查),而從iPhone8開(kāi)始,藍(lán)牙已經(jīng)使用5.0了,可以支持藍(lán)牙m(xù)esh。

宣告與發(fā)現(xiàn)

BLE設(shè)備通過(guò)廣播宣告(advertising)數(shù)據(jù)包的方式被發(fā)現(xiàn),發(fā)現(xiàn)設(shè)備的等待時(shí)間存在概率性,取決于三個(gè)參數(shù)(宣告間隔、掃描間隔和掃描窗口)。對(duì)于iOS開(kāi)發(fā),并不需要關(guān)心這幾個(gè)參數(shù),系統(tǒng)底層已經(jīng)為我們做了最佳的選擇。

通信

藍(lán)牙技術(shù)聯(lián)盟沿用經(jīng)典藍(lán)牙的規(guī)范內(nèi)容,為藍(lán)牙低功耗定義了一些profile,這些profile定義了一個(gè)設(shè)備在特定應(yīng)用情景下如何工作。profile都基于通用屬性規(guī)范(GATT)。GATT定義了屬性,作為通用的封裝數(shù)據(jù)的單位,并定義了如何通過(guò)藍(lán)牙連接傳輸屬性從而達(dá)到傳輸數(shù)據(jù)的目的。
對(duì)于手機(jī)端來(lái)說(shuō),藍(lán)牙設(shè)備的數(shù)據(jù)簡(jiǎn)單來(lái)說(shuō)就是一堆UUID及其對(duì)應(yīng)的值,iOS就是一堆CBUUID。

Exp:

將一個(gè)WiFi+BLE設(shè)備接入家庭WiFi,共分幾步:

  1. 發(fā)現(xiàn)設(shè)備
  2. 獲取設(shè)備信息,即獲取設(shè)備的services和characters
  3. 連接設(shè)備
  4. 與設(shè)備交互信息
  5. 斷開(kāi)連接
  • 發(fā)現(xiàn)設(shè)備

//在你需要的地方初始化
//這里創(chuàng)建了一個(gè)串行隊(duì)列,如果queue為nil,則使用主線(xiàn)程
//實(shí)際使用時(shí)globle_queue也沒(méi)出現(xiàn)過(guò)問(wèn)題
self.serial_queue = dispatch_queue_create("ble_queue", DISPATCH_QUEUE_SERIAL);
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:self.serial_queue options:nil];

//開(kāi)始掃描
/* warn:掃描接口要在下面回調(diào)中調(diào)用,而不是初始化之后就可以調(diào)用。
在初始化之后立刻調(diào)用的話(huà),由于當(dāng)前central.state == CBManagerStateUnknown,會(huì)導(dǎo)致掃描失敗
*/
//實(shí)現(xiàn)CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
    if (central.state == CBManagerStatePoweredOn) {
        //在這里啟動(dòng)掃描接口,nil表示獲取所有services
        /*CBCentralManagerScanOptionAllowDuplicatesKey 
          YES表示只要收到一個(gè)BLE設(shè)備的廣播包就會(huì)上報(bào),不會(huì)對(duì)相同的BLE設(shè)備進(jìn)行過(guò)濾,這樣做是比較耗電的,但業(yè)務(wù)需求,要不斷更新周?chē)腂LE設(shè)備,所以這里還是選擇了YES
          NO為默認(rèn)值,同一個(gè)BLE設(shè)備的廣播包會(huì)被合并成,只通知一次事件
        */
        [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@(YES)}];
    }    
}

//掃描到外圍設(shè)備時(shí)回調(diào)此方法
//外圍設(shè)備:建立連接過(guò)程中接受建立一個(gè)活躍的物理連接請(qǐng)求的LE設(shè)備定義為Peripheral 角色
//中心設(shè)備:建立連接過(guò)程中發(fā)起建立活躍物理連接請(qǐng)求的LE 設(shè)備定義為Central 角色
//在我們這個(gè)例子中手機(jī)就是中心設(shè)備,要接入家庭WiFi的這個(gè)設(shè)備就是外圍設(shè)備
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
    //在該方法中,可以通過(guò)advertisementData解析過(guò)濾出自己的設(shè)備
    //RSSI為信號(hào)強(qiáng)度,負(fù)數(shù),值越大(越接近0),信號(hào)越好

    //找到自己要連接的設(shè)備后,可以將peripheral存起來(lái),用于后面在某個(gè)點(diǎn)擊事件中進(jìn)行連接
}
  • 連接設(shè)備

//property getter
//我們只獲取下面兩個(gè)services
- (NSArray<CBUUID *> *)serviceUUIDs {
    if (_serviceUUIDs == nil) {
        //這里遇到過(guò)一個(gè)偶現(xiàn)的奇葩問(wèn)題
        //128bit初始化uuid時(shí)偶現(xiàn)過(guò)崩潰,原因未知,所以使用16bit初始化
        CBUUID* uuid1 = [CBUUID UUIDWithString:@"FF00"];
        CBUUID* uuid2 = [CBUUID UUIDWithString:@"FF10"];
        _serviceUUIDs = @[uuid1, uuid2];
    }
    return _serviceUUIDs;
}

//在你合適的地方調(diào)用連接外圍設(shè)備
self.centralManager connectPeripheral:self.peripheral options:nil];

//實(shí)現(xiàn)CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    //獲取感興趣的服務(wù)及屬性
    [peripheral discoverServices:self.serviceUUIDs];
}


//實(shí)現(xiàn)CBPeripheralDelegate
//發(fā)現(xiàn)所有你要的服務(wù)后會(huì)回調(diào)該方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error {
    //獲取該屬性對(duì)應(yīng)的服務(wù)
    for (int i = 0; i < peripheral.services.count; i++) {
        CBService *service = peripheral.services[i];
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

//發(fā)現(xiàn)該服務(wù)對(duì)應(yīng)的屬性時(shí)會(huì)回調(diào)該方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error {
    //這里我是做了個(gè)標(biāo)記為,當(dāng)獲取到了所有我想要的屬性之后,再開(kāi)始讀寫(xiě)操作
    //但這里對(duì)我是不方便的,業(yè)務(wù)需要我全部獲取完之后才能讀寫(xiě),所以不能每次回調(diào)進(jìn)來(lái)都進(jìn)行相應(yīng)的操作

    if(discoverEnd) {
        //這里就可以按照自己的協(xié)議進(jìn)行讀寫(xiě)了
        ...write(ssid+password)
        ...
    }
}
  • 讀寫(xiě)設(shè)備

//實(shí)現(xiàn)相應(yīng)的delegate
//讀回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
    //這里有一點(diǎn)比較奇怪的是,我對(duì)某一屬性setNotifyValue
    //只有第一次是通過(guò)didUpdateNotificationStateForCharacteristic這個(gè)回調(diào)上來(lái)的
    //而其他時(shí)候都是通過(guò)讀回調(diào)上來(lái)的

    //ooxxoo
    //在通信完成后斷開(kāi)設(shè)備連接
    self.centralManager cancelPeripheralConnection:peripheral];
}

//通知回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {

}


- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {


}
?著作權(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)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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