一、背景
1.1 低功耗藍牙(BLE)協議棧
鏈路層(LL) 控制設備的射頻狀態,有五個設備狀態:待機、廣播、掃描、初始化和連接。
廣播 為廣播數據包,而 掃描 則是監聽廣播。
GAP通信中角色,中心設備(Central - 主機) 用來掃描和連接 外圍設備(Peripheral - 從機)。
1.2 BLE從初始化到建立連接的過程
外圍設備開始廣播,發送完一個廣播包后T_IFS,開啟射頻Rx窗口接收來自中心設備的數據包
中心設備掃描到廣播,在收取此廣播T_IFS后如果開啟了中心設備的掃描回復,中心設備將向外設發送回復
外設收取到中心設備的回復,做好接收準備,并返回ACK包
如果ACK包未被中心設備接收到,中心設備將一直發送回復直到超時,此期間內只要外設返回過一次ACK包就算連接成功
開始建立通信,后續中心設備將以收取到外設廣播的時間為原點,以Connection Interval為周期向外設發送數據包,數據包將具有兩個作用:同步兩設備時鐘和建立主從模式通信
外設每收到中心設備的一個包,就會把自己的時序原點重新設置,以和中心設備同步(Service向Client同步)
BLE通信在建立成功后變為主從模式,中心設備Central變為Master,外設Peripheral變為Slave,Slave只能在Master向它發送了一個包以后才能在規定的時間內把自己的數據回傳給Master
連接建立成功
外設自動停止廣播,其他設備無法再查找到該外設
按照以下時序進行通信,在中心設備發送包的間隔內,外設可以發送多個包
需要連接斷開時只需要中心設備停止連接(停止發送包)即可
中心設備可以將外設的addr寫入Flash或SRAM等存儲器件,保持監聽此addr,當再次收到外設廣播時就可以建立通信。BLE Server設備為了省電,當一段時間內沒有數據要發送時,可以不再發送包,雙方就會因為連接超時(connection timeout)斷開,這時需要中心設備啟動監聽,這樣,當BLE Server設備需要發送數據時,就可以再次連接
1.3 ESP32藍牙應用結構
藍牙是?種短距通信系統,其關鍵特性包括魯棒性、低功耗、低成本等。藍牙系統分為兩種不同的技術:經典藍牙 (Classic Bluetooth) 和藍牙低功耗 (Bluetooth Low Energy)。
ESP32 支持雙模藍牙,即同時支持經典藍牙和藍牙低功耗。
從整體結構上,藍牙可分為控制器 (Controller) 和主機 (Host) 兩?部分:控制器包括了 PHY、Baseband、Link Controller、Link Manager、Device Manager、HCI 等模塊,用于硬件接?管理、鏈路管理等等;主機則包括了 L2CAP、SMP、SDP、ATT、GATT、GAP 以及各種規范,構建了向應用層提供接口的基礎,方便應用層對藍牙系統的訪問。主機可以與控制器運行在同?個宿主上,也可以分布在不同的宿主上。ESP32 可以支持上述兩種方式。
1.4 Bluedroid主機架構
在 ESP-IDF 中,使用經過大量修改后的 BLUEDROID 作為藍牙主機 (Classic BT + BLE)。BLUEDROID 擁有較為完善的功能,?持常用的規范和架構設計,同時也較為復雜。經過大量修改后,BLUEDROID 保留了大多數 BTA 層以下的代碼,幾乎完全刪去了 BTIF 層的代碼,使用了較為精簡的 BTC 層作為內置規范及 Misc 控制層。修改后的 BLUEDROID 及其與控制器之間的關系如下圖:
二、API說明
以下控制器和虛擬 HCI 接口位于 bt/include/esp32/include/esp_bt.h。
2.1 esp_bt_controller_mem_release
2.2 esp_bt_controller_init
2.3 esp_bt_controller_enable
以下 GAP 接口位于 bt/host/bluedroid/api/include/api/esp_bt_main.h 和 bt/host/bluedroid/api/include/api/esp_gap_ble_api.h。
2.4 esp_bluedroid_init
2.5 esp_bluedroid_enable
2.6 esp_ble_gap_register_callback
2.7 esp_ble_gap_set_scan_params
2.8 esp_ble_gap_start_scanning
2.9 esp_ble_gap_stop_scanning
2.10 esp_ble_resolve_adv_data
2.11 esp_ble_gap_disconnect
以下 GATT 接口位于 bt/host/bluedroid/api/include/api/esp_gattc_api.h
2.12 esp_ble_gattc_open
2.13 esp_ble_gattc_close
三、BT控制器和協議棧初始化
使用 esp-idfexamplesbluetoothbluedroidblegatt_client 中的例程
.........//esp_bt_controller_config_t是藍牙控制器配置結構體,這里使用了一個默認的參數
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
//初始化藍牙控制器,此函數只能被調用一次,且必須在其他藍牙功能被調用之前調用
ret = esp_bt_controller_init(&bt_cfg);
if (ret) {
ESP_LOGE(GATTC_TAG, '%s initialize controller failed: %sn', __func__, esp_err_to_name(ret));
return;
}
//使能藍牙控制器,mode是藍牙模式,如果想要動態改變藍牙模式不能直接調用該函數,
//應該先用disable關閉藍牙再使用該API來改變藍牙模式
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(GATTC_TAG, '%s enable controller failed: %sn', __func__, esp_err_to_name(ret));
return;
}
//初始化藍牙并分配系統資源,它應該被第一個調用
/*
藍牙棧bluedroid stack包括了BT和BLE使用的基本的define和API
初始化藍牙棧以后并不能直接使用藍牙功能,
還需要用FSM管理藍牙連接情況
*/
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(GATTC_TAG, '%s init bluetooth failed: %sn', __func__, esp_err_to_name(ret));
return;
}
//使能藍牙棧
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(GATTC_TAG, '%s enable bluetooth failed: %sn', __func__, esp_err_to_name(ret));
return;
}
//建立藍牙的FSM(有限狀態機)
//這里使用回調函數來控制每個狀態下的響應,需要將其在GATT和GAP層的回調函數注冊
/*esp_gattc_cb和esp_gap_cb處理藍牙棧可能發生的所有情況,達到FSM的效果*/
ret = esp_ble_gap_register_callback(esp_gap_cb);
if (ret){
ESP_LOGE(GATTC_TAG, '%s gap register failed, error code = %xn', __func__, ret);
return;
}
ret = esp_ble_gattc_register_callback(esp_gattc_cb);
if(ret){
ESP_LOGE(GATTC_TAG, '%s gattc register failed, error code = %xn', __func__, ret);
return;
}
//下面創建了BLE GATT服務A,相當于1個獨立的應用程序
ret = esp_ble_gattc_app_register(PROFILE_A_APP_ID);
if (ret){
ESP_LOGE(GATTC_TAG, '%s gattc app register failed, error code = %xn', __func__, ret);
}
/*
設置了MTU的值(經過MTU交換,從而設置一個PDU中最大能夠交換的數據量)。
例如:主設備發出一個1000字節的MTU請求,但是從設備回應的MTU是500字節,那么今后雙方要以較小的值500字節作為以后的MTU。
即主從雙方每次在做數據傳輸時不超過這個最大數據單元。
*/
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
if (local_mtu_ret){
ESP_LOGE(GATTC_TAG, 'set local MTU failed, error code = %x', local_mtu_ret);
}.......
四、應用程序配置文件
應用程序配置文件是為一個或多個服務器應用程序設計的功能進行分組的一種方法。例如,可以將應用程序配置文件連接到心率傳感器,并將另一個應用程序配置文件連接到溫度傳感器。每個應用程序配置文件創建一個GATT接口以連接到其他設備。代碼中的應用程序配置文件是gattc_profile_inst結構的實例,其定義如下:
struct gattc_profile_inst {
esp_gattc_cb_t gattc_cb;
uint16_t gattc_if;
uint16_t app_id;
uint16_t conn_id;
uint16_t service_start_handle;
uint16_t service_end_handle;
uint16_t char_handle;
esp_bd_addr_t remote_bda;};
結構包括:
gattc_cb:GATT客戶端回調函數
gattc_if:此配置文件的GATT客戶端接口號
app_id:應用程序配置文件ID號
conn_id:連接ID號
service_start_handle:服務頭部句柄
service_end_handle:服務末尾句柄
char_handle:特征句柄
remote_bda:連接到此客戶端的遠程設備地址
本例中有一個應用程序配置文件,其ID定義為:
#define PROFILE_NUM 1#define PROFILE_A_APP_ID 0
應用程序配置文件存儲在gl_profile_tab數組中,并分配相應的回調函數gattc_profile_a_event_handler()。GATT 客戶端上的不同應用程序使用不同的接口,由 gattc_if 參數表示。對于初始化,此參數設置為ESP_GATT_IF_NONE,這意味著應用程序配置文件尚未鏈接到任何服務端。
/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {
[PROFILE_A_APP_ID] = {.gattc_cb = gattc_profile_event_handler,
.gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
},};
應用程序配置文件注冊觸發ESP_GATTC_REG_EVT事件,該事件由esp_gattc_cb()事件處理程序處理。處理程序獲取事件返回的GATT接口,并將其存儲在profile表中:
static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param){
ESP_LOGI(GATTC_TAG, 'EVT %d, gattc if %d', event, gattc_if);
/* If event is register event, store the gattc_if for each profile */
if (event == ESP_GATTC_REG_EVT) {
if (param->reg.status == ESP_GATT_OK) {
gl_profile_tab[param->reg.app_id].gattc_if = gattc_if;
} else {
ESP_LOGI(GATTC_TAG, 'reg app failed, app_id %04x, status %d',
param->reg.app_id,
param->reg.status);
return;
}
}…
最后,回調函數調用gl_profile_tab表中每個配置文件的相應事件處理程序。
…/* If the gattc_if equal to profile A, call profile A cb handler,
* so here call each profile's callback */
do {
int idx;
for (idx = 0; idx < PROFILE_NUM; idx++) {
if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
gattc_if == gl_profile_tab[idx].gattc_if) {
if (gl_profile_tab[idx].gattc_cb) {
gl_profile_tab[idx].gattc_cb(event, gattc_if, param);
}
}
}
} while (0);}
五、獲取掃描結果
啟動掃描吼,掃描結果在到達ESP_GAP_BLE_SCAN_RESULT_EVT事件時立即顯示,該事件包括以下參數:
/**
* @brief ESP_GAP_BLE_SCAN_RESULT_EVT
*/
struct ble_scan_result_evt_param {
esp_gap_search_evt_t search_evt; /*!< Search event type */
esp_bd_addr_t bda; /*!< Bluetooth device address which has been searched */
esp_bt_dev_type_t dev_type; /*!< Device type */
esp_ble_addr_type_t ble_addr_type; /*!< Ble device address type */
esp_ble_evt_type_t ble_evt_type; /*!< Ble scan result event type */
int rssi; /*!< Searched device's RSSI */
uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; /*!< Received EIR */
int flag; /*!< Advertising data flag bit */
int num_resps; /*!< Scan result number */
uint8_t adv_data_len; /*!< Adv data length */
上一篇:ESP32學習筆記(33)——BLE GATT客戶端發現服務和讀寫特征值
下一篇:ESP32學習筆記(31)——BLE帶有屬性表的GATT服務
推薦閱讀最新更新時間:2025-06-09 10:39


設計資源 培訓 開發板 精華推薦
- LT1172HVCT、5V/1.25A 正降壓轉換器的典型應用
- 【訓練營】四條腿機器狗
- SG3525A 推挽式脈寬調制器控制電路的典型應用
- LT1108CS8-12掌上電腦邏輯電源微功率DC/DC轉換器典型應用電路
- AM2M-1515DH30-NZ ±15 Vout、2W 雙路輸出 DC-DC 轉換器的典型應用
- LTC2945HMS 具有高達 200V 浪涌保護的堅固型 4V 至 70V 高壓側功率監視器的典型應用
- 常用MCU全能燒錄器
- 【航順訓練營】國產航順MCU開發學習板
- LTC3624EMSE-25 5V 輸出電壓、2A 同步降壓型穩壓器的典型應用,具有 1MHz、突發模式操作
- 具有備用電源監控功能的 LTC4420IMSE 18V 雙輸入微電源路徑優先器的典型應用電路