Unexpected sd_ble_gatts_hvx() return values with S140 v7.3.0

We have an nRF52840 operating as BLE peripheral that ix using sd_ble_gatts_hvx() to notify an attribute value. Because we are using an RTOS, after calling sd_ble_gatts_hvx() our function then makes a blocking RTOS call to wait on an RTOS event, which is set when we receive BLE_GATTS_EVT_HVN_TX_COMPLETE. In the case where sd_ble_gatts_hvx() returns NRFX_ERROR_INVALID_STATE — which can occur if a BLE disconnect or disable occurs in parallel with the thread that's calling sd_ble_gatts_hvx() -- our function gracefully exits with an error, but all other errors we've been treating as serious errors that represent something fundamentally with our code or system.

However, in testing we've run across two other unexpected return values from sd_ble_gatts_hvx(). We'd like to understand what could be causing these errors.

1) We've run into a case where sd_ble_gatts_hvx() returns NRF_SUCCESS but on exit the uint32_t pointed to by the p_len field of the ble_gatts_hvx_params_t struct we pass contains zero instead of the byte count we actually requested to notify. This occurred in a case where we were testing rapid BLE connections and disconnections while shifting a device quickly in and out of range to test the reliability of reconnections. Two questions about this:

  • What does this mean if it happens? Why aren't we getting an error return code if the SoftDevice indicates that no data was sent? 
  • If this occurs, will we still get a BLE_GATTS_EVT_HVN_TX_COMPLETE event notification following the sd_ble_gatts_hvx() call?

2) We've run into a case where sd_ble_gatts_hvx() returns NRF_ERROR_RESOURCES. We understand that this means that all the BLE buffers in the SoftDevice are full. But we're confused about why this is occurring, since we only call sd_ble_gatts_hvx() from this single function, we always wait for an BLE_GATTS_EVT_HVN_TX_COMPLETE event before exiting the function after sd_ble_gatts_hvx() is called, and the function is protected by a mutex, so we don't think our code should be using more than one BLE buffer for transmit at a time. Two questions about this:

  • We understand that we can modify the number of TX queue elements used by the SoftDevice by calling sd_ble_cfg_set(), but can you help us understand -- given how we're using sd_ble_gatts_hvx() -- why we'd be getting an NRF_ERROR_RESOURCES result?
  • We do have a timeout while waiting on the RTOS event being set by BLE_GATTS_EVT_HVN_TX_COMPLETE, which is currently set to 2 seconds. Is there an upper bounds to how long after sd_ble_gatts_hvx() is called the BLE_GATTS_EVT_HVN_TX_COMPLETE may be sent? Or to put it another way, does sd_ble_gatts_hvx() ever time out and, if so, how long does that take?
  • 1.) We do not see (from the code) how you are able to call sd_ble_gatts_hvx() with p_len=0 and that the call return NRF_SUCCESS (this should not be possible). Are you able to double check that this is indeed the case or provide a way to recreate it? 

    2.)  I don't see how this is possible either no, from the documentaiton:

    * @retval ::NRF_ERROR_RESOURCES Too many notifications queued.
    * Wait for a @ref BLE_GATTS_EVT_HVN_TX_COMPLETE event and retry.

    Can you on BLE_GATTS_EVT_HVN_TX_COMPLETE print the number sent by checking:

    p_ble_evt->evt.gatts_evt.params.hvn_tx_complete.count

    This will indicate how many indications were sent, and you can possible get some indication whether you are calling sd_ble_gatts_hvx() faster than you believe. In general you may find this useful:
    https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s140.api.v7.3.0/group___b_l_e___g_a_t_t_s___h_v_n___m_s_c.html 

    There is no timeout here, only if link loss timeout.

    Kenneth

  • Kenneth,

    Thank you for the response. Feedback on a few of your questions:

    1) For clarification, we are not passing p_len = 0 to sd_ble_gatts_hvx. Here is what our code that cals sd_ble_gatts_hvx() looks like:

            uint16_t hvx_len = buff_in_len;
    
            ble_gatts_hvx_params_t params;
            memset(&params, 0, sizeof(params));
    
            params.type = BLE_GATT_HVX_NOTIFICATION;
            params.handle = s_transmit_char_handles.value_handle;
            params.p_data = p_buff_in;
            params.p_len = &hvx_len;
    
            uint32_t result = sd_ble_gatts_hvx(
                conn_handle,
                &params);
    

    What should happen if (result == NRF_SUCCESS) is that hvx_len should contain the number of bytes requested to notify (buff_in_len in our case) when sd_ble_gatts_hvx() exits. The vast majority of the time this works perfectly, but we've seen one case where (result == NRF_SUCCESS) but hvx_len is zero on exit, instead of the number of bytes requested. We're not sure how to interpret that.

    2) p_ble_evt->evt.gatts_evt.params.hvn_tx_complete.count just contains 1 when we receive BLE_GATTS_EVT_HVN_TX_COMPLETE, both before and after sd_ble_gatts_hvx() returns NRF_ERROR_RESOURCES.

  • Can you make the hvx_len 'static volatile' keyword in case the memory address is re-used by the compiler?

    Best regards,
    Kenneth

Related