[UPDATE (Feb 8, 2016): I have incorporated the feedback that I received from several link-layer wizards in Trondheim.]
Last fall I purchased an inexpensive quadcopter that uses Nordic's ShockBurst (SB) radio protocol. The question that I was trying to answer when I started disassembling that poor quadcopter was simple: Can Bluetooth Low Energy (BLE) and the S110 SoftDevice's Multiprotocol Timeslot API provide a link with low enough latency to control a twitchy aircraft? After reverse engineering the quadcopter's binding and control packets, I ended up using an nRF51-DK to build a bridge that allowed me to comfortably control the quadcopter from my Android phone using BLE.
In order for this project to succeed, two conditions needed to be met:
Control data from the phone needed to be sent frequently enough to keep the quadcopter under control. Obviously, this data stream can only tolerate a very small amount of latency before the quadcopter careens out of control and heads straight for a defenseless houseplant (in this case, failure smells a lot like a freshly-trimmed lawn).
The quadcopter needed to receive a sufficient number of control packets to not only keep it under control but also prevent it from thinking that its transmitter had gone away.
Some of the attendees of the Global Tech Tour in Seattle, WA or Portland, OR saw me flying the quadcopter around during breaks. As promised, the documentation and code for the project can be found here. Furthermore, I'd like to go into a little more detail here and point out the two issues that I encountered during this project that consumed more of my time than I had anticipated.
For example, according to the documentation for the writeCharacteristic function:
Once the write operation has been completed, the onCharacteristicWrite(BluetoothGatt, BluetoothGattCharacteristic, int) callback is invoked, reporting the result of the operation.
This description makes using the function sound trivial: write a value, wait for the callback, ensure that the write succeeded, and then write the next value. As it turns out, on my Nexus 5 at least, the callback is really only indicating that the driver has received the write command and is capable of dealing with it. In fact, the driver is capable of buffering several write commands so a backlog of commands quickly accumulates and causes about a half second of latency between the moment when a backlogged command is written and when the command is sent to the quadcopter. I fixed the problem by limiting the frequency at which writeCharacteristic is called in order to prevent a backlog from forming. In general, it's advisable to incorporate BLE API functions individually and verify that they actually do what their descriptions say they will before moving on.
In the following screenshot a pin is being set high at the beginning of each timeslot and set low at the end. The space between consecutive timeslots is being used by the SoftDevice for BLE connection events. Normally, the radio is split evenly. However, every 13th timeslot is delayed.
due to the SoftDevice requiring more time during the preceding connection event
The reason that One of the possible reasons for why this happens is because the SoftDevice periodically spends some time to do chores and clean up its scheduling mechanism. This process is done in the link layer module, is done at the end of selected radio events, and runs in the radio's elevated interrupt priority.
In the context of this post, I refer to this activity as "housekeeping." However, this term is not part of the official SoftDevice nomenclature.
Although I was quick to blame housekeeping for the delay above, the wizards agree that it's more likely a consequence of the slave's clock drifting out of sync with the master's clock.
The result of all of this is that even during a BLE connection, when connection events are
deterministic fairly predictable, the granting of timeslots may be difficult to predict. The good news is that efforts are being made to improve the timeslot-related documentation in future SoftDevice Specifications.