BLE GATTS_READ_EVT - diferenciate via handle

jtantera
Posts: 6
Joined: Thu Mar 03, 2022 3:22 pm

BLE GATTS_READ_EVT - diferenciate via handle

Postby jtantera » Thu Nov 23, 2023 4:36 pm

Hi to all.
I am making a bluetooth BLE aplication and i want to create serveral characteristics under the same service, some of those are for Notify, some are for Read and some for Write.
I had made the Notify ones and they work as i want. I have a function that its called via a timer that indicates when the data is ready and that function sends the notify data.
The problem its when i want to read or write... since there are several characteristics and i am trying to diferenciate from the handler. I cant find how to make this. I dont know what to compare... what to write inside the "if" statment.
I had started from the "gatt_server_service_table" example.
I have asked google and chatGPT but no success.

Can you help me?

Search for "-----------------------------> HERE IS THE PROBLEM <----------------------------------" in the codebox

Full Database Description

Code: BluetoothDriver.c Select all

/* 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_RSP_BY_APP}, {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_RSP_BY_APP}, {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}},

/* Descripción para Característica B */
[IDX_CHAR_DESCR_B] =
{{ESP_GATT_RSP_BY_APP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_B), strlen(CHAR_DESCRIP_B), (uint8_t *)CHAR_DESCRIP_B}},

/* 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}},

/* Descripción para Característica C */
[IDX_CHAR_DESCR_C] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_C), strlen(CHAR_DESCRIP_C), (uint8_t *)CHAR_DESCRIP_C}},

/* Agregado por Jose */
/* Characteristic Declaration */
[IDX_CHAR_D] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_D] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_D, 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_D] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_D] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_D), strlen(CHAR_DESCRIP_D), (uint8_t *)CHAR_DESCRIP_D}},

/* Characteristic Declaration */
[IDX_CHAR_E] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_E] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_E, 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_E] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_E] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_E), strlen(CHAR_DESCRIP_E), (uint8_t *)CHAR_DESCRIP_E}},


//CARACTERISTICAS DE ESCRITURA
/* Characteristic Declaration */
[IDX_CHAR_0] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_0] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_0, 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_0] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_0] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_0), strlen(CHAR_DESCRIP_0), (uint8_t *)CHAR_DESCRIP_0}},

[IDX_CHAR_1] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_1] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_1, 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_1] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_1] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_1), strlen(CHAR_DESCRIP_1), (uint8_t *)CHAR_DESCRIP_1}},

[IDX_CHAR_2] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_2] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_2, 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_2] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_2] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_2), strlen(CHAR_DESCRIP_2), (uint8_t *)CHAR_DESCRIP_2}},
};

Code: BluetoothDriver.c Select all


static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
switch (event) {
case ESP_GATTS_REG_EVT:{
esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME);
if (set_dev_name_ret){
ESP_LOGE(GATTS_TABLE_TAG, "set device name failed, error code = %x", set_dev_name_ret);
}
#ifdef CONFIG_SET_RAW_ADV_DATA
esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data));
if (raw_adv_ret){
ESP_LOGE(GATTS_TABLE_TAG, "config raw adv data failed, error code = %x ", raw_adv_ret);
}
adv_config_done |= ADV_CONFIG_FLAG;
esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_scan_rsp_data, sizeof(raw_scan_rsp_data));
if (raw_scan_ret){
ESP_LOGE(GATTS_TABLE_TAG, "config raw scan rsp data failed, error code = %x", raw_scan_ret);
}
adv_config_done |= SCAN_RSP_CONFIG_FLAG;
#else
//config adv data
esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data);
if (ret){
ESP_LOGE(GATTS_TABLE_TAG, "config adv data failed, error code = %x", ret);
}
adv_config_done |= ADV_CONFIG_FLAG;
//config scan response data
ret = esp_ble_gap_config_adv_data(&scan_rsp_data);
if (ret){
ESP_LOGE(GATTS_TABLE_TAG, "config scan response data failed, error code = %x", ret);
}
adv_config_done |= SCAN_RSP_CONFIG_FLAG;
#endif
esp_err_t create_attr_ret = esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, SVC_INST_ID);
if (create_attr_ret){
ESP_LOGE(GATTS_TABLE_TAG, "create attr table failed, error code = %x", create_attr_ret);
}
}
break;
case ESP_GATTS_READ_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_READ_EVT");
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;

if (-----------------------------> HERE IS THE PROBLEM <----------------------------------){
if ( getWifiConnStatus() != WIFI_STATUS_OFF ) {
rsp.attr_value.len = 6;
uint64_t productUID = getUniqueID(); //TODO Modificar esto para que lea el UID desde la memoria y no desde la MAC, por si mañana cambia
//ESP_LOGI(GATTS_TABLE_TAG, "MAC = %lld", productUID);
for (uint8_t i = 0 ; i < 6 ; i++){
rsp.attr_value.value[i] = ((uint8_t)(productUID>>(8*i)) & 0xFF);
//ESP_LOGI(GATTS_TABLE_TAG, "Byte = %d", rsp.attr_value.value[i]);
}
}
}

esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
ESP_GATT_OK, &rsp);
break;
case ESP_GATTS_WRITE_EVT:
if (!param->write.is_prep){
// the data length of gattc write must be less than GATTS_DEMO_CHAR_VAL_LEN_MAX.
ESP_LOGI(GATTS_TABLE_TAG, "GATT_WRITE_EVT, handle = %d, value len = %d, value :", param->write.handle, param->write.len);
esp_log_buffer_hex(GATTS_TABLE_TAG, param->write.value, param->write.len);

//Habilitacion de notificaciones de caracteristicas
if (heart_rate_handle_table[IDX_CHAR_CFG_A] == param->write.handle && param->write.len == 2){
uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0];
if (descr_value == 0x0001){
ESP_LOGI(GATTS_TABLE_TAG, "notify enable");
heart_rate_profile_tab[PROFILE_APP_IDX].conn_id = param->write.conn_id;

}else if (descr_value == 0x0002){
ESP_LOGI(GATTS_TABLE_TAG, "indicate enable");
uint8_t indicate_data[15];
for (int i = 0; i < sizeof(indicate_data); ++i)
{
indicate_data[i] = i % 0xff;
}
//the size of indicate_data[] need less than MTU size
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],
sizeof(indicate_data), indicate_data, true);
}
else if (descr_value == 0x0000){
ESP_LOGI(GATTS_TABLE_TAG, "notify/indicate disable ");
//setBTMotorSpeedNotifStatus(NOTIF_DISABLED);

}else{
ESP_LOGE(GATTS_TABLE_TAG, "unknown descr value");
esp_log_buffer_hex(GATTS_TABLE_TAG, param->write.value, param->write.len);
}

}

//Velocidad del Motor
if (heart_rate_handle_table[IDX_CHAR_CFG_D] == param->write.handle && param->write.len == 2){
uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0];
if (descr_value == 0x0001){
ESP_LOGI(GATTS_TABLE_TAG, "notify enable");
heart_rate_profile_tab[PROFILE_APP_IDX].conn_id = param->write.conn_id;
setBTMotorSpeedNotifStatus(NOTIF_ENABLED); //Agregado por Jose

}else if (descr_value == 0x0002){
ESP_LOGI(GATTS_TABLE_TAG, "indicate enable");
uint8_t indicate_data[15];
for (int i = 0; i < sizeof(indicate_data); ++i)
{
indicate_data[i] = i % 0xff;
}
//the size of indicate_data[] need less than MTU size
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],
sizeof(indicate_data), indicate_data, true);
}
else if (descr_value == 0x0000){
ESP_LOGI(GATTS_TABLE_TAG, "notify/indicate disable ");
xTimerStop(notify_timer_handle, 0);
setBTMotorSpeedNotifStatus(NOTIF_DISABLED); //Agregado por Jose

}else{
ESP_LOGE(GATTS_TABLE_TAG, "unknown descr value");
esp_log_buffer_hex(GATTS_TABLE_TAG, param->write.value, param->write.len);
}

}

//Potencia del Motor
if (heart_rate_handle_table[IDX_CHAR_CFG_E] == param->write.handle && param->write.len == 2){
uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0];
if (descr_value == 0x0001){
ESP_LOGI(GATTS_TABLE_TAG, "notify enable");
heart_rate_profile_tab[PROFILE_APP_IDX].conn_id = param->write.conn_id;
setBTMotorPowerNotifStatus(NOTIF_ENABLED); //Agregado por Jose

}else if (descr_value == 0x0002){
ESP_LOGI(GATTS_TABLE_TAG, "indicate enable");
uint8_t indicate_data[15];
for (int i = 0; i < sizeof(indicate_data); ++i)
{
indicate_data[i] = i % 0xff;
}
//the size of indicate_data[] need less than MTU size
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],
sizeof(indicate_data), indicate_data, true);
}
else if (descr_value == 0x0000){
ESP_LOGI(GATTS_TABLE_TAG, "notify/indicate disable ");
xTimerStop(notify_timer_handle, 0);
setBTMotorPowerNotifStatus(NOTIF_DISABLED); //Agregado por Jose

}else{
ESP_LOGE(GATTS_TABLE_TAG, "unknown descr value");
esp_log_buffer_hex(GATTS_TABLE_TAG, param->write.value, param->write.len);
}

}

/* send response when param->write.need_rsp is true*/
if (param->write.need_rsp){
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
}
}else{
/* handle prepare write */
example_prepare_write_event_env(gatts_if, &prepare_write_env, param);
}
break;
case ESP_GATTS_EXEC_WRITE_EVT:
// the length of gattc prepare write data must be less than GATTS_DEMO_CHAR_VAL_LEN_MAX.
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_EXEC_WRITE_EVT");
example_exec_write_event_env(&prepare_write_env, param);
break;
case ESP_GATTS_MTU_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);
break;
case ESP_GATTS_CONF_EVT:
//ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_CONF_EVT, status = %d, attr_handle %d", param->conf.status, param->conf.handle);
break;
case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "SERVICE_START_EVT, status %d, service_handle %d", param->start.status, param->start.service_handle);
break;
case ESP_GATTS_CONNECT_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_CONNECT_EVT, conn_id = %d", param->connect.conn_id);

//Agregada por Jose
setBTStatus(BT_CONNECTED);

esp_log_buffer_hex(GATTS_TABLE_TAG, param->connect.remote_bda, 6);
esp_ble_conn_update_params_t conn_params = {0};
memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
/* For the iOS system, please refer to Apple official documents about the BLE connection parameters restrictions. */
conn_params.latency = 0;
conn_params.max_int = 0x20; // max_int = 0x20*1.25ms = 40ms
conn_params.min_int = 0x10; // min_int = 0x10*1.25ms = 20ms
conn_params.timeout = 400; // timeout = 400*10ms = 4000ms
//start sent the update connection parameters to the peer device.
esp_ble_gap_update_conn_params(&conn_params);
break;
case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_DISCONNECT_EVT, reason = 0x%x", param->disconnect.reason);

setBTStatus(BT_DISCONNECTED); //Agregada por Jose

esp_ble_gap_start_advertising(&adv_params);
break;
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;
}
case ESP_GATTS_STOP_EVT:
case ESP_GATTS_OPEN_EVT:
case ESP_GATTS_CANCEL_OPEN_EVT:
case ESP_GATTS_CLOSE_EVT:
case ESP_GATTS_LISTEN_EVT:
case ESP_GATTS_CONGEST_EVT:
case ESP_GATTS_UNREG_EVT:
case ESP_GATTS_DELETE_EVT:
default:
break;
}
}

MicroController
Posts: 2661
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: BLE GATTS_READ_EVT - diferenciate via handle

Postby MicroController » Fri Nov 24, 2023 12:15 pm

The example creates an 'attribute table' and then receives the ESP_GATTS_CREAT_ATTR_TAB_EVT from which it takes the handles of all attributes so that it can later reference a handle back to an attribute.

jtantera
Posts: 6
Joined: Thu Mar 03, 2022 3:22 pm

Re: BLE GATTS_READ_EVT - diferenciate via handle

Postby jtantera » Fri Nov 24, 2023 2:42 pm

The example creates an 'attribute table' and then receives the ESP_GATTS_CREAT_ATTR_TAB_EVT from which it takes the handles of all attributes so that it can later reference a handle back to an attribute.
Sorry but i dont quite undertand. I am quite new in ESP32 and specially in Bluetooth BLE.
This line memcpy takes the handles al copys them into the heart_rate_handle_table.
Where i can connect the read event from the IDX_CHAR_B or any other one to a function that can reply the information that i want.

I dont understand quite yet all the data_base created.

Thanks for your response

MicroController
Posts: 2661
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: BLE GATTS_READ_EVT - diferenciate via handle

Postby MicroController » Sat Nov 25, 2023 11:19 am

Where i can connect the read event from the IDX_CHAR_B or any other one to a function that can reply the information that i want.
Yeah, the example doesn't make it very clear.
The trick is that you define your GATT database, i.e. a list/array of all the BLE attributes you want to provide. Then you pass this list of N attributes (N=HRS_IDX_NB in this case) to esp_ble_gatts_create_attr_tab(...) and in response you get the ESP_GATTS_CREAT_ATTR_TAB_EVT event which contains a list of N handles. In this handle list, the i-th entry corresponds to the i-th attribute from the list you passed to esp_ble_gatts_create_attr_tab(...).

So you could do:

Code: Select all

static const uint32_t ATTRIBUTE_COUNT = sizeof(gatt_db)/sizeof(*gatt_db); // # of attribute entries in the gatt_db
static uint16_t handle_list[ATTRIBUTE_COUNT]; // <- to be filled by handling ESP_GATTS_CREAT_ATTR_TAB_EVT 

const esp_gatts_attr_db_t* get_attribute_by_handle(const uint16_t handle) {
  for(int i = 0; i < ATTRIBUTE_COUNT; ++i ) {
    if(handle_list[i] == handle) {
	  return &(gatt_db[i]);
    }
  }
  return NULL; // handle not found?!
}
or like the example does it:

Code: Select all

if(handle_list[MY_ATTRIBUTE_X_INDEX] == handle) {
  // Handle attribute X...
} else
if(handle_list[MY_ATTRIBUTE_Y_INDEX] == handle) {
  // Handle attribute Y...
} ...

jtantera
Posts: 6
Joined: Thu Mar 03, 2022 3:22 pm

Re: BLE GATTS_READ_EVT - diferenciate via handle

Postby jtantera » Mon Nov 27, 2023 2:17 pm

I think that I understand you.
You propose to create this function

Code: Select all

const esp_gatts_attr_db_t* get_attribute_by_handle(const uint16_t handle)
This function returns the index in the table acording to the handle passed.

You propoouse tu fill the static uint16_t handle_list[ATTRIBUTE_COUNT] during the ESP_GATTS_CREAT_ATTR_TAB_EVT

Something like this?

Code: Select all

memcpy(handle_list, param->add_attr_tab.handles, sizeof(handle_list));
Then when the event ESP_GATTS_READ_EVT its triggered, I can select the handler using the index return of

Code: Select all

get_attribute_by_handle(param->read.handle)
.

I understand right?



I can't find this in the example, i asume that its an example made by you
or like the example does it:

Code: Select all

if(handle_list[MY_ATTRIBUTE_X_INDEX] == handle) {
  // Handle attribute X...
} else
if(handle_list[MY_ATTRIBUTE_Y_INDEX] == handle) {
  // Handle attribute Y...
} ...

MicroController
Posts: 2661
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: BLE GATTS_READ_EVT - diferenciate via handle

Postby MicroController » Tue Nov 28, 2023 8:03 am

I understand right?
Yes :)
I can't find this in the example, i asume that its an example made by you
or like the example does it:

Code: Select all

if(handle_list[MY_ATTRIBUTE_X_INDEX] == handle) {
  // Handle attribute X...
} else
if(handle_list[MY_ATTRIBUTE_Y_INDEX] == handle) {
  // Handle attribute Y...
} ...
It's just my attempt to more clearly show the logic in the example(s), i.e.

Code: Select all

//Habilitacion de notificaciones de caracteristicas
if (heart_rate_handle_table[IDX_CHAR_CFG_A] == param->write.handle && param->write.len == 2){
...

jtantera
Posts: 6
Joined: Thu Mar 03, 2022 3:22 pm

Re: BLE GATTS_READ_EVT - diferenciate via handle

Postby jtantera » Tue Nov 28, 2023 8:48 am

Thank you VERY MUCH!
I will try to implement this and let you know how it works.

Again thanks for your time! :D

jtantera
Posts: 6
Joined: Thu Mar 03, 2022 3:22 pm

Re: BLE GATTS_READ_EVT - diferenciate via handle

Postby jtantera » Tue Nov 28, 2023 10:55 am

So, i implemented some code and star testing it.

Here is how my DB was configured:

Code: Untitled.c Select all


/* 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_RSP_BY_APP}, {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_RSP_BY_APP}, {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}},

/* Descripción para Característica B */
[IDX_CHAR_DESCR_B] =
{{ESP_GATT_RSP_BY_APP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_B), strlen(CHAR_DESCRIP_B), (uint8_t *)CHAR_DESCRIP_B}},

/* Client Characteristic Configuration Descriptor */
[IDX_CHAR_CFG_B] =
{{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_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}},

/* Descripción para Característica C */
[IDX_CHAR_DESCR_C] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_C), strlen(CHAR_DESCRIP_C), (uint8_t *)CHAR_DESCRIP_C}},

/* Client Characteristic Configuration Descriptor */
[IDX_CHAR_CFG_C] =
{{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_D] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_D] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_D, 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_D] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_D] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_D), strlen(CHAR_DESCRIP_D), (uint8_t *)CHAR_DESCRIP_D}},

/* Characteristic Declaration */
[IDX_CHAR_E] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_E] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_E, 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_E] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_E] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_E), strlen(CHAR_DESCRIP_E), (uint8_t *)CHAR_DESCRIP_E}},


//CARACTERISTICAS DE ESCRITURA
/* Characteristic Declaration */
[IDX_CHAR_0] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_0] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_0, 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_0] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_0] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_0), strlen(CHAR_DESCRIP_0), (uint8_t *)CHAR_DESCRIP_0}},

[IDX_CHAR_1] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_1] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_1, 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_1] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_1] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_1), strlen(CHAR_DESCRIP_1), (uint8_t *)CHAR_DESCRIP_1}},

[IDX_CHAR_2] =
{{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_notify}},

/* Characteristic Value */
[IDX_CHAR_VAL_2] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_2, 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_2] =
{{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}},

/* Descripción para Característica E */
[IDX_CHAR_DESCR_2] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&descr_uuid, ESP_GATT_PERM_READ,
strlen(CHAR_DESCRIP_2), strlen(CHAR_DESCRIP_2), (uint8_t *)CHAR_DESCRIP_2}},
};
Here its the implementation of the ESP_GATTS_READ_EVT

Code: Untitled.c Select all


case ESP_GATTS_READ_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_READ_EVT");
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;

ESP_LOGI(GATTS_TABLE_TAG, "param->read.handle = %d", param->read.handle);
ESP_LOGI(GATTS_TABLE_TAG, "heart_rate_handle_table[IDX_CHAR_CFG_A] = %d", heart_rate_handle_table[IDX_CHAR_CFG_A]);
ESP_LOGI(GATTS_TABLE_TAG, "heart_rate_handle_table[IDX_CHAR_CFG_B] = %d", heart_rate_handle_table[IDX_CHAR_CFG_B]);
ESP_LOGI(GATTS_TABLE_TAG, "heart_rate_handle_table[IDX_CHAR_CFG_C] = %d", heart_rate_handle_table[IDX_CHAR_CFG_C]);
ESP_LOGI(GATTS_TABLE_TAG, "heart_rate_handle_table[IDX_CHAR_CFG_D] = %d", heart_rate_handle_table[IDX_CHAR_CFG_D]);
ESP_LOGI(GATTS_TABLE_TAG, "heart_rate_handle_table[IDX_CHAR_CFG_E] = %d", heart_rate_handle_table[IDX_CHAR_CFG_E]);
ESP_LOGI(GATTS_TABLE_TAG, "heart_rate_handle_table[IDX_CHAR_CFG_0] = %d", heart_rate_handle_table[IDX_CHAR_CFG_0]);
ESP_LOGI(GATTS_TABLE_TAG, "heart_rate_handle_table[IDX_CHAR_CFG_1] = %d", heart_rate_handle_table[IDX_CHAR_CFG_1]);
ESP_LOGI(GATTS_TABLE_TAG, "heart_rate_handle_table[IDX_CHAR_CFG_2] = %d", heart_rate_handle_table[IDX_CHAR_CFG_2]);

if (heart_rate_handle_table[IDX_CHAR_CFG_A] == param->read.handle){
ESP_LOGI(GATTS_TABLE_TAG, "Read event IDX_CHAR_CFG_A");
} else if (heart_rate_handle_table[IDX_CHAR_CFG_B] == param->read.handle) {
ESP_LOGI(GATTS_TABLE_TAG, "Read event IDX_CHAR_CFG_B");
} else if (heart_rate_handle_table[IDX_CHAR_CFG_C] == param->read.handle) {
ESP_LOGI(GATTS_TABLE_TAG, "Read event IDX_CHAR_CFG_C");
}

esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
ESP_GATT_OK, &rsp);
break;
And here its the terminal LOG:
Image


I am using the app nRF Connect for Android to reand and write the values on the ESP32.
Image

The problem is when I use the A characteristic the event and handler works fine. But if i use the B characteristic the number of handler its wrong. I change the comparison of IDX_CHAR_CFG_B with other values to check but clearly there are several things that I don't quite understand about how the BT database works.

Can you tell me where I am missing?

I bought a course on UDEMY to try to understand it better but it's pretty bad and doesn't explain how things work.
Can you recommend a course or book to understand better this topic?

Again, thanks

MicroController
Posts: 2661
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: BLE GATTS_READ_EVT - diferenciate via handle

Postby MicroController » Tue Nov 28, 2023 2:00 pm

The characteristics in your service each are composed of three attributes, one for the characteristic declaration (e.g. at IDX_CHAR_A), one for the characteristic's value (e.g. IDX_CHAR_VAL_A) and one for the cccd (e.g. IDX_CHAR_CFG_A).
Commonly, the declaration is constant and the cccd should be handled by Bluedroid internally. So 1) you can leave them set to ESP_GATT_AUTO_RSP, and 2) you only care about accesses to the value attribute. If you want to handle these value accesses yourself, you should set the attribute to ESP_GATT_RSP_BY_APP instead of ESP_GATT_AUTO_RSP, and of course react to the handles of the value attribute(s) (e.g. IDX_CHAR_VAL_A), but not the other attributes (e.g. IDX_CHAR_CFG_A, IDX_CHAR_A) which are set to ESP_GATT_AUTO_RSP.

jtantera
Posts: 6
Joined: Thu Mar 03, 2022 3:22 pm

Re: BLE GATTS_READ_EVT - diferenciate via handle

Postby jtantera » Tue Nov 28, 2023 3:52 pm

What you just told me, make me realize that i was clicking the wrong button on the "nRF Connect" app.
Now its replying like it should.

Anyways, for all the only read characteristics i will leave ESP_GATT_RSP_BY_APP configuration, meanwhile all the notification characteristics i will leave the ESP_GATT_AUTO_RSP configuration.

Thanks again for your help.

Who is online

Users browsing this forum: Bytespider, ChatGPT-User, Google [Bot] and 17 guests