Skip to content

Commit df5d6cd

Browse files
committed
Merge branch 'feat/ble_read_long_value_v5.0' into 'release/v5.0'
feat(ble/bluedroid): Add Read Long Characteristic Values example (v5.0) See merge request espressif/esp-idf!37485
2 parents 16a0789 + 5117099 commit df5d6cd

File tree

3 files changed

+130
-25
lines changed

3 files changed

+130
-25
lines changed

examples/bluetooth/bluedroid/ble/gatt_server/main/Kconfig.projbuild

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
11
menu "Example 'GATT SERVER' Config"
2+
config EXAMPLE_CHAR_READ_DATA_LEN
3+
int "Length of characteristic value for Read Long support"
4+
default 4
5+
range 4 1000
6+
help
7+
Defines the length of the characteristic value used in the
8+
Read Characteristic Values procedure.
9+
10+
If the characteristic value length exceeds (MTU - 1) bytes,
11+
the Read Long procedure is automatically triggered, requiring
12+
multiple `ATT_READ_BLOB_REQ` requests from the client to retrieve
13+
the full value. For details, refer to Bluetooth Core Specification
14+
Vol 3, Part G §4.8.3.
15+
16+
Note: To ensure compatibility with certain smartphones,
17+
the Read Long response length should ideally not exceed 500 bytes.
18+
If a device receives more than 600 bytes, it may stop sending
19+
`ATT_READ_BLOB_REQ`, resulting in incomplete data reception.
20+
221

322
config EXAMPLE_SET_RAW_ADV_DATA
423
bool "Use raw data for advertising packets and scan response data"

examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Unlicense OR CC0-1.0
55
*/
@@ -59,6 +59,19 @@ static char test_device_name[ESP_BLE_ADV_NAME_LEN_MAX] = "ESP_GATTS_DEMO";
5959
#define PREPARE_BUF_MAX_SIZE 1024
6060

6161
static uint8_t char1_str[] = {0x11,0x22,0x33};
62+
63+
static uint16_t descr_value = 0x0;
64+
/**
65+
* Current MTU size for the active connection.
66+
*
67+
* This simplified implementation assumes a single connection.
68+
* For multi-connection scenarios, the MTU should be stored per connection ID.
69+
*/
70+
static uint16_t local_mtu = 23;
71+
72+
static uint8_t char_value_read[CONFIG_EXAMPLE_CHAR_READ_DATA_LEN] = {0xDE,0xED,0xBE,0xEF};
73+
74+
6275
static esp_gatt_char_prop_t a_property = 0;
6376
static esp_gatt_char_prop_t b_property = 0;
6477

@@ -348,17 +361,53 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
348361
esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_A_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_A);
349362
break;
350363
case ESP_GATTS_READ_EVT: {
351-
ESP_LOGI(GATTS_TAG, "Characteristic read, conn_id %d, trans_id %" PRIu32 ", handle %d", param->read.conn_id, param->read.trans_id, param->read.handle);
364+
ESP_LOGI(GATTS_TAG,
365+
"Characteristic read request: conn_id=%d, trans_id=%" PRIu32 ", handle=%d, is_long=%d, offset=%d, need_rsp=%d",
366+
param->read.conn_id, param->read.trans_id, param->read.handle,
367+
param->read.is_long, param->read.offset, param->read.need_rsp);
368+
369+
// If no response is needed, exit early (stack handles it automatically)
370+
if (!param->read.need_rsp) {
371+
return;
372+
}
373+
352374
esp_gatt_rsp_t rsp;
353375
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
354376
rsp.attr_value.handle = param->read.handle;
355-
rsp.attr_value.len = 4;
356-
rsp.attr_value.value[0] = 0xde;
357-
rsp.attr_value.value[1] = 0xed;
358-
rsp.attr_value.value[2] = 0xbe;
359-
rsp.attr_value.value[3] = 0xef;
360-
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
361-
ESP_GATT_OK, &rsp);
377+
378+
// Handle descriptor read request
379+
if (param->read.handle == gl_profile_tab[PROFILE_A_APP_ID].descr_handle) {
380+
memcpy(rsp.attr_value.value, &descr_value, 2);
381+
rsp.attr_value.len = 2;
382+
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp);
383+
return;
384+
}
385+
386+
// Handle characteristic read request
387+
if (param->read.handle == gl_profile_tab[PROFILE_A_APP_ID].char_handle) {
388+
uint16_t offset = param->read.offset;
389+
390+
// Validate read offset
391+
if (param->read.is_long && offset > CONFIG_EXAMPLE_CHAR_READ_DATA_LEN) {
392+
ESP_LOGW(GATTS_TAG, "Read offset (%d) out of range (0-%d)", offset, CONFIG_EXAMPLE_CHAR_READ_DATA_LEN);
393+
rsp.attr_value.len = 0;
394+
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_INVALID_OFFSET, &rsp);
395+
return;
396+
}
397+
398+
// Determine response length based on MTU
399+
uint16_t mtu_size = local_mtu - 1; // ATT header (1 byte)
400+
uint16_t send_len = (CONFIG_EXAMPLE_CHAR_READ_DATA_LEN - offset > mtu_size) ? mtu_size : (CONFIG_EXAMPLE_CHAR_READ_DATA_LEN - offset);
401+
402+
memcpy(rsp.attr_value.value, &char_value_read[offset], send_len);
403+
rsp.attr_value.len = send_len;
404+
405+
// Send response to GATT client
406+
esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp);
407+
if (err != ESP_OK) {
408+
ESP_LOGE(GATTS_TAG, "Failed to send response: %s", esp_err_to_name(err));
409+
}
410+
}
362411
break;
363412
}
364413
case ESP_GATTS_WRITE_EVT: {
@@ -367,7 +416,7 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
367416
ESP_LOGI(GATTS_TAG, "value len %d, value ", param->write.len);
368417
ESP_LOG_BUFFER_HEX(GATTS_TAG, param->write.value, param->write.len);
369418
if (gl_profile_tab[PROFILE_A_APP_ID].descr_handle == param->write.handle && param->write.len == 2){
370-
uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0];
419+
descr_value = param->write.value[1]<<8 | param->write.value[0];
371420
if (descr_value == 0x0001){
372421
if (a_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY){
373422
ESP_LOGI(GATTS_TAG, "Notification enable");
@@ -412,6 +461,7 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
412461
break;
413462
case ESP_GATTS_MTU_EVT:
414463
ESP_LOGI(GATTS_TAG, "MTU exchange, MTU %d", param->mtu.mtu);
464+
local_mtu = param->mtu.mtu;
415465
break;
416466
case ESP_GATTS_UNREG_EVT:
417467
break;
@@ -490,6 +540,7 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
490540
ESP_LOGI(GATTS_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
491541
ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason);
492542
esp_ble_gap_start_advertising(&adv_params);
543+
local_mtu = 23; // Reset MTU for a single connection
493544
break;
494545
case ESP_GATTS_CONF_EVT:
495546
ESP_LOGI(GATTS_TAG, "Confirm receive, status %d, attr_handle %d", param->conf.status, param->conf.handle);

examples/bluetooth/bluedroid/ble/gatt_server/tutorial/Gatt_Server_Example_Walkthrough.md

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -672,24 +672,59 @@ bool need_rsp; /*!< The read operation need to do response */
672672

673673
In this example, a response is constructed with dummy data and sent back to the host using the same handle given by the event. In addition to the response, the GATT interface, the connection ID and the transfer ID are also included as parameters in the `esp_ble_gatts_send_response()` function. This function is necessary if the auto response byte is set to NULL when creating the characteristic or descriptor.
674674

675+
This example also implements the Read Long Characteristic Values procedure, efficiently handling fragmented read requests while dynamically adapting to connection-specific MTU sizes.
676+
675677
```c
676678
case ESP_GATTS_READ_EVT: {
677-
ESP_LOGI(GATTS_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d\n",
678-
param->read.conn_id, param->read.trans_id, param->read.handle);
679-
esp_gatt_rsp_t rsp;
680-
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
681-
rsp.attr_value.handle = param->read.handle;
682-
rsp.attr_value.len = 4;
683-
rsp.attr_value.value[0] = 0xde;
684-
rsp.attr_value.value[1] = 0xed;
685-
rsp.attr_value.value[2] = 0xbe;
686-
rsp.attr_value.value[3] = 0xef;
687-
esp_ble_gatts_send_response(gatts_if,
688-
param->read.conn_id,
689-
param->read.trans_id,
690-
ESP_GATT_OK, &rsp);
691-
break;
679+
ESP_LOGI(GATTS_TAG,
680+
"Characteristic read request: conn_id=%d, trans_id=%" PRIu32 ", handle=%d, is_long=%d, offset=%d, need_rsp=%d",
681+
param->read.conn_id, param->read.trans_id, param->read.handle,
682+
param->read.is_long, param->read.offset, param->read.need_rsp);
683+
684+
// If no response is needed, exit early (stack handles it automatically)
685+
if (!param->read.need_rsp) {
686+
return;
687+
}
688+
689+
esp_gatt_rsp_t rsp;
690+
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
691+
rsp.attr_value.handle = param->read.handle;
692+
693+
// Handle descriptor read request
694+
if (param->read.handle == gl_profile_tab[PROFILE_A_APP_ID].descr_handle) {
695+
memcpy(rsp.attr_value.value, &descr_value, 2);
696+
rsp.attr_value.len = 2;
697+
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp);
698+
return;
692699
}
700+
701+
// Handle characteristic read request
702+
if (param->read.handle == gl_profile_tab[PROFILE_A_APP_ID].char_handle) {
703+
uint16_t offset = param->read.offset;
704+
705+
// Validate read offset
706+
if (param->read.is_long && offset > CONFIG_EXAMPLE_CHAR_READ_DATA_LEN) {
707+
ESP_LOGW(GATTS_TAG, "Read offset (%d) out of range (0-%d)", offset, CONFIG_EXAMPLE_CHAR_READ_DATA_LEN);
708+
rsp.attr_value.len = 0;
709+
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_INVALID_OFFSET, &rsp);
710+
return;
711+
}
712+
713+
// Determine response length based on MTU
714+
uint16_t mtu_size = local_mtu - 1; // ATT header (1 byte)
715+
uint16_t send_len = (CONFIG_EXAMPLE_CHAR_READ_DATA_LEN - offset > mtu_size) ? mtu_size : (CONFIG_EXAMPLE_CHAR_READ_DATA_LEN - offset);
716+
717+
memcpy(rsp.attr_value.value, &char_value_read[offset], send_len);
718+
rsp.attr_value.len = send_len;
719+
720+
// Send response to GATT client
721+
esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp);
722+
if (err != ESP_OK) {
723+
ESP_LOGE(GATTS_TAG, "Failed to send response: %s", esp_err_to_name(err));
724+
}
725+
}
726+
break;
727+
}
693728
```
694729

695730
## Managing Write Events

0 commit comments

Comments
 (0)