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

(nRF52832 / SDK 14.0 / s132 5.0.0) Optimizing BLE throughput between nRF52 (peripheral) and Android device (central)

I'm using the nRF52382 with SDK 14.0 and softdevice s132 5.0.0, and I need to send up to hundreds or thousands of kilobytes to an Android phone over BLE. I need to optimize throughput, and I've read all the forum and blog posts I could find, and so I knew generally that I need to send data to the phone using notifications. And I knew that I need to support higher MTU and data length values. Most discussions say to look at the ble_app_att_mtu_throughput example, but that example uses an nRF52 in both roles, so it was hard to know which of that example's approaches still apply when using an Android device in the central role.

I primarily test with a Samsung Galaxy S7 running Android 7.0 and Bluetooth 4.2, so all of my throughput numbers here were recorded with that phone running nRFConnect version 4.17.0.

My baseline functionality left maximum MTU at 23 and used a connection interval of 15ms, and I typically got throughput speeds of about 20kbit/s. To optimize throughput, I've done the following:

  • Increase NRF_SDH_BLE_GATT_MAX_MTU_SIZE in sdk_config.h to 247. This requires updating the linker file to allocate more RAM to the softdevice. This also results in data length extension being used, which apparently requires the NRF_SDH_BLE_GAP_EVENT_LENGTH setting in sdk_config.h to be increased from 3 (3.75ms) to 5 (6.25ms).
  • Use as low a connection interval as possible. In the last few major Android releases (since Android 6.0 maybe?), this appears to be 11.25ms. But nRFConnect and some Android Bluetooth libraries appear to only allow setting connection interval as low as 15ms, so that's what I currently use.
  • Increase NRF_SDH_BLE_GAP_EVENT_LENGTH. I actually don't fully understand this parameter, but trial and error showed that setting this to match my connection interval at 15ms seemed to maximize throughput.
  • Disable wifi on the Android device. I believe the antenna is time-multiplexed between wifi and Bluetooth, so disabling wifi apparently allows more time for Bluetooth. Limited testing shows this might've bumped my throughput up by 50kbit/s or more.

With all these, I'm seeing data rates up to about 375kbit/s with my phone right next to my nRF52 board. (I also briefly tested my old Samsung Galaxy S5 with Android 5.1.1 and Bluetooth 4.0 and got throughput of about 100kbit/s.)

And so here are some questions:

  1. What exactly does the NRF_SDH_BLE_GAP_EVENT_LENGTH setting do? Why does increasing it require more RAM? Is setting it to match my connection interval the right approach? Are there any other connection interval/event length combinations that would increase throughput? I tried setting both to 30ms and 45ms, but it didn't seem to help.
  2. What sorts of throughput numbers can we expect with an Android device in the central role? The throughput example claims numbers about 2x as fast as I'm seeing with Bluetooth 4.2. Is this expected? What are the limitations?
  3. Are there any optimizations that I haven't mentioned that I may not already be using? I'm not in a position to test the 2M PHY yet. The throughput example uses the connection event extension feature with longer connection intervals, but I read somewhere that that is actually a Nordic feature rather than a Bluetooth spec feature, so my understanding is that I can't use that feature with an Android device.
  4. With these settings (MTU of 247, data length of 251, connection interval of 15ms, event length of 15ms), throughput is actually _very_ erratic and _heavily_ dependent on signal strength. Is it my imagination, or is it possible for throughput to actually drop below throughput of some lower performance configurations in some poor signal strength scenarios? If it's possible to sacrifice some throughput for better stability, what would be the best approach?
  • 1. the NRF_SDH_BLE_GAP_EVENT_LENGTH  in the length of the connection event. The bigger you set this variable, the more packets (or longer packets) you can send in one connection event because the connection event is longer. This means the buffer to store packets that can be sent has to be bigger, which increases your RAM usage. 

    You can send more than one packet in a connection event. If you set your MTU to 247 and you have a NRF_SDH_BLE_GAP_EVENT_LENGTH  that allows to send 6 packets per connection event, you maybe need a higher connection interval to be able to send 6 packets of 247B. So increasing your connection interval can optimize your throughput.

    Have a look here on how to optimize throughput: www.novelbits.io/.../

  • Interesting- I currently have event length set to 15ms since that's what I typically use for connection interval, but it didn't appear that setting event length any higher (say, to 30ms or 45ms) in case I wanted to use longer connection intervals required any additional RAM.

    The 6 packets per connection interval is a limitation of Android, right? Not Bluetooth?

    And thanks, yeah, I'd seen that article about optimizing throughput, but the numbers they measure are for nRF52-to-nRF52, so I'm curious about how much slower we should expect to see a connection with an Android phone to be, where the bottlenecks are when transferring to an Android phone, and whether I'm already hitting those bottlenecks or if there are further improvements I can make.

  • Hi Sydney!

    I also would like to measure throughput between my phone and an nrf board.

    Could you please describe me how you did this test?

    Which android app did you use, which nordic example did you use, what modification did in the example if needed, and please describe the process of the throughput test.

    Thank You,

    Kalman

  • Hi Kalman,

    Unfortunately my code is pretty far removed from the examples at this point so I'm not sure I can help you set up any of the nRF52 examples to transfer data to an Android phone. All of the testing I described in my original post was based on my custom firmware. And unfortunately I didn't write the early stages of the data transfer code so I'm not sure which example(s) the code was originally based on. But it essentially just queues and sends notifications as quickly as possible, with each notification containing some number of bytes as determined by the negotiated MTU size.

    That said, I measure throughput between an nRF52 running our firmware and an Android phone in two ways. The simplest way is to put a known amount of data on my nRF52 device, transfer that known amount of data to my Android phone, and time the transfer with a stopwatch or something similar. So if I have 512 kilobytes of data to transfer and I time the transfer at 16 seconds, that's 32 kilobytes per second or 256 kilobits per second.

    I also track time and bytes transferred in my nRF52 firmware, so I calculate and log throughput in real time during transfers. This is probably the most accurate way I measure, but I can only access the logging output if I can physically access the debug connector on the board, which isn't always the case. (Similarly, I'd think if you're writing your own Android application, it should be pretty easy to calculate throughput on the Android side.)

    We do most of our testing with our own internally developed Android application, but I also test with the Android version of nRF Connect pretty extensively.

    To test with nRF Connect, most of the process is going to be specific to firmware's custom data transfer implementation, unfortunately. Essentially you'd need to connect to your nRF52 with nRF Connect, subscribe to notifications (in nRF Connect) on whichever characteristic(s) will be sending your data, and then do whichever action you've designed your device to initiate the data transfer. This could be a button press, Android writing a specific sequence of characteristics to your device, a timer, etc. After connecting but before initiating the transfer, you'll probably want to request a higher connection priority in nRF Connect. This will lower the connection interval, allowing notifications to be sent more often, speeding up your throughput.

Related