weliem / blessed-android

BLESSED, a Bluetooth Low Energy (BLE) library for Android
MIT License
557 stars 120 forks source link

Receiving notifications slower than expected #117

Closed JustinVirtuix closed 3 years ago

JustinVirtuix commented 3 years ago

I have written a test program that sends and receives notifications. I am sending 100 128 bytes messages with an MTU set to 131. Hardware wise I am using a Pixel 2XL and and One plus 7 Pro. On the send side it seems to take around 100 ms to send all 100 of the notifications but on the receive side it takes at least 8 seconds. Additionally I have some custom hardware that sends the same notifications and I see the same results on the receive side on those phones, however when the custom hardware is connected to a Windows test application it receives all of the notifications in well under a second.

This is receive code I am currently using

    private val bluetoothPeripheralCallback : BluetoothPeripheralCallback = object : BluetoothPeripheralCallback() {
        var startStartTime = 0L
        var numberMessage = 0
        override fun onCharacteristicUpdate(peripheral: BluetoothPeripheral, value: ByteArray, characteristic: BluetoothGattCharacteristic, status: GattStatus) {
            numberMessage++
            if(startStartTime == 0L) {
                startStartTime = Date().time
            }
            if(numberMessage == 100) {
                Log.d(TAG,"Time: ${Date().time - startStartTime}")
                numberMessage = 0
                startStartTime = 0
            }
            super.onCharacteristicUpdate(peripheral, value, characteristic, status)
        }
}

The code sending the notifications looks like:

        override fun onNotifyingEnabled(bluetoothCentral: BluetoothCentral, characteristic: BluetoothGattCharacteristic) {
            Log.d(TAG, "onNotifyingEnabled ${bluetoothCentral.name}::${characteristic.uuid}")
            if(characteristic.uuid == GAB_CHARACTERISTIC_UUID) {
                GlobalScope.launch(Dispatchers.IO) {
                    val junkData = ByteArray(128)
                    (0..127).forEach { value ->
                      junkData[value] = value.toByte();
                    }
                    val startTime = Date().time
                    repeat(100) {
                        peripheralManager.notifyCharacteristicChanged(junkData, characteristic)
                    }
                    Log.d(TAG, "Time to send ${Date().time - startTime} ms")
                }
            }
            super.onNotifyingEnabled(bluetoothCentral, characteristic)
        }

I would expect to be able to receive all of the notifications in less than second. Is there some setting I need to change? If need be I can upload all of the code to a repo for you to review.

weliem commented 3 years ago

Well this is a complex problem. I don't know what is going on from your description. I also don't to know if it it fair to expect 100 Hz of notifications of 128 bytes. I have never seen anything that fast. The maximum I have seen is 30Hz of 20 bytes.

But there are several variables that play a role here:

Can you upload your code so I can try it myself?

JustinVirtuix commented 3 years ago

Thank you for your quick reply.

It might be that Android doesn't support BLE updates this quickly. That said the custom hardware has connected to a Windows test application and we were seeing something around 200 updates with notifications of 128 bytes with a 131 MTU. What we are really trying do is from the custom embedded hardware send notifications around 70 - 90 a second with around 90 bytes of data to hardware running Android. We noticed that the embedded hardware didn't seem to have a problem but it seemed we couldn't receive more than 18 a second on the Android side. As a test we removed the embedded hardware from the equation and wrote a test sending notifications and receiving them and saw the rate actually drop to more like 13 a second.

I have uploaded the test code to https://github.com/JustinVirtuix/bleTransfer Please let me know if have any questions with the test code.

weliem commented 3 years ago

Adding peripheral.requestConnectionPriority(ConnectionPriority.HIGH) in onServicesDiscovered(peripheral: BluetoothPeripheral) gives me 4000 miliseconds as a result. So that definately helps.

weliem commented 3 years ago

Changed your handler to use the Main thread and I get to 712 miliseconds

        binding.deviceButton.setOnClickListener {
            context?.let { validContext ->
                val handler = Handler(Looper.getMainLooper())
                val listenerDevice = BlessedDevice(validContext, handler)
                listenerDevice.scanForGabPeripheral()
                disableButtonsUpdateStatusText(statusText = validContext.getString(R.string.status_text_device_mode))
            }
        }
weliem commented 3 years ago

Actually running it on the main thread doesn't seem necessary....sorry

The real problem is that your characteristic has both PROPERTY_INDICATE and PROPERTY_NOTIFY. The Blessed library then chooses INDICATE. That is much slower than NOTIFY.

I modified the characteristic to be only PROPERTY_NOTIFY and now I consistently get +/- 500 ms

weliem commented 3 years ago

Issue is fixed in release 2.1.0

JustinVirtuix commented 3 years ago

This fix worked. Thanks.