iOS低功耗藍(lán)牙(Corebluetooth)開發(fā)(swift版)

現(xiàn)在公司手頭的項(xiàng)目基本上是基于藍(lán)牙的開發(fā)益咬,所以對低功耗藍(lán)牙還是有一定的了解戳护,今天特意把我寫的藍(lán)牙模塊兒整理出來嚎货,供大家參考。


一 : 初識低功耗藍(lán)牙

1, 中心(central)和外設(shè)(peripheral)

剛接觸藍(lán)牙開發(fā)的時候虑凛,找了許多專業(yè)的資料碑宴,講到藍(lán)牙開發(fā)分為兩種模式,中心模式(central),和外設(shè)模式(peripheral)桑谍。云里霧里延柠,以至于搞了兩三天都不知道自己做的東西到底是做為中心還是做為外設(shè)。其實(shí)遠(yuǎn)遠(yuǎn)沒有那么復(fù)雜锣披,一般來講贞间,我們做的需要在軟件內(nèi)連接硬件,通過連接硬件給硬件發(fā)送指令以完成一些動作的藍(lán)牙開發(fā)都是基于中心模式(central)模式的開發(fā)雹仿,也就是說我們開發(fā)的app是中心增热,我們要連接的硬件是外設(shè)。外設(shè)有個特點(diǎn)是它會一直廣播自己胧辽,告訴中心我在這里峻仇,我在這里,快來連我---,這個時候呢邑商,我們的中心就可以通過掃描發(fā)現(xiàn)并且點(diǎn)擊連接了摄咆。

2凡蚜,服務(wù)(service)和特征(character)

我們通過藍(lán)牙連接了硬件,無非就是想通過連接硬件的讀取一些信息吭从,例如硬件通過溫度傳感器獲取的環(huán)境溫度朝蜘,或者濕度傳感器獲取的環(huán)境濕度,又或者是硬件自己的電量等等影锈。或者是給這個設(shè)備發(fā)送一些指令以達(dá)到控制硬件行為的目的蝉绷,例如我們的app連接了空調(diào)鸭廷,想發(fā)送指令過去改變空調(diào)的預(yù)設(shè)溫度,或者發(fā)送指令給共享單車熔吗,讓單車的鎖解開辆床。我們的app中心與設(shè)備之間的通信需要一個橋梁,這個橋梁就是服務(wù)與特征桅狠。一個設(shè)備中可能包含很多服務(wù)讼载,而一個服務(wù)可能又包含幾個特征。比方說中跌,標(biāo)準(zhǔn)藍(lán)牙模塊會有幾個內(nèi)置的服務(wù)與特征如溫度咨堤,心跳,等等漩符。


藍(lán)牙標(biāo)準(zhǔn)的服務(wù).png

這些服務(wù)與特征都是硬件工程師可以直接拿來用的一喘。如果有其他特殊的需求,硬件工程師也可以再重新自己定義服務(wù)與特征嗜暴。


外設(shè)服務(wù)與特征的關(guān)系.png
3凸克,讀(read) , 寫(write), 訂閱(notify)

我們的目的是讀取設(shè)備中的數(shù)據(jù)(read) , 或者給設(shè)備寫入一定的數(shù)據(jù)(write)。有時候我們還想設(shè)備的數(shù)據(jù)變化的時候不需要我們手動去讀取這個值闷沥,需要設(shè)備自動通知我們它的值變化了萎战,值是多少。把值告訴app舆逃,這個時候就需要訂閱這個特征了(notify)


二 : app做為中心(central)編碼

鑒于大部分需求都是以app做為中心蚂维,硬件設(shè)備做為外設(shè),所以這里只針對中心模式做詳細(xì)介紹路狮。
一般來講鸟雏,都要求我們的app時刻與硬件保持連接,當(dāng)然了览祖,這里的時刻保持連接指的是物理上的孝鹊,我們的app能看到的是當(dāng)前設(shè)備處于連接狀態(tài)。低功耗藍(lán)牙內(nèi)部的實(shí)現(xiàn)絕對不是時刻保持連接展蒂,否則也不能稱之為低功耗藍(lán)牙了又活。Corebluetooth框架內(nèi)部app做為中心的情況下藍(lán)牙管理有一個中心類苔咪,叫CBCentralManager,我們叫它中心管理類柳骄。我們手機(jī)的藍(lán)牙開啟狀態(tài)团赏,手機(jī)與藍(lán)牙設(shè)備等連接,斷開連接耐薯,連接失敗等等都由這個中心管理類來控制舔清。為了這個中心管理類在app的生命周期都存在。我們一般把它進(jìn)行封裝曲初,做成一個單例体谒。這樣app整個生命周期中這個單例時刻存在。下面是封裝的藍(lán)牙單例代碼臼婆。

import Foundation
import CoreBluetooth

//用于看發(fā)送數(shù)據(jù)是否成功!
class LLBlueTooth:NSObject {
    
    //單例對象
    internal static let instance = LLBlueTooth()
    
    //中心對象
    var central : CBCentralManager?
    
    //中心掃描到的設(shè)備都可以保存起來抒痒,
    //掃描到新設(shè)備后可以通過通知的方式發(fā)送出去,連接設(shè)備界面可以接收通知颁褂,實(shí)時刷新設(shè)備列表
    var deviceList: NSMutableArray?
    
    // 當(dāng)前連接的設(shè)備
    var peripheral:CBPeripheral!
    
    //發(fā)送數(shù)據(jù)特征(連接到設(shè)備之后可以把需要用到的特征保存起來故响,方便使用)
    var sendCharacteristic:CBCharacteristic?
    
    
    override init() {
        
        super.init()
        
        self.central = CBCentralManager.init(delegate:self, queue:nil, options:[CBCentralManagerOptionShowPowerAlertKey:false])
        
        self.deviceList = NSMutableArray()
        
    }

    
    // MARK: 掃描設(shè)備的方法
    func scanForPeripheralsWithServices(_ serviceUUIDS:[CBUUID]?, options:[String: AnyObject]?){
        
        self.central?.scanForPeripherals(withServices: serviceUUIDS, options: options)
        
    }
    
    
    // MARK: 停止掃描
    func stopScan() {
        
        self.central?.stopScan()
        
    }
    
    // MARK: 寫數(shù)據(jù)
    func writeToPeripheral(_ data: Data) {
        peripheral.writeValue(data , for: sendCharacteristic!, type: CBCharacteristicWriteType.withResponse)
    }
    
    
    // MARK: 連接某個設(shè)備的方法
    /*
     *  設(shè)備有幾個狀態(tài)
     @available(iOS 7.0, *)
     public enum CBPeripheralState : Int {
         case disconnected
         
         case connecting
         
         case connected
         
         @available(iOS 9.0, *)
         case disconnecting
     }
     */
    func requestConnectPeripheral(_ model:CBPeripheral) {
        
        if (model.state != CBPeripheralState.connected) {
            
            central?.connect(model , options: nil)
            
        }
        
    }
    
}


//MARK: -- 中心管理器的代理
extension LLBlueTooth : CBCentralManagerDelegate{
    
    // MARK: 檢查運(yùn)行這個App的設(shè)備是不是支持BLE。
    func centralManagerDidUpdateState(_ central: CBCentralManager){
        
        if #available(iOS 10.0, *) {
            switch central.state {
                
            case CBManagerState.poweredOn:
                print("藍(lán)牙打開")
                
            case CBManagerState.unauthorized:
                print("沒有藍(lán)牙功能")
                
            case CBManagerState.poweredOff:
                print("藍(lán)牙關(guān)閉")
                
            default:
                print("未知狀態(tài)")
            }
        }
        // 手機(jī)藍(lán)牙狀態(tài)發(fā)生變化颁独,可以發(fā)送通知出去彩届。提示用戶
        
    }
    
    
    // 開始掃描之后會掃描到藍(lán)牙設(shè)備,掃描到之后走到這個代理方法
    // MARK: 中心管理器掃描到了設(shè)備
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        
        //  在這個地方可以判讀是不是自己本公司的設(shè)備,這個是根據(jù)設(shè)備的名稱過濾的
        guard peripheral.name != nil , peripheral.name!.contains("*****") else {
            return
        }
        
        //  這里判斷重復(fù)誓酒,加到devielist中惨缆。發(fā)出通知。
        
    }
    
    
    // MARK: 連接外設(shè)成功丰捷,開始發(fā)現(xiàn)服務(wù)
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral){
        
        // 設(shè)置代理
        peripheral.delegate = self
        
        // 開始發(fā)現(xiàn)服務(wù)
        peripheral.discoverServices(nil)
        
        // 保存當(dāng)前連接設(shè)備
        self.peripheral = peripheral
        
        // 這里可以發(fā)通知出去告訴設(shè)備連接界面連接成功
        
    }
    
    // MARK: 連接外設(shè)失敗
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        
        // 這里可以發(fā)通知出去告訴設(shè)備連接界面連接失敗
        
    }
    
    // MARK: 連接丟失
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        NotificationCenter.default.post(name: Notification.Name(rawValue: "DidDisConnectPeriphernalNotification"), object: nil, userInfo: ["deviceList": self.deviceList as AnyObject])

        // 這里可以發(fā)通知出去告訴設(shè)備連接界面連接丟失
        
    }
    
}


// 外設(shè)的代理
extension LLBlueTooth : CBPeripheralDelegate {
    
    //MARK: - 匹配對應(yīng)服務(wù)UUID
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?){
        
        if error != nil {
            return
        }
        
        for service in peripheral.services! {
            peripheral.discoverCharacteristics(nil, for: service )
        }
        
    }
    
    //MARK: - 服務(wù)下的特征
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?){
        
        if (error != nil){
            return
        }
        
        for  characteristic in service.characteristics! {
            
            switch characteristic.uuid.description {
                
            case "A28DA977":
                // 訂閱特征值坯墨,訂閱成功后后續(xù)所有的值變化都會自動通知
                peripheral.setNotifyValue(true, for: characteristic)
            case "******":
                // 讀區(qū)特征值,只能讀到一次
                peripheral.readValue(for:characteristic)
            default:
                print("掃描到其他特征")
            }
            
        }
        
    }
    
    //MARK: - 特征的訂閱狀體發(fā)生變化
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?){
        
        guard error == nil  else {
            return
        }
        
    }
    
    // MARK: - 獲取外設(shè)發(fā)來的數(shù)據(jù)
    // 注意病往,所有的捣染,不管是 read , notify 的特征的值都是在這里讀取
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)-> (){
        
        if(error != nil){
            return
        }
        
        switch characteristic.uuid.uuidString {
            
        case "***************":
            print("接收到了設(shè)備的溫度特征的值的變化")
        default:
            print("收到了其他數(shù)據(jù)特征數(shù)據(jù): \(characteristic.uuid.uuidString)")
        }
        
    }
    
    
    
    //MARK: - 檢測中心向外設(shè)寫數(shù)據(jù)是否成功
    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        
        if(error != nil){
            print("發(fā)送數(shù)據(jù)失敗!error信息:\(String(describing: error))")
        }
        
    }
    
}

如果有沒有寫清楚的地方,歡迎大家提問和補(bǔ)充停巷。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耍攘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子畔勤,更是在濱河造成了極大的恐慌蕾各,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庆揪,死亡現(xiàn)場離奇詭異式曲,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門吝羞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兰伤,“玉大人,你說我怎么就攤上這事钧排《厍唬” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵恨溜,是天一觀的道長符衔。 經(jīng)常有香客問我,道長糟袁,這世上最難降的妖魔是什么判族? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮系吭,結(jié)果婚禮上五嫂,老公的妹妹穿的比我還像新娘颗品。我一直安慰自己肯尺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布躯枢。 她就那樣靜靜地躺著则吟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锄蹂。 梳的紋絲不亂的頭發(fā)上氓仲,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音得糜,去河邊找鬼敬扛。 笑死,一個胖子當(dāng)著我的面吹牛朝抖,可吹牛的內(nèi)容都是我干的啥箭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼治宣,長吁一口氣:“原來是場噩夢啊……” “哼急侥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起侮邀,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤坏怪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绊茧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铝宵,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年华畏,在試婚紗的時候發(fā)現(xiàn)自己被綠了捉超。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胧卤。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖拼岳,靈堂內(nèi)的尸體忽然破棺而出枝誊,到底是詐尸還是另有隱情,我是刑警寧澤惜纸,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布叶撒,位于F島的核電站,受9級特大地震影響耐版,放射性物質(zhì)發(fā)生泄漏祠够。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一粪牲、第九天 我趴在偏房一處隱蔽的房頂上張望古瓤。 院中可真熱鬧,春花似錦腺阳、人聲如沸落君。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绎速。三九已至,卻和暖如春焙蚓,著一層夾襖步出監(jiān)牢的瞬間纹冤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工购公, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留萌京,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓宏浩,卻偏偏與公主長得像知残,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子绘闷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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