目錄
- 一: 概述
- 二: 命令的分類
- 三: 如何最簡潔的跑起來一個 BLE 應用
- 3.1 一個 iBeacon Demo
- 3.2 如何掃描周邊的藍牙設備
- 3.3 如何建立一個BLE連接
- 3.4 如何進行數據的收發
- 3.4.1 server 創建並開啓服務
- 3.4.2 server 查詢本地服務
- 3.4.3 client 做服務發現
- 3.4.4 client 端的讀和寫
- 3.4.5 register notification
- 3.4.6 server 主動向 client 發送數據
- 四: 如何處理 BLE 加密
- 4.1 加密參數該如何設置
- 4.2 不同的加密參數組合會出現什麼樣的結果
- 4.3 加密示例
- 五: 如何使用 BLE SPP
一: 概述
ESP-AT BLE
命令支持 BLE server
以及 BLE client
, 目前兩種角色只能同一時刻使用一種.
做 BLE server
的時候, 可以同時被 3 個 client
相連, 做 BLE client
的時候, 可以同時連接 3 個 server
.
除了通用的 GATT
以及 GAP
指令, ESP-AT BLE
還支持 BLE HID
, 用來模擬鼠標鍵盤等等.
目前 ESP-AT BLE
的 命令基本能覆蓋客戶的所有需求, 包括自定義 sevices
, 自定義廣播數據, 服務發現, 數據通信, 連接參數更新, MTU size
更新以及所有的加密參數相關的設置.
除此之外, 我們還自定義了一套私有的 BLE
透傳協議, 如果客戶不使用經典藍牙的透傳, 也可以使用 BLE
透傳.
二: 命令的分類
因爲我們 ESP-AT
的 BLE
的命令做的比較細, 對 BLE
協議本身不是很熟悉的人, 初次上手的時候可能會覺得有些困惑, 所以我們首先對這些命令進行一些分類, 這樣理解以及用起來會更順手一點.
2.1 角色劃分
目前 ESP-AT BLE
的命令都是基於 GAP
, GATT
層實現的, 我們在初始化的時候, 首先需要決定是做 client
還是做 server
.
初始化爲 client
:
AT+BLEINIT=1
OK
初始化爲 server
:
AT+BLEINIT=2
OK
如果已經初始化爲
server
, 想切換爲client
, 這個時候需要將協議棧deinit
, 再重新初始化爲想要的角色.
關閉藍牙:
AT+BLEINIT=0
OK
不同的角色, 可以使用的命令也不同, 詳細的劃分請見下面, 看命令後面的註釋可以先粗略瞭解這條命令是做什麼的:
BLE client 特有的命令:
AT+BLESCANPARAM // 設置掃描參數
AT+BLESCAN // 開啓或者關閉掃描
AT+BLECFGMTU // Exchange MTU size
AT+BLECONN // 連接遠端設備
AT+BLEGATTCPRIMSRV // 做服務發現. 獲取主要服務
AT+BLEGATTCINCLSRV // 獲取主要服務下面的 include 服務
AT+BLEGATTCCHAR // 查詢服務下面的 characteristics
AT+BLEGATTCWR // 對 characteristic 進行寫的操作
AT+BLEGATTCRD // 對 characteristic 進行讀的操作
AT+BLECONNPARAM // 更新連接參數
BLE server 特有的命令:
AT+BLEADVPARAM // 設置廣播參數
AT+BLEADVDATA // 設置廣播數據
AT+BLESCANRSPDATA // 設置掃描返回數據 scan rsp data
AT+BLEADVSTART // 開始廣播
AT+BLEADVSTOP // 關閉廣播
AT+BLEGATTSSRVCRE // 創建服務
AT+BLEGATTSSRVSTART // 開啓服務
AT+BLEGATTSSRV // 查詢本地的服務
AT+BLEGATTSCHAR // 查詢本地服務下面的 characteristic
AT+BLEGATTSNTFY // 向 client 發送 notify
AT+BLEGATTSIND // 向 client 發送 indicate
AT+BLEGATTSSETATTR // 設置本地 characteristic 的值
BLE client 與 BLE server 都可以使用的命令:
AT+BLEINIT // BLE 協議棧初始化
AT+BLEADDR // 查詢 BLE MAC 地址
AT+BLENAME // 設置或者查詢 BLE 名字
AT+BLEDISCONN // 主動斷開與遠端設備的連接
AT+BLEDATALEN // 設置 packet data length
AT+BLESPPCFG // 配置 BLE 透傳參數
AT+BLESPP // 進入 BLE 透傳模式
AT+BLESECPARAM // 設置 BLE 加密參數
AT+BLEENC // 發起 BLE 加密請求
AT+BLEENCRSP // 對 BLE 加密請求作出應答
AT+BLEKEYREPLY // 輸入對端設備顯示的密碼
AT+BLEENCDEV // 查詢本地已經 bonding 的設備信息
AT+BLEENCCLEAR // 清除本地 bonding 設備的信息
AT+BLESETKEY // 設置 BLE 靜態密碼
2.2 幾個主要的功能
雖然命令數量繁多, 有 30 多個, 其實主要也就是這幾個功能:
- 初始化
- 廣播與掃描
- 連接與通信
- 參數更新
- 加密
初始化
初始化是第一步, 在這裏首先需要使能 BLE
協議棧, 然後配置一些本地的參數, 比如:
- 掃描參數:
AT+BLESCANPARAM
- 廣播參數:
AT+BLEADVPARAM
- 設備名稱:
AT+BLENAME
- 加密參數:
AT+BLESECPARAM
- 廣播數據:
AT+BLEADVDATA
,AT+BLESCANRSPDATA
如果不去主動設置這些參數, 會用默認的參數, 至於默認參數是多少, 這裏就不花篇幅介紹了, 可以直接用查詢指令去查詢看看, 也可參照 ESP-AT
指令介紹文檔.
廣播與掃描
廣播與掃描比較簡單, 看命令的名字就能認出來.
- 廣播:
AT+BLEADVSTART
,AT+BLEADVSTOP
- 掃描:
AT+BLESCAN
廣播是建立連接的前提條件. client
如果想和 server
建立連接, server
必須是處於廣播的狀態 (可連接的廣播).
client
如果已經提前知道了 server
的 MAC
地址, 可以不掃描, 直接發起連接的命令;
如果不知道 server
的 MAC
地址, 可以先掃描一下.
連接與通信
一般的 BLE
應用, 還是需要基於連接的, 除非是 beacon
類設備.
與連接相關的命令只有這兩條:
- 發起連接:
AT+BLECONN
- 斷開連接:
AT+BLEDISCONN
其中, 發起連接的命令, 只能 client
下發, 主動斷開的命令, 兩邊都可以.
因爲 BLE
的數據通信都是基於屬性協議, 所以 BLE server
端需要在被連接之前把服務開啓, 需要下發兩條命令:
- 創建服務:
AT+BLEGATTSSRVCRE
- 開啓服務:
AT+BLEGATTSSRVSTART
這兩條命令的先後順序不能顛倒, 只需要在 AT+BLEINIT=2
之後, 被 client
連接之前就可以, 中間或者前後都可以穿插別的命令, 一般我們建議是在 AT+BLEADVSTART
之前, 因爲只要廣播開啓了, 就可能被遠端設備連接上, 對端會發現不到服務.
設備連接成功之後, client
端需要做服務發現, 服務發現的命令有這幾個:
- 獲取對端主要服務:
AT+BLEGATTCPRIMSRV
- 獲取
include
服務:AT+BLEGATTCINCLSRV
- 獲取服務下面的
char
:AT+BLEGATTCCHAR
而 server
端查詢本地的服務和 char
用到這兩個命令:
- 查詢服務:
AT+BLEGATTSSRV
- 查詢服務下面的
char
:AT+BLEGATTSCHAR
查詢服務是必須的操作, 因爲 ESP-AT BLE
指令的讀寫操作都是依賴查詢的得到的 \<index>
參數, 具體的介紹與用法, 會在下面的實例章節詳細介紹, 這裏僅需瞭解下大概的流程.
查詢好了服務相關的參數, 就可以數據通信了, client
往 server
發送數據需要通過寫的方式:
- 寫
char
或者desc
:AT+BLEGATTCWR
- 讀
char
或者desc
:AT+BLEGATTCRD
server
向 client
發送數據需要通過 notify
和 indicate
的方式.
- 發送
notify
:AT+BLEGATTSNTFY
- 發送
indicate
:AT+BLEGATTSIND
參數更新
有些時候, 針對一些應用場景, 可能需要更新參數, 最常用的兩個就是 MTU
和連接參數:
- 更新
MTU size
:AT+BLECFGMTU
- 更新連接參數:
AT+BLECONNPARAM
MTU size
只能在 client 端發起, 目前 ESP-AT
這裏最大能支持 517. 如果不更新的話, 默認是 23. 更新一個較大的 MTU 的好處就是單包可以發送更長的數據了, 一包最長是(MTU size - 3
), 也就是說, 默認情況下, 一包最多發送 20 字節的數據.
連接參數的更新可以在連接建議起來之後任意時刻開始, 不同的連接參數會有什麼影響我們在下面章節詳細說明.
加密
目前 ESP-AT
支持的 BLE
所有加密等級, 用戶可以自由設置加密參數, 發起加密或者接受加密, 彈出密碼, 輸入密碼, bonding
, 等等.
- 設置加密參數:
AT+BLESECPARAM
- 發起加密請求:
AT+BLEENC
- 接受或者拒絕加密:
AT+BLEENCRSP
- 設置靜態的密碼:
AT+BLESETKEY
- 輸入KEY:
AT+BLEKEYREPLY
- 確認KEY:
AT+BLECONFREPLY
- 查詢
bonding
的設備:AT+BLEENCDEV
- 清除
bonding
信息:AT+BLEENCCLEAR
3. 如何最簡潔的跑起來一個 BLE 應用
不同的應用場景, 用的指令都有差異, 我們先從最簡單的說起.
3.1 一個 iBeacon Demo
iBeacon
主要用於廣告推送, 室內定位等等, 其實他的原理很簡單, 就是發送一些特定格式的 BLE
廣播包, 關於 ibeacon
, 本文不具體介紹, 用戶可以自行參閱蘋果官方文檔 iBeacon.pdf.
我這這裏舉一個例子, ESP-AT
發送 iBeacon
廣播包, 用微信搖一搖搜索周邊設備:
首先初始化爲 server
AT+BLEINIT=2
OK
然後設置廣播參數
如果不做這一步, 直接配置廣播包並開啓廣播也是可以被微信搖一搖周邊搜索到的, 但是一般我們還是配置下廣播類型爲不可連接廣播比較好, 避免被不可預知的設備連接上.
AT+BLEADVPARAM=64,64,3,0,7 //第三個參數 '3', 表示廣播包的類型是 ADV_TYPE_NONCONN_IND
OK
其他參數的具體描述請參閱指令文檔.
配置廣播包
AT+BLEADVDATA="0201061AFF4C000215FDA50693A4E24FB1AFCFC6EB0764782527B7F206C5"
OK
這裏只是舉個例子, 客戶可以參考 iBeacon
的格式定義, 自己重新定義.
設置好廣播 data
之後, 就可以開啓廣播了.
AT+BLEADVSTART
OK
現在可以用手機微信的搖一搖功能, 發現我們的 ibeacon
包.
除了Apple
的 iBeacon
, 還有谷歌的 eddystone
, 都是同樣的道理, 一些特定格式的廣播包.
Tips:
- 如果不配置廣播包, 也可以直接開啓廣播, 只是廣播包的
payload
是空, 但是不影響被client
連接. - 廣播包最大長度是 31 字節, 如果用戶的數據大於這個長度, 可以配置
scan rsp data
, 不過scan rsp data
的最大長度也是 31 字節.
3.2 如何掃描周邊的藍牙設備
掃描是一個很常見的場景, 比如掃描周圍的 beacon
包, 掃描周圍想要連接的目標設備等等.
ESP-AT 中關於掃描是指令 AT+BLESCAN
.
開啓掃描的前提是初始化角色爲 client
:
AT+BLEINIT=1
OK
開啓持續掃描:
AT+BLESCAN=1
OK
開啓掃描一段時間, 比如3秒:
AT+BLESCAN=1,3
OK
停止掃描:
AT+BLESCAN=0
OK
掃描示例:
AT+BLESCAN=1
OK
+BLESCAN:c4:4f:33:16:f8:c3,-50,,,0
+BLESCAN:4d:71:ae:b0:72:08,-40,02011a0aff4c001005131c5fc0b3,,1
+BLESCAN:c4:4f:33:16:f8:c3,-55,,,0
+BLESCAN:58:81:89:b9:a2:de,-45,02010613ff4c000c0e0806040a6279452dfb65b171ddf9,,1
+BLESCAN:58:81:89:b9:a2:de,-47,02010613ff4c000c0e0806040a6279452dfb65b171ddf9,,1
+BLESCAN:ac:bc:32:c2:0e:da,-46,0201060aff4c0010050b1c7ac126,,0
+BLESCAN:ac:bc:32:c2:0e:da,-55,0201060aff4c0010050b1c7ac126,,0
+BLESCAN:4c:38:cc:bf:3f:64,-68,02010613ff4c000c0e009a8b60fd84a3fa91cb767de4a2,,1
+BLESCAN:c4:4f:33:16:f8:c3,-47,,,0
+BLESCAN:c4:4f:33:16:f8:c3,-56,,,0
+BLESCAN:77:64:1d:9a:46:ea,-44,0201060aff4c00100541187179a3,,1
+BLESCAN:c4:4f:33:16:f8:c3,-48,,,0
+BLESCAN:c4:4f:33:16:f8:c3,-57,,,0
AT+BLESCAN=0
OK
上面的 log 是開啓掃描然後廣播掃描的例子, 掃描結果的開頭都是 +BLESCAN:
, 然後是 MAC
地址, 比如 "4d:71:ae:b0:72:08"
, 下面跟着 RSSI
值, 再緊接着的是 adv data
和 scan rsp data
. 最後一個參數是地址的類型, 0 表示 public
, 1 表示 random
.
Tips:
AT+BLESCAN
不會阻塞, 如果開啓持續掃描的話, 會一刻不停的打印掃描信息, 除非周圍沒有任何BLE
廣播包.- 如果用戶覺得信息太多, 建議開始定時功能, 比如只掃描 3 秒或者 5 秒
- 目前掃描返回的數據, 僅支持符合
BLE
廣播包格式的數據, 如果對端廣播的是raw data
, 比如{0x00, 0x01, 0x02, 0x03}
這種, 掃描得到的數據內容以及長度一般都是錯誤的. - 在
ESP-AT
中, 限定只有client
才能發起掃描. - 有的時候, 如果發現
MAC
地址對的, 都是連接的時候就是連不上, 這個時候要注意地址的類型對不對, 下面的連接示例中對此會有說明.
3.3 如何建立一個 BLE 連接
關於連接的命令有兩個, AT+BLECONN
和 AT+BLEDISCONN
.
發起連接:
+BLECONN:0,"c4:4f:33:16:f8:c3"
OK
發起連接有兩個必須輸入的參數:conn_index
, mac addr
conn_index
是由用戶指定的, 用來區分不同的連接, 當多連接的時候, 只要每次輸入的不一樣就可以, 不能重複, 如果連接斷開, 這個 index
就釋放出來可以再次使用. 這個例子中是 0 , 當然客戶也可以選擇輸入 9527, 這也是可以的, 只要不重複.
發起連接的第三個參數是可選參數, 表示對端設備的地址類型, 默認認爲對端設備是 public
地址類型, 如果對端設備是 random
地址類型的話, 需要輸入第三個可選參數爲 1.
查詢連接:
AT+BLECONN?
+BLECONN:0,c4:4f:33:16:f8:c3
OK
如果有連接存在, 就輸出這樣的 log, 第一個參數就是 conn_index
, 發起連接的時候輸入多少, 這裏查詢出來就是多少. 基於連接的命令都需要輸入這樣的參數.
斷開連接:
AT+BLEDISCONN=0
+BLEDISCONN:0,"c4:4f:33:16:f8:c3"
OK
這是主動斷開連接的命令, 如果是被動斷開, 也會有相應的 log 提示:
+BLEDISCONN:0,"xx:xx:xx:xx:xx:xx"
Tips:
- 連接只能
client
端發起, 斷開連接兩端都可以發起. - 如果對端設備的地址類型是
random
, 需要加上第三個可選參數 1.
3.4 如何進行數據的收發
BLE
的數據通信都是基於屬性協議的, services
在 server
端, server
端初始化之後, 首先先要創建以及開啓服務.
3.4.1 server 創建並開啓服務
AT+BLEGATTSSRVCRE // 創建服務
OK
AT+BLEGATTSSTART // 開啓服務
OK
創建服務需要在開啓服務之前, 只有 server
端能下發這兩個命令, 建議在開啓廣播之前把服務開啓.
3.4.2 server 查詢本地服務
ESP-AT BLE
很多指令都用到 index
這樣的參數, 比如連接裏面的 conn_index
, Services
這裏也有很多 index
, 比如:
- service index
- characteristic index
- description index
在 ESP-AT
中, 所有關於 services
的操作都是基於 index
來操作的, ble_data.bin
一定需要燒錄, 不然無法創建服務, 服務創建好之後就可以查詢本地的服務了, 比如:
AT+BLEINIT=2
OK
AT+BLEGATTSSRVCRE
OK
AT+BLEGATTSSRV?
+BLEGATTSSRV:1,0,0xA002,1
OK
AT+BLEGATTSCHAR?
+BLEGATTSCHAR:"char",1,1,0xC300,0x02
+BLEGATTSCHAR:"desc",1,1,1,0x2901
+BLEGATTSCHAR:"char",1,2,0xC301,0x02
+BLEGATTSCHAR:"desc",1,2,1,0x2901
+BLEGATTSCHAR:"char",1,3,0xC302,0x08
+BLEGATTSCHAR:"desc",1,3,1,0x2901
+BLEGATTSCHAR:"char",1,4,0xC303,0x04
+BLEGATTSCHAR:"desc",1,4,1,0x2901
+BLEGATTSCHAR:"char",1,5,0xC304,0x08
+BLEGATTSCHAR:"char",1,6,0xC305,0x10
+BLEGATTSCHAR:"desc",1,6,1,0x2902
+BLEGATTSCHAR:"char",1,7,0xC306,0x20
+BLEGATTSCHAR:"desc",1,7,1,0x2902
+BLEGATTSCHAR:"char",1,8,0xC307,0x02
+BLEGATTSCHAR:"desc",1,8,1,0x2901
OK
當創建了服務之後, 我們就可以查詢:
+BLEGATTSSRV:<srv_index>,<start>,<srv_uuid>,<srv_type>
對比 log 和參數說明, 我們可以看到, server
上面只有一個 service
, 它的 index
是 1, 第二個參數表示這個服務還沒有被開啓, 第三個參數是 UUID
, 第四個參數表示這個服務是 primary service
.
還可以查詢服務下面的 characteristics
和 description
:
+BLEGATTSCHAR:"char",<srv_index>,<char_index>,<char_uuid>,<char_prop>
+BLEGATTSCHAR:"desc",<srv_index>,<char_index>,<desc_index>
對着這些格式說明看上面的 log, 可以發現, index
爲 1 的這個服務(0xA002
)下面有8個 characteristics
, 每個 characteristic
下面還有相應的 description
.
例如:
+BLEGATTSCHAR:"char",1,6,0xC305,0x10
+BLEGATTSCHAR:"desc",1,6,1,0x2902
表示UUID
(0xC305
) 這個 characteristic
是第一個 service
(0xA002
)下面的第 6 個 characteristic
, 他的 prop
是 0x10
. 這個 characteristic
下面還跟着一個 description,
UUID
是 0x2902
.
其實看到這個UUID
0x2902
, 就應該知道這個是 ccc
(client Characteristic Configuration Descriptor), 這個 characteristic
肯定是可以 notification
的, client
端使能 notify/indicate
就是需要寫這個 ccc
.
建議對屬性協議不熟悉的客戶, 可以先翻閱一下協議的 ATT/GATT
部分, 有助於理解.
3.4.3 client 做服務發現
當連接建立之後, client
就可以做服務發現, 與服務發現相關的命令有 3 條:
- AT+BLEGATTCPRIMSRV
- AT+BLEGATTCINCLSRV
- AT+BLEGATTCCHAR
AT+BLEGATTCPRIMSRV
用來發現對端設備上面的 primary service
, 它需要帶一條參數 conn_index
, 也就是下面連接示例裏提到的 conn_index
.
爲什麼
server
端查詢服務不需要帶參數, 而client
端需要帶參數呢?
因爲server
即使被多個client
相連, 也是共用服務的, 不區分幾個連接, 而client
不一樣, 如果連接兩個server
, 他們上面的服務是相互獨立的, 所以需要帶個連接參數來指定發現哪個server
上面的服務.
AT+BLEINIT=1
OK
AT+BLECONN=0,"c4:4f:33:16:f8:c3"
+BLECONN:0,"c4:4f:33:16:f8:c3"
OK
AT+BLEGATTCPRIMSRV=0
+BLEGATTCPRIMSRV:0,1,0x1801,1
+BLEGATTCPRIMSRV:0,2,0x1800,1
+BLEGATTCPRIMSRV:0,3,0xA002,1
OK
AT+BLEGATTCINCLSRV=0,3
OK
AT+BLEGATTCCHAR=0,3
+BLEGATTCCHAR:"char",0,3,1,0xC300,0x02
+BLEGATTCCHAR:"desc",0,3,1,1,0x2901
+BLEGATTCCHAR:"char",0,3,2,0xC301,0x02
+BLEGATTCCHAR:"desc",0,3,2,1,0x2901
+BLEGATTCCHAR:"char",0,3,3,0xC302,0x08
+BLEGATTCCHAR:"desc",0,3,3,1,0x2901
+BLEGATTCCHAR:"char",0,3,4,0xC303,0x04
+BLEGATTCCHAR:"desc",0,3,4,1,0x2901
+BLEGATTCCHAR:"char",0,3,5,0xC304,0x08
+BLEGATTCCHAR:"char",0,3,6,0xC305,0x10
+BLEGATTCCHAR:"desc",0,3,6,1,0x2902
+BLEGATTCCHAR:"char",0,3,7,0xC306,0x20
+BLEGATTCCHAR:"desc",0,3,7,1,0x2902
+BLEGATTCCHAR:"char",0,3,8,0xC307,0x02
+BLEGATTCCHAR:"desc",0,3,8,1,0x2901
OK
測試 log 是 client
初始化之後發起連接請求, 然後做服務發現, 服務發現的結果是:
- +BLEGATTCPRIMSRV:0,1,0x1801,1
- +BLEGATTCPRIMSRV:0,2,0x1800,1
- +BLEGATTCPRIMSRV:0,3,0xA002,1
對端 server
上面一共有 3 個服務, 請直接忽略 0x1801
和 0x1800
, 這兩個是 GAP
和 GATT
, 最下面那個第三個服務 0xA002
纔是剛纔我們創建的服務.
在例子中, 我們進一步的查詢了第三個服務下面的有哪些 characteristic
和 descripton
, 用戶可以對比下這裏查詢到的服務是不是和剛纔在 server
上創建的一樣.
// server
AT+BLEGATTSCHAR?
+BLEGATTSCHAR:"char",1,1,0xC300,0x02
+BLEGATTSCHAR:"desc",1,1,1,0x2901
+BLEGATTSCHAR:"char",1,2,0xC301,0x02
+BLEGATTSCHAR:"desc",1,2,1,0x2901
+BLEGATTSCHAR:"char",1,3,0xC302,0x08
+BLEGATTSCHAR:"desc",1,3,1,0x2901
+BLEGATTSCHAR:"char",1,4,0xC303,0x04
+BLEGATTSCHAR:"desc",1,4,1,0x2901
+BLEGATTSCHAR:"char",1,5,0xC304,0x08
+BLEGATTSCHAR:"char",1,6,0xC305,0x10
+BLEGATTSCHAR:"desc",1,6,1,0x2902
+BLEGATTSCHAR:"char",1,7,0xC306,0x20
+BLEGATTSCHAR:"desc",1,7,1,0x2902
+BLEGATTSCHAR:"char",1,8,0xC307,0x02
+BLEGATTSCHAR:"desc",1,8,1,0x2901
OK
// client
AT+BLEGATTCCHAR=0,3
+BLEGATTCCHAR:"char",0,3,1,0xC300,0x02
+BLEGATTCCHAR:"desc",0,3,1,1,0x2901
+BLEGATTCCHAR:"char",0,3,2,0xC301,0x02
+BLEGATTCCHAR:"desc",0,3,2,1,0x2901
+BLEGATTCCHAR:"char",0,3,3,0xC302,0x08
+BLEGATTCCHAR:"desc",0,3,3,1,0x2901
+BLEGATTCCHAR:"char",0,3,4,0xC303,0x04
+BLEGATTCCHAR:"desc",0,3,4,1,0x2901
+BLEGATTCCHAR:"char",0,3,5,0xC304,0x08
+BLEGATTCCHAR:"char",0,3,6,0xC305,0x10
+BLEGATTCCHAR:"desc",0,3,6,1,0x2902
+BLEGATTCCHAR:"char",0,3,7,0xC306,0x20
+BLEGATTCCHAR:"desc",0,3,7,1,0x2902
+BLEGATTCCHAR:"char",0,3,8,0xC307,0x02
+BLEGATTCCHAR:"desc",0,3,8,1,0x2901
OK
除了 index
不一樣, 其他都是一樣的, index
只在本地有效, 方便用戶精準的操作任意一個屬性值.
3.4.4 client 端的讀和寫
client
向 server
發送數據可以用 AT+BLEGATTCWR
, 主動讀取可以用 AT+BLEGATTCRD
.
例如去讀第一個 characteristic
0xC300
:
AT+BLEGATTCRD=0,3,1
+BLEGATTCRD:0,1,0 //長度爲 1, 數值爲 0
OK
現在我們去寫第二個 characteristic
0xC301
:
AT+BLEGATTCWR=0,3,4,10
> // 這裏我們輸入了10個字符 abcdefghij
OK
在 server
端可以看到寫的 log:
+WRITE:0,1,4,,10,abcdefghij
這裏面的參數很多, 第一個都是 conn_index
, 後面跟的依次是 serv_index
, char_index
, desr_index
, length
以及 data
.
如果寫的是 characteristic
的值, 那麼 desc_index
就會被省略.
Tips
- 如果指定輸入字符的長度, 真實輸入的字符超過了這個長度, 會提示
ERROR
- 如果寫了不可寫的
characteristic
, 或者讀了不可讀的characteristic
,會報ERROR
3.4.5 register notification
BLE
的 characteristic
的屬性除了讀寫, 還有 notify
和 indicate
, 這兩種都是 server
向 client
發送數據的方式. 但是要想真的發送成功, 需要 client
提前 register notification
, 也就是寫 ccc
的值.
如果是可 notify
, 需要寫 0x1
, 如果是可 indicate
的, 需要寫 0x2
(寫 0x2902
這個 description
).
比如我們 ESP-AT
裏面默認的服務中, 0xC305
是可 notify
的, 0xC306
是可 indicate
的, 我們分別寫這兩個 characteristics
下面的 0x2902
description
:
// client
AT+BLEGATTCWR=0,3,6,1,2
> // 寫 0x01
OK
// server
+WRITE:0,1,6,1,2,<0x01>,<0x00>
AT+BLEGATTCWR=0,3,7,1,2
> // 寫 0x02
OK
// server
+WRITE:0,1,6,1,2,<0x02>,<0x00>
寫 ccc
是 server
可以發送 notify
和 indicate
的前提條件
Tips
- 其實如果客戶向這些
description
寫錯誤的值,ESP-AT
內部也會轉成正確的.
3.4.6 server 主動向 client 發送數據
在上一個環節, 我們已經做好了發送 notify
和 indicate
的前提條件, 我們現在就可以直接發送數據了
// server
AT+BLEGATTSNTFY=0,1,6,8
> //輸入 123456789
OK
// client
+NOTIFY:0,3,6,8,12345678
// server
AT+BLEGATTSIND=0,1,7,9
> //輸入 aaabbbccc
OK
// client
+INDICATE:0,3,7,9,aaabbbccc
每個參數定義其實和上面都類似, 用戶可以參考指令文檔詳細瞭解每個參數的意義.
四: 如何處理 BLE 加密
上一章節介紹了 BLE
數據通信的示例, 本節介紹 BLE
加密相關的內容.
BLE
加密相關的指令主要就是做這些事情:
- 設置加密參數
- 發起加密請求
- 響應加密請求
KEY
的交互, 比如彈出密碼或者輸入密碼- 查詢
bonding
信息或者清楚bonding
信息
Tips:
- 加密和
bonding
是兩碼事,bonding
只是加密成功之後本地存了一個長期的key
4.1 加密參數該如何設置
BLE
加密參數可以在初始化之後, 發起加密之前的任意時刻下發, 也可以多次下發, 但是僅僅最近的那次下發有效, 會覆蓋前面的設置.
AT+BLESECPARAM=<auth_req>,<iocap>,<key_size>,<init_key>,<rsp_key>[,<auth_option>]
OK
這裏面的參數, 客戶僅僅需要關注最前面的兩個就可以了, 後面的幾個參數可以參照 example
裏面默認設置.
第一個參數是 auth_req:
- 0 : NO_BOND
- 1 : BOND
- 4 : MITM
- 8 : SC_ONLY
- 9 : SC_BOND
- 12 : SC_MITM
- 13 : SC_MITM_BOND
這個參數主要影響加密等級, 表達自身的加密訴求. 比如是想要 MITM
, 還是 security connection
, 還是bonding
.
第二個參數是 IO CAP:
- 0 : DisplayOnly
- 1 : DisplayYesNo
- 2 : KeyboardOnly
- 3 : NoInputNoOutput
- 4 : Keyboard displa
這個參數就是用來設置自身的輸入輸入能力的, 表示自己是否可輸入可輸出.
注意:
如果
auth_req
設置的是MITM
, 但是IO CAP
是NoInputNoOutput
, 那麼肯定就不是MITM
了, 只能是just work
, 有人會說, 這不是矛盾了嘛, 我設置的是MITM
啊, 其實並不矛盾, 通俗的講,MITM
是期望,IO
是能力, 能力不足, 再高的期望也是達不到的. 同理, 如果自己是可輸入可輸出, 但是對方設備是不能輸入不能輸出, 那麼也只能是just work
, 隊友能力不足也不行.
4.2 不同的加密參數組合會出現什麼樣的結果
不同的 IO
設置組合, 會出不同的現象, 比如:
- 一端可輸入另一端可輸出
- 兩端都可輸入輸出
這裏的組合實在太多, 用戶可以參閱core spec
根據不同的組合, 大致上最常見的就是兩種情況, 一個是彈出密碼給對端輸入, 另一種就是輸入對端給的密碼
4.3 加密示例
我們現在舉個例子, 用兩塊 ESP-AT
來加密並 bonding
, 前面建立連接的步驟這裏不重複了, 假設我們現在連接已經建立.
首先設置兩端的加密參數
// client
AT+BLESECPARAM=13,4,16,3,3 // IO是 Keyboard displa
// server
AT+BLESECPARAM=13,2,16,3,3 // IO是 KeyboardOnly
一個是可輸出一個是可輸入, 而且要求 MITM
, 這個時候肯定是 client
彈出密碼, server
輸入密碼.
第二部是發起加密請求, 這個請求可以 client
發起, 也可以 server
發起, 都一樣的, 這裏的例子中, 我們用 client
發起
AT+BLEENC=0,3 // 具體參數描述請參閱指令文檔
OK
這個時候, server
端會彈出加密有加密請求的 log:
+BLESECREQ:0 // 表示第一個連接的設備請求加密
這個時候需要對加密請求作出相應, 決定是同意加密, 還是拒絕. 這裏我們選擇同意
AT+BLEENCRSP=0,1 // 表示同意加密
OK
下一步就是 KEY
交互的過程了, 如我們上面分析一樣, 是 client
彈出密碼
+BLESECNTFYKEY:0,344804 // 密碼是 344804
然後我們在 server
端輸入密碼
AT+BLEKEYREPLY=0,344804
如果密碼輸入正確, 那麼加密就會成功, log 如下:
// client
+BLESECKEYTYPE:0,2
+BLESECKEYTYPE:0,16
+BLESECKEYTYPE:0,1
+BLESECKEYTYPE:0,32
+BLEAUTHCMPL:0,0
// server
+BLESECKEYTYPE:0,16
+BLESECKEYTYPE:0,1
+BLESECKEYTYPE:0,32
+BLESECKEYTYPE:0,2
+BLEAUTHCMPL:0,0
關於 KEYTYPE
的 log 都可以忽略, 僅僅關注 +BLEAUTHCMPL:0,0
就可以, 第二個參數爲 0, 表示成功, 不爲 0 表示失敗, 第一個參數是 conn_index
, 取決於當初連接的時候設置的是多少.
上面的例子僅僅只是一種情況, 如果我們當時將 IO
顛倒下, 那麼結果又是不一樣的, 用戶可以自由嘗試, 其實無非是就是兩端配合着輸入輸出密碼, 或者直接 just work
, 不需要人工參與交互.
五: 如何使用 BLE SPP
因爲 BLE SPP
沒有標準的協議, 這裏的 BLE SPP
是我們自己定義的, 與別的廠商不兼容. 如果客戶需要使用, 可以使用兩塊 ESP32
的模組, 或者按照我們的方法在手機端或者別的廠商芯片上面實現.
BLE SPP
的原理就是利用兩個 characteristic
的來進行數據收發, 一個需要可以被寫, 另一個需要可以 notify
或者 indicate
.
我們用兩個 ESP32
來舉例, 首先我們分別初始化兩塊板子:
// client:
AT+BLEINIT=1
OK
// server:
AT+BLEINIT=2
OK
然後我們在 server
上面創建並開啓服務, 開啓廣播, 等待被連接
AT+BLEGATTSSRVCRE
OK
AT+BLEGATTSSRVSTART
OK
AT+BLEADVSTART
OK
client
發起連接請求, 如果不知道對端的地址, 可以先掃描一下
AT+BLECONN=0,”XX:XX:XX:XX:XX:XX”
OK
如果連接成功, server
會有 log 打印
+BLECONN:0,”XX:XX:XX:XX:XX:XX”
client
做服務發現, server
也查詢下自身的服務, 並設置 SPP
參數
// client:
AT+BLEGATTCPRIMSRV=0
AT+BLEGATTCCHAR=0,3
AT+BLESPPCFG=1,3,3,3,7
// server:
AT+BLEGATTSSRV?
AT+BLEGATTSCHAR?
AT+BLESPPCFG=1,1,7,1,3
SPP
參數其實就是指定用那兩個 characteristic
來收發數據
AT+BLESCANPARAM=<option>[,<tx_service_index>,<tx_char_index>,<rx_service_index>,<rx_char_index>]
第一個參數在設置的時候必須設置爲 1, 如果是 0, 表示清除配置
後面的四個參數分別是:
- tx_service_index
- tx_char_index
- rx_service_index
- rx_char_index
分別指定用哪個 service
下面的哪個 characteristic
來收發數據.
在例子中, client
端設置是的第三個服務下面的第三個 characteristic
來發送數據, 這個 characteristic
的屬性是 write
, 是可寫的, 對於 client
端來講, 用來 tx
的 characteristic
必須是可以被 write
或者 write without rsp
. 用來 rx
的 characteristic
是第七個 characteristic
, 他的屬性是 indicate
, 是用來接收 server
發送數據的.
然後就可以進入 SPP
模式了:
// client:
AT+BLESPP
// server:
AT+BLESPP