1. 程式人生 > >藍芽BLE GATT完全分析和運用

藍芽BLE GATT完全分析和運用

轉載自:

https://blog.csdn.net/yueqian_scut/article/details/50752314


很多人都做過藍芽開發,很多人也能夠通過仿照GATT例程的方式新增一個屬性服務,但是很多人都未必能夠清晰地理解BLE的屬性profile,也很容易被屬性Attribute和特性characteristic所混淆。

本文結合BLE的服務發現協議標準和DA14580平臺、CC2541平臺的應用實踐來深入分析GATT,讓大家能夠自如地構建一個BLE的屬性資料庫。

 

一、 BLE GATT(Generic Attribute Profile)規範

1.   GATT定義

GATT是低功耗藍芽屬性應用規範,應用於主機和從裝置之間的資料傳輸。其與GAP並列為BLE兩大profile。

Attribute是屬性的意思。

何為屬性?

在各藍芽單晶片平臺的SDK實際使用中,屬性是指一條帶有標籤的、可以被定址的資料。

在藍芽實際的規範中,定址即用handle控制代碼來表示。

每個屬性都對應一個唯一的handle。

2.   對屬性協議需求的思考

藍芽是無線通訊,BLE利用屬性協議進行傳輸,其如此重要,如果我們不理解其需求,那麼我們也很難從真正去理解其規範。

儘管在實際的藍芽單晶片SDK中很容易通過模仿的方法進行應用,但是如果想深入地理解其為什麼要設計呢?

1)連線的引數是一個裝置的固有引數,一般會作為一個服務來提供,如GAP服務;而假設這個裝置是一個溫度採集器,那麼這個溫度採集明顯跟裝置的引數不屬於一類,因此可以再作為一個服務。

所以屬性協議應該支援多個服務。如何來區分這些不同的服務?
這即對應藍芽標準規範規定的UUID。我們可以認為不同的UUID對應不同的確定的服務。

2)連線的引數可以有多個,如Connection Interval、Slave Latency等等,我們如何區分這是一個服務,又如何區分服務包含了這些特性引數。我們可以認為一個服務包含了多個特性(引數)。在藍芽標準裡面,同樣是用不同的UUID來區分服務型別、特性型別等等。

3)對於每個特性characteristic,要讓對方獲取這個特性,就必須要分別告訴對方這個特性的長度是多少,值是多少,而不能只給數值。除此之外,特性還可能有描述值(說明特性名稱或者作用等)、特性單位等(國際單位,如米是公里/每小時還是米/秒)。後面這兩個是非必選的。

4)屬性還應該有一個訪問控制,如可讀可寫還是讀寫、或者是通知notify/indicate等等,這是資料通訊必須具有的許可權控制,不管是服務還是特性,它都具有訪問控制屬性。

3.   屬性和屬性型別

屬性由屬性控制代碼、屬性型別、屬性值組成。如下圖:

1)  屬性控制代碼在實際的運用中可以認為是屬性在屬性陣列中的下標。

我們都知道在實際的程式設計中,下標並不需要專門儲存,而只是通過元素的結構體來進行索引即可。

因此可以認為屬性控制代碼是一個無形的東西,它只能被所在的裝置程式所認識,而不能用於無線傳輸。

2)  屬性型別是真實存在的,其和屬性值都會被實際儲存。

屬性型別是由藍芽標準組織所規範,其一般通過128位的UUID來表徵一個具體的屬性。由於BLE的GATT可以認為是藍芽標準規範的精簡版,所以BLE被允許只傳輸前面2位元組(16位)的UUID,所有的BLE的UUID的基數都是一樣的,如下,只有前面兩位元組不同。

利用2位元組(16位)也可以定義65536種屬性了。事實上,藍芽標準組織對這些UUID進行了分類。如下:

屬性型別即是0x2800~0x28ff,在實際的應用中,屬性型別主要包括:我們主要使用服務和特性定義兩種,其他兩個很少用到。

3)  藍芽標準不僅通過UUID來進行屬性分類,而且還用UUID來確定各種具體的服務和特性。所以我們會看到UUID可能會出現在屬性的屬性型別和屬性值兩個地方。

4)  藍芽標準組織規定兩個ATT_DECL_PRIMARY_SERVICE服務之間的特性都隸屬於第一個服務。這樣可以理解在藍芽服務發現協議中先通過UUID找到目標服務,然後通過ATT_DECL_PRIMARY_SERVICE這個屬性型別找到下一個服務,接著即可以在這兩個服務中進行特性的遍歷,遍歷的結果即是目標服務的所有特性。

4.   屬性值

屬性值的長度可以最長到512位元組,但對於某些屬性,其長度是固定的。對於藍芽標準裡面規定的UUID所對應的屬性(包括服務、特性定義、特性值、特性描述等等),服務、特性定義的長度是確定的,而特性值則是不固定長度的。

所以,對於不同的屬性,其屬性值是不一樣的。也即對於以上五類(通用服務、單位、屬性型別、特性描述和區分特性型別)等屬性,其屬性值的規範是不一樣,具體到不同的特性型別,其屬性值也是不同的。

1)通用服務類通過唯一的UUID(0x1800~0x26ff)來標識一種明確的服務。好比,0x180f代表電池電量服務。

2)計量單位類通過唯一的UUID來標識一種單位。

3)區分屬性型別類通過唯一的UUID來標識該屬性是首要服務定義、次要服務、包含服務還是特性定義等。其好比程式中的變數的型別,是整型、位元組型、還是確定的結構體。

4)特性描述類除了描述特性的名稱、作用之外,還有一個非常重要的配置作用。例如如果提供的特性服務需要主動告知對方,那麼對方就必須在連線時進行訂閱配置。這樣在該特性的資料值發生變更時能夠主動地進行notify或者indicate。

5)區分特性型別用於使用者定義不同的特性,用於區分該裝置裡面所有的特性。

5.   特性

把特性理解為一個程式中的一個變數是最好理解的。變數有變數型別和值,變數型別有int整型、位元組型等等(其實就是變數的儲存長度),值即具體的數值。相應地,而特性則有值和儲存值的長度的概念。如同變數的宣告和定義,特性characteristic也有宣告和定義(賦值)的概念。

一般地,在藍芽標準裡面,特性一般包括三個要素:宣告、數值和描述。前兩者都是必須的。作為通訊互動,一個特性必須要告訴對方宣告(儲存長度和訪問控制)、定義(具體賦值)。在某些特性(如notify或者indicate)裡面,特性還需要告知對方附加的配置屬性(提供訂閱等)。

特性宣告必須作為服務屬性之後的第一條屬性,而數值必須緊隨其後。

1)  特性宣告

性質為一個8位欄位,指示訪問控制權限,包括讀、寫、notify或者indicate等。對於特性宣告而言,其一般是隻讀的(這裡只針對宣告這條屬性本身,而不是針對對應的特性數值)。數值控制代碼即用於直接定址接下來的特性數值。其對於通訊的對方是很有好處的,因為對方只需要記錄該控制代碼即可在後續的訪問中直接定址,否則每次通訊都要遍歷。在實際的程式設計應用中,我們往往在初始化時填入0,代表由底層邏輯來自動更新該handle。而屬性UUID和接下來的特性數值屬性的區分特性型別值是一致的。

2)特性數值

特性數值也是一個屬性,其屬性型別填入特性宣告的屬性UUID。屬性值要填入特性數值的訪問許可權、長度和數值。

3)特性描述

      其可以是字串表示的特性名稱,或者是notify/indicate要求的配置等等。

二、對屬性協議重要的理解原則

1)無論是服務還是特性,它們都是一條條屬性;特性的各個要素也是一條條屬性。只不過,不同的服務是獨立的;而一個服務如果有多個特性,那麼不同的特性也是獨立的;一個特性包含的多條屬性則是關聯的。

2)對於藍芽通訊來說,其都是通過一個個不同的UUID來標識區分不同的服務,區分不同的特性,甚至服務/特性之間的類別。

3)對於各個藍芽單晶片SDK平臺,其上層應用對於屬性協議的支援並不一致,它們只需要保證底層的藍芽資料格式一致即可。

三、屬性協議範例說明

例如一個電池服務包括一個特性(電池電量),那麼其至少包括以下屬性:首要服務定義(電池服務)、當前電量的特性定義(定義值長度)、當前電量的特性值。如果希望電池電量在低於某個水平時主動告知對方,那麼這個電量特性值不僅應該是可讀的,還應該是能夠notify的。由於有主動告知,因此該特性還需要包括配置要素,用於對方來訂閱。

1.對於電池服務這個屬性,其屬性型別是ATT_DECL_PRIMARY_SERVICE(0x2800),屬性值是訪問可讀和藍芽標準組織規定的0x180f(位於0x1800到0x26ff之間);

2.電池當前電量的特性定義這個屬性,其屬性型別是ATT_DECL_CHARACTERISTIC(0x2803),屬性值是可讀、特性值控制代碼和特性值的UUID(0x2A19)。

3.對於當前電量的特性值這個屬性,其屬性型別是0x2A19(0x2A00~0x7fff之間,區分特性型別),其用於區分多種不同的特性(如一個溫度採集器可能要採集多個溫度,這裡就要使用者通過不同的UUID來區分不同的特性了),屬性值即訪問控制(可讀/indicate)、長度(1位元組)、數值。

4.配置屬性,用於notify的訂閱配置。其屬性型別是0x2902,屬性值是可讀/可寫(要能寫入訂閱方的handle)、長度(2個位元組)、handle值。

      根據以上分析,我們來重構這個藍芽的資料底層資料庫。

四、DA14580平臺SDK屬性結構定義

1.單個屬性定義

對於不同的屬性,其length長度由value的型別來決定。例如服務的屬性值對應的結構就是uint16_t;而特性宣告的屬性值對應的結構就是:

 

2.電池電量服務定義

五、CC254X平臺SDK屬性結構定義

1.單個屬性定義

對於不同的屬性,其length長度由value的型別來決定。例如服務的屬性值對應的結構就是uint16_t;而特性宣告的屬性值對應的結構就是:

同樣的,屬性值pValue的資料結構由其對應的UUID來決定。

例如服務屬性的pValue對應的結構是gattAttrType_t(包括長度2和具體的服務UUID)。
而特性宣告的pValue對應的結構是一個位元組的訪問許可權控制。特性數值的pValue對應的結構則是直接的變數定義,其可能是一個位元組或者是位元組陣列,其長度由底層通過判斷陣列來決定。

2.電池電量服務定義

五、CC254X平臺SDK屬性結構定義

1.單個屬性定義

同樣的,屬性值pValue的資料結構由其對應的UUID來決定。例如服務屬性的pValue對應的結構是gattAttrType_t(包括長度2和具體的服務UUID)。而特性宣告的pValue對應的結構是一個位元組的訪問許可權控制。特性數值的pValue對應的結構則是直接的變數定義,其可能是一個位元組或者是位元組陣列,其長度由底層通過判斷陣列來決定。

2.電池電量服務定義