New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BLE GATT server doesn't read/write value (IDFGH-663) #3102
Comments
Hi phatpaul : |
you can use |
Great thanks @Weijian-Espressif . That's the function I was looking for. But it doesn't seem well documented. Perhaps add some notes that mention Also, my other questions, which I will try to figure out experimentally:
|
So it looks like the answer is
(I am using ESP_GATTS_EXEC_WRITE_EVT to receive long writes. Seems I have to buffer the attr handle from one of the Prepare writes so I know which characteristic is being written to on a ESP_GATTS_EXEC_WRITE_EVT - as there is no reference to the handle given in that event?!) But
Is there another event which happens earlier during a read? I want to have the opportunity to set the value before it is sent out. (I suppose I could set the BLE internal char value whenever my value changes. But it seems wasteful to frequently copy it to the BLE stack internal variable even if it is not being read by BLE) |
@phatpaul, |
@Weijian-Espressif thanks for confirming. I want to use ESP_GATT_AUTO_RSP because my characteristic length might be larger than MTU. I didn't want to write my own code to handle the chunking for long-read and long-write. I saw there is some code to implement long-write in the example, but I don't see any example for long-read. (example_write_event_env() and example_exec_write_event_env())
(I found this implementation in C++, but a C example would be helpful - https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLECharacteristic.cpp#L348 ) |
@phatpaul, |
@Weijian-Espressif thanks, that worked for sending long value with
|
@Weijian-Espressif let me take back my previous report that esp_ble_gatts_send_response() works for me. I like the behavior that if I use ESP_GATT_AUTO_RSP, and MTU is still = 23, the long read just works.
|
@phatpaul , The length of a response must less than MTU size. |
Is the source code available for how the stack implements the long-read and chunking internally? |
@phatpaul please refer.
|
Thanks @Weijian-Espressif that helped a lot. Here's my solution, since I want to trigger the APP read handler once when a long read is begun, and buffer the value from the APP read handler so it can be pushed out in chunks during subsequent read events. I based it on your example plus the existing example_prepare_write_event_env() functions. Would you please review it to look for memory leak etc? Feel free to include it in the example. typedef struct {
uint8_t *prepare_buf;
int prepare_len;
uint16_t handle;
} prepare_type_env_t;
static prepare_type_env_t prepare_write_env;
static prepare_type_env_t prepare_read_env;
...
// support read and long read
void gatts_proc_read(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_read_env, esp_ble_gatts_cb_param_t *param, uint8_t *p_rsp_v, uint16_t v_len)
{
if(!param->read.need_rsp) {
return;
}
uint16_t value_len = gatts_mtu - 1;
if(v_len - param->read.offset < (gatts_mtu - 1)) { // read response will fit in one MTU?
value_len = v_len - param->read.offset;
}
else if (param->read.offset == 0) // it's the start of a long read (could also use param->read.is_long here?)
{
ESP_LOGI(TAG, "long read, handle = %d, value len = %d", param->read.handle, v_len);
if (v_len > PREPARE_BUF_MAX_SIZE) {
ESP_LOGE(TAG, "long read too long");
return;
}
if (prepare_read_env->prepare_buf != NULL) {
ESP_LOGW(TAG, "long read buffer not free");
free(prepare_read_env->prepare_buf);
prepare_read_env->prepare_buf = NULL;
}
prepare_read_env->prepare_buf = (uint8_t *)malloc(PREPARE_BUF_MAX_SIZE * sizeof(uint8_t));
prepare_read_env->prepare_len = 0;
if (prepare_read_env->prepare_buf == NULL) {
ESP_LOGE(TAG, "long read no mem");
return;
}
memcpy(prepare_read_env->prepare_buf, p_rsp_v, v_len);
prepare_read_env->prepare_len = v_len;
prepare_read_env->handle = param->read.handle;
}
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;
rsp.attr_value.len = value_len;
rsp.attr_value.offset = param->read.offset;
memcpy(rsp.attr_value.value, &p_rsp_v[param->read.offset], value_len);
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp);
}
// continuation of read, use buffered value
void gatts_proc_long_read(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare_read_env, esp_ble_gatts_cb_param_t *param)
{
if (prepare_read_env->prepare_buf && (prepare_read_env->handle == param->read.handle)) // something buffered?
{
gatts_proc_read(gatts_if, prepare_read_env, param, prepare_read_env->prepare_buf, prepare_read_env->prepare_len);
if(prepare_read_env->prepare_len - param->read.offset < (gatts_mtu - 1)) // last read?
{
free(prepare_read_env->prepare_buf);
prepare_read_env->prepare_buf = NULL;
prepare_read_env->prepare_len = 0;
ESP_LOGI(TAG,"long_read ended");
}
}
else
{
ESP_LOGE(TAG,"long_read not buffered");
}
} 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)
{
...
case ESP_GATTS_READ_EVT:
{
ESP_LOGI(TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d offset %d", param->read.conn_id, param->read.trans_id, param->read.handle, param->read.offset);
// Note that if char is defined with ESP_GATT_AUTO_RSP, then this event is triggered after the internal value has been sent. So this event is not very useful.
// Otherwise if it is ESP_GATT_RSP_BY_APP, then call helper function gatts_proc_read()
if (!param->read.is_long) {
// If is.long is false then this is the first (or only) request to read data
int attrIndex;
/*
* Add calls to handle read events for each external service here
* Template:
* if( (attrIndex = getAttributeIndexBy#SERVICE#Handle(param->read.handle)) < #SERVICE MAX INDEX# )
* {
* ESP_LOGI(TAG, "#SERVICE# READ");
* handle#SERVICE#ReadEvent(attrIndex, param, &rsp);
* }
*/
if( (attrIndex = getAttributeIndexByMyHandle(param->read.handle)) < MY_SERV_NUM_ATTR )
{
//ESP_LOGI(TAG,"MY READ");
handleMyReadEvent(attrIndex, &rsp);
}
/* END read handler calls for external services */
gatts_proc_read(gatts_if, &prepare_read_env, param, rsp.attr_value.value, rsp.attr_value.len);
}
else // a continuation of a long read. Dont invoke the handle#SERVICE#ReadEvent again, just keep pumping out buffered data.
{
gatts_proc_long_read(gatts_if, &prepare_read_env, param);
}
} //Handle any reads of characteristics here
void handleMyReadEvent(int attrIndex, esp_gatt_rsp_t* rsp)
{
switch( attrIndex )
{
// Characteristic read values
// Note that if char is defined with ESP_GATT_AUTO_RSP, this event is triggered AFTER the internal value has been sent.
// So this event is not useful for setting the value with esp_ble_gatts_set_attr_value() - should do that somewhere else.
// But if char is defined with ESP_GATT_RSP_BY_APP, then build the response here before it is sent:
case MY_CHAR_VAL:
{
memcpy(rsp->attr_value.value, vol_db, VOL_DB_SIZE);
rsp->attr_value.len = VOL_DB_SIZE;
break;
}
}
} |
@phatpaul, If there are no more questions, we will close this issue. |
Thanks. Close it.
FYI my example of long read/write working with any MTU is here.
eagi223/esp-idf_Bluetooth_Multi-Service#6
…On Sun, Mar 31, 2019, 10:35 PM weijian_Espressif ***@***.***> wrote:
@phatpaul <https://github.com/phatpaul>, If there are no more questions,
we will close this issue.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#3102 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ADVVd0qUBEkSHXC9va9n9xIwFdxZv-bpks5vcXBugaJpZM4bScm2>
.
|
@phatpaul Thanks for reporting the issue. Feel free to reopen if the issue still exists. Thanks. |
Environment
git describe --tags
to find it): v3.1-151-g7063cdecdxtensa-esp32-elf-gcc --version
to find it): 1.22.0-80-g6c4433a5Problem Description
BLE GATT server does not read or write my global array when characteristic is defined with ESP_GATT_AUTO_RSP.
Expected Behavior
The global array that I provided via pointer to the stack on initialization should be read from and written to by the stack when the characteristic is defined with ESP_GATT_AUTO_RSP.
Actual Behavior
I can read and write the characteristic via BLE, but it seems my global array is never changed.
So it seems there is some internal buffer in the BLE stack storing the value because I can read back what I wrote via BLE.
It seems the global array that I passed to the stack on creation is only used to initialize the internal value.
Steps to reproduce
(or the better organized example project here: https://github.com/eagi223/esp-idf_Bluetooth_Multi-Service )
When I first read the characteristic, it returns what I have initialized the global to. I read [11 22 33 44], just as expected.
Then I write the value via BLE to 0x1234.
I can then read via BLE and it returns [12 34] as expected.
But my global array has not been changed.
Also, if I change my global array value via other means (not via BLE), the value I read via BLE is not changed.
Code to reproduce this issue
We have some characteristics defined using ESP_GATT_AUTO_RSP, and I supplied a pointer to the value when creating the gatt table.
The value is stored in a global uint8_t array.
I added some debug hints to print out the value of char_value[4] in gatts_profile_event_handler:
Debug Logs
Other items if possible
will provide if requested
Questions:
How and when do I access the internal value?
If the BLE stack is supposed to write to my global array automatically, does it do so before or after triggering ESP_GATTS_WRITE_EVT (or ESP_GATTS_EXEC_WRITE_EVT)?
If the BLE stack is supposed to read from my global array automatically, does it do so before or after triggering ESP_GATTS_READ_EVT?
Where is the intended behavior documented?
Note: I realize that I could use ESP_GATT_RSP_BY_APP and write code to handle the read/write to my global array. But I am using long-write and long-read, because the size of array may be larger than MTU. i.e. static uint8_t char_value[128] I want to use ESP_GATT_AUTO_RSP because it seems that the BLE stack is (somehow) taking care of the chunking required for long-write and long-read automatically.
BTW: I asked this on esp32.com a week ago and got no response: https://www.esp32.com/viewtopic.php?f=13&t=9345 . Also here's a relevant post: https://www.esp32.com/viewtopic.php?f=13&t=2459
The text was updated successfully, but these errors were encountered: