BLE通信由兩種設備類型構成—— Client和Server。
Server提供數據服務,所以一般來說設備是Server,手機是Client。Server和Client通過ATT PDU進行交互,Server通過characteristic對數據進行封裝。多個characteristic組成一個Service,一個Service是一個獨立的服務單元,或者說service是一個基本的BLE應用。如果某個service是一個藍牙聯盟定義的標準服務,也可以稱其為profile,比如HID/心率計/體溫計/血糖儀等,都是標準藍牙服務,因此都有相應的profile規格書。
一. characteristic
一個characteristic包含三種條目:characteristic聲明,characteristic的值以及characteristic的描述符(可以有多個描述符):
1.1 Characteristic declaration
就是每個characteristic的分界符。解析時一旦遇到characteristicdeclaration,就可以認為接下來又是一個新的characteristic了,同時characteristic declaration還將包含value的讀寫屬性等。
1.2 Characteristic value
就是數據的值了,這個比較好理解就不再說了。
1.3 Characteristic descriptor
就是數據的額外信息。比如溫度的單位是什么,數據是用小數表示還是百分比表示等之類的數據描述信息。CCCD是一種特殊的characteristicdescriptor,當characteristic具有notify或者indicate操作功能時,那么必須為其添加相應CCCD,以方便client來使能或者禁止notify或者indicate功能。
不管是characteristic declaration,characteristic value還是characteristic descriptor,實現的時候,我們都是用attribute來表達的,也就是說,他們每一個都是一個attribute,attribute可以用下圖來表示:
二. Attribute
2.1 Attribute handle
Attribute句柄。Client要訪問Server的Attribute,都是通過這個句柄來訪問的,也就是說ATT PDU一般都包含handle的值。用戶在軟件代碼添加characteristic的時候,系統會自動按順序地為相關attribute生成句柄。
2.2 Attribute type
Attribute類型。在BLE中我們使用UUID來定義數據的類型,UUID是128 bit的,所以我們有足夠的UUID來表達萬事萬物。其中有一個UUID非常特殊,它被藍牙聯盟采用為官方UUID,這個UUID如下所示:0000xxxx-0000-1000-8000-00805F9B34FB, 由于這個UUID眾所周知,藍牙聯盟將自己定義的attribute或者數據只用16bit UUID來表示,比如0x1234,其實它也是128bit,完整表示為:
00001234-0000-1000-8000-00805F9B34FB = 16 bit UUID 0x1234
Attribute type一般是由service和characteristic規格來定義,站在藍牙協議棧角度來看,ATT層定義了一個通信的基本框架,數據的基本結構,以及通信的指令,而GATT層就是前文所述的service和characteristic,GATT層用來賦予每個數據一個具體的內涵,讓數據變得有結構和意義。換句話說,沒有GATT層,低功耗藍牙也可以通信起來,但會產生兼容性問題以及通信的低效率。
2.3 Attribute value
就是數據真正的值,0到512字節長。
2.4 Attribute permissions
Attribute的權限屬性,權限屬性不會直接在空中包中體現,而是隱含在ATT命令的操作結果中。目前主要有如下四種權限屬性:
●Open,直接可以讀或者寫
●No Access,禁止讀或者寫
●Authentication,需要配對才能讀或者寫,由于配對有多種類型,因此authentication又衍生多種子類型,比如帶不帶MITM,有沒有LESC
●Authorization,跟open一樣,不過server返回attribute的值之前需要應用先授權,也就是說應用可以在回調函數里面去修改讀或者寫的原始值。
●Signed,簽名后才能讀或者寫,這個用得比較少。
Client和Server之間是通過ATT PDU來通信的,ATT PDU主要包括4類:讀,寫,notify和indicate。如果一個命令需要response,那么會在相應命令后面加上request;如果一個命令只需要ACK而不需要response,那么它的后面就不會帶request。這里要特別強調一點,BLE所有命令都是“必達”的,也就是說每個命令發出去之后,會立馬等ACK信息,如果收到了ACK包,發起方認為命令完成;否則發起方會一直重傳該命令直到超時導致BLE連接斷開。換句話說,只要你的BLE沒有斷開,那么你之前發送的數據包,不管它是用什么ATT PDU來發送的,它肯定被對方收到了。我估計很多人對此會產生疑問,因為他們經常碰到丟包的情況,其實大家經常碰到的“丟包”,不是空中把包丟了或者包在空中被干擾了,而是大家發送的代碼寫得有問題,導致你要發送的包沒有被安全送達到協議棧射頻FIFO中,所以以后大家碰到丟包情況,請先檢查你的代碼,保證你的數據包正確完整安全地送達到協議棧射頻FIFO中,只要數據包放到了協議棧射頻FIFO中,藍牙協議棧就能保證該數據包“必達”對方。既然每個ATT命令都必達對方,那么還需要request做什么?如果一個命令帶有request后綴,那么發起方就可以收到命令的response包,這個response包在應用層是有回調事件的,而前述的ACK包在應用層是沒有回調事件的。所以采用request/response方式,應用層可以按順序地發送一些數據包,這個在很多應用場合是非常有用的。相反,如果你對應用層數據包的順序沒有要求,那么就可以不使用request/response形式。另外Request/response有一個副作用:大大降低通信的吞吐率,因為request/response必須在不同的連接間隔中出現,也就是說,你在間隔1中發送了一個request命令,那么response包必須在間隔2或者稍后間隔中回復,而不能在間隔1中回復,這就導致兩個連接間隔最多只能發一個數據包,而不帶request后綴的ATT命令就沒有這個問題,在同一個連接間隔中,你可以同時發多個數據包,這樣將大大提高數據的吞吐率。
常用的帶request的命令:所有read命令,writerequest,indication等,而常用的不帶request的命令有write command,notification等。