BLE協議棧爲何要分層?怎麼理解BLE「鏈接」?若是BLE協議只有ATT層沒有GATT層會發生什麼?html
協議棧框架
通常而言,咱們把某個協議的實現代碼稱爲協議棧(protocol stack),BLE協議棧就是實現低功耗藍牙協議的代碼,理解和掌握BLE協議是實現BLE協議棧的前提。在深刻BLE協議棧各個組成部分以前,咱們先看一下BLE協議棧總體架構。安全
![](http://static.javashuo.com/static/loading.gif)
如上圖所述,要實現一個BLE應用,首先須要一個支持BLE射頻的芯片,而後還須要提供一個與此芯片配套的BLE協議棧,最後在協議棧上開發本身的應用。能夠看出BLE協議棧是鏈接芯片和應用的橋樑,是實現整個BLE應用的關鍵。那BLE協議棧具體包含哪些功能呢?簡單來講,BLE協議棧主要用來對你的應用數據進行層層封包,以生成一個知足BLE協議的空中數據包,也就是說,把應用數據包裹在一系列的幀頭(header)和幀尾(tail)中。具體來講,BLE協議棧主要由以下幾部分組成:架構
- PHY層(Physical layer物理層)。PHY層用來指定BLE所用的無線頻段,調製解調方式和方法等。PHY層作得好很差,直接決定整個BLE芯片的功耗,靈敏度以及selectivity等射頻指標。
- LL層(Link Layer鏈路層)。LL層是整個BLE協議棧的核心,也是BLE協議棧的難點和重點。像Nordic的BLE協議棧能同時支持20個link(鏈接),就是LL層的功勞。LL層要作的事情很是多,好比具體選擇哪一個射頻通道進行通訊,怎麼識別空中數據包,具體在哪一個時間點把數據包發送出去,怎麼保證數據的完整性,ACK如何接收,如何進行重傳,以及如何對鏈路進行管理和控制等等。LL層只負責把數據發出去或者收回來,對數據進行怎樣的解析則交給上面的GAP或者GATT。
- HCI(Host controller interface)。HCI是可選的(具體請參考文章: 三種藍牙架構實現方案(藍牙協議棧方案)),HCI主要用於2顆芯片實現BLE協議棧的場合,用來規範二者之間的通訊協議和通訊命令等。
- GAP層(Generic access profile)。GAP是對LL層payload(有效數據包)如何進行解析的兩種方式中的一種,並且是最簡單的那一種。GAP簡單的對LL payload進行一些規範和定義,所以GAP能實現的功能極其有限。GAP目前主要用來進行廣播,掃描和發起鏈接等。
- L2CAP層(Logic link control and adaptation protocol)。L2CAP對LL進行了一次簡單封裝,LL只關心傳輸的數據自己,L2CAP就要區分是加密通道仍是普統統道,同時還要對鏈接間隔進行管理。
- SMP(Secure manager protocol)。SMP用來管理BLE鏈接的加密和安全的,如何保證鏈接的安全性,同時不影響用戶的體驗,這些都是SMP要考慮的工做。
- ATT(Attribute protocol)。簡單來講,ATT層用來定義用戶命令及命令操做的數據,好比讀取某個數據或者寫某個數據。BLE協議棧中,開發者接觸最多的就是ATT。BLE引入了attribute概念,用來描述一條一條的數據。Attribute除了定義數據,同時定義該數據可使用的ATT命令,所以這一層被稱爲ATT層。
- GATT(Generic attribute profile )。GATT用來規範attribute中的數據內容,並運用group(分組)的概念對attribute進行分類管理。沒有GATT,BLE協議棧也能跑,但互聯互通就會出問題,也正是由於有了GATT和各類各樣的應用profile,BLE擺脫了ZigBee等無線協議的兼容性困境,成了出貨量最大的2.4G無線通訊產品。
我相信不少人看了上面的介紹,仍是不懂BLE協議棧的工做原理,以及每一層具體幹什麼的,爲何要這麼分層。下面我以如何發送一個數據包爲例來說解BLE協議棧各層是如何緊密配合,以完成發送任務的。框架
如何經過無線發送一個數據包
假設有設備A和設備B,設備A要把本身目前的電量狀態83%(十六進制表示爲0x53)發給設備B,該怎麼作呢?做爲一個開發者,他但願越簡單越好,對他而言,他但願調用一個簡單的API就能完成這件事,好比send(0x53),實際上咱們的BLE協議棧就是這樣設計的,開發者只需調用send(0x53)就能夠把數據發送出去了,其他的事情BLE協議棧幫你搞定。不少人會想,BLE協議棧是否是直接在物理層就把0x53發出去,就以下圖所示:大數據
![](http://static.javashuo.com/static/loading.gif)
這種方式初看起來挺美的,但因爲不少細節沒有考慮到,實際是不可行的。首先,它沒有考慮用哪個射頻信道來進行傳輸,在不更改API的狀況下,咱們只能對協議棧進行分層,爲此引入LL層,開發者仍是調用send(0x53),send(0x53)再調用send_LL(0x53,2402M)(注:2402M爲信道頻率)。這裏還有一個問題,設備B怎麼知道這個數據包是發給本身的仍是其餘人的,爲此BLE引入access address概念,用來指明接收者身份,其中,0x8E89BED6這個access address比較特殊,它表示要發給周邊全部設備,即廣播。若是你要一對一的進行通訊(BLE協議將其稱爲鏈接),即設備A的數據包只能設備B接收,一樣設備B的數據包只能設備A接收,那麼就必須生成一個獨特的隨機access address以標識設備A和設備B二者之間的鏈接。加密
廣播方式
咱們先來看一下簡單的廣播狀況,這種狀況下,咱們把設備A叫advertiser(廣播者),設備B叫scanner或者observer(掃描者)。廣播狀態下設備A的LL層API將變成send_LL(0x53,2402M, 0x8E89BED6)。因爲設備B能夠同時接收到不少設備的廣播,所以數據包還必須包含設備A的device address(0xE1022AAB753B)以確認該廣播包來自設備A,爲此send_LL參數須要變成(0x53,2402M, 0x8E89BED6, 0xE1022AAB753B)。LL層還要檢查數據的完整性,即數據在傳輸過程當中有沒有發生竄改,爲此引入CRC24對數據包進行檢驗 (假設爲0xB2C78E) 。同時爲了調製解調電路工做更高效,每個數據包的最前面會加上1個字節的preamble(前導幀),preamble通常爲0x55或者0xAA。這樣,整個空中包就變成(注:空中包用小端模式表示!):設計
![](http://static.javashuo.com/static/loading.gif)
上面這個數據包還有以下問題:server
- 沒有對數據包進行分類組織,設備B沒法找到本身想要的數據0x53。爲此咱們須要在access address以後加入兩個字段:LL header和長度字節。LL header用來表示數據包的LL類型,長度字節用來指明payload的長度
- 設備B何時開啓射頻窗口以接收空中數據包?如上圖case1所示,當設備A的數據包在空中傳輸的時候,設備B把接收窗口關閉,此時通訊將失敗;一樣對case2來講,當設備A沒有在空中發送數據包時,設備B把接收窗口打開,此時通訊也將失敗。只有case3的狀況,通訊才能成功,即設備A的數據包在空中傳輸時,設備B正好打開射頻接收窗口,此時通訊才能成功,換句話說,LL層還必須定義通訊時序。
- 當設備B拿到數據0x53後,該如何解析這個數據呢?它到底表示溼度仍是電量,仍是別的意思?這個就是GAP層要作的工做,GAP層引入了LTV(Length-Type-Value)結構來定義數據,好比020105,02-長度,01-類型(強制字段,表示廣播flag,廣播包必須包含該字段),05-值。因爲廣播包最大隻能爲31個字節,它能定義的數據類型極其有限,像這裏說的電量,GAP就沒有定義,所以要經過廣播方式把電量數據發出去,只能使用供應商自定義數據類型0xFF,即04FF590053,其中04表示長度,FF表示數據類型(自定義數據),0x0059是供應商ID(自定義數據中的強制字段),0x53就是咱們的數據(設備雙方約定0x53就是表示電量,而不是其餘意思)。
最終空中傳輸的數據包將變成:htm
- AAD6BE898E600E3B75AB2A02E102010504FF5900538EC7B2
- AA – 前導幀(preamble)
- D6BE898E – 訪問地址(access address)
- 60 – LL幀頭字段(LL header)
- 0E – 有效數據包長度(payload length)
- 3B75AB2A02E1 – 廣播者設備地址(advertiser address)
- 02010504FF590053 – 廣播數據
- 8EC7B2 – CRC24值
![](http://static.javashuo.com/static/loading.gif)
有了PHY,LL和GAP,就能夠發送廣播包了,但廣播包攜帶的信息極其有限,並且還有以下幾大限制:blog
- 沒法進行一對一雙向通訊 (廣播是一對多通訊,並且是單方向的通訊)
- 因爲不支持組包和拆包,所以沒法傳輸大數據
- 通訊不可靠及效率低下。廣播信道不能太多,不然將致使掃描端效率低下。爲此,BLE只使用37(2402MHz) /38(2426MHz) /39(2480MHz)三個信道進行廣播和掃描,所以廣播不支持跳頻。因爲廣播是一對多的,因此廣播也沒法支持ACK。這些都使廣播通訊變得不可靠。
- 掃描端功耗高。因爲掃描端不知道設備端什麼時候廣播,也不知道設備端選用哪一個頻道進行廣播,掃描端只能拉長掃描窗口時間,並同時對37/38/39三個通道進行掃描,這樣功耗就會比較高。
而鏈接則能夠很好解決上述問題,下面咱們就來看看鏈接是如何將0x53發送出去的。
鏈接方式
到底什麼叫鏈接(connection)?像有線UART,很容易理解,就是用線(Rx和Tx等)把設備A和設備B相連,即爲鏈接。用「線」把兩個設備相連,實際是讓2個設備有共同的通訊媒介,並讓二者時鐘同步起來。藍牙鏈接有未嘗不是這個道理,所謂設備A和設備B創建藍牙鏈接,就是指設備A和設備B二者一對一「同步」成功,其具體包含如下幾方面:
- 設備A和設備B對接下來要使用的物理信道達成一致
- 設備A和設備B雙方創建一個共同的時間錨點,也就是說,把雙方的時間原點變成同一個點
- 設備A和設備B二者時鐘同步成功,即雙方都知道對方何時發送數據包何時接收數據包
- 鏈接成功後,設備A和設備B通訊流程以下所示:
![](http://static.javashuo.com/static/loading.gif)
如上圖所示,一旦設備A和設備B鏈接成功(此種狀況下,咱們把設備A稱爲Master或者Central,把設備B稱爲Slave或者Peripheral),設備A將週期性以CI(connection interval)爲間隔向設備B發送數據包,而設備B也週期性地以CI爲間隔打開射頻接收窗口以接收設備A的數據包。同時按照藍牙spec要求,設備B收到設備A數據包150us後,設備B切換到發送狀態,把本身的數據發給設備A;設備A則切換到接收狀態,接收設備B發過來的數據。因而可知,鏈接狀態下,設備A和設備B的射頻發送和接收窗口都是週期性地有計劃地開和關,並且開的時間很是短,從而大大下降系統功耗並大大提升系統效率。
如今咱們看看鏈接狀態下是如何把數據0x53發送出去的,從中你們能夠體會到藍牙協議棧分層的妙處。
- 對開發者來講,很簡單,他只須要調用send(0x53)
- GATT層定義數據的類型和分組,方便起見,咱們用0x0013表示電量這種數據類型,這樣GATT層把數據打包成130053(小端模式!)
- ATT層用來選擇具體的通訊命令,好比讀/寫/notify/indicate等,這裏選擇notify命令0x1B,這樣數據包變成了:1B130053
- L2CAP用來指定connection interval(鏈接間隔),好比每10ms同步一次(CI不體如今數據包中),同時指定邏輯通道編號0004(表示ATT命令),最後把ATT數據長度0x0004加在包頭,這樣數據就變爲:040004001B130053
- LL層要作的工做不少,首先LL層須要指定用哪一個物理信道進行傳輸(物理信道不體如今數據包中),而後再給此鏈接分配一個Access address(0x50655DAB)以標識此鏈接只爲設備A和設備B直連服務,而後加上LL header和payload length字段,LL header標識此packet爲數據packet,而不是control packet等,payload length爲整個L2CAP字段的長度,最後加上CRC24字段,以保證整個packet的數據完整性,因此數據包最後變成:
- AAAB5D65501E08040004001B130053D550F6
- AA – 前導幀(preamble)
- 0x50655DAB – 訪問地址(access address)
- 1E – LL幀頭字段(LL header)
- 08 – 有效數據包長度(payload length)
- 04000400 – ATT數據長度,以及L2CAP通道編號
- 1B – notify command
- 0x0013 – 電量數據handle
- 0x53 – 真正要發送的電量數據
- 0xF650D5 – CRC24值
- 雖然開發者只調用了 send(0x53),但因爲低功耗藍牙協議棧層層打包,最後空中實際傳輸的數據將變成下圖所示的模樣,這就既知足了低功耗藍牙通訊的需求,又讓用戶API變得簡單,可謂一舉兩得!
![](http://static.javashuo.com/static/loading.gif)
上面只是對BLE協議棧實現原理作了一個簡單概述,即使如此,因爲都是關於BLE協議棧底層的東西,不少開發者仍是會以爲比較枯燥和晦澀,並且對不少開發者來講,他們也不關心BLE協議棧是如何實現的,他們更關心的是BLE協議棧的使用,即怎麼開發一個BLE應用。BLE應用是實打實的東西,不能像上面講述協議棧同樣泛泛而談,必須結合具體的藍牙芯片和藍牙協議棧來說解,爲此後面將以Nordic芯片及協議棧做爲範例,來具體講解如何開發BLE應用,以及如何經過代碼去理解BLE協議中定義的一些概念和術語。