本篇文档用于说明如何基于 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 服务源文件定义如下:

esp32ble的使用 esp32 ble_esp32ble的使用

esp32ble的使用 esp32 ble_属性表_02

以下内容是对上表的说明:

  • 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 个特征,简单总结见下表:

esp32ble的使用 esp32 ble_BLE_03

使用手机作 client,查询到的服务如下:

esp32ble的使用 esp32 ble_自定义_04

esp32ble的使用 esp32 ble_属性表_05

esp32ble的使用 esp32 ble_属性表_06

如果需要自定义一个 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

手机端查询到的服务如下:

esp32ble的使用 esp32 ble_BLE_07

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 设备,查询到服务如下:

esp32ble的使用 esp32 ble_esp32ble的使用_08

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”。

esp32ble的使用 esp32 ble_BLE_09

为了进行自动响应和 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。

esp32ble的使用 esp32 ble_#define_10

如果想要通过应用程序发送写响应数据,也可以按照类似的方式修改,不过调用 esp_ble_gatts_send_response() 发送写响应时,建议不要带应用数据,写响应带应用数据属于不合规的操作。 写响应的 ATT PDU 定义如下:

Parameter

Size (octets)

Description

Attribute Opcode

1

0x13 = Write Response