swift 藍(lán)牙集成開發(fā)

前一階段剛剛做完了對(duì)接體脂稱和智能音箱忌愚,空下來整理一下藍(lán)牙的簡(jiǎn)單集成,不瞎叭叭了,進(jìn)入正題。

一嬉荆、概況

藍(lán)牙4.0標(biāo)準(zhǔn)包括傳統(tǒng)藍(lán)牙部分和低功耗藍(lán)牙模塊部分

二、藍(lán)牙模塊

藍(lán)牙模塊是指集成藍(lán)牙功能的芯片基本電路集合简十,用于短距離2.4G的無線通訊模塊。
對(duì)于終端用戶來說撬腾,藍(lán)牙模塊是半成品螟蝙,通過在模塊的基礎(chǔ)上做功能二次開發(fā)、封裝外殼等工序时鸵,實(shí)現(xiàn)能夠利用藍(lán)牙通訊的最終產(chǎn)品胶逢。

藍(lán)牙模塊按照應(yīng)用和支持協(xié)議劃分主要分為兩種:

1厅瞎、經(jīng)典藍(lán)牙模塊(BT):

泛指支持藍(lán)牙協(xié)議在4.0以下的模塊,一般用于數(shù)量比較的傳輸初坠,如:語音和簸、音樂等較高數(shù)據(jù)量傳輸。

經(jīng)典藍(lán)牙模塊可再細(xì)分為傳統(tǒng)藍(lán)牙模塊和高速藍(lán)牙模塊

高速模塊相比于傳輸藍(lán)牙模塊速率提高到約24Mbps,是傳統(tǒng)藍(lán)牙模塊的八倍碟刺,可以用于錄像機(jī)锁保、高清電視、PC半沽、PMP爽柒、UMPC和打印機(jī)之間的資料傳輸。

2者填、低功耗藍(lán)牙模塊(BLE):

指支持藍(lán)牙協(xié)議4.0或更高的模塊浩村,也成為BLE模塊,最大的特點(diǎn)是成本和功效的降低占哟,應(yīng)用于實(shí)時(shí)性要去比較高的產(chǎn)品中心墅,比如:智能家居類(藍(lán)牙鎖、藍(lán)牙燈)榨乎、傳感設(shè)備的數(shù)據(jù)發(fā)送(血壓計(jì)怎燥、溫度傳感器)、消費(fèi)類電子(電子煙蜜暑、遙控玩具)等铐姚。
藍(lán)牙低功耗技術(shù)采用可變連接時(shí)間間隔,這個(gè)間隔根據(jù)具體應(yīng)用可以設(shè)置為幾毫秒到幾秒不等肛捍。
另外隐绵,因?yàn)锽LE技術(shù)采用非常快速的連接方式篇梭,因此可以處于“非連接”狀態(tài)(節(jié)省能源)氢橙,此時(shí)鏈路兩端只有在必要時(shí)才能開啟鏈路酝枢,然后在盡可能短的時(shí)間內(nèi)關(guān)閉鏈路恬偷。

三、藍(lán)牙模塊對(duì)比

1帘睦、按協(xié)議劃分

單模藍(lán)牙模塊:是指支持藍(lán)牙某一種協(xié)議的模塊
雙模藍(lán)牙模塊:是指同時(shí)支持經(jīng)典藍(lán)牙(BT)和低功耗藍(lán)牙(BLE) 協(xié)議的模塊

2袍患、按應(yīng)用分

藍(lán)牙數(shù)據(jù)模塊:一般多使用BLE地功耗藍(lán)牙模塊,擁有極低的運(yùn)行和待機(jī)功耗竣付,使用一顆紐扣電池可連續(xù)工作數(shù)年之久诡延。
藍(lán)牙音頻模塊:音頻需要大碼流的數(shù)據(jù)傳輸更適合使用BT經(jīng)典藍(lán)牙模塊。

3古胆、按照濕度等級(jí)分為:

工業(yè)級(jí):溫度范圍-40攝氏度~85攝氏度肆良,能夠在室外筛璧、干擾大等惡劣環(huán)境下正常運(yùn)行
商業(yè)級(jí):溫度范圍為0攝氏度~70攝氏度,一般應(yīng)用在普通民用產(chǎn)品中惹恃。

四夭谤、集成

iOS對(duì)藍(lán)牙庫進(jìn)行了封裝,封裝在CoreBluetooth庫

import CoreBluetooth

接下來是對(duì)一些代碼中需要使用到的名詞的備注

CBCentralManager - 中心管理者
CBPeripheralManager - 外設(shè)管理者
CBPeripheral - 外設(shè)對(duì)象
CBService - 外設(shè)服務(wù)
CBCharacteristic - 外設(shè)服務(wù)的特征

注:一個(gè)CBPeripheral可以包含多個(gè)CBService,而一個(gè)CBService也可以包含多個(gè)CBCharacteristic

接下來介紹藍(lán)牙從打開到連接到發(fā)送數(shù)據(jù)到接收數(shù)據(jù)的一整個(gè)流程

1巫糙、設(shè)置權(quán)限朗儒,在info.plist里面加入
Privacy - Bluetooth Peripheral Usage Description
2、開發(fā)流程

 /******藍(lán)牙******/
var bleManager: CBCentralManager? //系統(tǒng)藍(lán)牙設(shè)備管理對(duì)象
var peripheral: CBPeripheral? //外圍設(shè)備
var writeChar: CBCharacteristic? // 寫服務(wù)的特征值

override func viewDidLoad() {
        super.viewDidLoad()
        // MARK: - 可以不用系統(tǒng)的藍(lán)牙提示框参淹,使用自定義的藍(lán)牙提示框
        self.bleManager = CBCentralManager.init(delegate: self, queue: nil, options: nil)
       //MARK:-使用系統(tǒng)的藍(lán)牙提示框
//        self.bleManager = CBCentralManager.init(delegate: self, queue: nil)
        // 設(shè)置代理
        self.bleManager?.delegate = nil
}
  • 判斷藍(lán)牙狀態(tài)

// 判斷藍(lán)牙狀態(tài)醉锄,通過CBCentralManager的state來獲取
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
           // 確認(rèn)藍(lán)牙打開狀態(tài),然后進(jìn)行一系列的操作
            // MARK: - 根據(jù)UUID篩選
//            let uuid = CBUUID(string: "0000180A-0000-1000-8000-00805F9B34FB")
//            self.bleManager?.scanForPeripherals(withServices: [uuid], options: nil)
            // MARK: - 根據(jù)名字篩選
            self.blueToothStatus = true
           self.bleManager?.scanForPeripherals(withServices: nil, options: nil)
            GMLog("藍(lán)牙打開浙值,正在掃描")
        case .unknown:
            GMAlert.gm_alert(title: "", message: "藍(lán)牙權(quán)限不清楚", cancelBtnTitle: "設(shè)置", okBtnTitle: "好") { (alertIndex) in
                
            }
        case .poweredOff:
                GMAlert.gm_alert(title: "", message: "打開藍(lán)牙來允許\"****\"連接到配件", cancelBtnTitle: "設(shè)置", okBtnTitle: "好") {[weak self] (alertIndex) in
                    if alertIndex == .ok {
                        GMLog("ok")
                        GMAlert.gm_alertSingle(title: "提示", message: "手機(jī)藍(lán)牙未開啟\n請(qǐng)?jiān)谠O(shè)置中開啟藍(lán)牙", okBtnTitle: "確定", handler: { (alertIndex) in
                            if #available(iOS 10.0, *) {
                                self?.settingAuthority()
                            } else {
                                // Fallback on earlier versions
                            }
                        })
                    }else {
                        GMLog("設(shè)置")
                        
                        if #available(iOS 10.0, *) {
                            self?.settingAuthority()
                        } else {
                            // Fallback on earlier versions
                            GMLog("版本低于10")
                        }
                    }
                    
                }
        default:
            GMLog("----藍(lán)牙關(guān)閉")
        }
    }
  • 掃描到外設(shè)
    // 掃描到外設(shè)
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        GMLog("----scanPeripheral: \(peripheral)") // , \(peripheral.identifier.uuidString), \(peripheral.identifier.uuid)
        GMLog("*********adver:\(advertisementData)")
        //        NSData *data = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"];
        let data = advertisementData["kCBAdvDataManufacturerData"]
        if data != nil {
            let dataString = dataToString(data: data as! Data)          
        }
        // 連接設(shè)備
        self.bleManager?.connect(peripheral, options: nil)
    }

連接成功&失敗

    // 連接成功
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        GMLog("----連接成功")
//        GMLog("\(self.peripheral)")
        // 停止掃描
        self.bleManager?.stopScan()
        self.peripheral = peripheral
        peripheral.delegate = self
        self.peripheral?.delegate = self
        peripheral.discoverServices(nil)
//        let uuid = "9000"
//        peripheral.discoverServices([CBUUID.init(string: uuid)])
    }
    
    
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        GMLog("----連接失敗")
    }
  • 斷開設(shè)備連接
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        GMLog("----斷開連接, \(self.isBindState), \(String(describing: self.peripheral))")
        self.peripheral = nil
        self.writeChar = nil
// 如果需要重新掃描
        //        self.bleManager?.scanForPeripherals(withServices: nil, options: nil)
    }
  • 搜索到服務(wù)
    //一旦我們讀取到外設(shè)的相關(guān)服務(wù)UUID就會(huì)回調(diào)下面的方法
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        
        for aService in peripheral.services! {
            GMLog("aaaaa-----\(aService.uuid.uuidString)")
//            let uuid = "9000"
            peripheral.discoverCharacteristics(nil, for: aService)
//            peripheral.discoverCharacteristics([CBUUID.init(string: uuid)], for: aService)
            
            // 過濾條件
            //            if aService.uuid == CBUUID.init(string: "0xFFF0") {
            //                peripheral.discoverCharacteristics(nil, for: aService)
            //            }
        }
    }
  • 搜索到特征
    // 根據(jù)特征值去判斷讀操作還是寫操作
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
//        GMLog("!!!!!!!!!!!!!!!!\(service.characteristics)")
        for aChar in service.characteristics! {
            GMLog("#########\(aChar.uuid.uuidString)")
            self.writeChar = aChar
//            self.sendValue1()
            
            // 過濾條件
            //            if aChar.uuid == CBUUID.init(string: "0xFFF1") { // 0xFFF1 寫數(shù)據(jù)
            //
            //                self.writeChar = aChar
            //            }
            //                // 0xFFF4 讀數(shù)據(jù)
            //            else if aChar.uuid == CBUUID.init(string: "0xFFF4") {
            //
            //                peripheral.setNotifyValue(true, for: aChar)
            //            }
            
            if aChar.uuid == CBUUID.init(string: "9999") {
                peripheral.setNotifyValue(true, for: aChar)
            }
        }
    }
  • 收到外設(shè)消息更新
    // 發(fā)送
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        self.sendValue1()
//        if characteristic.isNotifying {
//            self.sendValue1()
//        }
    }
    
    
    // 接收
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        
        guard let data = characteristic.value else {
            return
        }
        GMLog("\(characteristic)")
        GMLog("----接收數(shù)據(jù):\(self.dataToString(data: data))")
        let dataString = self.dataToString(data: data)
        let resultString = String(dataString.suffix(dataString.count - 12))
//        let acceptString = string(from: self.dataTo(from: resultString))
        self.deviceId = string(from: self.dataTo(from: resultString))
        GMLog("\(string(from: self.dataTo(from: resultString)))")
        
        // didDisconnectPeripheral
        
        // MARK: - 如果接收到deviceId 斷開藍(lán)牙恳不;否則繼續(xù)接收
        if self.deviceId != nil {
            if let peripheral = self.peripheral {
                self.bleManager?.cancelPeripheralConnection(peripheral)
            }
        }
        // didDisconnectPeripheral
//        self.bleManager?.cancelPeripheralConnection(self.peripheral!)
        // FIXME: - 接收到數(shù)據(jù)后請(qǐng)求后臺(tái)接口
        self.newAnlanyData(data: data)
    }
    
    //向peripheral中寫入數(shù)據(jù)后的回調(diào)函數(shù)
    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
        GMLog("寫入成功")
    }
    
    
    /// 新的計(jì)算方法
    private func newAnlanyData(data: Data) {
        if data[0] == 0xCF {
            GMLog("進(jìn)這里")
        } else if data[0] == 0x00 && data.count == 2 {
            self.sendDeviceOff()
        }
    }
}

說明

  • 藍(lán)牙的流程:搜索-連接-連接成功/失敗(設(shè)置外設(shè)代理开呐,搜索服務(wù))-搜索到服務(wù)(搜索特征)-搜索到特征-監(jiān)聽需要的特征(讀寫妆够、讀、寫等根據(jù)情況來確定)-通過外設(shè)讀寫特征寫入指令-收到設(shè)備返回信息-斷開連接
  • CBCentralManagerDelegate:中心管理者代理负蚊,負(fù)責(zé)搜索神妹,設(shè)備狀態(tài)的一些回調(diào)
    CBPeripheralDelegate:外設(shè)代理,負(fù)責(zé)對(duì)外設(shè)的一些操作家妆,特征的訂閱鸵荠,以及設(shè)備信息和消息的更新回調(diào)

16進(jìn)制的轉(zhuǎn)換

16進(jìn)制類型的字符串[A-F,0-9]和Data之間的轉(zhuǎn)換可以使用下面的方法。如果是包含=之類的可以直接用字符串轉(zhuǎn)換Data即可

extension String {
    ///16進(jìn)制字符串轉(zhuǎn)Data
    func hexData() -> Data? {
        var data = Data(capacity: count / 2)
        let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
        regex.enumerateMatches(in: self, range: NSMakeRange(0, utf16.count)) { match, flags, stop in
            let byteString = (self as NSString).substring(with: match!.range)
            var num = UInt8(byteString, radix: 16)!
            data.append(&num, count: 1)
        }
        guard data.count > 0 else { return nil }
        return data
    }
    func utf8Data()-> Data? {
        return self.data(using: .utf8)
    } 
}
extension Data {
    ///Data轉(zhuǎn)16進(jìn)制字符串
    func hexString() -> String {
        return map { String(format: "%02x", $0) }.joined(separator: "").uppercased()
    }
}

OTA升級(jí)

OTA是DFU(Device Firmware Update)的一種類型伤极,準(zhǔn)確說蛹找,OTA的全稱應(yīng)該是OTA DFU,就是設(shè)備固件升級(jí)的意思哨坪。只不過大家為了方便起見庸疾,直接用OTA來指代固件空中升級(jí)(有時(shí)候大家也將OTA稱為FOTA)。
OTA升級(jí)并不復(fù)雜当编,只需要按照硬件定制的協(xié)議届慈,把數(shù)據(jù)按照正常的寫入方式發(fā)送給硬件即可(注意查看硬件是否規(guī)定數(shù)據(jù)的大小端)

大端模式:是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的高地址中忿偷,這樣的存儲(chǔ)模式有點(diǎn)類似于把數(shù)據(jù)當(dāng)作字符串順序處理:地址由小向大增加金顿,而數(shù)據(jù)從高位往低位放;這和我們的閱讀習(xí)慣一致鲤桥。
小端模式:是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的高地址中揍拆,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的低地址中,這種存儲(chǔ)模式將地址的高低和數(shù)據(jù)位權(quán)有效地結(jié)合起來茶凳,高地址部分權(quán)值高嫂拴,低地址部分權(quán)值低播揪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市筒狠,隨后出現(xiàn)的幾起案子剪芍,更是在濱河造成了極大的恐慌,老刑警劉巖窟蓝,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罪裹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡运挫,警方通過查閱死者的電腦和手機(jī)状共,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谁帕,“玉大人峡继,你說我怎么就攤上這事⌒偻冢” “怎么了碾牌?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長儡循。 經(jīng)常有香客問我舶吗,道長,這世上最難降的妖魔是什么择膝? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任誓琼,我火速辦了婚禮,結(jié)果婚禮上肴捉,老公的妹妹穿的比我還像新娘腹侣。我一直安慰自己,他們只是感情好齿穗,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布傲隶。 她就那樣靜靜地躺著,像睡著了一般窃页。 火紅的嫁衣襯著肌膚如雪跺株。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天腮出,我揣著相機(jī)與錄音帖鸦,去河邊找鬼芝薇。 笑死胚嘲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的洛二。 我是一名探鬼主播馋劈,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼攻锰,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了妓雾?” 一聲冷哼從身側(cè)響起娶吞,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎械姻,沒想到半個(gè)月后妒蛇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡楷拳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年绣夺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欢揖。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陶耍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出她混,到底是詐尸還是另有隱情烈钞,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布坤按,位于F島的核電站毯欣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏臭脓。R本人自食惡果不足惜仪媒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谢鹊。 院中可真熱鬧算吩,春花似錦、人聲如沸佃扼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兼耀。三九已至压昼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘤运,已是汗流浹背窍霞。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拯坟,地道東北人但金。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像郁季,于是被迫代替她去往敵國和親冷溃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钱磅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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

  • iOS開發(fā)藍(lán)牙4.0初識(shí)轉(zhuǎn)載 2015-09-20 15:26:44標(biāo)簽:ios開發(fā)藍(lán)牙ios開發(fā)藍(lán)牙4.0ios...
    Jany_4a9a閱讀 2,733評(píng)論 0 3
  • 公司項(xiàng)目需要用到BLE以CBCentralManager的身份和硬件交互盖淡,開發(fā)過程中解決了一些遇到的問題和一些處理...
    daihz閱讀 5,296評(píng)論 3 32
  • 前言 很久沒有更新技術(shù)博客了。最近一年都在做低功耗藍(lán)牙和物聯(lián)網(wǎng)等相關(guān)的事情凿歼,想把一些經(jīng)驗(yàn)和心得寫成博客分享出來褪迟,也...
    熊小宇閱讀 6,979評(píng)論 2 7
  • 背景 藍(lán)牙歷史說到藍(lán)牙,就不得不說下藍(lán)牙技術(shù)聯(lián)盟(Bluetooth SIG),它負(fù)責(zé)藍(lán)牙規(guī)范制定和推廣的國際組織...
    徐正峰閱讀 12,387評(píng)論 6 33
  • 追女秘籍一:極力贊美女人答憔,讓她慢慢融化牵咙。 女人,都喜歡被人夸獎(jiǎng)和贊美攀唯,即使說的不是實(shí)話洁桌,聽后心里也很開心。楊玉環(huán)在...
    紅豆印跡閱讀 831評(píng)論 0 1