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

tx buffers and sd_ble_gatts_hvx()


Hello,

I'd like to understand a bit more about transmit buffers for sd_ble_gatts_hvx() calls. Our peripheral application is developed using SDK 15.3.0 and S112 for nRF52832 and is based originally on the ble_app_uart example.

For S112, BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT is 1. For our app, NRF_SDH_BLE_GATT_MAX_MTU_SIZE is set low at 23, NRF_SDH_BLE_GAP_EVENT_LENGTH is 6 (7.5 ms) and min/max connection intervals are 60/120 ms. When iOS device is the connected central, 120 ms is the selected connection interval. Peripheral and central exchange at most 19 bytes of data at a time.

Our application has low data throughput, but there is the possibility of two or three calls to sd_ble_gatts_hvx() (notification) in quick succession. I understand that if no buffers are available, sd_ble_gatts_hvx() will return NRF_ERROR_RESOURCES and the app should wait for BLE_GATTS_EVT_HVN_TX_COMPLETE event then try again. Given:

NRF_SDH_BLE_GATT_MAX_MTU_SIZE is 23
NRF_SDH_BLE_GAP_EVENT_LENGTH is 6 (7.5 ms)
connection interval is 120 ms

how many notifications can be handled in each connection event? I'm trying to determine if our app needs to handle NRF_ERROR_RESOURCES error from sd_ble_gatts_hvx(). Or can we be confident this will never happen given above parameters?

Should I consider increasing NRF_SDH_BLE_GAP_EVENT_LENGTH? I've read that doing so will increase the number of tx buffers available.

Our application uses the time slot API for radio use in between BLE activity and I assume that the larger connection interval (120 ms) with short connection event length (7.5 ms) increases interoperability of BLE with time slot API usage.

Many thanks,

Tim

Parents
  • Hi 

    There is no easy formula to calculate exactly how many packets you can buffer with a certain SoftDevice configuration. I would suggest you run a quick test, and see how many times you can call the sd_ble_gatts_hvx() function in a loop before NRF_ERROR_RESOURCES is returned. 

    That said, with a GAP event length of 6 you should be able to queue a fair number of packets when the NRF_SDH_BLE_GATT_MAX_MTU_SIZE is only set to 23. 

    I wouldn't recommend ignoring the NRF_ERROR_RESOURCES return, as there is no guarantee how many packets you will be able to send on every connection interval. If the link is poor and you have high packet loss, then the Bluetooth stack will be busy retransmitting the same packet over and over, and the buffers won't be emptied. So even if you don't see NRF_ERROR_RESOURCES in normal situations, it might happen occasionally if you have a lot of interference. 

    When communicating with phones there is also a buffer limit in the phone that affects the total number of packet you can send, and this limit you have no control over. 

    Should I consider increasing NRF_SDH_BLE_GAP_EVENT_LENGTH? I've read that doing so will increase the number of tx buffers available.

    When sending short packets a GAP event length of 6 should be more than sufficient, and I wouldn't expect larger numbers to influence the throughput much. At least not on phones, which typically limit the number of packets pr connection event more than the SoftDevice. 

    Our application uses the time slot API for radio use in between BLE activity and I assume that the larger connection interval (120 ms) with short connection event length (7.5 ms) increases interoperability of BLE with time slot API usage.

    There will be more time available for the timeslot API when using a longer connection interval, that is correct. 

    Best regards
    Torbjørn

  • Thanks Torbjørn. Very helpful and all makes sense.

    I found that when connected to an older iPhone, the app can call sd_ble_gatts_hvx() 5 times before NRF_ERROR_RESOURCES is generated. This is under ideal RF circumstances. I hadn't considered the case of high packet loss and this is reason to, as you say, handle NRF_ERROR_RESOURCES errors.

    I understand two general approaches to handling NRF_ERROR_RESOURCES from sd_ble_gatts_hvx().

    1. Loop while sd_ble_gatts_hvx() returns NRF_ERROR_RESOURCES. Once tx buffers are available, sd_ble_gatts_hvx() will succeed and return NRF_SUCCESS. This is the approach the ble_app_uart example uses:

    do
    {
        uint16_t length = (uint16_t)index;
        err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
        if ((err_code != NRF_ERROR_INVALID_STATE) &&
            (err_code != NRF_ERROR_RESOURCES) &&
            (err_code != NRF_ERROR_NOT_FOUND))
        {
            APP_ERROR_CHECK(err_code);
        }
    } while (err_code == NRF_ERROR_RESOURCES);

    2. Create my own message queue. If sd_ble_gatts_hvx() returns NRF_ERROR_RESOURCES, queue the data then wait until BLE_GATTS_EVT_HVN_TX_COMPLETE occurs and try calling sd_ble_gatts_hvx() again. This involves counting calls to sd_ble_gatts_hvx() and BLE_GATTS_EVT_HVN_TX_COMPLETE events (considering the count returned in such events).

    The second approach is more elegant and power efficient but more work to code. The first approach is simple to implement. Power consumption is not important for our product, and given that this condition will likely occur rarely, I'm leaning towards approach 1 above. In our application, all calls the sd_ble_gatts_hvx() occur within the main() loop scope (not in interrupt routines). Reasonable? Any other important considerations?

    Many thanks Torbjørn.

    Tim

  • Hi Tim

    In general method 2 is definitely the recommended one, as it doesn't require the application to hang in the loop for an indeterminate amount of time. If you do this outside interrupt context it should still work fine, and if this is something that doesn't happen much it is not likely to have a big impact on the application, so I understand that you might want to consider method 1 for reason of simplicity. 

    Still, I don't agree that creating a message queue is a specific necessity for method 2.
    If there is a risk that data can be produced in your device faster than the BLE stack can transmit it, you need a buffer/queue regardless of which method you use. 

    And if you can control and hold back the sampling/production of data, then you don't need a message queue in either case. 

    You can use method 2 and just make sure you don't overwrite the data_array passed to the ble_nus_data_send(..) function until the BLE_GATTS_EVT_HVN_TX_COMPLETE event occurs, in the case that you get NRF_ERROR_RESOURCES. 

    Best regards
    Torbjørn

Reply
  • Hi Tim

    In general method 2 is definitely the recommended one, as it doesn't require the application to hang in the loop for an indeterminate amount of time. If you do this outside interrupt context it should still work fine, and if this is something that doesn't happen much it is not likely to have a big impact on the application, so I understand that you might want to consider method 1 for reason of simplicity. 

    Still, I don't agree that creating a message queue is a specific necessity for method 2.
    If there is a risk that data can be produced in your device faster than the BLE stack can transmit it, you need a buffer/queue regardless of which method you use. 

    And if you can control and hold back the sampling/production of data, then you don't need a message queue in either case. 

    You can use method 2 and just make sure you don't overwrite the data_array passed to the ble_nus_data_send(..) function until the BLE_GATTS_EVT_HVN_TX_COMPLETE event occurs, in the case that you get NRF_ERROR_RESOURCES. 

    Best regards
    Torbjørn

Children
  • Thanks Torbjørn.

    Generally, a sensor sends data (5 bytes) once a minute although there can be occasions where it occurs a couple times within a couple seconds. However, in addition to sensor data, there can be other data packets sent occasionally at random times (once every 3 minutes, triggered by timer compare, but still sent outside of interrupt context) and it’s possible this happens right when a sensor data packet it sent. It would be very rare that more than 5 packets are queued in quick succession—only if RF environment is noisy and lots of packet loss. Even then, it’s more likely that supervision timeout (4 seconds) occurs before more than 5 packets are queued to be sent.

    Still, though, I will consider approach 2 simply because architecturally it’s a better solution.

    Reasonable?

    Many thanks,

    Tim

  • Hi Tim

    That sounds reasonable, definitely Slight smile

    As I said it should be possible to do approach 2 without a queue if you simply keep the sensor data in a global buffer. Worst case you need to discard data if you get more than 6 packets in quick succession. 

    If it is usually up to a minute between sensor updates packet loss should be extremely unlikely.  

    Best regards
    Torbjørn

Related