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

2083

被折叠的 条评论
为什么被折叠?



