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.
- /* Full Database Description - Used to add attributes into the database */
- static const uint8_t char_value[4] = {0x11, 0x22, 0x33, 0x44};
- static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] =
- {
- /* 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}},
- /* 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 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}},
- };
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.
- void log_char_value_task(void* param)
- {
- while (1) {
- ESP_LOGI(GATTS_TABLE_TAG, "Characteristic A Value:");
- 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);
- ESP_LOGI(GATTS_TABLE_TAG, "Characteristic B Value:");
- 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);
- ESP_LOGI(GATTS_TABLE_TAG, "Characteristic C Value:");
- 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);
- vTaskDelay(5000/portTICK_PERIOD_MS);
- }
- }
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.
- /// Full HRS Database Description - Used to add attributes into the database
- static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] =
- {
- // Heart Rate Measurement Characteristic Value
- [HRS_IDX_HR_MEAS_VAL] =
- {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ,
- HRPS_HT_MEAS_MAX_LEN,0, NULL}},
- // Body Sensor Location Characteristic Value
- [HRS_IDX_BOBY_SENSOR_LOC_VAL] =
- {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&body_sensor_location_uuid, ESP_GATT_PERM_READ,
- sizeof(uint8_t), sizeof(body_sensor_loc_val), (uint8_t *)body_sensor_loc_val}},
- // Heart Rate Control Point Characteristic Value
- [HRS_IDX_HR_CTNL_PT_VAL] =
- {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_ctrl_point, ESP_GATT_PERM_WRITE|ESP_GATT_PERM_READ,
- sizeof(uint8_t), sizeof(heart_ctrl_point), (uint8_t *)heart_ctrl_point}},
- };
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?