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

How to increase notification throughput on nRF52840?

Hello there,

I'm developing a C++ application on the BLE654 development bord which shall be able to generate and send 100+ BLE notifications per second on a single BLE connection.

On my first try it managed 5.

After some digging, I found that the soft device (s140, Ver 6.1.0) got a BLE_GAP_EVT_CONN_PARAM_UPDATE event, which set the minimum and maximum connection interval to 200 ms, wich was the defined MAX_CONN_INTERVAL (the code burrowed from the blinky example). After setting this to 20 ms I could get (close to) 50 notifications per second, but further decreasing the value again reduced the throughput (maybe the Android on the 'other side' of the BLE connection prohibited a further decrease).

I read somewere that soft devices from s120 up were able to process up to 6 notifications in a 7.5 ms connectipon interval, but here it looks as is the sd only does one notification per interval. What can/must be done to further increase the througput to reach at least 100 notifications per second?

Notes:

-in our application, we send the notifications with sd_ble_gatts_hvx(). I don't have exact numbers, but the internal queue of the soft device seemd rather small and we got a NRF_ERROR_RESOURCES error quickly, after wich we wait for a BLE_GATTS_EVT_HVN_TX_COMPLETE until we call sd_ble_gatts_hvx() again. I am certain the soft device at all time had notifications in its queue.

-as recipient of the notifications we used two tablets with Android 6.0.1 and 7.0, respectively. Both showed similar throughputs, and the final application shall also run on an Android device.

Regards,

     Lasse

  • Hi Lasse,

    Although your device can do better than what it's currently handling, you won't get 6 notifications in 7.5ms connection intervals on pretty much any phone out there. The phone operating system has to allow an arbitrary number of Bluetooth apps to run simultaneously, so it won't allow a single application to hog the entire radio interface for itself. On the latest OS's it might be a bit better, but a couple years ago I found that most phones at the time would allow either 6 notifications at 30ms connection intervals max, or 4 notifications at 20ms connection intervals max, which both come out to ~4KB/s theoretical max rate assuming 20 byte packets for BLE 4.0 level operation. You can now take advantage of BLE 4.2 and 5.0's longer packet sizes to increase the throughput though. 

    One thing you could do that could help right away is in your application, from the Bluetooth GATT class call the requestConnectionPriority function with the CONNECTION_PRIORITY_HIGH argument. What this will do is cause Android to pick the lowest connection interval that both the phone and the device will support, so you can keep reasonable MIN_CONN_INTERVAL and MAX_CONN_INTERVAL values in your device code. When you're done with high transfer rate, you can call requestConnectionPriority again with either the balanced or the low priority settings to save battery power. 

    To queue up multiple notifications, push the data you want to output to the phone onto a software queue, and call a function that tries to write them out to the softdevice in a loop. The softdevice call that handles putting a packet onto its outgoing queue is sd_ble_gatts_hvx, and once it has all the notifications it can handle it will return with the error code NRF_ERROR_RESOURCES. Basically whenever sd_ble_gatts_hvx returns success, remove the packet from your software queue and try pushing the next one. If you get an error or run out of packets to queue up, break out of the loop and retry after getting a BLE_GATTS_EVT_HVN_TX_COMPLETE event from the BLE events handler. The event will tell you how many notifications were sent successfully during the connection interval associated with the event. 

    Also I'm pretty sure you've check this already but just to double check, make sure you're using notifications and not indications, as indications are limited to one at a time max. 

    Edit: Whoops, needed to read your notes, you're already doing the NRF_ERROR_RESOURCES thing. The exact size of the internal queue should be 6, so you could write a piece of test code to verify it. And definitely check that you aren't accidentally doing indications instead of notifications. 

  • Hello Lasse,

    It is true, like says, a phone will give an application 100% of the radio time, so a phone will typically not allow a connection interval too low.

    The minimum connection interval allowed by the BLE stack is 7.5ms. At this connection interval, you will not have time to send 6 packets (because 1 packet takes longer than 7.5ms/6). However, phones would typically allow 15ms or 30ms connection intervals.

    What is the critical part for your application? Do you need updates every 10ms (low latency), or do you need a high throughput?

    Low latency => short connection interval => more. but shorter packages => more headers => less throughput.

    High latency offers longer packs which could mean less headers and more throughput.

    So do you need to transfer data continuously, and is it important that the phone receives these packs as soon as possible, or is it OK for the application to gather up several packs and send it as one big pack?

    Best regards,

    Edvin

  • Ih Nathan,

    I'm using sd_ble_gatts_hvx() with BLE_GATT_HVX_NOTIFICATION type, so unless there is some hidden overruling at work I'm certain that it ain't indications.

    Thanks for the CONNECTION_PRIORITY_HIGH advice, I'll forward it to our android programmer (I'm programming the embedded side only) and let you know how it worked.

    Best regards,

         Lasse

  • Hi Edvin,

    the throughput is definitively our priority. A latency of ~100 ms is no problem, and even if it gets larger we can accept it as long as no notifications get lost.

    You say that a high connection interval is preferrable for our application due to the lesser overhead. So far I consent, but my problem is that no matter if the interval is 200ms or 20 ms, only one notification is transferred per interval (5 at 200ms, (nearly) 50 at 20 ms).

    Is there an option i've overlooked (or uintentionally set) that has to be set (or unset) to enable the soft device to gather several notifications in one big packet?

    Best regards,

         Lasse

  • There is no functionality in the SoftDevice to put together small packets to larger packets, unfortunately. You will have to do this in the application. So you have to collect the data that you want to send in larger packs, before you send them to the softdevice. The limit of the size of the pack is defined by the MTU size of the connection. 

    I don't know what SDK version you use, but at least in the later SDKs, v15.0.0 and later, the ble_app_uart example is initialized with maximum MTU. The actual MTU, though, is dependent on the connected device. If it is a fairly new mobile phone, you will usually get an MTU of about 180B, but if it is connected to another nRF (with the ble_app_uart_c example) you will get the maximum possible MTU of 247.

    You can see the actual MTU in the gatt_evt_handler(), which will update the m_ble_nus_max_data_len in the ble_app_uart example.

    If you change the application to send messages based on UART input, but instead send packets from the main-loop, you can test the throughput.

    In this example, you can use ble_nus_data_send() with arrays of size m_ble_nus_max_data_len, and send them until you get the return value NRF_ERROR_RESOURCES. This means that your queue is full. Then you must wait for the BLE_GATTS_EVT_HVN_TX_COMPLETE event (you must add this to the ble_evt_handler() ), which means that a packet has been ACKed, and you can queue another.

    Best regards,

    Edvin

Related