I am working on a BLE app. I am through to establishing communication between Mobile app (CENTRAL - BluetoothGattClient) and PERIPHERAL (GATT SERVER) albeit not neatly.
PERIPHERAL has one service which in turn has single characteristic on which CENTRAL has enabled notification. Read/Write from both sides happen through this characteristic
Here is the complete flow of communication from CENTRAL (Mobile) side
Problem 1
Sometime while receiving multiple packets from PERIPHERAL, I receive read notification - 'onCharcteristicChanged()' before I finish writing ACK (STEP 9) i.e. onCharcteristicChanged() is called before onCharctristicWrite().
In this scenario though I issue bleGatt.readCharacteristic(charac) but onCharactristicRead() never gets called. CENTRAL gets stuck. It is unable to read while waiting.
Problem 2
Sometime, while sending ACK, after issuing bleGatt.writeCharactristic(charac), onChactristicWrite() is not called hence ACK is not received by PERIPHERAL and communication stops.
Problem 3
Overall communication is very slow. It takes > 400ms to write one chunk (<=20 bytes) of data.
Is there any better strategy to sync Read/Write properly? I tried to do it by using BluetoothGattCallbacks but as explained it is not working properly.
What can be done to increase the speed of communication?
Both READ and WRITE is happening in same thread.
I have tested this on Moto E (Marshmallow), Moto G5 (Nougat) and Samsung S6 (Lollipop). The behaviour is consistent across devices.
My PERIPHERAL device is nRF52840.
Thanks
The easiest way to make sure your GATT communication works correctly is to use a queue for the BluetoothGatt object, since only one GATT operation can run at a time. That's why it doesn't work when you for example call readCharacteristic before the onCharacteristicWrite is called by the system.
The reason you get the notification before the write response is because the peripheral sends the notification before the write response.
What you can do to speed up the communication is to use Write Without Response instead of Write With Response. That way multiple packets can be sent in one round trip. The onCharacteristicWrite will then be called immediately (as long as the internal buffers are not filled up). Just be aware of https://issuetracker.google.com/issues/37121017.
Related
I am doing a firmware upgrade of a BLE peripheral device based on ESP32 from an Android central.
The firmware file is sent in parts of 512 bytes each. The ESP32 device (the GATT server) sends a notification to the central (GATT client) and the central sends the next part, followed by a write command to the peripheral. Then the next notification is sent and so on.
The upgrade works, however, it takes a long time to complete (~10-12 min for a 600kB file).
I sniffed the traffic with Wireshark and it turned out there are 15-20 empty PDUs between the sending of each notification by the peripheral and the start of sending the part by the central. I searched what may be the problem on the server side but could not find anything relevant.
Maybe something is happening on the Android central that delays the sending process? Or maybe I am missing something with the ESP32? Here is a Wireshark capture (I’ve underlined in red where the sending should start):
EDIT: I haven't added extra sleep on server and if I had, there would be no empty server PDUs, correct?
I tried what you suggested, to use just android's internal mechanism for confirmation and the download is now about 3x faster. Thank you! However, in the captures there are some strange (to me) things like a lot of 26-byte response packets from the server to the master (captures below). Why is that and is it possible to combine them into 3 packets, the way they were sent from the master?
Also, about he explanation in the link you gave:
The Bluetooth controller sends a "number of packets complete" event back to Android's Bluetooth stack over HCI which indicates that the Link Layer of the remote's device has acknowledged a packet.
I didn't quite get that. If its a Write_No_Response from the master, how does the remote device acknowledge receiving a packet?
And is this Android flow control mechanism a possible explanation of my original problem with the empty packets?
Seems like the Android device is not fast enough, or you have added an extra sleep.
Assuming the peripheral can handle the data, you can quite reliably send write commands without using a notification acknowledging scheme. See onCharacteristicWrite and onNotificationSent are being called too fast - how to acquire real outgoing data rates?. Basically just wait for onCharacteristicWrite before sending the next one.
I am trying to send 10 bytes of data, every 30milisecond,s between 2 android devices using bluetooth low enegry notifications. The goal is to achieve low latency communication and there is no interest in low energy.
You can check the results in the Delay graph(Y field = delta time between packets). The first 10seconds the communication is being perfomed good enough. (The interval between the packets is max 30-40ms). However after the 10th second, the intervals are either extremly high or extremely low.
This is probably happenning due to non strict connection interval parameters. You can check a sample of hci_events that shows clearly the slow down is caused by the hci_events.
I have tried to configure the parameters of the connection, calling from the master the function
requestConnectionPriority
However, it didn't change anything on the result graph. I also check the packets using wireshark and findout that the master didn't even send a request for configuring the connection interval. I also tried another configuration request with
requestMtu
with exactly the same results, no update packets was send from the master.
So, there are 2 questions that show up from this observations.
Has ble a timeout in the 10th second in order to ensure low energy consumption? If so, how can I disable it?
Is some thing wrong calling requestConnectionPriority, from the android 6.0.1 with the role of the central(master)? Can you give me a simple example on how is being called properly?
Note that requestConnectionPriority is called right after the connection being establish in the onConnectionStateChange of the BluetoothGattCallback.
The device that has role of a slave(server) is running (Android 5.1.1) and the master(client) is running (Android 6.0.1).
Thanks a lot!
IMPORTANT UPDATE: It's crucial to mention the way the connection is being initiated between the devices.
The central device calls device.connectGatt, where device is BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address). Address is a hardcoded string, the peripheral's public mac address. You can check logcat logs and also hci logs.
I have also tried connecting with the legit method (peripheral advertising and connect to scanned random address), but I get an error 'initiate direct connection fail, topology limitation'. You can check logcat logs and also hci logs.
Has ble a timeout in the 10th second in order to ensure low energy consumption? If so, how can I disable it?
No
Is some thing wrong calling requestConnectionPriority, from the android 6.0.1 with the role of the central(master)? Can you give me a simple example on how is being called properly?
I'm not aware of any bugs regarding requestConnectionPriority. But you could try to execute that method after the onServicesDiscovered callback. Android temporarily changes connection interval to 7.5 ms during the GATT service discovery so it might be confused if you don't wait until the service discovery is completed.
Regarding requestMtu, make sure you execute that method while there are no other GATT operations pending (otherwise it won't work).
One additional tip is to use an air sniffer and not only look at the hci logs.
I'm working on an Android app that communicates with a custom bluetooth device. After calling BluetoothGatt.Disconnect() I am seeing that the OnConnectionStateChange callback is called, and the new state is Disconnected, however, there seems to be a lag between when that happens and when the Device itself is actually disconnected. For example, if I call BluetoothManager.GetConnectionState(...) with the device that was connected, it still returns Connected. Sometimes it takes several seconds before GetConnectionState returns Disconnected. Is this normal? Is it possible that I am doing something wrong in my application that could be causing this? e.g. disconnecting from a non-UI tread, or something like that? Or, is it possible that the physical bluetooth device itself is not handling the disconnect properly and maybe not completing the disconnect event promptly?
Android's BLE system is so messed up. I've seen what you've described, except much worse - where you disconnect from Android, but under the hood it maintains a persistent connection with your peripheral.
Usually takes 30 seconds to finally disconnect, sometimes takes minutes! All depending on which phone you were using at the time.
If you have the ability, I highly recommend adding a disconnection characteristic to the peripheral, so that you actually disconnect by writing a disconnection request, and letting the peripheral force the disconnection - and then Android will pick it up.
The benefit I've seen is that it ALWAYS works (because a 'hard' disconnection is always picked up by Android, whereas a 'soft' disconnection request can cause some issues on certain phones). Typically 'good' phones don't exhibit this behaviour (especially Marshmallow and on), but back in those KitKat days.... Wow....
Another benefit... If you're using iOS, you can scan for or re-connect to disconnected peripherals much faster.
When you call "disconnect()" you only disconnect your client object (BluetoothGatt object). You can have multiple BluetoothGatt objects connected to the same physical device. Multiple apps can also have own BluetoothGatt objects connected to the same device.
As soon as you call "disconnect()" the request is processed in the Bluetooth stack in the system and it immediately then calls the onConnectionStateChange callback in your app when it has completed processing the request. However, it will not disconnect the link until all other clients have disconnected. Newer versions of Android also delay the physical disconnection a few seconds (not sure why). Also, once the disconnect request has been sent to the Bluetooth controller it may take some time to actually disconnect since the remote device needs to acknowledge the disconnection (or time out). The default time out was 20 seconds until it was recently changed to 5 seconds in the latest Android version.
In the context of BLE (Bluetooth Low Energy), Write Commands can be used to write from a Client to the Server, and Notifications to write from the Server to the Client. In my setup, the Client is a Central device (Android phone), and the Server is a Peripheral (dev board).
After performing several data throughput tests with multiple phones, I noticed that the throughput varies greatly with the phone, which is expected because a great deal of the BLE lower layers implementation is up to the manufacturer to figure out. But what caught my attention was that Write Command always achieve a much lower throughput that Notifications, independently from the phone. Why is that?
They should have the same throughput. Multiple write commands and notifications can be sent during one connection event. They are treated the same.
You could use an air sniffer to see if you find any problems.
How long the connection event should be open can be suggested when the connection is created and with connection parameter updates. Sadly, Android's BLE stack hard codes this to the default value, which means no recommendation. That will in practice mean you are limited to 3 or 4 packets per connection event.
I am currently developing Android BLE, and encounters a lot of problems with the Android BLE stacks..
My development tool is Samsung Galaxy J with Android 4.3.
I want to know how can I read a characteristics from the BLE and the write the characteristics (is like i verify what data I have received, and then I send another data using the BLE)
and I have serious problem understanding how the Android BLE callbacks works,
I dont understand these 5 functions...and the manual is not clear, can anyone good soul explain in simple form???
onCharacteristicWrite
onCharacteristicRead
onCharacteristicChanged
onDescriptorRead
onDescriptorWrite
My current situation is, I managed to read the data in onCharacteristicChanged() callback and then I verified the received the data I try to send the data by using
characteristics.setValue(data)
gatt.writeCharacteristic(characteristics)
But, the Android BLE stack is not calling onCharacteristicsWrite() and in fact, Android just hangs there..
I try to google about Android BLE, there is not much information and only bunch of complains on how unstable the BLE stacks is......
Each of the callback from the Android BLE has its functions;
onDescriptorRead and onDescriptorWrite
This is used to write/read the configuration settings for the BLE device, some manufactures might require to send some data to the BLE device and acknowledge it by reading, before you can connect to the BLE device
onCharacteristicWrite
This is used to send data to the BLE device, usually in data mode for the BLE device.
This callback is called when you type
gatt.writeCharacteristic(characteristics);
onCharacteristicRead
This is used to read data from the BLE device
The callback is called when you write this code
gatt.readCharacteristic(characteristics);
onCharacteristicChanged
This callback is called when you are trying to send data using writeCharacteristic(characteristics) and the BLE device responds with some value.
Usually a BLE device has few characteristics, to make it simple, I name a few characteristics
WRITE - write Characteristics
READ - read Characteristics
To make it clear, when you send data, you will need to use WRITE characteristics and then when the BLE device responds Android app will call READ characteristics
A very important point to note is Android BLE stack allows you to write characteristics one at a time only!!
Example: IF you try to call write characteristics twice at a same time
gatt.writeCharacteristic(characteristics);
gatt.writeCharacteristic(characteristics);
The Android BLE stack will not issue the 2nd write characteristics!
Before setValue:characteristics.setValue(data) you should use gatt.setCharacteristicNotification(Char,true) to setNotification.