I have a device that transmits data to my Android (4.4.2) over a BLE GATT characteristic. The device sends multiple 20byte packets per connection interval, where each packet has a sequence number. Lets say the device is sending 4 packets per connection interval, as shown... |sq1,sq2,sq3,sq4|sq5,sq6,sq7,sq8|..
I notice at the Android end, may calls to onCharacteristicChanged() are coming in like... |sq4,sq4,sq4,sq4|sq8,sq8,sq8,sq8|...
Does Android support sending multiple packets per connection interval?
Michael
Yes, it supports multiple packets per connection interval, but the API is badly designed, and the characteristic value is a shared object, which may be updated by multiple threads. Each notification may be handled in a different thread and they then call
onCharacteristicChanged
on one thread (either set inconnectGatt(...)
, or some unspecified thread for older Android versions. If the connection interval is very small, or the device sends many packets in a single interval, it may happen that before you receive the callback with the first notification it will be overwritten by another.There are 2 advices I may give:
onCharacteristicChanged
method, as it may soon be changed (by another notification, or write operation). Ref: https://github.com/NordicSemiconductor/Android-BLE-Library/issues/54connectGatt(..., Handler)
method, but rather rely on the default handler. If you set a handler, this will add additional time before you receive the callback, so more opportunity for the data to be overwritten. Ref: https://github.com/NordicSemiconductor/Android-BLE-Library/issues/54The above advices do not guarantee 100% of success, however, but lower the chance of loosing data.
Also, when you write and listen to notification, I recommend splitting that into 2+ characteristics as the characteristic's value is then shared between write and notify operations. Value may be updated before it's sent, and you'll end up sending what was received. Ref: https://github.com/NordicSemiconductor/Android-BLE-Library/issues/60
The iOS API is much better, as the data are set as a parameter to the
writeValue(...)
and received as value inperipheralDidUpdateValueFor(...)
. They are not shared.