目錄
1、背景
1.1參考資料
1.2 GATT是什么玩意
2、ESP32 例程分析
2.1 GATT 服務(wù)器的架構(gòu)組織
2.2 從GATT回調(diào)函數(shù)注冊(cè)程序esp_ble_gatts_register_callback開始深入分析
3 建立連接之前的GATT狀態(tài)機(jī)
3.1 創(chuàng)建服務(wù) creating services
3.2 啟動(dòng)服務(wù)并創(chuàng)建Characteristics
3.2.1 添加Characteristic Value declaration ATT-------看這里
3.3 添加Characteristic descriptor 描述符
4 建立連接時(shí)GATT的狀態(tài)機(jī)研究
4.1連接后觸發(fā)的連接回調(diào)函數(shù)
4.2連接后MTU大小確定
4.3 發(fā)送應(yīng)答數(shù)據(jù)
4.4 客戶端和服務(wù)器端 收發(fā)數(shù)據(jù)
4.4.1 手機(jī)端的操作
4.4.2 設(shè)備端的操作
4.4.4.1 使能通知
4.4.4.2 讀寫數(shù)據(jù)
4.5 Characteristic結(jié)構(gòu)
5. 數(shù)據(jù)交互其他必要知道的
6.結(jié)束語
1、背景
雖然看了一些BLE的資料,可是對(duì)藍(lán)牙依舊不太了解?,F(xiàn)在從ESP32 的示例出發(fā)結(jié)合esp_gatts_api.h學(xué)習(xí)GATT。
本文以創(chuàng)建Service和Characteristic為中心展開分析。
1.1參考資料
BLE GATT 介紹 http://www.cnblogs.com/smart-mutouren/p/5937990.html
ble v4.2
1.2 GATT是什么玩意
GATT是用A他tribute Protocal(屬性協(xié)議)定義的一個(gè)service(服務(wù))框架。這個(gè)框架定義了Services以及它們的Characteristics的格式和規(guī)程。規(guī)程就是定義了包括發(fā)現(xiàn)、讀、寫、通知、指示以及配置廣播的characteristics。------BLE V4.2給出了如下定義(翻譯有待加強(qiáng))。
Profile中所使用的Protocols(協(xié)議)
為實(shí)現(xiàn)這個(gè)Profile的設(shè)備定義了兩種角色:Client(客戶端)、Server(服務(wù)器)。角色不是不固定的哦.....
在GATT的Profile的定義11個(gè)features,映射了程序Procedure。
Feature對(duì)應(yīng)的Procedure
序號(hào)
Feature
Procedure
1
Server Configuration 服務(wù)器配置
Exchange MTU 連接期間只能配置一次,確定連接通信的ATT_MTU大小;否則就用默認(rèn)的23-3 = 20
2
Primary Service Discover 主服務(wù)查找
Discover All Primary Services/Discover Primary Services By Service UUID 把主服務(wù)都找出來,
3
Relationship Discover
查找Included Services
4
Characteristic Discover 特征查找
Discover All Characteristic of a Service./Discover Characteristic By UUID
這里找到的是Characteristic declaration的ATT Handle 和ATT Value。這個(gè)ATT value包括Characteristic Properties, 特征值的句柄和Characteristic UUID。特征值的句柄在讀寫特征值時(shí)要用到。
5
6
7
8
9
10
11
1)配置交換(exchanging configuration)
2)發(fā)現(xiàn)一個(gè)設(shè)備上的服務(wù)s和特征s
3)讀取一個(gè)特征值(characteristic value)
4)寫入一個(gè)特征值
5)通知一個(gè)特征值
6)指示一個(gè)特征值
2、ESP32 例程分析
一旦兩個(gè)設(shè)備建立了連接,GATT就開始發(fā)揮效用,同時(shí)意味著GAP協(xié)議管理的廣播過程結(jié)束了。
GATT連接是獨(dú)占的,即一個(gè)BLE周邊設(shè)備同時(shí)只能與一個(gè)中心設(shè)備連接。??????
profile 可以理解為一種規(guī)范,一個(gè)標(biāo)準(zhǔn)的通信協(xié)議中,存于從機(jī)(Server)中。藍(lán)牙組織規(guī)定了一些標(biāo)準(zhǔn)的Profile。每個(gè)profile中包含多個(gè)Service,每個(gè)service代表從機(jī)的一種能力。
2.1 GATT 服務(wù)器的架構(gòu)組織
一個(gè)GATT 服務(wù)器應(yīng)用程序架構(gòu)(由Application Profiles組織起來)如下:
每個(gè)Application Profile描述了一個(gè)方法來對(duì)為一個(gè)客戶端應(yīng)用程序設(shè)計(jì)的功能進(jìn)行分組 ,例如在智能手機(jī)或平板電腦上運(yùn)行的移動(dòng)應(yīng)用程序。
每個(gè)Profile定義為一個(gè)結(jié)構(gòu)體,結(jié)構(gòu)體成員依賴于該Application Profile 實(shí)現(xiàn)的services服務(wù)和characteristic特征。結(jié)構(gòu)體成員還包括GATT interface(GATT 接口)、Application ID(應(yīng)用程序ID)和處理profile事件的回調(diào)函數(shù)。
每個(gè)profile包括GATT interface(GATT 接口)、Application ID(應(yīng)用程序ID)、 Connection ID(連接ID)、Service Handle(服務(wù)句柄)、Service ID(服務(wù)ID)、Characteristic handle(特征句柄)、Characteristic UUID(特征UUID)、ATT權(quán)限、Characteristic Properties、描述符句柄、描述符UUID 。
如果Characteristic 支持通知(notifications)或指示(indicatons),它就必須是實(shí)現(xiàn)CCCD(Client Characteristic Configuration Descriptor)----這是額外的ATT。描述符有一個(gè)句柄和UUID。
struct gatts_profile_inst {
esp_gatt_srvc_id_t service_id;
esp_gatt_char_prop_t property;
esp_bt_uuid_t descr_uuid;
Application Profile存儲(chǔ)在數(shù)組中,并分配相應(yīng)的回調(diào)函數(shù)gatts_profile_a_event_handler() 和 gatts_profile_b_event_handler()。
在GATT客戶機(jī)上的不同的應(yīng)用程序使用不同的接口,用gatts_if參數(shù)來表示 。在初始化時(shí),gatts-if參數(shù)初始化為ESP_GATT_IF_NONE,這意味著Application Profile還沒有連接任何客戶端。
/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */
static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = {
.gatts_cb = gatts_profile_a_event_handler,
.gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
.gatts_cb = gatts_profile_b_event_handler,/* This demo does not implement, similar as profile A */
.gatts_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
這是兩個(gè)元素的數(shù)組??梢杂肁pplication ID來注冊(cè)Application Profiles,Application ID是由應(yīng)用程序分配的用來標(biāo)識(shí)每個(gè)Profile。 通過這種方法,可以在一個(gè)Server中run多個(gè)Application Profile。
esp_ble_gatts_app_register(PROFILE_A_APP_ID);
2.2 從GATT回調(diào)函數(shù)注冊(cè)程序esp_ble_gatts_register_callback 開始深入分析
看樣子,也是一個(gè)狀態(tài)機(jī),下面是GATT回調(diào)函數(shù)的注冊(cè)函數(shù)esp_ble_gatts_register_callback 。
esp_err_t esp_ble_gatts_register_callback(esp_gatts_cb_t callback);
作用:向BTA GATTS模塊注冊(cè)應(yīng)用程序回調(diào)函數(shù)。
callback 回調(diào)函數(shù)處理 從BLE堆棧推送到應(yīng)用程序的所有事件 。
對(duì)于GATT server回調(diào)函數(shù)類型進(jìn)行分析
typedef void (* esp_gatts_cb_t)(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
回調(diào)函數(shù)的參數(shù):
event: esp_gatts_cb_event_t 這是一個(gè)枚舉類型,表示調(diào)用該回調(diào)函數(shù)時(shí)的事件(或藍(lán)牙的狀態(tài))
gatts_if: esp_gatt_if_t (uint8_t) 這是GATT訪問接口類型,通常在GATT客戶端上不同的應(yīng)用程序用不同的gatt_if(不同的Application profile對(duì)應(yīng)不同的gatts_if) 調(diào)用esp_ble_gatts_app_register()時(shí),注冊(cè)Application profile 就會(huì)有一個(gè)gatts_if。
param: esp_ble_gatts_cb_param_t 指向回調(diào)參數(shù),是個(gè)聯(lián)合體類型,不同的事件類型采用聯(lián)合體內(nèi)不同的成員結(jié)構(gòu)體。
第一步、看看藍(lán)牙狀態(tài)機(jī)有哪些狀態(tài)類型esp_gatts_cb_event_t
ESP_GATTS_REG_EVT = 0, /*!< When register application id, the event comes */
ESP_GATTS_READ_EVT = 1, /*!< When gatt client request read operation, the event comes */
ESP_GATTS_WRITE_EVT = 2, /*!< When gatt client request write operation, the event comes */
ESP_GATTS_EXEC_WRITE_EVT = 3, /*!< When gatt client request execute write, the event comes */
ESP_GATTS_MTU_EVT = 4, /*!< When set mtu complete, the event comes */
ESP_GATTS_CONF_EVT = 5, /*!< When receive confirm, the event comes */
ESP_GATTS_UNREG_EVT = 6, /*!< When unregister application id, the event comes */
ESP_GATTS_CREATE_EVT = 7, /*!< When create service complete, the event comes */
ESP_GATTS_ADD_INCL_SRVC_EVT = 8, /*!< When add included service complete, the event comes */
ESP_GATTS_ADD_CHAR_EVT = 9, /*!< When add characteristic complete, the event comes */
ESP_GATTS_ADD_CHAR_DESCR_EVT = 10, /*!< When add descriptor complete, the event comes */
ESP_GATTS_DELETE_EVT = 11, /*!< When delete service complete, the event comes */
ESP_GATTS_START_EVT = 12, /*!< When start service complete, the event comes */
ESP_GATTS_STOP_EVT = 13, /*!< When stop service complete, the event comes */
ESP_GATTS_CONNECT_EVT = 14, /*!< When gatt client connect, the event comes */
ESP_GATTS_DISCONNECT_EVT = 15, /*!< When gatt client disconnect, the event comes */
ESP_GATTS_OPEN_EVT = 16, /*!< When connect to peer, the event comes */
ESP_GATTS_CANCEL_OPEN_EVT = 17, /*!< When disconnect from peer, the event comes */
ESP_GATTS_CLOSE_EVT = 18, /*!< When gatt server close, the event comes */
ESP_GATTS_LISTEN_EVT = 19, /*!< When gatt listen to be connected the event comes */
ESP_GATTS_CONGEST_EVT = 20, /*!< When congest happen, the event comes */
/* following is extra event */
ESP_GATTS_RESPONSE_EVT = 21, /*!< When gatt send response complete, the event comes */
ESP_GATTS_CREAT_ATTR_TAB_EVT = 22, /*!< When gatt create table complete, the event comes */
ESP_GATTS_SET_ATTR_VAL_EVT = 23, /*!< When gatt set attr value complete, the event comes */
ESP_GATTS_SEND_SERVICE_CHANGE_EVT = 24, /*!< When gatt send service change indication complete, the event comes */
第二步、再來看一個(gè)很有意思的聯(lián)合體類型esp_ble_gatts_cb_param_t,不同的事件類型聯(lián)合體的對(duì)象也不同。
* @brief ESP_GATTS_REG_EVT
struct gatts_reg_evt_param {
esp_gatt_status_t status; /*!< Operation status */
uint16_t app_id; /*!< Application id which input in register API */
} reg; /*!< Gatt server callback param of ESP_GATTS_REG_EVT */
* @brief ESP_GATTS_SET_ATTR_VAL_EVT
struct gatts_set_attr_val_evt_param{
uint16_t srvc_handle; /*!< The service handle */
uint16_t attr_handle; /*!< The attribute handle */
esp_gatt_status_t status; /*!< Operation status*/
} set_attr_val; /*!< Gatt server callback param of ESP_GATTS_SET_ATTR_VAL_EVT */
* @brief ESP_GATTS_SEND_SERVICE_CHANGE_EVT
struct gatts_send_service_change_evt_param{
esp_gatt_status_t status; /*!< Operation status*/
} service_change; /*!< Gatt server callback param of ESP_GATTS_SEND_SERVICE_CHANGE_EVT */
} esp_ble_gatts_cb_param_t;
示例中的gatts_event_handler()回調(diào)函數(shù)---調(diào)用esp_ble_gatts_app_register(1),觸發(fā)ESP_GATTS_REG_EVT時(shí)
1.完成對(duì)每個(gè)profile 的gatts_if 的注冊(cè)
gl_profile_tab[param->reg.app_id].gatts_if = gatts_if;
2.如果gatts_if == 某個(gè)Profile的gatts_if時(shí),調(diào)用對(duì)應(yīng)profile的回調(diào)函數(shù)處理事情。
if (gatts_if == ESP_GATT_IF_NONE||gatts_if == gl_profile_tab[idx].gatts_if) {
if (gl_profile_tab[idx].gatts_cb) {
gl_profile_tab[idx].gatts_cb(event, gatts_if, param);
狀態(tài)機(jī)一般狀態(tài)轉(zhuǎn)變過程:
以gatts_server這個(gè)demo為例,講解GATT狀態(tài)機(jī)的一般過程:
沒有Client連接之前:REGISTER_APP_EVT---->CREATE_SERVICE_EVT---->SERVICE_START_EVT---->ADD_CHAR_EVT--->ADD_DESCR_EVT
有Client開始連接之后:
CONNECT_EVT---->ESP_GATTS_MTU_EVT---> GATT_WRITE_EVT---> ESP_GATTS_CONF_EVT-->GATT_READ_EVT
1>當(dāng)-調(diào)用esp_ble_gatts_app_register()注冊(cè)一個(gè)應(yīng)用程序Profile(Application Profile),觸發(fā)ESP_GATTS_REG_EVT 事件,除了可以完成對(duì)應(yīng)profile的gatts_if的注冊(cè),還可以調(diào)用esp_bel_create_attr_tab() 來創(chuàng)建profile Attributes 表或創(chuàng)建一個(gè)服務(wù)esp_ble_gatts_create_service()
esp_err_t esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t *gatts_attr_db,
作用:創(chuàng)建一個(gè)服務(wù)Attribute表。
參數(shù)
gatts_attr_db :指向加入profile的服務(wù) attr 表 (從Service 到 Characteristic....)
gatts_if: GATT服務(wù)器的訪問接口
max_nb_attr: 加入服務(wù)數(shù)據(jù)庫(kù)的attr的數(shù)目
srvc_inst_id: 服務(wù)instance
esp_attr_control_t attr_control; /*!< The attribute control type */
esp_attr_desc_t att_desc; /*!< The attribute type */
對(duì)于結(jié)構(gòu)體esp_gatts_attr_db_t的成員attr_control的可取值
#define ESP_GATT_RSP_BY_APP 0 //由應(yīng)用程序回復(fù)寫入\讀取操作應(yīng)答
#define ESP_GATT_AUTO_RSP 1 //由GATT堆棧自動(dòng)回復(fù)吸入\讀取操作應(yīng)答
成員 att_desc的結(jié)構(gòu)體類型
* @brief Attribute description (used to create database)
uint16_t uuid_length; /*!< UUID length */
uint8_t *uuid_p; /*!< UUID value */
uint16_t perm; /*!< Attribute permission */
uint16_t max_length; /*!< Maximum length of the element*/
uint16_t length; /*!< Current length of the element*/
uint8_t *value; /*!< Element value array*/
以心率計(jì)Profile為例說明:
esp_ble_gatts_create_attr_tab(heart_rate_gatt_db, gatts_if,
HRS_IDX_NB, HEART_RATE_SVC_INST_ID);
詳細(xì)的heart_rate_gatt_db
/// Full HRS Database Description - Used to add attributes into the database
static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] =
// Heart Rate Service Declaration
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
sizeof(uint16_t), sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}},
// Heart Rate Measurement Characteristic Declaration
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_notify}},
// Heart Rate Measurement Characteristic Value
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ,
HRPS_HT_MEAS_MAX_LEN,0, NULL}},
// Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor
[HRS_IDX_HR_MEAS_NTF_CFG] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
sizeof(uint16_t),sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}},
// Body Sensor Location Characteristic Declaration
[HRS_IDX_BOBY_SENSOR_LOC_CHAR] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read}},
// Body Sensor Location Characteristic Value
[HRS_IDX_BOBY_SENSOR_LOC_VAL] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&body_sensor_location_uuid, ESP_GATT_PERM_READ_ENCRYPTED,
sizeof(uint8_t), sizeof(body_sensor_loc_val), (uint8_t *)body_sensor_loc_val}},
// Heart Rate Control Point Characteristic Declaration
[HRS_IDX_HR_CTNL_PT_CHAR] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE,CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write}},
// Heart Rate Control Point Characteristic Value
[HRS_IDX_HR_CTNL_PT_VAL] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_ctrl_point, ESP_GATT_PERM_WRITE_ENCRYPTED|ESP_GATT_PERM_READ_ENCRYPTED,
sizeof(uint8_t), sizeof(heart_ctrl_point), (uint8_t *)heart_ctrl_point}},
上面的att 表有一個(gè)service、三個(gè)characteristic。
........
先把建立連接之前的GATT狀態(tài)機(jī)搞清楚 。
3 建立連接之前的GATT狀態(tài)機(jī)
3.1 創(chuàng)建服務(wù) creating services
在觸發(fā)ESP_GATTS_REG_EVT時(shí),除了創(chuàng)建表還可以創(chuàng)建服務(wù)S,調(diào)用esp _ble_gatts_create_service來創(chuàng)建服務(wù)S。
esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if,
esp_gatt_srvc_id_t *service_id, uint16_t num_handle);
作用:創(chuàng)建一個(gè)service。當(dāng)一個(gè)service創(chuàng)建成功(done)后,ESP_CREATE_SERVICE_EVT 事件觸發(fā)回調(diào)函數(shù)被調(diào)用,該回調(diào)函數(shù)報(bào)告了profile的stauts和service ID 。當(dāng)要添加include service和 characteristics/descriptors入服務(wù)service,Service ID在回調(diào)函數(shù)中用到。
參數(shù):gatts_if——GATT 服務(wù)器訪問接口
service_id: 服務(wù)UUID相關(guān)信息
num_handle:該服務(wù)所需的句柄數(shù) service handle、characteristic declaration handle、 characteristic value handle、characteristic description handle 的句柄數(shù)總和
查看參數(shù)service_id的類型
#define ESP_UUID_LEN_16 2
#define ESP_UUID_LEN_32 4
#define ESP_UUID_LEN_128 16
uint16_t len; /*!< UUID length, 16bit, 32bit or 128bit */
uint8_t uuid128[ESP_UUID_LEN_128];
} __attribute__((packed)) esp_bt_uuid_t;
* @brief Gatt id, include uuid and instance id
esp_bt_uuid_t uuid; /*!< UUID */
uint8_t inst_id; /*!< Instance id */
} __attribute__((packed)) esp_gatt_id_t;
* @brief Gatt service id, include id
* (uuid and instance id) and primary flag
esp_gatt_id_t id; /*!< Gatt id, include uuid and instance */
bool is_primary; /*!< This service is primary or not */
} __attribute__((packed)) esp_gatt_srvc_id_t;
服務(wù)ID包括
is_primary參數(shù)當(dāng)前服務(wù)是否是首要的;---------服務(wù)聲明中的Attribute Type 0x2800---Primary/0x2801---Secondary
id參數(shù)UUID的信息(包括uuid 和實(shí)例instance) ---------uuid是UUID的信息包括UUID的長(zhǎng)度(16bit/32bit/128bit)及UUID具體值。
例如:服務(wù)被定義為16bit的UUID的主服務(wù)。服務(wù)ID以實(shí)例ID為0,UUID為0x00FF來初始化。
服務(wù)實(shí)例ID是用來區(qū)分在同一個(gè)Profile中 具有相同UUID的多個(gè)服務(wù)。Application Profile中擁有相同UUID的兩個(gè)服務(wù),需要用不同的實(shí)例ID來引用。
如下示例展示了一個(gè)Service的創(chuàng)建。
ESP_LOGI(GATTS_TAG, "REGISTER_APP_EVT, status %d, app_id %d\n", param->reg.status, param->reg.app_id);
gl_profile_tab[PROFILE_B_APP_ID].service_id.is_primary = true;
gl_profile_tab[PROFILE_B_APP_ID].service_id.id.inst_id = 0x00;
gl_profile_tab[PROFILE_B_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_B_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_B;
esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_B_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_B);
3.2 啟動(dòng)服務(wù)并創(chuàng)建Characteristics
當(dāng)一個(gè)服務(wù)service創(chuàng)建成功(done)后,由該profile GATT handler 管理的 ESP_GATTS_CREATE_EVT事件被觸發(fā),在 這個(gè)事件可以啟動(dòng)服務(wù)和添加characteristics到服務(wù) 中。調(diào)用esp_ble_gatts_start_service來啟動(dòng)指定服務(wù)。
Characteristic是在GATT規(guī)范中最小的邏輯數(shù)據(jù)單元,由一個(gè)Value和多個(gè)描述特性的Desciptior組成。實(shí)際上,在與藍(lán)牙設(shè)備打交道,主要就是讀寫Characteristic的value 來完成。同樣的,Characteristic也是通過16bit或128bit的UUID唯一標(biāo)識(shí) 。
我們根據(jù)藍(lán)牙設(shè)備的協(xié)議用對(duì)應(yīng)的Characteristci進(jìn)行讀寫即可達(dá)到與其通信的目的。
ESP_GATTS_CREATE_EVT 事件中回調(diào)函數(shù)參數(shù)的類型為gatts_create_evt_param(包括操作函數(shù)、servic的句柄、服務(wù)的id<UUID+其他信息>) 如下所示。
* @brief ESP_GATTS_CREATE_EVT
struct gatts_create_evt_param {
esp_gatt_status_t status; /*!< Operation status */
uint16_t service_handle; /*!< Service attribute handle */
esp_gatt_srvc_id_t service_id; /*!< Service id, include service uuid and other information */
} create; /*!< Gatt server callback param of ESP_GATTS_CREATE_EVT */
esp_err_t esp_ble_gatts_start_service(uint16_t service_handle)
esp_ble_gatts_start_service ()這個(gè)函數(shù)啟動(dòng)一個(gè)服務(wù)。
參數(shù): service_handle-------要被啟動(dòng)的服務(wù)句柄。
首先,由BLE堆棧生成生成的服務(wù)句柄(service handle)存儲(chǔ)在Profile表中,應(yīng)用層將用服務(wù)句柄來引用這個(gè)服務(wù)。調(diào)用esp_ble_gatts_start_service ()和先前產(chǎn)生服務(wù)句柄來啟動(dòng)服務(wù)。
這樣ESP_ATTS_START_EVT事件觸發(fā)時(shí),將打印輸出信息啟動(dòng)的Service Handle之類的信息 。
添加特征到service中,調(diào)用esp_ble_gatts_add_char ()來添加characteristics連同characteristic 權(quán)限和property到服務(wù)service中。
有必要再次解釋一下property
Characteristic Properties這個(gè)域(bit控制)決定了Characteristic Value如何使用、Characteristic descriptors 如何訪問。只要下標(biāo)中對(duì)應(yīng)bit被設(shè)備,那么對(duì)應(yīng)描述的action就被允許。
3.2.1 添加Characteristic Value declaration ATT-------看這里
添加Characteristic的接口
esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_uuid,
esp_gatt_perm_t perm, esp_gatt_char_prop_t property, esp_attr_value_t *char_val, esp_attr_control_t *control)
參數(shù):service_handle-------Characteristic要添加到的服務(wù)的Service handler服務(wù)句柄,一個(gè)Characteristic至少包括2個(gè)屬性ATT,一個(gè)屬性用于characteristic declaration/另一個(gè)用于存放特征值(characteristic value declaration).
char_uuid-------Characteristic 的UUID; 屬于Characteristic declaration 這個(gè)ATT
perm------特征值聲明(Characteristic value declaration) 屬性(Attribute) 訪問權(quán)限 ;
ATT具有一組與其關(guān)聯(lián)的權(quán)限值,權(quán)限值指定了讀/寫權(quán)限、認(rèn)證權(quán)限、授權(quán)許可
permission權(quán)限的可取值{可讀、可加密讀、可加密MITM讀、可寫、可加密寫、可加密MITM寫}
* @brief Attribute permissions
#define ESP_GATT_PERM_READ (1 << 0) /* bit 0 - 0x0001 */ /* relate to BTA_GATT_PERM_READ in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_READ_ENCRYPTED (1 << 1) /* bit 1 - 0x0002 */ /* relate to BTA_GATT_PERM_READ_ENCRYPTED in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_READ_ENC_MITM (1 << 2) /* bit 2 - 0x0004 */ /* relate to BTA_GATT_PERM_READ_ENC_MITM in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE (1 << 4) /* bit 4 - 0x0010 */ /* relate to BTA_GATT_PERM_WRITE in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE_ENCRYPTED (1 << 5) /* bit 5 - 0x0020 */ /* relate to BTA_GATT_PERM_WRITE_ENCRYPTED in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE_ENC_MITM (1 << 6) /* bit 6 - 0x0040 */ /* relate to BTA_GATT_PERM_WRITE_ENC_MITM in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE_SIGNED (1 << 7) /* bit 7 - 0x0080 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED in bta/bta_gatt_api.h */
#define ESP_GATT_PERM_WRITE_SIGNED_MITM (1 << 8) /* bit 8 - 0x0100 */ /* relate to BTA_GATT_PERM_WRITE_SIGNED_MITM in bta/bta_gatt_api.h */
property-----Characteristic Properties (特征值聲明屬性的Properties)
char_val------屬性值 Characteristic Value
control-------屬性響應(yīng)控制字節(jié)
characteristic declaration的Attribute Value 包括 property 、characteristic Value declaration handle、char_uuid 三個(gè)段;其中property、char_uuid在添加Characteristic調(diào)用的函數(shù)的參數(shù)中已經(jīng)指明,只有characteristic Value declaration handle 尚未明確指出。
示例:
gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle;
gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_A;
esp_ble_gatts_add_char(gl_profile_tab[PROFILE_A_APP_ID].service_handle,
&gl_profile_tab[PROFILE_A_APP_ID].char_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
characteristic value------這個(gè)屬性ATT怎么添加入Characteristic中,看gatts_demo_char1_val的具體部分
* @brief set the attribute value type
uint16_t attr_max_len; /*!< attribute max value length */
uint16_t attr_len; /*!< attribute current value length */
uint8_t *attr_value; /*!< the pointer to attribute value */
uint8_t char1_str[] = {0x11,0x22,0x33};
esp_attr_value_t gatts_demo_char1_val =
.attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(char1_str),
在這個(gè)結(jié)構(gòu)體里面包括了屬性值最大長(zhǎng)度、當(dāng)前長(zhǎng)度、當(dāng)前值。由這個(gè)來指定 characteristic value declaration的。有這些值就足以構(gòu)成一個(gè) characteristic value declaration ATT了 。
3.3 添加Characteristic descriptor 描述符
當(dāng)特征添加到service中成功(done)時(shí),觸發(fā)ESP_GATTS_ADD_CHAR_EVT 事件。觸發(fā)ESP_GATTS_ADD_CHAR_EVT 事件時(shí),回調(diào)函數(shù)參數(shù)param的結(jié)構(gòu)體為gatts_add_char_evt_param,包括操作狀態(tài)、特征ATT的handle()、service_handle(服務(wù)句柄)、characteristc uuid(服務(wù)的UUID) 。
* @brief ESP_GATTS_ADD_CHAR_EVT
struct gatts_add_char_evt_param {
esp_gatt_status_t status; /*!< Operation status */
uint16_t attr_handle; /*!< Characteristic attribute handle */
uint16_t service_handle; /*!< Service attribute handle */
esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */
} add_char; /*!< Gatt server callback param of ESP_GATTS_ADD_CHAR_EVT */
還可以通過調(diào)用esp_ble_gatts_get_attr_value()來獲取跟具體的Characteristic Value declartation 屬性的具體信息。
下面是調(diào)用的例子,輸入?yún)?shù)是特征句柄;輸出參數(shù)是length和prf_char
esp_ble_gatts_get_attr_value(param->add_char.attr_handle, &length, &prf_char);
查看esp_ble_gatts_get_attr_value() 源碼,進(jìn)行分析
esp_err_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, const uint8_t **value)
if (attr_handle == ESP_GATT_ILLEGAL_HANDLE) {
btc_gatts_get_attr_value(attr_handle, length, (uint8_t **)value);
void btc_gatts_get_attr_value(uint16_t attr_handle, uint16_t *length, uint8_t **value)
BTA_GetAttributeValue(attr_handle, length, value);
void BTA_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value)
bta_gatts_get_attr_value(attr_handle, length, value);
void bta_gatts_get_attr_value(UINT16 attr_handle, UINT16 *length, UINT8 **value)
GATTS_GetAttributeValue(attr_handle, length, value);
/*******************************************************************************
** Function GATTS_GetAttributeValue
** Description This function sends to set the attribute value .
** Parameter attr_handle: the attribute handle
** length:the attribute value length in the database
** value: the attribute value out put
** Returns GATT_SUCCESS if successfully sent; otherwise error code.
*******************************************************************************/
tGATT_STATUS GATTS_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value)
tGATT_HDL_LIST_ELEM *p_decl;
GATT_TRACE_DEBUG("GATTS_GetAttributeValue: attr_handle: %u\n",
if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) {
GATT_TRACE_ERROR("Service not created\n");
return GATT_INVALID_HANDLE;
status = gatts_get_attribute_value(&p_decl->svc_db, attr_handle, length, value);
關(guān)于gatts_get_attribute_value()更底層的東西就要看ble協(xié)議棧的更底層的實(shí)現(xiàn),這里很難查下去。但是可以通過打印輸出來判斷這里實(shí)現(xiàn)些什么。
有上述打印可以看出,這里輸出Characteristic value declaration的信息,因此esp_ble_gatts_get_attr_value()來輸出特征值長(zhǎng)度和特征值。
還可在ESP_GATTS_ADD_CHAR_EVT事件的回調(diào)函數(shù)中,給characteristic添加characteristic description ATT。
下面是添加char_descr的例子
gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle;
gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_A_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
esp_err_t add_descr_ret = esp_ble_gatts_add_char_descr(
gl_profile_tab[PROFILE_A_APP_ID].service_handle, &gl_profile_tab[PROFILE_A_APP_ID].descr_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, NULL, NULL);
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
gl_profile_tab[PROFILE_A_APP_ID].descr_handle = param->add_char_descr.attr_handle;
ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d\n",
param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle);
看看esp_ble_gatts_add_char_descr()這個(gè)函數(shù)的原型
esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle,
esp_bt_uuid_t *descr_uuid,
esp_gatt_perm_t perm, esp_attr_value_t *char_descr_val,
esp_attr_control_t *control);
參數(shù):service_handle:這個(gè)characteristic descriptor要添加的service handle。
perm: 描述符訪問權(quán)限
descr_uuid:描述符UUID
char_descr_val:描述符值
control:ATT 應(yīng)答控制字節(jié)
這個(gè)函數(shù)被用來添加Characteristic descriptor。當(dāng)添加完成時(shí),BTA_GATTS_ADD_DESCR_EVT 回調(diào)函數(shù)被調(diào)用去報(bào)告它的狀態(tài)和ID。
gatt_server例子中:一共建立兩個(gè)profile。A profile中包括的service UUID為0x00FF, characteristic UUID為0xFF01;
B profile中包括的service UUID為0x00EE, characteristic UUID為0xEE01
下面看是看開始建立連接后,GATT的狀態(tài)機(jī)轉(zhuǎn)變過程:
手機(jī)client連接到server,觸發(fā)ESP_GATTS_CONNECT_EVT事件 。
* @brief ESP_GATTS_CONNECT_EVT
struct gatts_connect_evt_param {
uint16_t conn_id; /*!< Connection id */
esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */
} connect; /*!< Gatt server callback param of ESP_GATTS_CONNECT_EVT */
/// Bluetooth address length
#define ESP_BD_ADDR_LEN 6
/// Bluetooth device address
typedef uint8_t esp_bd_addr_t[ESP_BD_ADDR_LEN];
ESP_GATTS_CONNECT_EVT事件回調(diào)函數(shù)param參數(shù)包括連接ID以及遠(yuǎn)端藍(lán)牙設(shè)備地址。調(diào)用esp_ble_update_conn_params(&conn_params)來更新連接參數(shù)。這個(gè)函數(shù)只有在連接上以后才可以調(diào)用。
esp_ble_conn_update_params_t conn_params = {0};
memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
/* For the IOS system, please reference the apple official documents about the ble connection parameters restrictions. */
conn_params.max_int = 0x20; // max_int = 0x20*1.25ms = 40ms
conn_params.min_int = 0x10; // min_int = 0x10*1.25ms = 20ms
conn_params.timeout = 400; // timeout = 400*10ms = 4000ms
esp_ble_gap_update_conn_params(&conn_params);
參數(shù)連接更新參數(shù)(藍(lán)牙設(shè)備地址、最小連接間隔、最大連接間隔、連接數(shù)量、LE LINK超時(shí))。
/// Connection update parameters
esp_bd_addr_t bda; /*!< Bluetooth device address */
uint16_t min_int; /*!< Min connection interval */
uint16_t max_int; /*!< Max connection interval */
uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */
uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80.
Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec
Time Range: 100 msec to 32 seconds */
} esp_ble_conn_update_params_t;
4 建立連接時(shí)GATT的狀態(tài)機(jī)研究
設(shè)備設(shè)備連上外圍藍(lán)牙時(shí),打印如下所示:第一個(gè)觸發(fā)了CONNECT回調(diào)函數(shù);接著觸發(fā)了MTU尺寸的大小確認(rèn)事件。
4.1連接后觸發(fā)的連接回調(diào)函數(shù)
* @brief ESP_GATTS_CONNECT_EVT
struct gatts_connect_evt_param {
uint16_t conn_id; /*!< Connection id */
esp_bd_addr_t remote_bda; /*!< Remote bluetooth device address */
上面是連接回調(diào)函數(shù)param的參數(shù)結(jié)構(gòu)體,包括連接id和遠(yuǎn)端(對(duì)端)藍(lán)牙設(shè)備地址(bda)。
在一般回調(diào)函數(shù)處理中,記錄對(duì)端的信息,且發(fā)送更新后的連接參數(shù)到對(duì)端設(shè)備 。
//start sent the update connection parameters to the peer device.
esp_ble_gap_update_conn_params(&conn_params);
深入到 esp_ble_gap_update_conn_params(&conn_params)。這個(gè)函數(shù)只能在連接上時(shí)使用,用于更新連接參數(shù)。
* @brief Update connection parameters, can only be used when connection is up.
* @param[in] params - connection update parameters
/// Connection update parameters
esp_bd_addr_t bda; /*!< Bluetooth device address */
uint16_t min_int; /*!< Min connection interval */
uint16_t max_int; /*!< Max connection interval */
uint16_t latency; /*!< Slave latency for the connection in number of connection events. Range: 0x0000 to 0x01F3 */
uint16_t timeout; /*!< Supervision timeout for the LE Link. Range: 0x000A to 0x0C80.
Mandatory Range: 0x000A to 0x0C80 Time = N * 10 msec
Time Range: 100 msec to 32 seconds */
} esp_ble_conn_update_params_t;
esp_err_t esp_ble_gap_update_conn_params(esp_ble_conn_update_params_t *params)
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_GAP_BLE;
msg.act = BTC_GAP_BLE_ACT_UPDATE_CONN_PARAM;
memcpy(&arg.conn_update_params.conn_params, params, sizeof(esp_ble_conn_update_params_t));
return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gap_args_t), NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
繼續(xù)深入bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg_deep_copy_t copy_func)
bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg_deep_copy_t copy_func)
return BT_STATUS_PARM_INVALID;
BTC_TRACE_DEBUG("%s msg %u %u %u %p\n", __func__, msg->sig, msg->pid, msg->act, arg);
memcpy(&lmsg, msg, sizeof(btc_msg_t));
lmsg.arg = (void *)osi_malloc(arg_len);
memset(lmsg.arg, 0x00, arg_len); //important, avoid arg which have no length
memcpy(lmsg.arg, arg, arg_len);
copy_func(&lmsg, lmsg.arg, arg);
return btc_task_post(&lmsg, TASK_POST_BLOCKING);
到此,不繼續(xù)下去。
4.2連接后MTU大小確定
當(dāng)有手機(jī)(client客戶端)連上server時(shí),觸發(fā)ESP_GATTS_MTU_EVT事件,其打印如下圖所示
ESP_GATTS_MTU_EVT事件對(duì)應(yīng)的回調(diào)函數(shù)中參數(shù)param的結(jié)構(gòu)體為gatts_mtu_evt_param(包括連接id和MTU大?。?/p>
* @brief ESP_GATTS_MTU_EVT
struct gatts_mtu_evt_param {
uint16_t conn_id; /*!< Connection id */
uint16_t mtu; /*!< MTU size */
} mtu; /*!< Gatt server callback param of ESP_GATTS_MTU_EVT */
在例子中設(shè)置本地的MTU大小為500,代碼如下所示:
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
如上所述,設(shè)置了MTU的值(經(jīng)過MTU交換,從而設(shè)置一個(gè)PDU中最大能夠交換的數(shù)據(jù)量)。例如:主設(shè)備發(fā)出一個(gè)150字節(jié)的MTU請(qǐng)求,但是從設(shè)備回應(yīng)的MTU是23字節(jié),那么今后雙方要以較小的值23字節(jié)作為以后的MTU。即主從雙方每次在做數(shù)據(jù)傳輸時(shí)不超過這個(gè)最大數(shù)據(jù)單元 。 MTU交換通常發(fā)生在主從雙方建立連接后。MTU比較小,就是為什么BLE不能傳輸大數(shù)據(jù)的原因所在。
-----參照一分鐘讀懂低功耗(BLE)MTU交換數(shù)據(jù)包https://blog.csdn.net/viewtoolsz/article/details/76177465 這篇文章就可以了解MTU交換過程。
MTU交換請(qǐng)求 用于client通知server關(guān)于client最大接收MTU大小并請(qǐng)求server響應(yīng)它的最大接收MTU大小。
Client的接收MTU 應(yīng)該大于或等于默認(rèn)ATT_MTU(23).這個(gè)請(qǐng)求已建立連接就由client發(fā)出。這個(gè)Client Rx MTU參數(shù)應(yīng)該設(shè)置為client可以接收的attribute protocol PDU最大尺寸。
MTU交換應(yīng)答發(fā)送用于接收到一個(gè)Exchange MTU請(qǐng)求
這個(gè)應(yīng)答由server發(fā)出,server的接收MTU必須大于或等于默認(rèn)ATT_MTU大小。這里的Server Rx MTU應(yīng)該設(shè)置為 服務(wù)器可以接收的attribute protocol PDU 最大尺寸。
Server和Client應(yīng)該設(shè)置ATT_MTU為Client Rx MTU和Server Rx MTU兩者的較小值。
這個(gè)ATT_MTU在server在發(fā)出這個(gè)應(yīng)答后,在發(fā)其他屬性協(xié)議PDU之前生效;在client收到這個(gè)應(yīng)答并在發(fā)其他屬性協(xié)議PDU之前生效。
4.3 發(fā)送應(yīng)答數(shù)據(jù)
#if (GATTS_INCLUDED == TRUE)
esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id,
esp_gatt_status_t status, esp_gatt_rsp_t *rsp);
btc_ble_gatts_args_t arg;
ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED);
msg.sig = BTC_SIG_API_CALL;
msg.act = BTC_GATTS_ACT_SEND_RESPONSE;
arg.send_rsp.conn_id = BTC_GATT_CREATE_CONN_ID(gatts_if, conn_id);
arg.send_rsp.trans_id = trans_id;
arg.send_rsp.status = status;
return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_gatts_args_t),
btc_gatts_arg_deep_copy) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);
這個(gè)函數(shù)用于發(fā)送應(yīng)答給對(duì)應(yīng)請(qǐng)求。
參數(shù): gatts_if-------GATT server 訪問接口
conn_id-----連接ID
trans_id-----傳輸ID
status----應(yīng)答狀態(tài)
rsp-----應(yīng)答數(shù)據(jù) gatt attribute value
查看GATT 讀應(yīng)答結(jié)構(gòu)
#define ESP_GATT_MAX_ATTR_LEN 600 //as same as GATT_MAX_ATTR_LEN
uint8_t value[ESP_GATT_MAX_ATTR_LEN]; /*!< Gatt attribute value */
uint16_t handle; /*!< Gatt attribute handle */
uint16_t offset; /*!< Gatt attribute value offset */
uint16_t len; /*!< Gatt attribute value length */
uint8_t auth_req; /*!< Gatt authentication request */
/// GATT remote read request response type
esp_gatt_value_t attr_value; /*!< Gatt attribute structure */
uint16_t handle; /*!< Gatt attribute handle */
4.4 客戶端和服務(wù)器端 收發(fā)數(shù)據(jù)
4.4.1 手機(jī)端的操作
接下去看,連接以后收發(fā)數(shù)據(jù)
首先了解一下手機(jī)端在藍(lán)牙連接、數(shù)據(jù)交互過程中的操作。
作者:WuTa0
鏈接:https://www.jianshu.com/p/f8130a0bfd94
檢測(cè)藍(lán)牙是否可用,綁定藍(lán)牙服務(wù)
使用BluetoothAdapter.startLeScan來掃描 低功耗藍(lán)牙設(shè)備
在掃描到設(shè)備的 回調(diào)函數(shù)中會(huì)得到BluetoothDevice對(duì)象 ,并使用BluetoothAdapter.stopLeScan停止掃描
使用BluetoothDevice.connectGatt來 獲取到BluetoothGatt對(duì)象 /*****************************************************************************************************************************************/
執(zhí)行BluetoothGatt.discoverServices,這個(gè)方法是異步操作,在回調(diào)函數(shù)onServicesDiscovered中得到status, 通過判斷status是否等于BluetoothGatt.GATT_SUCCESS來判斷 查找服務(wù)Service 是否成功
如果成功了,則通過BluetoothGatt.getServices()來獲取所有的 Services ;根據(jù)Sevice_UUID來查找想要的服務(wù) BluetoothGattService
接著通過BluetoothGattService.getCharacteristic(spiecial_char_UUID)獲取BluetoothGattCharacteristic,最基本的一般獲取 Read_UUID 的Charcteristic、 Write_UUID 的Characteristic、 Notification_UUID 的Characteristic。
設(shè)置通知打開------通知打開或關(guān)閉實(shí)際山是一次寫入操作
然后通過BluetoothGattCharacteristic. getDescriptor 獲取 BluetoothGattDescriptor
對(duì)于發(fā)送和接收數(shù)據(jù) 都是從BluetoothGatt.readCharacteristic和BluetoothGatt.writeCharcteristic來實(shí)現(xiàn)。
注意:在寫時(shí)參數(shù)characteristic,調(diào)用setValue設(shè)置Characteristic的屬性值,調(diào)用setWriteType設(shè)置寫的方式(如WRITR_TYPE_NO_RESPONSE)
第3步掃描到設(shè)備會(huì)觸發(fā)回調(diào),在回調(diào)中,通常根據(jù)設(shè)備名稱找到想要連接的設(shè)備,完成連接;連接后,獲取到BluetoothGATT 對(duì)象,再?gòu)腂luetoothGatt.discoverServices中獲取各個(gè)Services ,根據(jù)硬件工程師提供的UUID連接你需要的Service 。最后,根據(jù)硬件工程師提供的UUID找到讀、寫和通知 的UUID,然后再進(jìn)行讀寫操作。 讀寫和設(shè)置通知 均為單步操作,執(zhí)行完一個(gè)才能執(zhí)行下一個(gè)。
從這里出發(fā),現(xiàn)在仍感覺不是很痛快,因此我從小程序的低功耗藍(lán)牙接口出發(fā),兩相對(duì)照看。
寫特征值
wx.writeBLECharacteristicValue(Object object)
deviceId::string //必填,藍(lán)牙設(shè)備id
serviceId::string //必填,藍(lán)牙特征值Characteristic對(duì)應(yīng)服務(wù)的UUID
characteristicId::string //必填,藍(lán)牙特征值的UUID
value::ArrayBuffer //必填,藍(lán)牙設(shè)備特征值對(duì)應(yīng)的二進(jìn)制值
success::function //非必填,接口調(diào)用成功后的回調(diào)函數(shù)
fail::function //非必填,接口調(diào)用失敗后的回調(diào)函數(shù)
complete::function //非必填,接口調(diào)用結(jié)束后的回調(diào)函數(shù)(調(diào)用成功、失敗都會(huì)執(zhí)行)
向低功耗藍(lán)牙設(shè)備特征值中寫入二進(jìn)制數(shù)據(jù)。 必須設(shè)備的特征值支持write權(quán)限才可以成功調(diào)用。
具體的如下所示,
wx.writeBLECharacteristicValue({
deviceId: that.data.deviceId,//設(shè)備deviceId
serviceId: that.data.service_id,//設(shè)備service_id
characteristicId: that.data.write_id,//設(shè)備write特征值
value: buffer,//寫入數(shù)據(jù)
success: function (res) {
console.log('發(fā)送數(shù)據(jù):', res.errMsg)
原文:https://blog.csdn.net/caohoucheng/article/details/81633822
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!
讀特征值
wx.readBLECharacteristicValue(Object object)
deviceId::string //必填,藍(lán)牙設(shè)備id
serviceId::string //必填,藍(lán)牙特征值對(duì)應(yīng)服務(wù)的UUID
characteristic::string //必填,藍(lán)牙特征值UUID
success::function //非必填,接口調(diào)用成功后回調(diào)函數(shù)
fail::function //非必填,接口調(diào)用失敗后回調(diào)函數(shù)
complete::function //非必填,接口調(diào)用結(jié)束后回調(diào)函數(shù)(調(diào)用成功、失敗都會(huì)執(zhí)行)
具體的如下所示
wx.readBLECharacteristicValue({
// 這里的 deviceId 需要已經(jīng)通過 createBLEConnection 與對(duì)應(yīng)設(shè)備建立鏈接
// 這里的 serviceId 需要在 getBLEDeviceServices 接口中獲取
// 這里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中獲取
console.log('readBLECharacteristicValue:', res.errCode)
小程序上低功耗藍(lán)牙一般操作流程
初始化藍(lán)牙模塊(wx.openBluetoothAdapter)------>開始搜索藍(lán)牙外圍設(shè)備(耗資源)(wx.startBluetoothDevicesDiscovery)----->
獲取在藍(lán)牙模塊生效期間所有已發(fā)現(xiàn)的藍(lán)牙設(shè)備(包括已經(jīng)和本機(jī)處于連接狀態(tài)的設(shè)備)(wx.getBluetoothDevices)----->
監(jiān)聽找到的新設(shè)備的事件(wx.onBluetoothDeviceFound(callback))------->連接低功耗藍(lán)牙設(shè)備(wx.createBLEConnection())------>
獲取藍(lán)牙設(shè)備所有services(wx.getBLEDeviceServices())------>獲取藍(lán)牙設(shè)備某個(gè)服務(wù)中所有Characteristic(特征)(wx.getBLEDeviceCharacteristics)-------->啟動(dòng)低功耗藍(lán)牙設(shè)備特征的值變化時(shí)的notify功能(wx.notifyBLECharacteristicValueChange())------->寫入wx.writeBLECharactericValue()
目前,微信小程序上也應(yīng)該是無應(yīng)答寫 。
4.4.2 設(shè)備端的操作
將ESP_GATTS_READ_EVT、ESP_GATTS_WRITE_EVT和ESP_GATTS_EXEC_WRITE_EVT三類事件param參數(shù)的類型如下
* @brief ESP_GATTS_READ_EVT
struct gatts_read_evt_param {
uint16_t conn_id; /*!< Connection id */
uint32_t trans_id; /*!< Transfer id */
esp_bd_addr_t bda; /*!< The bluetooth device address which been read */
uint16_t handle; /*!< The attribute handle */
uint16_t offset; /*!< Offset of the value, if the value is too long */
bool is_long; /*!< The value is too long or not */
bool need_rsp; /*!< The read operation need to do response */
} read; /*!< Gatt server callback param of ESP_GATTS_READ_EVT */
* @brief ESP_GATTS_WRITE_EVT
struct gatts_write_evt_param {
uint16_t conn_id; /*!< Connection id */
uint32_t trans_id; /*!< Transfer id */
esp_bd_addr_t bda; /*!< The bluetooth device address which been written */
uint16_t handle; /*!< The attribute handle */
uint16_t offset; /*!< Offset of the value, if the value is too long */
bool need_rsp; /*!< The write operation need to do response */
bool is_prep; /*!< This write operation is prepare write */
uint16_t len; /*!< The write attribute value length */
uint8_t *value; /*!< The write attribute value */
} write; /*!< Gatt server callback param of ESP_GATTS_WRITE_EVT */
* @brief ESP_GATTS_EXEC_WRITE_EVT
struct gatts_exec_write_evt_param {
uint16_t conn_id; /*!< Connection id */
uint32_t trans_id; /*!< Transfer id */
esp_bd_addr_t bda; /*!< The bluetooth device address which been written */
#define ESP_GATT_PREP_WRITE_CANCEL 0x00 /*!< Prepare write flag to indicate cancel prepare write */
#define ESP_GATT_PREP_WRITE_EXEC 0x01 /*!< Prepare write flag to indicate execute prepare write */
uint8_t exec_write_flag; /*!< Execute write flag */
4.4.4.1 使能通知
使能notify并讀取藍(lán)牙發(fā)過來的數(shù)據(jù),開啟這個(gè)后我們就能實(shí)時(shí)獲取藍(lán)牙發(fā)過來的值了。
使能通知(notify enable)的打印如下所示,打開通知實(shí)際上的一個(gè)WRITE。對(duì)應(yīng)于手機(jī)端的mBluetoothGatt.setCharacteristicNotification(characteristic,true).
【2018-12-25 16:01:24:307】_[0;32mI (27345) GATTS_DEMO: GATT_WRITE_EVT, conn_id 0, trans_id 3, handle 43_[0m
_[0;32mI (27345) GATTS_DEMO: GATT_WRITE_EVT, value len 2, value :_[0m
_[0;32mI (27345) GATTS_DEMO: 01 00 _[0m
_[0;32mI (27355) GATTS_DEMO: notify enable_[0m
_[0;33mW (27355) BT_BTC: btc_gatts_arg_deep_copy 11, NULL response_[0m
_[0;32mI (27365) GATTS_DEMO: ESP_GATTS_CONF_EVT, status 0 attr_handle 42_[0m
【2018-12-25 16:01:24:610】_[0;32mI (27645) GATTS_DEMO: GATT_WRITE_EVT, conn_id 0, trans_id 4, handle 47
_[0;32mI (27645) GATTS_DEMO: GATT_WRITE_EVT, value len 2, value :_[0m
_[0;32mI (27645) GATTS_DEMO: 01 00 _[0m
_[0;32mI (27655) GATTS_DEMO: notify enable_[0m
_[0;33mW (27655) BT_BTC: btc_gatts_arg_deep_copy 11, NULL response_[0m
_[0;32mI (27665) GATTS_DEMO: ESP_GATTS_CONF_EVT status 0 attr_handle 46_[0m
_[0;32mI (27725) GATTS_DEMO: GATT_READ_EVT, conn_id 0, trans_id 5, handle 42
如果write.handle和descr_handle相同,且長(zhǎng)度==2,確定descr_value描述值,根據(jù)描述值開啟/關(guān)閉 通知notify/indicate。
//the size of notify_data[] need less than MTU size
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_A_APP_ID].char_handle,
sizeof(notify_data), notify_data, false);
深入看看esp_ble_gatts_send_indicate
* @brief Send indicate or notify to GATT client.
* Set param need_confirm as false will send notification, otherwise indication.
* @param[in] gatts_if: GATT server access interface
* @param[in] conn_id - connection id to indicate.
* @param[in] attr_handle - attribute handle to indicate.
* @param[in] value_len - indicate value length.
* @param[in] value: value to indicate.
* @param[in] need_confirm - Whether a confirmation is required.
* false sends a GATT notification, true sends a GATT indication.
esp_err_t esp_ble_gatts_send_indicate(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t attr_handle,
uint16_t value_len, uint8_t *value, bool need_confirm);
該函數(shù)將notify或indicate發(fā)給GATT的客戶端;
need_confirm = false,則發(fā)送的是notification通知;
==true,發(fā)送的是指示indication。
其他參數(shù): 服務(wù)端訪問接口;連接id; 屬性句柄,value_len; 值
4.4.4.2 讀寫數(shù)據(jù)
看看Write 和Read 觸發(fā)的回調(diào)函數(shù);讀寫打印如下所示:
【2019-01-02 10:37:09:539】[0;32mI (4697198) GATTS_DEMO: GATT_WRITE_EVT, conn_id 0, trans_id 11, handle 42[0m
[0;32mI (4697198) GATTS_DEMO: GATT_WRITE_EVT, value len 3, value :[0m
[0;32mI (4697208) GATTS_DEMO: 31 32 33 [0m
[0;33mW (4697208) BT_BTC: btc_gatts_arg_deep_copy 11, NULL response[0m
[0;32mI (4697278) GATTS_DEMO: GATT_READ_EVT, conn_id 0, trans_id 12, handle 42
/*************************************************************************************************************************************/
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d", param->write.conn_id, param->write.trans_id, param->write.handle);
if (!param->write.is_prep){
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len);
esp_log_buffer_hex(GATTS_TAG, param->write.value, param->write.len);
example_write_event_env(gatts_if, &a_prepare_write_env, param);
深入example_write_event_env(),其核心代碼如下 esp_ble_gatts_send_response函數(shù)
if (param->write.need_rsp){
esp_err_t esp_ble_gatts_send_response(esp_gatt_if_t gatts_if, uint16_t conn_id, uint32_t trans_id,
esp_gatt_status_t status, esp_gatt_rsp_t *rsp);
根據(jù)param->write.need_rsp是否需要應(yīng)答來決定,是否調(diào)用esp_ble_gatts_send_response來應(yīng)答。
而esp_ble_gatts_send_response()函數(shù)實(shí)際調(diào)用了btc_transfer_context,將信息發(fā)送出去。
need_rsp如何確定這個(gè)值,我很有疑問,看到這里的看客,知道的請(qǐng)回答我一下 。
/***********************************************************************************************************************/
先把Write搞清楚,先看看寫的事件參數(shù)
* @brief ESP_GATTS_WRITE_EVT
struct gatts_write_evt_param {
uint16_t conn_id; /*!< Connection id */
uint32_t trans_id; /*!< Transfer id */
esp_bd_addr_t bda; /*!< The bluetooth device address which been written */
uint16_t handle; /*!< The attribute handle */
uint16_t offset; /*!< Offset of the value, if the value is too long */
bool need_rsp; /*!< The write operation need to do response */
bool is_prep; /*!< This write operation is prepare write */
uint16_t len; /*!< The write attribute value length */
uint8_t *value; /*!< The write attribute value */
這里的is_prep 是針對(duì)單獨(dú)一個(gè)Write Request Attribute Protocol 信息放不下的情況,即Write Long Characteristic;一般先prepare在excute; 由這個(gè)參數(shù)確定參數(shù)的是長(zhǎng)包還是短包。
offset 是這包數(shù)據(jù)相對(duì)長(zhǎng)數(shù)據(jù)的偏移,第一包是0x0000;
need_rsp是針對(duì)該寫操作是否需要應(yīng)答response。
value就是待寫入的Characteristic Value的值。
目前,我弄的是Write Without Response 。
/*****************************************************************************************************************************************/
接下去看看Read,看看讀的事件參數(shù)
* @brief ESP_GATTS_READ_EVT
struct gatts_read_evt_param {
uint16_t conn_id; /*!< Connection id */
uint32_t trans_id; /*!< Transfer id */
esp_bd_addr_t bda; /*!< The bluetooth device address which been read */
uint16_t handle; /*!< The attribute handle */
uint16_t offset; /*!< Offset of the value, if the value is too long */
bool is_long; /*!< The value is too long or not */
bool need_rsp; /*!< The read operation need to do response */
} read; /*!< Gatt server callback param of ESP_GATTS_READ_EVT */
is_long針對(duì)客戶端知道特征值句柄和特征值長(zhǎng)度大于單獨(dú)一個(gè)Read Response ATT 信息大小時(shí),表示傳輸?shù)氖情L(zhǎng)數(shù)據(jù),就用Read Blob Request。
handle 就是要讀的Characteristic Value的句柄;
offset 就是要讀的Characteristic Value偏移位置,第一包Read Blob Request時(shí),offset為0x00;
對(duì)應(yīng)的是Read Blob Response ATT 以一部分的Characteristic Value作為ATT Value 參數(shù)。
在看看ESP_IDF中關(guān)于read Response 的結(jié)構(gòu)體。
uint8_t value[ESP_GATT_MAX_ATTR_LEN]; /*!< Gatt attribute value */
uint16_t handle; /*!< Gatt attribute handle */
uint16_t offset; /*!< Gatt attribute value offset */
uint16_t len; /*!< Gatt attribute value length */
uint8_t auth_req; /*!< Gatt authentication request */
/// GATT remote read request response type
esp_gatt_value_t attr_value; /*!< Gatt attribute structure */
uint16_t handle; /*!< Gatt attribute handle */
在我的項(xiàng)目中,客戶端先寫再讀。
4.5 Characteristic結(jié)構(gòu)
再次對(duì)Characteristic的結(jié)構(gòu)分析一遍,記不住啊。
Characteristic 聲明、Characteistic Value 聲明、Characteristic Descriptor 聲明。
5. 數(shù)據(jù)交互其他必要知道的
權(quán)限對(duì)于數(shù)據(jù)交換,通常設(shè)置有讀Characteristic 和寫Characteristic ----------- Write_SSID/Read_SSID.
Read/Write讀/寫-----一次會(huì)同時(shí)出發(fā)讀和寫兩個(gè)回調(diào)事件;如上打印結(jié)果所述。
Read Only只讀-------只能觸發(fā)讀回調(diào)事件;
Write Only只寫------只能觸發(fā)寫回調(diào)事件;
6.結(jié)束語
關(guān)于GATT就先告一個(gè)段落,其他還有很多不足一處,需要一一修正補(bǔ)充。如果上述內(nèi)容有其他問題,請(qǐng)指正,及時(shí)糾正我。在此謝謝大家。