ESP32 自定义 BLE 服务

本篇文档用于说明如何基于 ESP32 自定义 BLE 服务,文档中协议相关的内容基于 Core 4.2 specification

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_control
    esp_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_desc
    esp_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 服务,分别为 A002A003
其中,服务 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 定义如下:

ParameterSize (octets)Description
Attribute Opcode10x0B = Read Response
Attribute Value0 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 定义如下:

ParameterSize (octets)Description
Attribute Opcode10x13 = Write Response
  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ESP32是一款功能强大的开源物联网开发板,它支持蓝牙低功耗(BLE)连接。使用ESP32通过BLE连接服务器,可以实现与设备之间的无线通信。 要建立ESP32服务器之间的BLE连接,首先需要确保ESP32上的BLE库和服务器上的BLE服务相互兼容。ESP32提供了许多BLE库,如ArduinoBLE、nRF Connect等,可以根据需要选择合适的库。 首先,在ESP32上启动BLE功能,并将其设置为外围设备(peripheral)。外围设备是指其它设备可以连接并与之通信的设备。然后,将ESP32服务器建立BLE连接的相关设置(如设备名称、UUID等)配置到ESP32中。 接下来,ESP32将扫描周围可用的BLE服务器,并尝试连接到其中一个服务器。可以使用ESP32提供的BLE库中的扫描函数来实现这一点。一旦ESP32成功连接到服务器,就可以开始发送和接收数据。 在连接建立后,可以使用BLE库提供的函数来读取和写入数据。例如,可以使用BLE库中的`read()`函数从服务器读取数据,或使用`write()`函数将数据写入到服务器。 在进行数据传输时,需要在ESP32服务器上分别实现相应的数据处理逻辑。例如,可以使用回调函数来处理接收到的数据,并在需要时发送响应。 要确保BLE连接的稳定性和安全性,可以使用加密和认证机制。例如,通过在连接过程中对数据进行加密和验证,可以确保数据的机密性和完整性。 总之,通过使用ESP32BLE功能,可以方便地实现与服务器的无线连接。无论是数据传输、设备控制还是传感器数据采集,ESP32BLE连接能够为物联网应用提供可靠、稳定和安全的通信方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值