本篇文档用于说明如何基于 ESP32 自定义 BLE 服务,文档中协议相关的内容基于 Core 4.2 specification。
1 ATT 和 GATT
2 结合 gatt_server_service_table 分析
3 AT 固件自定义蓝牙服务
4 常见问题
1 ATT 和 GATT
ATT(attribute protocol): 属性协议定义了两个角色:server 和 client。属性协议允许服务器向客户端公开一组属性及其相关值。 服务器公开的这些属性可以被客户端发现、读取和写入,并且可以由服务器指示(indication)和通知(notification)。
GATT(Generic Attribute Profile): GATT 用来规范 attribute 中的数据内容, 并运用 group(分组)的概念对 attribute 进行分类管理。 没有GATT,BLE 协议栈也能跑, 但互联互通就会出问题。
ATT 和 GATT : 站在蓝牙协议栈角度来看,ATT 层定义了一个通信的基本框架,数据的基本结构,以及通信的指令,而 GATT 层用来赋予每个数据一个具体的内涵,让数据变得有结构和意义。换句话说,没有 GATT 层,低功耗蓝牙也可以通信起来,但会产生兼容性问题以及通信的低效率。
有关 ATT 和 GATT 的详细介绍可以参考链接。
2 结合 gatt_server_service_table 分析
本章节结合 gatt_server_service_table 示例说明如何通过属性表来定义 GATT 服务。 示例中完整的属性表定义如下:
/* Full Database Description - Used to add attributes into the database */
static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] =
{
// Service Declaration
[IDX_SVC] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
sizeof(uint16_t), sizeof(GATTS_SERVICE_UUID_TEST), (uint8_t *)&GATTS_SERVICE_UUID_TEST}},
/* Characteristic Declaration */
[IDX_CHAR_A] =
{{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_notify}},
/* Characteristic Value */
[IDX_CHAR_VAL_A] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_A, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
/* Client Characteristic Configuration Descriptor */
[IDX_CHAR_CFG_A] =
{{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}},
/* Characteristic Declaration */
[IDX_CHAR_B] =
{{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}},
/* Characteristic Value */
[IDX_CHAR_VAL_B] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_B, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
/* Characteristic Declaration */
[IDX_CHAR_C] =
{{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_write}},
/* Characteristic Value */
[IDX_CHAR_VAL_C] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_C, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
};
该例程中使用 esp_ble_gatts_create_attr_tab() 来创建属性表,执行成功后,会上报 ESP_GATTS_CREAT_ATTR_TAB_EVT 事件,将属性句柄保存在数组 heart_rate_handle_table 中。 该数组定义如下:
uint16_t heart_rate_handle_table[HRS_IDX_NB];
数组索引定义如下:
enum
{
IDX_SVC,
IDX_CHAR_A,
IDX_CHAR_VAL_A,
IDX_CHAR_CFG_A,
IDX_CHAR_B,
IDX_CHAR_VAL_B,
IDX_CHAR_C,
IDX_CHAR_VAL_C,
HRS_IDX_NB,
};
参考下面代码:
case ESP_GATTS_CREAT_ATTR_TAB_EVT:{
if (param->add_attr_tab.status != ESP_GATT_OK){
ESP_LOGE(GATTS_TABLE_TAG, "create attribute table failed, error code=0x%x", param->add_attr_tab.status);
}
else if (param->add_attr_tab.num_handle != HRS_IDX_NB){
ESP_LOGE(GATTS_TABLE_TAG, "create attribute table abnormally, num_handle (%d) \
doesn't equal to HRS_IDX_NB(%d)", param->add_attr_tab.num_handle, HRS_IDX_NB);
}
else {
ESP_LOGI(GATTS_TABLE_TAG, "create attribute table successfully, the number handle = %d\n",param->add_attr_tab.num_handle);
memcpy(heart_rate_handle_table, param->add_attr_tab.handles, sizeof(heart_rate_handle_table));
esp_ble_gatts_start_service(heart_rate_handle_table[IDX_SVC]);
}
break;
}
可以通过定义 esp_gatts_attr_db_t 类型的数组来定义 GATT 服务。esp_gatts_attr_db_t 结构体定义如下:
/**
* @brief attribute type added to the gatt server database
*/
typedef struct
{
esp_attr_control_t attr_control; /*!< The attribute control type */
esp_attr_desc_t att_desc; /*!< The attribute type */
} esp_gatts_attr_db_t;
该结构体包含两个成员变量,分别是:
- attr_controlesp_attr_control_t 结构体定义如下:
/**
* @brief attribute auto response flag
*/
typedef struct
{
#define ESP_GATT_RSP_BY_APP 0
#define ESP_GATT_AUTO_RSP 1
/**
* @brief if auto_rsp set to ESP_GATT_RSP_BY_APP, means the response of Write/Read operation will by replied by application.
if auto_rsp set to ESP_GATT_AUTO_RSP, means the response of Write/Read operation will be replied by GATT stack automatically.
*/
uint8_t auto_rsp;
} esp_attr_control_t;
attr_control是自动响应参数,可以设置为:
- ESP_GATT_AUTO_RSP:BLE 堆栈在读取或写入事件到达时自动进行响应;
- ESP_GATT_RSP_BY_APP:应用程序需要调用 esp_ble_gatts_send_response()手动响应消息。
- att_descesp_attr_desc_t 结构体定义如下:
/**
* @brief Attribute description (used to create database)
*/
typedef struct
{
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*/
} esp_attr_desc_t;
以属性表 gatt_db 的第一个元素为例:
/* Full Database Description - Used to add attributes into the database */
static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] =
{
// Service Declaration
[IDX_SVC] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
sizeof(uint16_t), sizeof(GATTS_SERVICE_UUID_TEST), (uint8_t *)&GATTS_SERVICE_UUID_TEST}},
- [IDX_SVC]:属性索引
- ESP_GATT_AUTO_RSP:GATT 堆栈自动响应
- ESP_UUID_LEN_16:UUID 长度设置为 16-bit
- (uint8_t *)&primary_service_uuid:将服务标识为主要服务的 UUID (0x2800)
#define ESP_GATT_UUID_PRI_SERVICE 0x2800
- ESP_GATT_PERM_READ:服务的读取权限 属性权限定义如下:
/* relate to BTA_GATT_PERM_xxx in bta/bta_gatt_api.h */
/**
* @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 */
#define ESP_GATT_PERM_READ_AUTHORIZATION (1 << 9) /* bit 9 - 0x0200 */
#define ESP_GATT_PERM_WRITE_AUTHORIZATION (1 << 10) /* bit 10 - 0x0400 */
typedef uint16_t esp_gatt_perm_t;
- sizeof(uint16_t):服务 UUID 的最大长度,此处为 16-bit。
- sizeof(GATTS_SERVICE_UUID_TEST):当前服务长度设置为 GATTS_SERVICE_UUID_TEST的大小,为 16-bit。
static const uint16_t GATTS_SERVICE_UUID_TEST = 0x00FF;
- (uint8_t *)&GATTS_SERVICE_UUID_TEST:服务属性值设置为 GATTS_SERVICE_UUID_TEST
3 AT 固件自定义蓝牙服务
测试环境如下: 软件: ESP32-WROOM-32_AT_Bin_V2.2.0.0 硬件:ESP32-WROOM-32 可以使用指令查询 AT 固件默认支持的 BLE 服务以及特征:
AT+BLEINIT=2
OK
AT+BLEGATTSSRVCRE
OK
AT+BLEGATTSSRV?
+BLEGATTSSRV:1,0,0xA002,1
+BLEGATTSSRV:2,0,0xA003,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
+BLEGATTSCHAR:"char",2,1,0xC400,0x02
+BLEGATTSCHAR:"desc",2,1,1,0x2901
+BLEGATTSCHAR:"char",2,2,0xC401,0x02
+BLEGATTSCHAR:"desc",2,2,1,0x2901
OK
BLE 服务源文件是 ESP-AT 工程创建低功耗蓝牙服务所依据的文件,文件位于 ble_data/example.csv ,可以通过自定义该文件实现 BLE 服务的自定义。
默认的 BLE 服务源文件定义如下:
以下内容是对上表的说明:
- uuid_len:UUID 的长度,上表中 uuid_len 值为 16,代表 16-bit UUID
- uuid:表明属性类型,其中,0x2800 为主服务声明,0x2803 为特征声明,0x2901 为特征用户描述描述符,0x2902 为客户端特征配置描述符(CCCD),可以参考下表:
- perm:权限,用于描述属性是否可读或者可写。 定义如下:
#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 */
#define ESP_GATT_PERM_READ_AUTHORIZATION (1 << 9) /* bit 9 - 0x0200 */
#define ESP_GATT_PERM_WRITE_AUTHORIZATION (1 << 10) /* bit 10 - 0x0400 */
- val_max_len:数值的最大长度。
- val_cur_len:数值的当前长度。
- value:对应的数值。 如果 UUID 为 0x2800,那么该行为服务声明,对应的值应该为自定义的 UUID,以 index 0 所在的行为例,对应的值为 A002。 如果 UUID 为 0x2803,那么该行为特征声明,对应的值为特征的属性(property),以 index 1 所在的行为例,对应的值为 02,代表该特征具有读属性。 属性的定义如下:
#define ESP_GATT_CHAR_PROP_BIT_BROADCAST (1 << 0) /* 0x01 */ /* relate to BTA_GATT_CHAR_PROP_BIT_BROADCAST in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_READ (1 << 1) /* 0x02 */ /* relate to BTA_GATT_CHAR_PROP_BIT_READ in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_WRITE_NR (1 << 2) /* 0x04 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE_NR in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_WRITE (1 << 3) /* 0x08 */ /* relate to BTA_GATT_CHAR_PROP_BIT_WRITE in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_NOTIFY (1 << 4) /* 0x10 */ /* relate to BTA_GATT_CHAR_PROP_BIT_NOTIFY in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_INDICATE (1 << 5) /* 0x20 */ /* relate to BTA_GATT_CHAR_PROP_BIT_INDICATE in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_AUTH (1 << 6) /* 0x40 */ /* relate to BTA_GATT_CHAR_PROP_BIT_AUTH in bta/bta_gatt_api.h */
#define ESP_GATT_CHAR_PROP_BIT_EXT_PROP (1 << 7) /* 0x80 */ /* relate to BTA_GATT_CHAR_PROP_BIT_EXT_PROP in bta/bta_gatt_api.h */
- 特征声明接下来的一行为特征值定义,以 index 2 所在的行为例,0xC300为自定义特征的 UUID ,对应的数值长度只有一个字节,对应数值为 30。0x2902为客户端配置描述符(CCCD),可以看到,特征 0xC305(index 16) 具有 CCCD,其值占用两个字节,初始值为 0000,表示通知 (notification) 和指示 (indication) 被禁用。
上表中,自定义了两个 BLE 服务,分别为 A002 和 A003。
其中,服务 A002 有 8 个特征,服务 A003 有 2 个特征,简单总结见下表:
使用手机作 client,查询到的服务如下:
如果需要自定义一个 128-bit UUID 的服务,其值为 11111111-2222-2222-3333-333344444444,该服务有两个特征,UUID 分别为 0xFF01 和 0xFF02, 其中,0xFF01 具有读写属性,0xFF02 具有读属性和通知属性,以及描述符 CCCD,可以定义如下:
index,uuid_len,uuid,perm,val_max_len,val_cur_len,value
0,16,0x2800,0x01,16,16,11111111222222223333333344444444
1,16,0x2803,0x01,1,1,0A
2,16,0xFF01,0x11,3,3,313233
3,16,0x2803,0x01,1,1,12
5,16,0xFF02,0x01,512,512,3031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031
6,16,0x2902,0x11,2,2,0000
,,,,,,
通过 AT 指令查询服务如下:
AT+BLEINIT=2
OK
AT+BLEGATTSSRVCRE
OK
AT+BLEGATTSSRVSTART
OK
AT+BLEGATTSSRV?
+BLEGATTSSRV:1,1,0x11111111222222223333333344444444,1
OK
AT+BLEGATTSCHAR?
+BLEGATTSCHAR:"char",1,1,0xFF01,0x0a
+BLEGATTSCHAR:"char",1,2,0xFF02,0x12
+BLEGATTSCHAR:"desc",1,2,1,0x2902
OK
手机端查询到的服务如下:
4 常见问题
1.如何自定义一个 UUID 长度为 128 位的服务? A:例如,要定义的服务 UUID 为 11111111-2222-2222-3333-333344444444,那么代码中可以将 UUID 定义如下:
static const uint8_t custom_service_uuid_test[16] = {0x44, 0x44, 0x44, 0x44, 0x33, 0x33, 0x33, 0x33, 0x22, 0x22, 0x22, 0x22, 0x11, 0x11, 0x11, 0x11};
属性表中定义如下:
[IDX_SVC] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
16, sizeof(custom_service_uuid_test), (uint8_t *)&custom_service_uuid_test}},
参考 Core 4.2 [Vol 3, Part G] 的 2.4 小节 PROFILE FUNDAMENTALS:
Multi-octet fields within the GATT Profile shall be sent least significant octet first (little endian). GATT 配置文件中的多八位字节字段应以最低有效八位字节优先(小端序)发送。
因此在上述定义中,将低字节数据 0x44 存储在低地址,优先发送。
手机通过 APP nRF connect 连接到 ESP32 设备,查询到服务如下:
2.如果通过应用层发送响应数据?
A:
以官方的 gatt_server_service_table 为例,
例如,如果想要 client 在读取 GATTS_CHAR_UUID_TEST_A 特征的数据时通过应用程序发送读响应数据,而不是底层自动发送,那么可定义如下:
/* Characteristic Declaration */
[IDX_CHAR_A] =
{{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_notify}},
/* Characteristic Value */
[IDX_CHAR_VAL_A] =
{{ESP_GATT_RSP_BY_APP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_A, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
需要将默认的 ESP_GATT_AUTO_RSP 改为 ESP_GATT_RSP_BY_APP ,此外,需要在应用代码中调用 esp_ble_gatts_send_response() 发送读响应数据。
读响应的 ATT PDU 定义如下:
Parameter | Size (octets) | Description |
Attribute Opcode | 1 | 0x0B = Read Response |
Attribute Value | 0 to (ATT_MTU-1) | The value of the attribute with the handle given |
修改 gatts_profile_event_handler() 中 ESP_GATTS_READ_EVT 对应的代码如下,通过调用 esp_ble_gatts_send_response() 发送响应数据。
case ESP_GATTS_READ_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n", param->read.conn_id, param->read.trans_id, param->read.handle);
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;
rsp.attr_value.len = 4;
rsp.attr_value.value[0] = 0x52;
rsp.attr_value.value[1] = 0x45;
rsp.attr_value.value[2] = 0x41;
rsp.attr_value.value[3] = 0x44;
if(heart_rate_handle_table[IDX_CHAR_VAL_A] == param->read.handle) {
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
ESP_GATT_OK, &rsp);
}
break;
上述代码中,通过 heart_rate_handle_table[IDX_CHAR_VAL_A] == param->read.handle 进行判断,表明只有在读取特征 GATTS_CHAR_UUID_TEST_A 的时候才会发送响应。
手机端通过 APP 发起读请求时,可以收到 APP 发送的响应数据 “READ”。
为了进行自动响应和 APP 响应的对比,以特征 B 为例,特征 GATTS_CHAR_UUID_TEST_B 也具有读属性,定义如下:
/* Characteristic Declaration */
[IDX_CHAR_B] =
{{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}},
/* Characteristic Value */
[IDX_CHAR_VAL_B] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_B, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
默认为自动响应,响应内容为 char_value,定义如下:
static const uint8_t char_value[4] = {0x11, 0x22, 0x33, 0x44};
可以看到,在对特征 B 进行读取时,响应的内容为 (0x)11-22-33-44。
如果想要通过应用程序发送写响应数据,也可以按照类似的方式修改,不过调用 esp_ble_gatts_send_response() 发送写响应时,建议不要带应用数据,写响应带应用数据属于不合规的操作。 写响应的 ATT PDU 定义如下:
Parameter | Size (octets) | Description |
Attribute Opcode | 1 | 0x13 = Write Response |