IOS BLE packet loss - ios

I'm writing an IOS/iPhone app that communicates with an sp-10c imu via bluetooth low energy. The packets I am receiving were originally 61 bytes long, but I have shortened them to 38 bytes so that the packet is sent in the minimum number of notifications.
I have no control over the actual programming of the sp-10c, but they do give me control of some of the settings. I can change what all info I receive (accelerations, gyros quaternions, etc) and thereby change the packet size, and I can also change the transmission interval (by 10 millisecond intervals). I have read a lot of the questions and answers on here related to this subject, but the only solutions I have seen require having programming control over the peripheral device, which I do not have.
I have also read Apple's handbook on this, which states that the minimum interval should be 20 ms. If I could reliably get entire packets at 50 hz, that would work for my needs. Anything less is pretty much worthless. My problem is that I'm getting intervals of massive packet loss at the 20ms interval (there are times when 40 or more packets are lost, and this happens regularly). I really don't see reliable results until I slow it down to an interval of 60 ms or more, which, as I said, is worthless. (I am trying to sense a sharp change in acceleration that lasts about 20 - 40ms)
Most of the questions and answers on here are somewhat dated, but I saw where someone said that things may have gotten worse as far as the BLE connection + Apple devices go. It has been suggested that classic bluetooth is the only way to go when large amounts of throughput are needed, but the sp-10c does not offer classic bluetooth as an option.
My questions are:
Is this the normal behavior for a ble connection with apple devices?
Has anyone had any success getting reliable ble notifications at 20ms with longer packets?
Should I give up now and try to find an imu with classic bluetooth?
I guess what I'm really getting at is this: Am I doing something wrong, or is this just par for the ble/iPhone course?
Would it help to try and limit the packet to less than 20 bytes so it is received in one notification?

Related

Bluetooth Low Energy data transmission on iOS

I'm recently working on a project which uses Bluetooth Low Energy. I implemented most of communication protocol, however I started having concerns, that actually I don't know how the data transmission works and if the solution that I implemented is going to behave in the same way with all devices.
So my main concern is what data chunk is received when I get a notification from peripheral(_:didUpdateValueFor:error:)? Is it only as big as negotatiated MTU size? Or maybe iOS receives information about chunk size and waits to receive it all before triggering peripheral(_:didUpdateValueFor:error:)?
When a peripheral sends chunks let's say 100 bytes each, can I assume that I will get always in a single notification 100 bytes? Or could it be last 50 bytes from previous chunk and first 50 bytes from the next one? That would be quite tricky and hard to detect where is the beginning of my frame.
I tried to find more information in Apple documentation but there is nothing about it.
My guess is that I receive always a single state of characteristic. Therefore it would mean that chunks depend on implementation on peripheral side. But what if characteristic is bigger than MTU size?
First, keep in mind that sending streaming data over a characteristic is not what characteristics are designed for. The point of characteristics is to represent some small (~20 bytes) piece of information like current battery level, device name, or current heartbeat. The idea is that a characteristics will change only when the underly value changes. It was never designed to be a serial protocol. So your default assumption should be that it's up to you to manage everything about that.
You should not write more data to a characteristic than the value you get from maximumWriteValueLength(for:). Chunking is your job.
Each individual value you write will appear to the receiver atomically. Remember, these are intended to be individual values, not chunks out of a larger data stream, so it would make no sense to overlap values from the same characteristic. "Atomically" means it all arrives or none of it. So if your MTU can handle 100 bytes, and you write 100 bytes, the other side will receive 100 bytes or nothing.
That said, there is very little error detection in BLE, and you absolutely can drop packets. It's up to you to verify that the data arrived correctly.
If you're able to target iOS 11+, do look at L2CAP, which is designed for serial protocols rather than using GATT.
If you can't do that, I recommend watching WWDC 2013 Session 703, which covers this use case in detail. (I am having trouble finding a link to it anymore, however.)

iOS Bluetooth BLE read data maximum size

I have an iOS app that reads/writes on a BLE device. The device is sending me data over 20 bytes long and I see they get trimmed. Based on the following thread
Bluetooth LE maximum transmission size
it looks like iOS is trimming the data. That thread shows the solution on how to write bigger data sizes, but how do we read info larger than 20 bytes?
For anyone looking at this post years later like I am, we ran into this question as well at one point. I would like to share some helpful hints for data larger than 20 bytes.
Since the data is larger than one packet can handle, you will need to send it in multiple packets. It helps significantly if your data ALWAYS ends with some sort of END byte. For us, our end byte gives the size of the total byte array so we can check that at the end of reading.
Create a loop that checks for a packet constantly and stops when it receives that end byte (would also be good to have a timeout for that loop).
Make sure to clear the "buffer" when you start a new read.
It is nice to have an "isBusy" boolean to keep track of whether another function is currently waiting to read from the device. This prevents read overlaps. For us, if the port is currently busy, we wait a half second and try again.
Hope this helps!

How can I estimate the power consumption of a BLE module?

I'm writing an iOS app for a device with a BLE module that advertises a few bytes of data on a consistent basis while it's connected. We are trying to estimate the power consumption of the BLE module so we can estimate the battery life for the device. I've scoured SO and Google looking for the appropriate way to estimate this, but I'm coming up empty. Is there a way to take the number of bytes that are being sent, multiplied by the frequency with which the data is sent and come up with a rough approximation of power consumption?
A typical BLE SoC (i.e. a all-in-one Application + Radio chip) typically consumes:
A few hundreds nA while in deep sleep,
2 to 10 µA while a RTC tracks time (needed between radio events while advertising or connected),
10 to 30 mA while CPU or Radio runs (computing data, TX, RX). RX and TX power consumption is roughly the same.
Life of a BLE peripheral basically consists of 3 main states:
Be idle (not advertising, not connected). Most people will tell your device is off. Unless it has a physical power switch, it still consumes a few hundred nanoamps though.
Advertise (before a connection takes place). Peripheral needs to be running approximatively 5 ms every 50 ms. This is the time when your device actually uses most power because advertising requires to send many packets, frequently. Average power consumption is in the 1-10 mA range.
Be connected. Here, consumption is application-dependant. If application is mostly idle, peripheral is required to wakeup periodically and must send a packet each time in order to keep the connection alive. Even if the peripheral has nothing useful to send, an empty packet is still sent. Side effect: that means low duty cycle applications basically transmit packets for free.
So to actually answer you question:
length of your payload is not a problem (as long as you keep your packets shorts): we're talking about transmitting during 1 µs more per bit, while the rest of the handling (waking up, receiving master packet, etc. kept us awake during at least 200 µs);
what you actually call "continuous" is the key point. Is it 5 Hz ? 200 Hz ? 3 kHz ?
Let's say we send data at a 5 Hz rate. Power estimate will be around 5 connection events every second, roughly 2 ms CPU + Radio per connection event, so 10 ms running every second. Average consumption: 200 µA (.01 * 20 mA + .99 * 5 µA)
This calculation does not take some parameters into account though:
You should add consumption from your sensors (Gyro/Accelerometers can eat a few mA),
You should consider on-board communication (i2c, SPI, etc),
If your design actually uses two chips (one for the application talking to a radio module), consumption will roughly double.

Flurry / Google Analytics / Localytics bandwidth consumption on iOS

I'm choosing an analytics service for my iOS app. I want to track quite a lot of events and the app I'm developing is going to be used outdoors, so there will be no wi-fi connection available, and even the cellular connectivity can be of a poor quality.
Analytics is the only thing that requires network connectivity in my app. Recently I've checked how much traffic it consumes, and it consumes much more than I've expected. That was about 500KB for Google Analytics and about 2MB for Flurry, and that's just for a 2-minute long session with a few hundred events. It seems very inefficient to me. (Flurry logs a little bit more parameters, but definitely not 4 times more.)
I wonder — have anybody compared other popular analytics solutions for their bandwidth consumption? Which one is the slimmest one?
Thank you
If you don't need real time data (and you probably don't with outdoor app), you can get the best network compression for Analytics by dispatching more hits at once to benefit from batching and compression. To do that set the dispatch interval to 30 minutes. The maximum size of uncompressed hit that analytics will accept is about 8k so you should be sending less then that. With compression that would bring it down to ~25% of the original size for individual hit assuming mostly ascii data. To generate 500k of data you should be sending few hundred hits individually. With batching and compression the hits will shrink down more efficiently. Usually batch of 20 hits will compress to less then 10% of the uncompressed size or about 800 bytes per hit at most. For further network savings just send less data per event or fewer events. Btw, Analytics has a rate limit of 60 tokens that are replenished at a rate of 1 hit every 2 seconds. If you are sending few hundred events in short period of time your data is likely getting rate limited.
https://developers.google.com/analytics/devguides/collection/ios/limits-quotas#ios_sdk

iOS UDP Server Message Processing Latency Too High ~35-40ms

We have a critical need to lower the latency of our UDP listener on iOS.
We're implementing an alternative to RTP-MIDI that runs on iOS but relies on a simple UDP server to receive MIDI data. The problem we're having is that RTP-MIDI is able receive and process messages around 20ms faster than our simple UDP server on iOS.
We wrote 3 different code bases in order to try and eliminate the possibility that something else in the code was causing the unacceptable delays. In the end we concluded that there is a lag between time when the iPAD actually receives a packet and when that packet is actually presented to our application for reading.
We measured this with with a scope. We put a pulse on one of the probes from the sending device every time it sent a Note-On command. We put another probe attached to the audio output of the ipad. We triggered on the pulse and measured the amount of time it took to hear the audio. The resulting timing was a reliable average of 45ms with a minimum of 38 and maximum around 53 in rare situations.
We did the exact same test with RTP-MIDI (a far more verbose protocol) and it was 20ms faster. The best hunch I have is that, being part of CoreMIDI, RTPMIDI could possibly be getting higher priority than our app, but simply acknowledging this doesn't help us. We really need to figure out how fix this. We want our app to be just as fast, if not faster, than RTPMIDI and I think this should be theoretically possible since our protocol will not be as messy. We've declared RTPMIDI to be unacceptable for our application due to the poor design of its journal system.
The 3 code bases that were tested were:
Objective-C implementation derived from the PGMidi example which would forward data received on UDP verbatim via virtual midi ports to GarageBand etc.
Objective-C source base written by an experienced audio engine developer with a built-in low-latency sine wave generator for output.
Unity3D application with Mono-based UDP listener and built-in sound-font synthesizer plugns.
All 3 implementations showed identical measurements on the scope test.
Any insights on how we can get our messages faster would be greatly appreciated.
NEWER INFORMATION in the search for answers:
I was digging around for answers, and I found this question which seems to suggest that iOS might respond more quickly if the communication were TCP instead of UDP. This would take some effort to test on our part because our embedded system lacks TCP capabilities, only UDP. I am curious as to whether maybe I could hold open a TCP connection for the sole purpose of keeping the Wifi responsive. Crazy Idea? I dunno. Has anyone tried this? I need this to be as real-time as possible.
Answering my own question here:
In order to keep the UDP latency down, it turns out, all I had to do was to make sure the Wifi doesn't go silent for more than 150ms (or so). The exact timing requirements are unknown to me at this time, however the initial tests I was running were with packets 500ms apart and that was too long. When I increased the packet rate to 1 every 150ms, the UDP latency was on par with RTPMIDI giving us total lag time of around 18ms average (vs. 45ms) using the same techniques I described in the original question. This was on par with our RTPMIDI measurements.

Resources