This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Need help understanding the behavior of `sd_ble_gatts_hvx()`

Update 2016-Mar-29: I used SDK v10 along with SoftDevice S110 v8 when I run the experiments described in this question.

Update 2016-Mar-30: Upload source code of the modified NUS module used in the experiments: test_characteristic.zip

I am modifying a copy of ble_nus.c (from SDK v10) to test the behavior of characteristics bigger than 20 bytes. Here is how I have the notifying part of ble_nus_string_send() modified to try to send a RX characteristic notification with new value length over 20.

uint32_t err_code;
uint16_t nTransactions = length/20;
int16_t i;
uint16_t tempU16;

hvx_params.handle = p_nus->rx_handles.value_handle;

if ((length % 20) != 0)
{
    nTransactions += 1;
}

// for (i = nTransactions - 1; i > -1; i--)
for (i = 0; i < nTransactions; i++)
{
    hvx_params.p_data = &p_string[i * 20];
    hvx_params.offset = i * 20;
    tempU16 = (length - i * 20) ;
    if (tempU16 > 20)
    {
        tempU16 = 20;
    }
    hvx_params.p_len    = &tempU16;
    hvx_params.type     = BLE_GATT_HVX_NOTIFICATION;
    err_code = sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
}

Of course I have also modified the characteristic sizes to BLE_GATTS_VAR_ATTR_LEN_MAX. I also enable read permission of the RX characteristic for my little experiment. My question is at the behavior of the code.

Both way of running the for loop (commented out way and the active way) only result in Android version of Master Control Panel receiving notification of the first 20 byte of the p_string I passed to the function. However:

  • With for (i = 0; i < nTransactions; i++), when I read the RX characteristic value, I have all the bytes of the p_string I tried to notify.

    For example, if I call with p_string = "01234567890123456789ABC", I will get a notify of value "01234567890123456789", but if I read, I can read all "01234567890123456789ABC"

  • With for (i = nTransactions - 1; i > -1; i--) I only got the first 20 byte of p_string, the rest is lost.

    For example, if I call with p_string = "01234567890123456789ABC", I will get a notify of value "01234567890123456789", and when I read, I could still only get "01234567890123456789"

I have tested with up to 59 bytes sent. I assume little would change with more/fewer bytes. So I have two questions:

  1. Why is there such a difference? Could it be due to how sd_ble_gatts_hvx() is implemented?

  2. Is there any way to perform a notification for more than 20 byte at all? Apparently this does not work. Back in 2013 a Nordic employee said that it was impossible, but I hope things have changed.

  • Carles, I think I know what you are talking about. However that is the reason behind question 1:

    With for (i = 0; i < nTransactions; i++), the Attribute Table will end up having all 20+ byte I was trying to send. However, with for (i = nTransactions - 1; i > -1; i--), the Attribute Table only store the first 20 byte.

    My guess is this was because with for (i = nTransactions - 1; i > -1; i--), the last call to sd_ble_gatts_hvx() have offset = 0. Do you think this is the case?

    Also, did you mean ATT_MTU = 23 up there?

  • Yes, your assumption seems to be correct, In general this is not a good way of sending more than 20 bytes of data. Instead the remote peer can read the whole of the value (500+ bytes) by using read request with different offsets.

    And yes, ATT_MTU = 23, I fixed that, sorry.

  • All good, Carles. And yeah I didn't really try to use that to send 20 bytes of data. I was just testing if I can notify the remote device of more than 20 bytes. It is just while experimenting that I observe this strange behavior and got really curious about the reason behind it. Thanks for your help!

  • @Carles Can you expand on your comment above: "using read request with different offsets"? How is using multiple reads from the central faster than enabling notifications and waiting for multiple notifications back?

Related