How can I increase the throughput of my BLE application? - android

I have an android client that functions as a central and have an app on my MAC (peripheral) that this central connects to and sends data.
At this point, I need to wait almost 100ms after I call writeCharacteristic(..) to receive the onCharacteristicWrite(..) callback. I am sending strings. If I send smaller strings, the throughput is great (understandably). When the string contains about 200 characters and I send 20 byte chunks, it takes almost a second before the entire string is seen at the peripheral. When I set the write type to NO_RESPONSE before writing the characteristic, I see no data on the peripheral.
After I connect, I have done the following to improve throughput:
Stopped discovery after services are discovered because it is an expensive operation
I set the write type to default first - When I do this, I see data on the peripheral. But, there is a significant delay. When I set the writeType to NO_RESPONSE, I see no data on the peripheral. I have no logic in onCharacteristicWrite(..) either. Sometimes, I see the data getting truncated on the peripheral.
I have set the desired connection latency to low on my mac app. Is there a way to set a value (as 7.5ms perhaps?).
When I set the write type to default and send a string of 200 characters - I split the string into 20 byte chunks. I now have 10 chunks to send. If I set characteristic value and call writeCharacteristic(..) in loop, I see no data. When I add a ~100ms delay after writeCharacteristic(..) before it executes the next iteration of the loop, I see data on the peripheral.
I see a huge increase in throughput between an iOS central - iOS peripheral. I don't see why Android central - iOS peripheral shouldn't work he same way. From my understanding, Android and iOS use the same chip.
Any reason the performance is so poor? Is there anything else I can do to improve throughput?

Please have a look at the MTU size. My experience:
Using a iOS central, the central automatically starts the MTU size negotiation with some large value. I think it is larger than 200 bytes.
On most Android devices I tested this does not start automatically but you have to start the MTU size negotiation by your app (central). If you do not do that, Android cuts your data into 20 byte pieces. This has big influence on your throughput.

Related

Websocket giving various numbers depending on OS

I'm doing a little test where I send out a short string(4-8 bytes) to a client every 0.5 seconds from a Node.js server using ws. The client is either using iOS/Android or a web browser. The client does not send anything back to the server, except for TCP-ACKS I suppose. The weird thing is, when I'm debugging the app in iOS using XCode network report, I can only see that the client sends out some bytes(approx 500) when the connections establishes, probably during the HTTP handshake. The remaining time ZERO data is going out from the device, there is only data coming in. The same results is achieved when receiving data in Chrome and tracking the data using Nettop.
The thing that makes so confused is that on the Android, almost the same amount of data that goes in to the device goes out when inspecting the network usage with Android profiler/Battery Historian/TrafficStats. I have tried using different libraries for the Websocket implementation and using different Android devices.
I have a hard time believing the ACKS sent out by the android is as big as the message received, even though it's just a small string of four characters.
So my questions are:
Could the case be that Nettop/XCode network report is simply ignoring all the ACKS, so in reality as much data is sent out in Chrome/iOS as in Android?
Is there something 'Wrong' with the libraries used in Android or could it be something with its operating system?
Could an ACK be as big as a simple TCP-package with 4 characters in it?
The result below when using Websocket
The data received/transmitted when using Android Battery Historian
The data received/transmitted on iOS using Network Report
Could an ACK be as big as a simple TCP-package with 4 characters in it?
An ACK consists of the IP and the TCP header and no payload. With IPv4 this means at least 20 bytes IP header and 20 bytes TCP header, i.e. 40 bytes. A packet with 4 bytes payload is only larger by 4 bytes, i.e. 44 bytes or just 10%.
The network report in Android shows 68350 in vs. 61370 in bytes, which is a difference of 11%. This matches the expected difference.
I'm not familiar with what iOS measure here, but it probably either measures only the application payload (i.e. the 4 bytes) or simply ignores packets with no payload, i.e. the ACK's.

Android BLE connection breaks when sending many packets with WRITE_TYPE_NO_RESPONSE

I am getting crazy with a project where I need to send firmware files from an Android device to a STM32F4 chip using Bluetooth LE.
I have already implemented BLE on both ends successfully and I am working with it with several characteristics for a long period without any problem.
Now a file transfer ought to be implemented that shall be able to send files in size of about 250K. My implementation seem to work but only in one of 10 cases. It does start sending packets in chunks of 20 bytes but then it
stops communication in 90% of the test cases on an undetermined point. I need to disconnect/reset and restart to get things up again.
Characteristic for file transger on the STM32F4 are defined as:
ret = aci_gatt_add_char(fileServiceHandle,
UUID_TYPE_128, // File xfer UUID
uuid, // Char UUID
FILEIO_RECORD_LEN, // Maximum length of the characteristic value (20)
CHAR_PROP_WRITE|CHAR_PROP_WRITE_WITHOUT_RESP|CHAR_PROP_NOTIFY, // WRITE NOTIFY me
ATTR_PERMISSION_NONE, // Nothing special
GATT_NOTIFY_ATTRIBUTE_WRITE, // The application will be notified when a client writes to this attribute.
// An #ref EVT_BLUE_GATT_ATTRIBUTE_MODIFIED will be issued.
16, // Encryption key size
0, // is fixed length (1== variable size)
&fileRequestHandle); // ReturnValue als handle
In Andoid I am setting the WRITE_TYPE_NO_RESPONSE flag in the service characteristic to
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
... aServiceCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
Writing the packets is done in the onCharacteristicWrite call back function for a FIFO of maximum 8 packets.
Build up to 8 fragments of file data and queue it to a fifo
wrtCharacteristic.setValue(firstQueueItem);
in onCharacteristicWrite call back: if queue not empy { wrtCharacteristic.setValue(nextQueueItem); }
If the last packet is received in the STM32F4, all packets in that group are verified and an acknowledge is send back causing an event in the APP.
The event then triggers sending the next 8 packets.
This looks pretty straight forward to me and seem to work sometimes. It works always though if I am setting the number of consecutive blocks to 1. All other sizes do not complete sending the in files in almost all cases.
There is no evidence of when the transfer is broken, sometimes immediately, sometimes after sending more than 80% of the data.
I have also tried to skip writing the received data on the STM32F4 to the flash storage to avoid SPI interferences without any changes in behavior.
Is there anything that I am missing here? Where could I check for errors. Any help wouldbe very much appreciated.
For unknown reasons, this problem does occur any longer. My implementation has not changed compared to what I stated on top. I tried to request a BLE response for the last packet of each group but that does not seem to make any difference.
I thank everybody who read and commented on this entry.

BLE: Read Long Characteristics Value using Android / iOS

With a normal Characteristics Read only the MTU Size (20bytes) of data will be read.
My customer will offer a characteristics with a larger size (about 100bytes).
I saw that BLE offers a "Long Read" feature which reads until the size of the characteristics is reached.
(https://bluegiga.zendesk.com/entries/25053373--REFERENCE-BLE-master-slave-GATT-client-server-and-data-RX-TX-basics)
attclient_read_long command - Starts a procedure where the client first sends normal read request to the server, and if the server returns an attribute value with a length equal to the BLE MTU (22 bytes), then the client continues to send "read long" requests until rest of the attribute is read. This only applies if you are reading attributes that are longer than 22 bytes. It is often simpler to construct your GATT server such that there are no long attributes, for simplicity. Note that the BLE protocol still requires that data is packetized into max. 22-byte chunks, so using "read long" does not save transmission time.
But how can I use this feature in Android?
The BluetoothGatt class only offers a simple "Read()" - same for iOS.
Increasing the MTU is not possible since we need to support devices with AP Level < 21 (increaseMTU was introduced at API 21)
I can confirm for iOS that a read operation as per standard will occur first. Then if the server returns a completely filled PDU, the iOS device will then continue to perform the blob read operation. Tested with iPhone 7 running iOS 11.2.x
You do NOT need to call the peripheral.readValue(characteristic) multiple times for long attributes. CoreBluetooth does all of this under the covers.
Refer to the Bluetooth Spec Core v5.0, specifically Vol 3, Part F. "Long Attribute Values".
Experiment to prove above.
I have an Android Thing acting as the server that I'm making return the maximum length with my iPhone during a read operation. iOS and my RPI3 exchange a MTU of 185. So the read response is (MTU - 1) 184 bytes long. The server (RPI) then receives a new read request with an offset of 184, which you can then return more data. This is continued until the offset is > 512, or the last read response returns less than the MTU - 1 length.
Based upon the fact that the BluetoothGattServer supports long attributes, I'd assume the BluetoothGatt object does as well. Since there is no way via the API to set the offset to be read, I'd assume you can invoke read just once.

What's the quickest way to send lots of data over BLE?

If I want to transfer a lot of data (e.g. 1 MB file) over BLE, what's the best way to do it?
I control both sides of the connection, but the client side is iOS/Android so only has access to GATT. I can't do anything with L2CAP.
I also can't wait for Bluetooth 4.1, 6LoWPAN, Connection-Oriented-Channels or anything like that.
I would assume the answer is to have one "request" characteristic that you write a data request to ("Give me 3000 bytes starting at byte 0"), and a "data out" characteristic that sends lots of 20 byte notifications (the maximum characteristic size) containing the data.
Is there a better way?
Yes we are using the approach you have mentioned.
Request data with the last index number(First time the index is 0)
The server send you with data with index no.Store the index no for subsequent format
continue Step 1 and 2 till the time server sends end of data-probably with index -1 or something.
Make sure you transfer the data you required in the most space efficient format.See if you can zip the files and transfer it.
You can update connection interval to small value with smallest 6*1.25 ms in remote BLE device.
Actually, BLE is designed for Low energy, small packet, low data rate.
L2cap data will be transmitted in different data channel with frequency hop. Packets TX/RX happen within each connection interval and max number of packets TX/RX in an event is restricted by specification, finally implemented by manufacture. So we can change connection interval as small as possible to increase data rate.
Refer BT 4.0 spec Vol 2, 7.8.18 LE Connection Update Command.
Try to negotiate a larger MTU than the default.
Then each notification can be larger. Even though it will be fragmented by the L2CAP layer, you will get a slightly larger throughput since the packet header will be smaller.

BLE Sending Blob request through android device

Is there someone using blob request (long read) from an android device?
We work with a CC2540 from TI, connected to a android 4.4.
We try to read a long characteristic value (size more than 23 bytes). In the android API for BLE, we have not seen a readBlob or readLong method.
We expect that the Android BLE Stack do the job for us, by reading a characteristic presentation format (same way has notification), but it doesn't works.
We have no idea how to send Blob Request through Android.
Let me make this clear that Android has only one method to read the value of a characteristic, readCharacteristic(characteristic). You can use this method to get the value of a characteristic of any length. Android takes care of forming a ReadBlob request; it's all in the back end. You'd have to change the code of your CC2540 though, to make it work with ReadBlob request. Once you make all the required changes at your CC2540 end, on calling readCharacteristic() from Android, you'll get the entire value of the characteristic which you can access in the onCharacteristicRead() callback.
You canĀ“t, BLE characteristic values are limited at 20 bytes. So if you want to send or receive more than 20 bytes, you have to split it into 20 byte chunks. See this topic on the issue.

Categories

Resources