Low data throughput rate issues

Hey,

I’m trying to use the Nano 33 BLE as a motion tracker of sorts.

The plan was to send acceleromoter and gyro readings over BLE through a characteristic set to notify. I’m also sending a 1 byte integer so I can reorder the data (I’m assuming things may arrive out of order or some messages may get dropped so I’d need to interpolate).

I originally planned to do this at 100Hz but the arduino would end up skipping 1-4 frames once a device is connected (I’m outputting the time between frames on the serial output).

I’ve dropped it to 30Hz and now it only skips when I move the arduino whilst connected to bluetooth. I’m not entirely sure why, I reckon its probably because moving the arduino quickly puts some strain on the USB connector.

But back to the point. The NINA-B306 (or any of the NINA-B chips) are advertised to have a maximum throughput of 1.8Mbit/s when not using their bootloader and 0.8Mbit/s when using their uconnect-xpress bootloader.

I was sending 9600 bit/s @ 100Hz and 3168 bit/s @ 30Hz but that doesn’t take into account protocol overhead or the possibility that mbed OS doesn’t handle bluetooth that well.

Do I need to get a segger J-link and us the B306 in open cpu mode or should I combine messages together to make the overhead not as painful?

Edit:
Noticed I was sending 14bytes not 13 (doesn’t make a huge difference) and added code

Added code:

#include <LSM9DS1_Registers.h>
#include <LSM9DS1_Types.h>
#include <SparkFunLSM9DS1.h>
#include <Wire.h>
#include <ArduinoBLE.h>
BLEService bleService(“19B10010-E8F2-537E-4F6C-D104768A1214”);
BLECharacteristic bleChar(“19B10011-E8F2-537E-4F6C-D104768A1214”, BLERead | BLENotify,14,true);
LSM9DS1 imu;
#define PRINT_CALCULATED
//#define PRINT_RAW
#define PRINT_SPEED 33 // 250 ms between prints
int16_t buffer[7];
void setupIMU(){
//Settings ommitted for brevity
}

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
// put your setup code here, to run once:
Serial.begin(115200);
Wire1.begin();
setupIMU();
if (imu.begin(0x6B,0x1E,Wire1) == false) // with no arguments, this uses default addresses (AG:0x6B, M:0x1E) and i2c port (Wire).
{
Serial.println(“Failed to communicate with LSM9DS1.”);
Serial.println(“Double-check wiring.”);
Serial.println("Default settings in this sketch will "
"work for an out of the box LSM9DS1 "
"Breakout, but may need to be modified "
“if the board jumpers are.”);
while (1);
}
if (!BLE.begin())
{
Serial.println(“starting BLE failed!”);
while (1);
}
BLE.setLocalName(“BLETest”);
BLE.setAdvertisedService(bleService);
bleService.addCharacteristic(bleChar);
BLE.addService(bleService);
bleChar.writeValue(buffer,6);

BLE.advertise();

Serial.println(“Init complete”);
}
bool gR,aR = false;

static unsigned long lastPrint = 0; // Keep track of print time
void loop()
{
BLE.poll();
if ( !gR && imu.gyroAvailable() ) //Reads imu data once per frame
{
imu.readGyro();
gR=true;
}
if ( !aR && imu.accelAvailable() )
{
imu.readAccel();
aR=true;
}

unsigned long t = millis();
short offset = t-lastPrint-PRINT_SPEED;
if (offset >= 0 )
{

Serial.println(offset);

buffer[0] = imu.gx;
buffer[1] = imu.gy;
buffer[2] = imu.gz;
buffer[3] = imu.ax;
buffer[4] = imu.ay;
buffer[5] = imu.az;
buffer[6] = (int16_t)(t>>16);
bleChar.writeValue(buffer,14);
lastPrint = t; // Update lastPrint time
gR = aR = false;

}

if((millis()%1000)<500)
digitalWrite(LED_BUILTIN, HIGH);
else
digitalWrite(LED_BUILTIN, LOW);
}

I've put together a poor man's profiler, since I don't have a debugger and can't seem to get visual micro working.

It appears all the lag spikes are due to sending too many packets. When you write some data to a characteristic it checks to see how many incoming packets are still pending to be processed and it will wait until the number of pending packets is less than the maximum.

I also checked to see how long it takes to send 14bytes and how costly that is. The answers are not long at all and quite a bit. Using an MTU of 23 requires 2 packets, each packet leaves 8 bytes for application data after header data. Increasing the MTU to 29 bytes could alleviate this problem as it would halve the number of packets being sent out.

However, I'm still being pretty inefficient, most of the data sent is protocol overhead. I should probably increase the MTU much higher and send data in bunches. This would improve efficiency further and reduce the number of packets needing to be dealt with.

Additionally, I could consider rewriting parts of the ArduinoBLE library for my own use case. I only care about sending data, I could completely ignore that incoming packets need to be processed. I think this will be a last resort.

I will try and see if I can improve performance with batching tomorrow. Worst case it will be on Thursday. If that doesn't work then I will look to rewriting the ArduinoBLE library within the next week or two.

Found some time to get some quick tests in.

It seems the arduino BLE library doesn’t respond well if incoming packets are dropped/delayed/missing.

If you have an explicit need to send packets at a particulary frequency then you need to modify <utility/HCI.cpp> in the Arduino BLE library.

https://github.com/arduino-libraries/ArduinoBLE/blob/8dc4290ab00b7de8164489768dccf6271f919cc3/src/utility/HCI.cpp#L418
You need to create a way for the number of pending packets to be ignored when sending data. I just created a public boolean in the HCI class and added it to the while loop condition. I disable waiting for pending packets when I’m streaming data, I have to keep it enabled otherwise as it seems to prevent devices from connecting.