iOS 低功耗藍牙(BLE)的IoT應用之 - 溫度檢測儀(上)

背景知識

藍牙技術最初由愛立信(也就是多年前手機做得最丑最奇葩的公司,最終被用戶以腳投票踢出市場)創(chuàng)制。技術始于愛立信公司的1994方案,它是研究在移動電話和其他配件間進行低功耗、低成本無線通信連接的方法。發(fā)明者希望為設備間的通訊創(chuàng)造一組統(tǒng)一規(guī)則(標準化協(xié)議),以解決用戶間互不兼容的移動電子設備。1997年前愛立信公司以此概念接觸了移動設備制造商,討論其項目合作發(fā)展,結果獲得支持。

1998年5月20日,索尼愛立信、國際商業(yè)機器、諾基亞等業(yè)界龍頭創(chuàng)立“特別興趣小組”(Special Interest Group,SIG),即[藍牙技術聯(lián)盟的前身,目標是開發(fā)一個成本低、效益高、可以在短距離范圍內(nèi)隨意無線連接的藍牙技術標準。

這項無線技術的名稱取自古代丹麥維京國王Harald Bl?tand的名字,他以統(tǒng)一了因宗教戰(zhàn)爭和領土爭議而分裂的挪威與丹麥而聞名于世,而這個名字的英文便是Harald Bluetooth。

1998年時藍牙推出0.7規(guī)格,支持Baseband與LMP(Link Manager Protocol)通訊協(xié)定兩部分。1999年推出先后0.8版,0.9版、1.0 Draft版,1.0a版、1.0B版。1.0 Draft版,完成SDP(Service Discovery Protocol)協(xié)定、TCS(Telephony Control Specification)協(xié)定。1999年7月26日正式公布1.0版,確定使用2.4GHz頻譜,最高資料傳輸速度1Mbps,同時開始了大規(guī)模宣傳。和當時流行的紅外線技術相比,藍牙有著更高的傳輸速度,而且不需要像紅外線那樣進行接口對接口的連接,所有藍牙設備基本上只要在有效通訊范圍內(nèi)使用,就可以進行隨時連接。

當1.0規(guī)格推出以后,藍牙并未立即受到廣泛的應用,除了當時對應藍牙功能的電子設備種類少,藍牙裝置也十分昂貴。2001年的1.1版正式列入IEEE標準,Bluetooth 1.1即為IEEE 802.15.1。同年,SIG成員公司超過2000家。過了幾年之后,采用藍牙技術的電子裝置如雨后春筍般增加,售價也大幅下降。為了擴寬藍牙的應用層面和傳輸速度,SIG先后推出了1.2、2.0版,以及其他附加新功能,例如EDR(Enhanced Data Rate,配合2.0的技術標準,將最大傳輸速度提高到3Mbps)、A2DP(Advanced Audio Distribution Profile,一個控音軌分配技術,主要應用于立體聲耳機、AVRCP(A/V Remote Control Profile)等。Bluetooth 2.0將傳輸率提升至2Mbps、3Mbps,遠大于1.x版的1Mbps(實際約723.2kbps)。

藍牙從1.0到2.0其速度與傳輸距離一直是其硬傷,使用體驗極差文件型的數(shù)據(jù)傳輸幾乎等于不可用,所以我一直對藍牙設備不感冒。直至遇到BLE的出現(xiàn)也就是低功耗藍牙,來看看4.0之后的藍牙有何技術特性吧:

藍牙4.0

藍牙4.0是Bluetooth SIG于2010年7月7日推出的新的規(guī)范。其最重要的特性是支持省電

  • Bluetooth 4.0,協(xié)議組成和當前主流的Bluetooth h2.x+EDR、還未普及的Bluetooth h3.0+HS不同,Bluetooth 4.0是Bluetooth從誕生至今唯一的一個綜合協(xié)議規(guī)范,
    還提出了“低功耗藍牙”、“傳統(tǒng)藍牙”和“高速藍牙”三種模式。
  • 其中:高速藍牙主攻數(shù)據(jù)交換與傳輸;傳統(tǒng)藍牙則以信息溝通、設備連接為重點;藍牙低功耗顧名思義,以不需占用太多帶寬的設備連接為主。前身其實是NOKIA開發(fā)的Wibree技術,本是作為一項專為移動設備開發(fā)的極低功耗的移動無線通信技術,在被SIG接納并規(guī)范化之后重命名為Bluetooth Low Energy(后簡稱低功耗藍牙)。這三種協(xié)議規(guī)范還能夠互相組合搭配、從而實現(xiàn)更廣泛的應用模式,此外,Bluetooth 4.0還把藍牙的傳輸距離提升到100米以上(低功耗模式條件下)。
  • 分Single mode與Dual mode。
  • Single mode只能與BT4.0互相傳輸無法向下兼容(與3.0/2.1/2.0無法相通);Dual mode可以向下兼容可與BT4.0傳輸也可以跟3.0/2.1/2.0傳輸
  • 超低的峰值、平均和待機模式功耗,覆蓋范圍增強,最大范圍可超過100米。
  • 速度:支持1Mbps數(shù)據(jù)傳輸率下的超短數(shù)據(jù)包,最少8個八組位,最多27個。所有連接都使用藍牙2.1加入的減速呼吸模式(sniff subrating)來達到超低工作循環(huán)。
  • 跳頻:使用所有藍牙規(guī)范版本通用的自適應跳頻,最大程度地減少和其他2.4 GHz ISM頻段無線技術的串擾。
  • 主控制:可以休眠更長時間,只在需要執(zhí)行動作的時候才喚醒。
  • 延遲:最短可在3毫秒內(nèi)完成連接設置并開始傳輸數(shù)據(jù)。
  • 健壯性:所有數(shù)據(jù)包都使用24-bit CRC校驗,確保最大程度抵御干擾。
  • 安全:使用AES-128 CCM加密算法進行數(shù)據(jù)包加密和認證。
  • 拓撲:每個數(shù)據(jù)包的每次接收都使用32位尋址,理論上可連接數(shù)十億設備;針對一對一連接最優(yōu)化,并支持星形拓撲的一對多連接;使用快速連接和斷開,數(shù)據(jù)可以在網(wǎng)狀拓撲內(nèi)轉(zhuǎn)移而無需維持復雜的網(wǎng)狀網(wǎng)絡。

藍牙4.1

藍牙4.1是藍牙技術聯(lián)盟于2013年底推出的新的規(guī)范,其目的是為了讓 Bluetooth Smart 技術最終成為物聯(lián)網(wǎng)(Internet of Things)發(fā)展的核心動力。
此版本為藍牙4.0的軟件更新版本,搭載藍牙4.0設備的終端可通過軟件更新獲得此版本。

對于開發(fā)人員而言,該更新是藍牙技術發(fā)展史上一項重要的進步。該更新提供了更高的靈活性和掌控度,讓開發(fā)人員能創(chuàng)造更具創(chuàng)新并催化物聯(lián)網(wǎng)(IOT)發(fā)展的產(chǎn)品。
支持多設備連接。

  • 智能連接:增加設置設備間連接頻率的支持。制造商可以對設備設置連接進行設置,使得設備可以更加智能的控制設備電量。

藍牙4.2

藍牙4.2是藍牙技術聯(lián)盟于2014年12月推出的新的規(guī)范。

藍牙5

藍牙5在2016年6月被宣布。在有效傳輸距離上將是4.2LE版本的4倍(理論上可達300米),傳輸速度將是4.2LE版本的2倍(速度上限為24Mbps)。藍牙5.0還支持室內(nèi)定位導航功能(結合WiFi可以實現(xiàn)精度小于1米的室內(nèi)定位),允許無需配對接受信標的數(shù)據(jù)(比如廣告、Beacon、位置信息等,傳輸率提高了8倍),針對物聯(lián)網(wǎng)進行了很多底層優(yōu)化。

看完這些特性除了不能上網(wǎng)以外其它的無線能力幾乎都能秒殺WIFI了。

藍牙常見名稱和縮寫

在進入IOS之前我們得深入到的理論領域,了解藍牙4.0中的一些基本的術語:

  • MFI:make for ipad, iphone, itouch 專們?yōu)樘O果設備制作的設備
  • BLE:buletouch low energy,藍牙4.0設備因為低耗電,所以也叫做BLE
  • peripheral:外設,被連接的設備
  • central:中心,發(fā)起連接的時central
  • service:服務,每個外設會有很多服務,每個服務中包含很多字段,這些字段的權限一般分為 讀read,寫write,通知notiy幾種,就是我們連接設備后具體需要操作的內(nèi)容。
  • characteristic:特征 每個設備會提供服務和特征,類似于服務端的api,但是機構不同。
  • Description:每個characteristic可以對應一個或多個Description用戶描述characteristic的信息或?qū)傩?br> 外設、服務、特征間的關系

外設、服務、特征間的關系

下圖你在Apple的開發(fā)者網(wǎng)站上也能找到:

外設、服務、特征間的關系

藍牙中心模式流程

  1. 建立中心角色
  2. 掃描外設(discover)
  3. 連接外設(connect)
  4. 掃描外設中的服務和特征(discover)
    4.1 獲取外設的services
    4.2 獲取外設的Characteristics,獲取Characteristics的值,獲取Characteristics的Descriptor和Descriptor的值
  5. 與外設做數(shù)據(jù)交互(explore and interact)
  6. 訂閱Characteristic的通知
  7. 斷開連接(disconnect)

藍牙設備工作的狀態(tài)

  • 準備(standby)
  • 廣播(advertising)
  • 監(jiān)聽掃描(Scanning
  • 發(fā)起連接(Initiating)
  • 已連接(Connected)

藍牙和版本的使用限制

  • 藍牙2.0:越獄設備
  • 藍牙4.0:IOS6 以上
  • MFI認證設備(Make For ipod/ipad/iphone):無限制

iOS 的實現(xiàn)

先創(chuàng)建一個 Swift 的工程,然后引入CoreBluetooth ,它就是iOS中提供藍牙通信的核心庫。然后ViewController必須實現(xiàn) CBCentralManagerDelegate,CBPeripheralDelegate 兩個接口:

import CoreBluetooth
import UIKit

class ViewController: UIViewController, CBCentralManagerDelegate,CBPeripheralDelegate {
        override func viewDidLoad() {
            super.viewDidLoad()
        }
} 

1.) 建立中心角色

然后要定義一個管理中心的對象變量,它就是藍牙控制的主入口,然后在viewDidLoad()中實例化它:

class ViewController: UIViewController, CBCentralManagerDelegate,CBPeripheralDelegate {
    var manager : CBCentralManager!        
    override func viewDidLoad() {
            super.viewDidLoad()
            manager = CBCentralManager.init(delegate: self, queue: DispatchQueue.main)
        }
} 

2.) 掃描外設

管理中心一但被初始化后就會檢查當前手機上的藍牙狀態(tài),并調(diào)用 centralManagerDidUpdateState 方法,假設完整初始化后就馬上進行設備掃描,那就要在centralManagerDidUpdateState中進行,在 viewDidLoad下方加入以下的代碼:

 func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .unknown:
            print("未知的藍牙設備")
        case .resetting:
            print("藍牙正在被重置中")
        case .unsupported:
            print("不支持藍牙服務")
        case .unauthorized:
            print("藍牙服務未被授權")
        case .poweredOff:
            print("藍牙服務已關閉")
        case .poweredOn:
            print("藍牙已啟動,開始掃描...")
            startScan()
        }
    }

 /// 掃描藍牙設備
  func startScan() {
        manager.scanForPeripherals(withServices: nil, options: nil)
  }

注:只有返回.poweredOn狀態(tài)下才能進行藍牙設備的掃描。

scanForPeripherals方法就是對可發(fā)現(xiàn)的藍牙設備進行掃描,輸入的參數(shù)可以作一些過濾條件,我這里是不加任何的過濾條件無差別化地掃描。

3.) 連接外設

scanForPeripherals方法被調(diào)用后,CoreBluetooth就會調(diào)用centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber),所以我們就得在這個方法內(nèi)將我們的目標藍牙設備找出來,然后用一個變量Hold住它,否則這個外設就會被釋放掉,然后你就會在XCode中得到一條提示信息說你沒有Hold住你需要的設備變量了。

// 定義一個變量來Hold住目標設備
var connectedPeripheral : CBPeripheral!

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
            if (peripheral.name=="BT05") {
                manager.connect(peripheral, options:nil)
                // 找到的設備必須持有它,否則CBCentralManager中也不會保存peripheral,那么CBPeripheralDelegate中的方法也不會被調(diào)用!
                connectedPeripheral = peripheral
                print("正在連接:\(peripheral.name)...")
            }
}

由于我不知道我的設備的ServiceUUID是什么,為了方便我編碼的需要所以我只找一個名為"BT05"的設備名稱(這是我藍牙模塊的默認名字)找到之后就馬上調(diào)用connect進行連接,注意:當中心對象不停止掃描(manager.stopScan())以上的方法就會不斷地被調(diào)用

一個主設備最多能連7個外設,每個外設最多只能給一個主設備連接,連接成功,失敗,斷開會進入各自的委托

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?)
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)

下文就會補充這幾個委托方法的實現(xiàn)

4.1) 獲取外設的服務

當連接成功后 centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) 就會被調(diào)用,在這里我們會設置找到的外設對象的委托(self),通常這個方法內(nèi)會做另一種篩選那就是服務特征,通俗點說就是這個藍牙設備可以提供些啥服務,例如寫入,讀取,或者其它什么的一些動作。

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral){        
        print("正在查找Service")
        // 外設尋找目標服務
        peripheral.discoverServices(nil)
        peripheral.delegate = self
        self.title = peripheral.name
        // 停止掃描
        manager.stopScan()
    }

4.2) 獲取外設特征

discoverServices(nil)傳入了nil 那么就啥服務信息都直接獲取,然后對服務進行發(fā)現(xiàn),這個過程和前文中的設備發(fā)現(xiàn)非常的像

    // 這個服務地址可以從設備中查到的
    let ServiceUUID =  "FFE0"
    //掃描到Services
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?){
        if (error != nil){
            print("查找 services 時 \(peripheral.name) 報錯 \(error?.localizedDescription)")
        }
        
        for service in peripheral.services! {
            // 需要連接的 CBCharacteristic 的 UUID
            if service.uuid.uuidString == ServiceUUID {
                peripheral.discoverCharacteristics(nil, for: service)
            }
        }
    }

5) 與外設做數(shù)據(jù)交互

這樣我們實現(xiàn)的CBPeripheralDelegate委托接口中的peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)將會被調(diào)用,用于讀取藍牙傳過來的數(shù)據(jù)。

   // 同理我們要Hold住特征變量,否則會被釋放掉
    var savedCharacteristic : CBCharacteristic!
    var lastString : NSString!

   // Interface Build 中的標簽對象,用來顯示從傳感器讀取的室溫
   @IBOutlet weak var ?lbTemperature: UILabel!
    //獲取的charateristic的值,處理收接到的數(shù)據(jù)
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        // 好了,charcteristic.value 就是從藍牙設備傳過來的原數(shù)據(jù)內(nèi)容,我們可以在這里進行處理
        let resultStr = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
        print(resultStr)
        
        // 將溫度顯示到標簽中
        lbTemperature.text = resultStr
        
        if lastString == resultStr {
            return;
        }

        self.savedCharacteristic = characteristic
    }

6) 訂閱 Characteristic 的通知

由于BLE4.0的"減弱式呼吸"工作特性我們需要對外設置發(fā)出的通知進行訂閱,如數(shù)據(jù)發(fā)生改變中心可以第一時間獲知。

    /// 掃描到 characteristic 時
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error? {
        if error != nil {
            print("查找 characteristics 時 \(peripheral.name) 報錯 \(error?.localizedDescription)")
        }
        
        //獲取Characteristic的值,讀到數(shù)據(jù)會進入方法:
        for characteristic in service.characteristics! {
            peripheral.readValue(for: characteristic)
            //設置 characteristic 的 notifying 屬性 為 true , 表示接受廣播
            peripheral.setNotifyValue(true, for: characteristic)
        }
    }

7) 斷開連接

其實到此這個App就完成了,以下的這些代碼是對相關的錯誤處理進行補全:

    /// 連接到Peripherals-失敗
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?){
        print("連接到名字為 \(peripheral.name) 的設備失敗,原因是 \(error?.localizedDescription)")
        
    }
    
    /// 斷開
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?){
        print("連接到名字為 \(peripheral.name) 的設備斷開,原因是 \(error?.localizedDescription)"
        let alertView = UIAlertController.init(title: "抱歉", message: "藍牙設備\(peripheral.name)連接斷開,請重新掃描設備連接", preferredStyle: UIAlertControllerStyle.alert)
        alertView.show(self, sender: nil)
        
    }

   func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?){
        if error != nil{
            print("寫入 characteristics 時 \(peripheral.name) 報錯 \(error?.localizedDescription)")
        }
        // 這里可以處理向設備寫入數(shù)據(jù)時的回調(diào)
    }

在下一篇中將會介紹藍牙設備與Arduino 一端關于硬件部分與固件部分的制作。

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

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

  • 藍牙簡介 藍牙( Bluetooth? ):是一種無線技術標準,可實現(xiàn)固定設備、移動設備和樓宇個人域網(wǎng)之間的短距離...
    Chefil閱讀 2,155評論 2 19
  • 首先進一則廣告: 藍牙技術聯(lián)盟(Bluetooth SIG)2010年7月7日宣布,正式采納藍牙4.0核心規(guī)范(B...
    L澤閱讀 1,525評論 3 4
  • 由于最近工作的東家是一家物聯(lián)網(wǎng)公司,網(wǎng)上BLE相關的資料確實比較少,尤其我還做了一些調(diào)試和加密相關的工作.由于調(diào)試...
    陳長見閱讀 2,170評論 5 11
  • 由于最近咨詢藍牙問題的較多,所以在此總結一下!馬尾哥的第一個完整項目就是藍牙與外設交互的項目!考慮到功耗的因素現(xiàn)在...
    zhangyajie閱讀 1,049評論 0 4
  • 彭京豪 初次相遇皮影戲,是我幼少時候在一個夕陽共霞光燦爛的傍晚。 古巷,青藤,鳥鳴。 ...
    星月無痕520閱讀 958評論 0 5

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