Increasing speed on an OTA transaction
I'm trying to get the most speed I can for upgrading a BLE peripheral's firmware.
I'm using an OtaTransaction object to manage each send(around 100 write_without_response) between ack responses.
I've also called SetConnectionPriority(HIGH) before beginning the send.
I've also tried increasing the MTU to the highest supported by the peripheral (68), but the android device can only send at an MTU of 20(even though it negotiates the MTU at 69 which is clearly wrong.)
Is there any more ideas you can give me to how to increase the transfer speed?
I've been trying to fit as many packets as I can into each connection event, following this article: https://punchthrough.com/maximizing-ble-throughput-on-ios-and-android/
I think the problem might be related to write_without_response.
I checked wireshark and it is using Write Command (0x52) which seems to be correct, it's method is 'Write Request' (0x12) though. Is that normal?
I've captured some packets over wireshark using the nrf connect app vs using our app with sweetblue. I can see some timing differences which I'm guessing are the real cause for the slow down(it just being slow to begin with).
Using write_command I'm trying to write at 6 packets at a time, and with the nrfconnect app you can see these come through one after the other, roughly 1 ms between. When I'm writing 20 packets at a time with the app you can see there being larger time differences between each packet with HCI_EVT packets in between.
ryanbis last edited by ryanbis
Looks like you're doing everything obvious that you can at this point. When requesting the MTU, what value are you requesting? 68? As a habit, I've always requested a higher amount than I know the device can handle (as setting the MTU is a negotiation anyway). If you're getting 69, that's coming from the device (or android is messing something up internally). I would look into the firmware a bit to see what's going on with the MTU negotiation. I haven't found any way to affect this on the phone side.
One last thing you can do to increase speed, is to lower the update rate for SweetBlue's internal update loop. The default is about 20ms. You can do this like so:
BleManagerConfig config = new BleManagerConfig(); config.autoUpdateRate = Interval.millis(1); BleManager mgr = BleManager.get(this, config);
You can always update the config later, and set it back on the manager instance using BleManager.setConfig(). I would suggest putting the loop back to 20ms when done with the OTA.
EDIT: I also find that article highly suspect with regards to the numbers they show for android. From our experience, bluetooth has always been faster on iOS. Android may have the capacity to have quicker transfers, but in practice, it's has ALWAYS been much slower, and buggier (hence the need for SweetBlue, and also why there is no SweetBlue for iOS).
@ryanbis Thanks Ryan.
I tried altering the 'auto update rate' but it didn't help unfortunately... Would it have been inserting unnecessary delays into callbacks or not necessarily?
One thing I noticed with the nordic lib is they do their queuing on the main thread, is it possible to have this as an option for high priority or compatibility reasons? I'm running out of ideas as to why it works so much faster in the nrf connect app than our app.
Also, In regards to your question about the MTU, I was using 68 since it's the peripheral's max however i was sometimes getting higher values even with other devices so I had to cap the value returned and use 68 instead. The manufacturer of the Amobile device I am using confirmed with me that it can only do an MTU of 23 so I'm stuck with the lower MTU size.
ryanbis last edited by
Hmm, you should have seen at least a slight improvement. You saw no difference between the calls? Looks like your original log, there was about 20ms between each send, which would account for the update tick.
It's possible to put everything on the main thread, though I don't think you'll get a speed increase (typically there's a lot going on in the main thread). It could be thread switching...the callbacks are posted to the main thread. You can shut that off via
I'd suggest looking through the options in the manager config class here https://api.sweetblue.io/com/idevicesinc/sweetblue/BleManagerConfig.html
@ryanbis thanks Ryan.
Maybe altering the update loop helped but i just expected it to increase the speed more dramatically.
A nice thing I noticed by writing to the characteristic via the android api is you can buffer the writes until it returns false.
I managed to get a speed increase by doing this on the main thread whenever I intercepted a characteristic read/write event. If I leave these callbacks on the main thread will they not thread switch by the time I receive them? I'm assuming android api calls them on the main thread.
ryanbis last edited by
Hah, what android does with callbacks is different depending on OS level, and manufacturer. Some post the callbacks to main, some are on separate threads, hence why SweetBlue posts them all on the main to be consistent.
However, I'm not really sure of what you're actually doing. What do you mean by "whenever I intercepted a characteristic read/write event."?
@ryanbis Ok, that's nice of android to post back on different threads Good thing you are making is consistent at least.
I mean't that I am using the device read/write listener to listen for when an OTA characteristic has been written without response, then I keep writing data to the characteristic(not waiting for the callback) until it returns false, in which case I can't keep buffering the data due to the buffer being full, so I wait for the next callback/readwrite event to do it again.
You can see from this wireshark screenshot using this method is much better now, I'm getting packets sent in bulk way more regularly. I managed to get the transfer down to 5 minutes for roughly 180k.
@ryanbis Do you think you could give me access to a onCharacteristicWritten callback(without any thread switching) for the device? This way I'd be able to have the writeCharacteristic/onCharacteristicWritten on a tight loop for OTA. Perhaps you could have a BluetoothGattCallback proxy object which is setable on the device for people which would like to use the native stack callbacks for certain things?
On another note, do you increase the priority of the thread/looper which the OtaTransaction runs on?