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

sd_ble_gatts_hvx sends wrong number of bytes

(Sorry for the formatting. I don't know what's going on with it--the preview looks ok!)

I'm creating an application that uses a modified version of the ble_nus example. We are on SDK 13.0 and softdevice s132 4.0.2. We send 76 bytes at approximately 125ms intervals (8x/second). Most of the time, it works just fine. However, I am running into two issues:

1)sd_ble_gatts_hvx occasionally (<10% of the time) returns NRF_ERROR_INVALID_PARAM (0x07), even though I'm sending the exact same amount of data.

2)the app I'm using (built in-house) occasionally receives 77 bytes at a time. Sometimes the first byte is repeated. Other times, it appears that extra bytes are inserted near the beginning of the data, and some bytes are truncated off the end.

These two problems appear to happen with similar frequency, but not regularly, and not necessarily at the same time. So here are my questions:

  1. what conditions will cause sd_ble_gatts_hvs to return NRF_ERROR_INVALID_PARAM, and why would it work 95% of the time? 2) what would cause the data received by the phone to be garbled like this? I have now tested it on two different android phones with the same result. It's almost like it transmitted bytes 0-7 of the first 76 bytes, then transmitted 69 bytes from the second set of 76 bytes, starting with byte 2

UPDATE: I changed the connection interval from the default (20-75 x 1.25ms) to something faster (15-22 x 1.25ms), and that has reduced the incidence of the 77-byte notifications significantly.

However, this has also resulted in a lot more NRF_ERROR_INVALID_PARAM being returned, often 6-9 times in a row (at 125ms intervals still). I should note that even when I get NRF_ERROR_INVALID_PARAM, the data still appears to be transmitted with no error.

IMPORTANT UPDATE 2: turns out the 77 was due to the parameter I pass to nrf_ble_gatt_att_mtu_periph_set(). If I increase that to 100, I now get notifications of various lengths, from 76 (what I intend) up to 97 bytes. This tells me that the undesired behavior is within the softdevice. When I look at the data, it's clear that the softdevice is sending some number of bytes from the previous call, and then sending 76 bytes from the current call. In other words, the app sees this data:

4600172A15623087152C15633287152B14623287162B146231...
4600172A15623087152C1563324600162A14623287142B1562...
4600132C16623287152B15623287162A15623187152B156231...
4600152A14623187152C17623387152B16623287142B156233...

Each line should begin with 4600, and the second line is the data with the extra bytes. You can see that the first 13 bytes of Line 2 are the same as Line 1, and then Line 2 starts with its own 4600 data.

UPDATE 3: Still no answers on the 0x07 NRF_ERROR_INVALID_PARAM return codes. It appears that the sd_ble_gatts_hvx() call modifies hvx_params.p_len to match....something? It leaves it as 76 when the call succeeds, and sets it to 0 when it gets the _INVALID_PARAM return code.

That seems consistent with the documentation

Here is an odd thing, though: If I do anything with that length variable, sd_ble_gatts_hvx() fails every single time. For example, this works

uint32_t ble_nus_string_send(ble_nus_t * p_nus, uint8_t * p_string, uint16_t length){
ble_gatts_hvx_params_t hvx_params;

VERIFY_PARAM_NOT_NULL(p_nus);

if ((p_nus->conn_handle == BLE_CONN_HANDLE_INVALID) || (!p_nus > is_notification_enabled))
{
    return NRF_ERROR_INVALID_STATE;
}

if (length > BLE_NUS_MAX_DATA_LEN)
{
    return NRF_ERROR_INVALID_PARAM;
}

hvx_params.handle = p_nus->tx_handles.value_handle;
hvx_params.p_data = p_string;
hvx_params.p_len  = &length;
hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;

uint32_t err_code = sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);

return err_code;

but this results in a NRF_ERROR_INVALID_PARAM every time:

uint32_t ble_nus_string_send(ble_nus_t * p_nus, uint8_t * p_string, uint16_t length)
{
    ble_gatts_hvx_params_t hvx_params;
    bool was76 = (length == 76);

    //other code as above

    uint32_t err_code = sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);
    if(err_code != NRF_SUCCESS){
        if(!was76){
            //do something
        }
    }
    return err_code;
Related