ESP32 实例分析:gatt_server

本文详细介绍了如何在ESP32上构建一个BLE GATT服务器,包括蓝牙初始化、服务创建、特性注册等步骤,并展示了如何处理手机端的连接、读写操作。通过对蓝牙配置、GATTS事件的解析,揭示了ESP32在物联网嵌入式硬件中的应用。

ESP32 实例分析:\bluetooth\bluedroid\ble\gatt_server

参考:
frameworks\esp-idf-v4.4.2\examples\bluetooth\bluedroid\ble\gatt_server

1 简介

该实例展示如何在BLE模式下,建立一个GATT服务并添加对应attribute的过程。
新建2个应用profile,对应profile建立一个service,每个service建立characteritstic。在这里插入图片描述

2 工程文件组成

sdkconfig.defaults : 为适应工程,需要额外打开的系统配置;
Kconfig.projbuild : 工程自定义的,系统配置选项;
gatts_demo.c : 实例代码文件;

3 文件详解

3.1 sdkconfig.defaults

CONFIG_BT_ENABLED=y   * 打开蓝牙开关
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y  * 只支持BLE模式
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n  * 只支持传统蓝牙模式
CONFIG_BTDM_CTRL_MODE_BTDM=n * BLE+传统蓝牙 二合一模式

3.2 Kconfig.projbuild

menu “Example ‘GATT SERVER’ Config” * 配置顶层增加目录
config SET_RAW_ADV_DATA * 增加工程使用的配置项
bool “Use raw 。。。 data” * 配置项属性
help * 帮助和说明文字

在这里插入图片描述

3.3 gatts_demo.c

3.3.1 BLE 蓝牙初始化

ret = nvs_flash_init();
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
ret = esp_bluedroid_init();
ret = esp_bluedroid_enable();

本部分代码基本,每个用到的蓝牙BLE功能的都一样。使用默认配置参数配置蓝牙。

3.3.1 GATT 实验代码

  • 注册GATT相关的events处理函数
    ret = esp_ble_gatts_register_callback(gatts_event_handler);
  • 注册GAP相关的events处理函数
    ret = esp_ble_gap_register_callback(gap_event_handler);
  • 注册第一个GATT profile,ID = PROFILE_A_APP_ID = 0
    ret = esp_ble_gatts_app_register(PROFILE_A_APP_ID);
  • 注册第二个GATT profile,ID = PROFILE_B_APP_ID = 1
    ret = esp_ble_gatts_app_register(PROFILE_B_APP_ID);
  • 设置MTU长度为500
    esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);

3.3.3 gatts_event_handler()

实验代码中调用
ret = esp_ble_gatts_app_register(PROFILE_A_APP_ID);
ret = esp_ble_gatts_app_register(PROFILE_B_APP_ID);
注册profile时,会触发ESP_GATTS_REG_EVT,同时2个参数被传递过来:

esp_gatt_status_t status;
uint16_t app_id;

该函数是注册的gatts相关event的入口函数。

/* If event is register event, store the gatts_if for each profile */
    if (event == ESP_GATTS_REG_EVT) {
        if (param->reg.status == ESP_GATT_OK) {
            gl_profile_tab[param->reg.app_id].gatts_if = gatts_if;
        } else {
            ESP_LOGI(GATTS_TAG, "Reg app failed, app_id %04x, status %d\n",
                    param->reg.app_id,
                    param->reg.status);
            return;
        }
    }

    /* If the gatts_if equal to profile A, call profile A cb handler,
     * so here call each profile's callback */
    do {
        int idx;
        for (idx = 0; idx < PROFILE_NUM; idx++) {
            if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
                    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);
                }
            }
        }
    } while (0);

如果注册状态为ESP_GATT_OK,则赋值 gl_profile_tab[param->reg.app_id].gatts_if = gatts_if;
这里的param->reg.app_id 为PROFILE_A_APP_ID 和 PROFILE_B_APP_ID。
而后,调用
gl_profile_tab[idx].gatts_cb(event, gatts_if, param);
gl_profile_tab在声明时,gatts_cb就被赋值。

static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = {
    [PROFILE_A_APP_ID] = {
        .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 */
    },
    [PROFILE_B_APP_ID] = {
        .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 */
    },
};

3.3.4 gatts_profile_a_event_handler()

该函数被gatts_event_handler()调用,来处理所有gatts相关events消息。

  • 1 ESP_GATTS_REG_EVT
    注册profile 首先触发该事件。
    gl_profile_tab[PROFILE_A_APP_ID].service_id.is_primary = true;
    gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00;
    gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16;
    gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A;
    设置serveice的UUID值 GATTS_SERVICE_UUID_TEST_A = 0x00ff。
    esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(TEST_DEVICE_NAME);
    设置设备名称"ESP_GATTS_DEMO"
    esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data);
    设置adv的数据
    ret = esp_ble_gap_config_adv_data(&scan_rsp_data);
    设置scan的数据
    这2个数据均为esp_ble_adv_data_t结构体。
    主要包含:serveice的128bit的UUID 。rsp和adv数据存在部分差异。
static esp_ble_adv_data_t adv_data = {
    .set_scan_rsp = false,
    .include_name = true,
    .include_txpower = false,
    .min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
    .max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
    .appearance = 0x00,
    .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data =  NULL, //&test_manufacturer[0],
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = sizeof(adv_service_uuid128),
    .p_service_uuid = adv_service_uuid128,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

而后针对该profile建立对于service。
esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_A);

  • ESP_GATTS_CREATE_EVT
    上面最后一步建立service会触发该消息。
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;

设置profile_tab数据中的服务句柄以及Characteristic的UUID 0xff01.
esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle);
启动服务;

a_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
esp_err_t add_char_ret = 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,
                                                        a_property,
                                                        &gatts_demo_char1_val, NULL);

添加Characteristic到服务中。
这里参数对应全局变量 gatts_demo_char1_val,真实存储在全局变量char1_str数组中。
而访问权限保护读,写和通知三种。

static uint8_t char1_str[] = {0x11,0x22,0x33};
static esp_attr_value_t gatts_demo_char1_val =
{
    .attr_max_len = GATTS_DEMO_CHAR_VAL_LEN_MAX,
    .attr_len     = sizeof(char1_str),
    .attr_value   = char1_str,
};
  • ESP_GATTS_START_EVT
    上面esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle);
    会触发该事件。
    不用做什么操作,看到了就好。

  • ESP_GATTS_ADD_CHAR_EVT
    esp_ble_gatts_add_char()会触发该事件,
    gl_profile_tab[PROFILE_A_APP_ID].char_handle = param->add_char.attr_handle;
    esp_err_t get_attr_ret = esp_ble_gatts_get_attr_value(param->add_char.attr_handle, &length, &prf_char);
    获取attr对应的值,这个值就是全局变量char1_str数组,长度为3.
    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);
    在服务中添加attr的对应描述。这里有描述的UUID,权限(读写)

  • ESP_GATTS_ADD_CHAR_DESCR_EVT
    上述添加attr描述会触发该事件,不需要额外的处理,知道就好。

  • ESP_GATTS_CONNECT_EVT
    通过手机app连接该蓝牙,会触发。
    gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id;
    设置全局profile数组
    conn_params.latency = 0;
    conn_params.max_int = 0x20; // max_int = 0x201.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);

  • ESP_GATTS_READ_EVT
    手机端读取时触发。
    rsp.attr_value.handle = param->read.handle;
    rsp.attr_value.len = 4;
    rsp.attr_value.value[0] = 0xde;
    rsp.attr_value.value[1] = 0xed;
    rsp.attr_value.value[2] = 0xbe;
    rsp.attr_value.value[3] = 0xef;
    esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
    ESP_GATT_OK, &rsp);
    设置返回消息结构。

  • ESP_GATTS_WRITE_EVT
    手机端写入时,触发。
    判断写入是 描述。
    写值== 0x0001:
    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);

写值 == 0x0002:
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_A_APP_ID].char_handle,
sizeof(indicate_data), indicate_data, true);

不是描述字段:
调用 example_write_event_env(gatts_if, &a_prepare_write_env, param);
将param写入全局变量;
判断 param->write.need_rsp 是否需要返回。
esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t));
gatt_rsp->attr_value.len = param->write.len;
gatt_rsp->attr_value.handle = param->write.handle;
gatt_rsp->attr_value.offset = param->write.offset;
gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;
memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len);
esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp);
不需要返回:
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, NULL);

3.5 gatts_profile_b_event_handler()

它是profileb的处理函数,它与a只有一点点区别,基本一致。

4 手机端 与 代码的关联

4.1 扫描

在这里插入图片描述

  • 设备名称 ESP_GATTS_DEMO 对应
    esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(TEST_DEVICE_NAME);
  • 发射功率和名字在 include_name ,include_txpower
    static esp_ble_adv_data_t scan_rsp_data = {
    .set_scan_rsp = true,
    .include_name = true,
    .include_txpower = true,
    };
  • 40ms
    ESP_GATTS_CONNECT_EVT 事件中对事件进行更新。
    conn_params.latency = 0;
    conn_params.max_int = 0x20; // max_int = 0x201.25ms = 40ms
    conn_params.min_int = 0x10; // min_int = 0x10
    1.25ms = 20ms
    conn_params.timeout = 400; // timeout = 400*10ms = 4000ms
    gl_profile_tab[PROFILE_A_APP_ID].conn_id = param->connect.conn_id;
    //start sent the update connection parameters to the peer device.
    esp_ble_gap_update_conn_params(&conn_params);
  • MAC地址,暂时代码未进行修改

4.2 连接后

在这里插入图片描述
官方规定的默认服务
服务1:uuid为0x1800的《Generic Access》
服务2:uuid为0x1801的《Generic Attribute》
服务3:uuid为0x00FF
#define GATTS_SERVICE_UUID_TEST_A 0x00FF
gl_profile_tab[PROFILE_A_APP_ID].service_id.is_primary = true;
gl_profile_tab[PROFILE_A_APP_ID].service_id.id.inst_id = 0x00;
gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_A_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A;
esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_A);
服务3:uuid为0x00EE
#define GATTS_SERVICE_UUID_TEST_B 0x00EE
同服务3一致。

4.3 自定义服务展开

在这里插入图片描述
服务1:0x00ff ,包含1个属性

  • 0xff01
    #define GATTS_CHAR_UUID_TEST_A 0xFF01
    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_err_t add_char_ret = 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,
    a_property,
    &gatts_demo_char1_val, NULL);
  • 属性
    a_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
  • 描述
    ESP_GATT_UUID_CHAR_CLIENT_CONFIG 使用官方定义。

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);

4.4 读数据

在这里插入图片描述

  • 点击0xFF01读取按钮。
    板卡输出
    I (5955931) GATTS_DEMO: GATT_READ_EVT, conn_id 0, trans_id 7, handle 43
    app显示 0x DE-ED-BE-EF
    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] = 0xde;
    rsp.attr_value.value[1] = 0xed;
    rsp.attr_value.value[2] = 0xbe;
    rsp.attr_value.value[3] = 0xef;
    esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
    ESP_GATT_OK, &rsp);
  • 点击描述读取
    板卡同样的操作,不过app因为受到4个字节,而建立时是2个字节,所以报长度错误。
  • 点击0xEE01,
    板卡I (5958851) GATTS_DEMO: GATT_READ_EVT, conn_id 0, trans_id 9, handle 47
    handle与前面不一样,因为分别是2个不同的服务。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值