背景知識
藍牙技術最初由愛立信(也就是多年前手機做得最丑最奇葩的公司,最終被用戶以腳投票踢出市場)創(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)站上也能找到:

藍牙中心模式流程
- 建立中心角色
- 掃描外設(discover)
- 連接外設(connect)
- 掃描外設中的服務和特征(discover)
4.1 獲取外設的services
4.2 獲取外設的Characteristics,獲取Characteristics的值,獲取Characteristics的Descriptor和Descriptor的值 - 與外設做數(shù)據(jù)交互(explore and interact)
- 訂閱Characteristic的通知
- 斷開連接(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 一端關于硬件部分與固件部分的制作。