一、背景
1.1 GATT協(xié)議
GATT(Generic Attributes Profile)的縮寫,中文是通用屬性協(xié)議,是已連接的低功耗藍牙設備之間進行通信的協(xié)議。
一旦兩個設備建立起了連接,GATT 就開始起作用了,這也意味著,你必需完成前面的GAP協(xié)議。
GATT使用了 ATT(Attribute Protocol)協(xié)議,ATT 協(xié)議把 Service,Characteristic 對應的數據保存在一個查找表中,查找表使用 16bit ID 作為每一項的索引。
GATT定義的多層數據結構簡要概括起來就是 服務(Service) 可以包含多個 特征(Characteristic),每個特征包含 屬性(Properties) 和 值(Value),還可以包含多個 描述(Descriptor)。
1.2 屬性協(xié)議(ATT)
屬性協(xié)議層 負責數據檢索,允許一個設備暴露一些數據塊給其他設備,其他設備稱之為“屬性”。
在ATT環(huán)境中,展示屬性的設備稱之為服務器,與它配對的設備稱之為客戶端。鏈路層的主機從機和這里的服務器、客服端是兩種概念,主設備既可以是服務器,也可以是客戶端。從設備毅然。
1.3 GATT通信中角色
從GATT的角度來看,處于連接狀態(tài)時的兩個設備,它們各自充當兩種角色中的一種:
服務端(Server)
包含被GATT客戶端讀取或寫入的特征數據的設備。
客戶端(Client)
從GATT服務器中讀取數據或向GATT服務器寫入數據的設備。
外圍設備(從機)作為 GATT 服務端(Server),它維持了 ATT 的查找表以及 service 和 characteristic 的定義;
客戶端和服務器的GATT角色獨立于外圍設備和中央設備的GAP角色。外圍設備可以是GATT客戶端或GATT服務器,中心可以是GATT客戶端或GATT服務器。
image
1.4 Bluedroid主機架構
在 ESP-IDF 中,使用經過大量修改后的 BLUEDROID 作為藍牙主機 (Classic BT + BLE)。BLUEDROID 擁有較為完善的功能,?持常用的規(guī)范和架構設計,同時也較為復雜。經過大量修改后,BLUEDROID 保留了大多數 BTA 層以下的代碼,幾乎完全刪去了 BTIF 層的代碼,使用了較為精簡的 BTC 層作為內置規(guī)范及 Misc 控制層。修改后的 BLUEDROID 及其與控制器之間的關系如下圖:
二、API說明
以下 GATT 接口位于 bt/host/bluedroid/api/include/api/esp_gattc_api.h
2.1 esp_ble_gattc_search_service
2.2 esp_ble_gattc_get_char_by_uuid
2.3 esp_ble_gattc_get_descr_by_char_handle
2.4 esp_ble_gattc_get_attr_count
2.5 esp_ble_gattc_write_char
2.6 esp_ble_gattc_write_char_descr
2.7 esp_ble_gattc_register_for_notify
三、發(fā)現(xiàn)服務
本篇是關于GATT客戶端發(fā)現(xiàn)服務和讀寫特征值,連接服務端的流程查看 ESP32學習筆記(32)——BLE GAP主機端連接
MTU配置事件還用于開始發(fā)現(xiàn)客戶端剛剛連接到的服務器中可用的服務。要發(fā)現(xiàn)服務,可以使用esp_ble_gattc_search_service()函數。該函數的參數包括GATT接口、應用程序配置文件連接ID和客戶端感興趣的應用程序UUID。
我們正在尋找的服務定義為:
#define REMOTE_SERVICE_UUID 0x00FFstatic esp_bt_uuid_t remote_filter_service_uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = REMOTE_SERVICE_UUID,},};
隨后進行查找服務:
esp_ble_gattc_search_service(gattc_if, param->cfg_mtu.conn_id, &remote_filter_service_uuid);
break;
找到的服務結果(如果有的話)將從ESP_GATTC_SEARCH_RES_EVT返回。對于找到的每個服務,將觸發(fā)事件來打印所發(fā)現(xiàn)服務的信息,具體取決于UUID的大小:
case ESP_GATTC_SEARCH_RES_EVT: {
esp_gatt_srvc_id_t *srvc_id = &p_data->search_res.srvc_id;
conn_id = p_data->search_res.conn_id;
if (srvc_id->id.uuid.len == ESP_UUID_LEN_16 && srvc_id->id.uuid.uuid.uuid16 == REMOTE_SERVICE_UUID) {
get_server = true;
gl_profile_tab[PROFILE_A_APP_ID].service_start_handle = p_data->search_res.start_handle;
gl_profile_tab[PROFILE_A_APP_ID].service_end_handle = p_data->search_res.end_handle;
ESP_LOGI(GATTC_TAG, 'UUID16: %x', srvc_id->id.uuid.uuid.uuid16);
}
break;
如果客戶端找到了它要查找的服務,就將get_server標記設置為true,并保存開始句柄值和結束句柄值,稍后將使用它們來獲得該服務的所有特征。在返回所有服務結果之后,將完成搜索并觸發(fā)ESP_GATTC_SEARCH_CMPL_EVT事件。
四、獲取特征
此示例實現(xiàn)從預定義服務獲取特征數據。我們想要獲得特征的服務UUID是0x00FF,我們感興趣的特征UUID是0xFF01:
#define REMOTE_NOTIFY_CHAR_UUID 0xFF01
使用esp_gatt_srvc_id_t結構定義服務:
/**
* @brief Gatt id, include uuid and instance id
*/typedef struct {
esp_bt_uuid_t uuid; /*!< UUID */
uint8_t inst_id; /*!< Instance id */} __attribute__((packed)) esp_gatt_id_t;
在這個例子中,我們定義了我們想要獲取特征的服務:
static esp_gatt_srvc_id_t remote_service_id = {
.id = {
.uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = REMOTE_SERVICE_UUID,},
},
.inst_id = 0,
},
.is_primary = true,};
定義之后,我們可以使用esp_ble_gattc_get_characteristic()函數從該服務獲取特征,該函數在服務搜索完成并且找到了它正在尋找的服務之后,在ESP_GATTC_SEARCH_CMPL_EVT事件中調用。
case ESP_GATTC_SEARCH_CMPL_EVT:
if (p_data->search_cmpl.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, 'search service failed, error status = %x', p_data->search_cmpl.status);
break;
}
conn_id = p_data->search_cmpl.conn_id;
if (get_server){
uint16_t count = 0;
esp_gatt_status_t status = esp_ble_gattc_get_attr_count( gattc_if,
p_data->search_cmpl.conn_id,ESP_GATT_DB_CHARACTERISTIC, gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,
INVALID_HANDLE,
&count);
if (status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, 'esp_ble_gattc_get_attr_count error');
}
if (count > 0){
char_elem_result = (esp_gattc_char_elem_t*)malloc (sizeof(esp_gattc_char_elem_t) * count);
if (!char_elem_result){
ESP_LOGE(GATTC_TAG, 'gattc no mem');
}else{
status = esp_ble_gattc_get_char_by_uuid( gattc_if,
p_data->search_cmpl.conn_id,
gl_profile_tab[PROFILE_A_APP_ID].service_start_handle,
gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,
remote_filter_char_uuid,
char_elem_result,
&count);
if (status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, 'esp_ble_gattc_get_char_by_uuid error');
}
/* Every service have only one char in our 'ESP_GATTS_DEMO' demo,
so we used first 'char_elem_result' */
if (count > 0 && (char_elem_result[0].properties
&ESP_GATT_CHAR_PROP_BIT_NOTIFY)){
gl_profile_tab[PROFILE_A_APP_ID].char_handle =
char_elem_result[0].char_handle;
esp_ble_gattc_register_for_notify (gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].remote_bda,
char_elem_result[0].char_handle);
上一篇:ESP32學習筆記(34)——BLE一主多從連接
下一篇:ESP32學習筆記(32)——BLE GAP主機端連接
推薦閱讀最新更新時間:2025-06-08 04:18


設計資源 培訓 開發(fā)板 精華推薦
- 芯原超低能耗NPU可為移動端大語言模型推理提供超40 TOPS算力
- 芯原AI-ISP芯片定制方案助力客戶智能手機量產出貨
- AI加持,安森美一站式服務推動助聽器市場持續(xù)進步
- 意法半導體推出用于匹配遠距離無線微控制器STM32WL33的集成的匹配濾波芯片
- 如何為您的應用選擇光傳感器
- Samtec新型農業(yè)漫談系列二 | 垂直農業(yè)案列分享
- Samtec應用科普 | C-V2X技術在汽車領域的應用
- 尼得科運動&能源事業(yè)本部在印度卡納塔克邦胡布利舉行新工廠竣工儀式
- 貿澤開售Qorvo適用于5G和mMIMO應用的新型QPA9822線性5G高增益/高驅動放大器
- 邊緣計算網關工業(yè)物聯(lián)網應用:空壓機遠程運維監(jiān)控管理