ESP-AT 系列: BLE 命令介紹和使用

目錄

一: 概述

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-ATBLE 的命令做的比較細, 對 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 如果已經提前知道了 serverMAC 地址, 可以不掃描, 直接發起連接的命令;
如果不知道 serverMAC 地址, 可以先掃描一下.

連接與通信
一般的 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> 參數, 具體的介紹與用法, 會在下面的實例章節詳細介紹, 這裏僅需瞭解下大概的流程.

查詢好了服務相關的參數, 就可以數據通信了, clientserver 發送數據需要通過寫的方式:

  • char 或者 desc: AT+BLEGATTCWR
  • char 或者 desc: AT+BLEGATTCRD

serverclient 發送數據需要通過 notifyindicate 的方式.

  • 發送 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 包.

除了AppleiBeacon, 還有谷歌的 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 datascan 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+BLECONNAT+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 的數據通信都是基於屬性協議的, servicesserver 端, 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.

還可以查詢服務下面的 characteristicsdescription:

+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, 他的 prop0x10. 這個 characteristic 下面還跟着一個 description, UUID0x2902.

其實看到這個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 個服務, 請直接忽略 0x18010x1800, 這兩個是 GAPGATT, 最下面那個第三個服務 0xA002 纔是剛纔我們創建的服務.

在例子中, 我們進一步的查詢了第三個服務下面的有哪些 characteristicdescripton, 用戶可以對比下這裏查詢到的服務是不是和剛纔在 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 端的讀和寫

clientserver 發送數據可以用 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

BLEcharacteristic 的屬性除了讀寫, 還有 notifyindicate, 這兩種都是 serverclient 發送數據的方式. 但是要想真的發送成功, 需要 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>

cccserver 可以發送 notifyindicate 的前提條件

Tips

  • 其實如果客戶向這些 description 寫錯誤的值, ESP-AT 內部也會轉成正確的.

3.4.6 server 主動向 client 發送數據

在上一個環節, 我們已經做好了發送 notifyindicate 的前提條件, 我們現在就可以直接發送數據了

// 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 CAPNoInputNoOutput, 那麼肯定就不是 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 端來講, 用來 txcharacteristic 必須是可以被 write 或者 write without rsp. 用來 rxcharacteristic 是第七個 characteristic, 他的屬性是 indicate, 是用來接收 server 發送數據的.

然後就可以進入 SPP 模式了:

// client:

AT+BLESPP

// server:

AT+BLESPP
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章