Where are characteristic values stored in ESP-IDF?

jhulbert
Posts: 7
Joined: Thu Jan 28, 2021 9:50 pm

Where are characteristic values stored in ESP-IDF?

Postby jhulbert » Thu Apr 08, 2021 2:16 am

I was looking through the gatts_table_creat_demo.c example and I noticed in the attribute table all of the characteristic value entries in the table, the "value" field (i.e. esp_attr_desc_t.value) is a pointer to char_value[4] (a literal array of 8-bit hex values).

This struck me as odd because I thought esp_attr_desc_t.value was a pointer to some memory containing the actually value of the characteristic attribute. If that were true, why would characteristics A, B, and C be a pointer to the same memory region? I've removed the other attributes in the table below for simplicity.
  1. /* Full Database Description - Used to add attributes into the database */
  2. static const uint8_t char_value[4]                 = {0x11, 0x22, 0x33, 0x44};
  3.  
  4. static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] =
  5. {
  6.     /* Characteristic Value */
  7.     [IDX_CHAR_VAL_A] =
  8.     {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_A, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
  9.       GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
  10.  
  11.     /* Characteristic Value */
  12.     [IDX_CHAR_VAL_B]  =
  13.     {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_B, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
  14.       GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
  15.  
  16.     /* Characteristic Value */
  17.     [IDX_CHAR_VAL_C]  =
  18.     {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_C, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
  19.       GATTS_DEMO_CHAR_VAL_LEN_MAX, sizeof(char_value), (uint8_t *)char_value}},
  20.  
  21. };

To dig into this deeper, I then added some code to print the value field of each of these attributes, as shown in the listing below. I used the nRF Connect app to connect to the example and write to the characteristics.
  1. void log_char_value_task(void* param)
  2. {
  3.     while (1) {
  4.         ESP_LOGI(GATTS_TABLE_TAG, "Characteristic A Value:");
  5.         esp_log_buffer_hex(GATTS_TABLE_TAG, gatt_db[IDX_CHAR_VAL_A].att_desc.value, gatt_db[IDX_CHAR_VAL_A].att_desc.length);
  6.         ESP_LOGI(GATTS_TABLE_TAG, "Characteristic B Value:");
  7.         esp_log_buffer_hex(GATTS_TABLE_TAG, gatt_db[IDX_CHAR_VAL_B].att_desc.value, gatt_db[IDX_CHAR_VAL_B].att_desc.length);
  8.         ESP_LOGI(GATTS_TABLE_TAG, "Characteristic C Value:");
  9.         esp_log_buffer_hex(GATTS_TABLE_TAG, gatt_db[IDX_CHAR_VAL_C].att_desc.value, gatt_db[IDX_CHAR_VAL_C].att_desc.length);
  10.         vTaskDelay(5000/portTICK_PERIOD_MS);
  11.     }
  12. }

What I found is that the att_desc.value printed for each characteristic did not change; it was always the default value in char_value[4] of {0x11, 0x22, 0x33, 0x44}. I guess this makes sense since char_value[4] is defined a const. The characteristic values did change as expected after writing with nRF Connect; what was shown in this app differed to what was printed below. Therefore, the ESP-IDF BLE code doesn't appear to directly expose the characteristic values in esp_attr_desc_t.value, which makes sense since reads and writes should be through the appropriate BLE protocol functions anyway.

I (51225) GATTS_TABLE_DEMO: Characteristic A Value:
I (51225) GATTS_TABLE_DEMO: 11 22 33 44
I (51225) GATTS_TABLE_DEMO: Characteristic B Value:
I (51225) GATTS_TABLE_DEMO: 11 22 33 44
I (51225) GATTS_TABLE_DEMO: Characteristic C Value:
I (51235) GATTS_TABLE_DEMO: 11 22 33 44
I (56235) GATTS_TABLE_DEMO: Characteristic A Value:
I (56235) GATTS_TABLE_DEMO: 11 22 33 44
I (56235) GATTS_TABLE_DEMO: Characteristic B Value:
I (56235) GATTS_TABLE_DEMO: 11 22 33 44
I (56235) GATTS_TABLE_DEMO: Characteristic C Value:
I (56245) GATTS_TABLE_DEMO: 11 22 33 44


This leaves me with some questions/knowledge gaps:

1. What is esp_attr_desc_t.value? Is it just the initial characteristic value?

2. When building an attribute table, how should esp_attr_desc_t.value be assigned? As mentioned above in gatts_table_creat_demo.c they are pointers to the same array for each characteristic. What's odd is this differs from the walkthrough tutorial for this example. There, esp_attr_desc_t.value are given as NULL, (uint8_t *)body_sensor_loc_val, and (uint8_t *)heart_ctrl_point.
  1. /// Full HRS Database Description - Used to add attributes into the database
  2. static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] =
  3. {
  4.     // Heart Rate Measurement Characteristic Value
  5.     [HRS_IDX_HR_MEAS_VAL]               =
  6.     {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ,
  7.       HRPS_HT_MEAS_MAX_LEN,0, NULL}},
  8.  
  9.           // Body Sensor Location Characteristic Value
  10.     [HRS_IDX_BOBY_SENSOR_LOC_VAL]   =
  11.     {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&body_sensor_location_uuid, ESP_GATT_PERM_READ,
  12.       sizeof(uint8_t), sizeof(body_sensor_loc_val), (uint8_t *)body_sensor_loc_val}},
  13.  
  14.        // Heart Rate Control Point Characteristic Value
  15.     [HRS_IDX_HR_CTNL_PT_VAL]             =
  16.     {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_ctrl_point, ESP_GATT_PERM_WRITE|ESP_GATT_PERM_READ,
  17.       sizeof(uint8_t), sizeof(heart_ctrl_point), (uint8_t *)heart_ctrl_point}},
  18. };

3. Where are characteristic values, i.e. the memory which is changed on a write event & returned on a read event, actually stored? Is this something under the hood that I need not worry about?

Who is online

Users browsing this forum: No registered users and 147 guests