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.
Related
I am trying to write a 120 byte data through ble to raspberrypi from my android app(I increased the MTU to the required limit). But all I am able to transfer is first 20 bytes. When I tried to search on the internet, I see that android limits the size to 20 byte for ble transfers and I will have to send multiple 20 byte packets.
But why I tried to end the same data from nrfConnect android app, I see that the data is being transferred without any issue. Can you help me understand how nrfConnect is able to do it with you writing it as packets?
Through the data is getting truncated, i am getting GATT_SUCCESS response from raspberrypi
Just as info, I am able to send 52 bytes to our custom board with nrf52 chip from the same app
Looks like android takes care of writing more than 20 bytes of data.
Below are the two cases that I had:
Case 1:
Android app trying to write data to custom program running in nrf52 chip.
Size of data : 50 bytes.
After increase the BLE MTU on the chip side, the write was successful without any changes from app side.(gatt.writeCharacteristic(characteristic))
Case 2:
Android app trying to write data to ble program running in raspberrypi 3
Size of data 120 bytes.
Issue:Even after increasing the BLE MTU in raspberry pi, when trying to write the data only 20 bytes are received, with successful write response.
Solution: After using gatt.requestMtu(120) and calling the write character in onMtuChanged() callback, was able to send the entire data.
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.
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.
I'm working on an Android Bluetooth project to send and receive data from a custom made hardware. I used Bluetooth Chat Sample in SDK as basis.
I am sending data from one device to another (LG Nexus 4). All is ok until I reach a length of 1004 bytes (it is the audio data). At that point it splits it into 2 messages of 990 and 14 bytes in most of cases. but is strange sometimes its sending 1004 without splitting (approx. 4 times in 100).
I am sending this packet of 1004 bytes, in which there is 4 bytes is my header and rest of 1000 bytes is actual data which I want to use as per command in header, now if packets are splitting as per above mentioned way than I cannot handle the flow.
So, please let me know why packets are splitting in such way and how can I stop this splitting or, if I cannot do this, than please suggest me any alternative way to do this.
Thanks.
Data sent via Bluetooth socket is abstracted as a stream. Here the transport layer is broken into packets, where packet has a maximum size of almost 1KB(1000 bytes). So you can devise a mechanism in which you can send the message length info in the header, then on receiving side you will have to make subsequent read() calls; each returning data for one packet.
I did a client/server(android/pc) and it seems that network usage from client uses a lot of CPU. Like to receive only 4k-5k from network, the cpu rises to 33 milliseconds. The cpu can be higher than 90-100 milliseconds if data is higher like ~32k.
First, I've tried the client(network part) in java version and after in c and the problem is still there.
I profiled the server part that send data and it uses about 0 millisecond.
Some details:
TCP connection.
The client connects to the server, client sends request, server sends
data (chunk of 4-10k), client send request, server sends...
Network part is threaded.
Get data with (recv or recv/select).
Smart Phone: Nexus one.
Tested in profiler mode (only network part and display fps/milliseconds).
Tested in Wifi (computer, phone, network are close).
Let me know if you have any suggestions or questions.
Thanks.
Are you using BufferedOutputStream on Android side to write the data? If not, it writes it byte by byte, which would explain the high CPU usage.
If this is not the case, please add some source code to the question.