This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

How do I know that a characteristic has been read completely?

So I've got this long characteristic which takes several read operations with offsets to read it completely. Idea is to have it refreshed only after the client has read it.

Is there a mechanism for figuring this out?

EDIT - This is the way Ulrich suggested doing it:

void ble_pem_hum_data_on_ble_evt(pem_hum_data_t *pem_hum_data, ble_evt_t *p_ble_evt)
{
    switch (p_ble_evt->header.evt_id)
    {
        ...
        case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
        {
            on_rw_auth_req(pem_hum_data, p_ble_evt);
        } break;
        ...
    }
}

static uint8_t data_buffer[ARRAY_SIZE_MAX] = {0};

static void on_rw_auth_req(my_service_t *my_service, ble_evt_t *p_ble_evt)
{
    ble_gatts_evt_rw_authorize_request_t *p_evt_rw_auth_req = &p_ble_evt->evt.gatts_evt.params.authorize_request;
    
    if (p_evt_rw_auth_req->type == BLE_GATTS_AUTHORIZE_TYPE_READ)
    {
        if (p_evt_rw_auth_req->request.read.handle == my_service->data_array_char_handles.value_handle)
        {
            uint16_t p_len;
            sd_ble_gatts_value_get(p_evt_rw_auth_req->request.read.handle, 
                                   p_evt_rw_auth_req->request.read.offset, 
                                   &p_len, data_buffer);
            
            ble_gatts_rw_authorize_reply_params_t p_rw_authorize_reply_params;
            p_rw_authorize_reply_params.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
            p_rw_authorize_reply_params.params.read.gatt_status = BLE_GATT_STATUS_SUCCESS;
            p_rw_authorize_reply_params.params.read.update = 1;
            p_rw_authorize_reply_params.params.read.len = p_len;
            p_rw_authorize_reply_params.params.read.p_data = (uint8_t *)&data_buffer[0];
            p_rw_authorize_reply_params.params.read.offset = p_evt_rw_auth_req->request.read.offset;
            sd_ble_gatts_rw_authorize_reply(my_service->conn_handle, &p_rw_authorize_reply_params);
        }
    }
}

It does seems to work, but there are two issues with it:

  1. To respond to the read request with data, I can't just get a pointer from sd_ble_gatts_value_get() and pass it to sd_ble_gatts_rw_authorize_reply(), I have to get a complete copy in data_buffer and then pass that. Seems kinda wasteful. Is there a way around that?

  2. I assumed that I'd have to manually manage the way data is transferred - given an offset by a client, pass that to sd_ble_gatts_value_get(), if length received in p_len is more than 20, truncate the data and send only the first 20 bytes, but it seems to work even when I pass the whole characteristic to sd_ble_gatts_rw_authorize_reply() at once - 126 bytes! Is there some mechanism which handles the data for me or am I completely misunderstanding this?

  • Hi andrey,

    You should look into enabling read authorization for the characteristic. This will produce a BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST event every time a read operation happens, and prompting you to allow the operation or not. The request->read field will contain the handle, context and offset.

    I believe you could set up a handler for these events and filter for your handle. If you then compare the current length of your characteristic to the offset, you should be able to see when the peer has read it all. It is also possible to change the value entirely during this authorization period, so you could update the value when the offset is 0 if your data is of a type that needs to be fresh.

  • I've added a couple of questions for you in the OP.

    1. If you want to send whatever is currently in the Attribute Table, you can simply set the "update" field to 0. That way you will be notified of the read but you won't have to provide any data

    2. Whatever you provide in p_data if your update field is 1 will be written into the Attribute Table before the stack replies to the read operation from the client. That means that you can write more than 20 bytes, but the reply itself over the air will only contain the maximum of 20 bytes

Related