BLE data interval

Hello

I am about to make a portable oscilloscope that would sample voltage at 500Hz in 16-bit resolution and stream the readings into my phone running an AppInventor app. Saving data to the SD card inside the phone would be also great. My phone is Samsung Galaxy S7.

My board can sample the voltage values without any problems, however streaming data over BLE seems to be more challenging than i thought. Here is a basic arduino code:

#include <ArduinoBLE.h>

BLEService batteryService("180F");
BLEUnsignedCharCharacteristic batteryLevelChar("2A19", BLERead | BLENotify);

int oldBatteryLevel = 0;
long previousMillis = 0;

void setup() {
  Serial.begin(500000);
  while (!Serial);
  pinMode(LED_BUILTIN, OUTPUT);

  if (!BLE.begin()) {
    Serial.println("starting BLE failed!");
    while (1);
  }

  BLE.setLocalName("BatteryMonitor");
  BLE.setAdvertisedService(batteryService);
  batteryService.addCharacteristic(batteryLevelChar);
  BLE.addService(batteryService);
  batteryLevelChar.writeValue(oldBatteryLevel);

  BLE.advertise();

  Serial.println("Bluetooth device active, waiting for connections...");
}

void loop() {

  BLEDevice central = BLE.central();

  if (central) {
    Serial.print("Connected to central: ");
    Serial.println(central.address());
    digitalWrite(LED_BUILTIN, HIGH);

    while (central.connected()) {
      long currentMillis = millis();

      if (currentMillis - previousMillis >= 20) {
        previousMillis = currentMillis;
        updateBatteryLevel();
      }
    }
    digitalWrite(LED_BUILTIN, LOW);
    Serial.print("Disconnected from central: ");
    Serial.println(central.address());
  }
}

void updateBatteryLevel() {
  int battery = random(0,100);
  batteryLevelChar.writeValue(battery);
}

BT_connmeter.aia (213.8 KB)

I know BLE will also add a random delay of 0-10ms while advertising (to reduce possibility of interference even if there are many BLE devices nearby), however once the connection is established, the random delay should not be added anymore.
So i assume the above code should send a new value every 20ms to my phone once it is connected to my board.

In other BLE related topics i have read that in AppInventor we need to use Clock.Timer in conjuction with BLE.ReadBytes and set that timer to ca. 80% of the interval the board uses to transmit data. This way we could provide the phone enough time to do all the math and avoid overflowing the receive buffers.

However in my App i am facing:

  • If i use the proposed method described above, the shortest interval between BLE transactions on the phone is about 100ms even if the board transmits new values in shorter intervals.

  • It seems that just the evaluation of Clock.Timer takes about 15ms (as the reported interval is that much longer regardless of what have set as the interval)

  • If i turn the logic to RegisterForBytes (and disable blocks of the Clock.Timer logic), then i can get BLE transaction intervals much closer to the interval at which the board sends new values but after a while the BLE transaction interval rises to about 50ms.

I know that the posted arduino code will send only 1 value at a time and the standard MTU could transmit 20bytes per transaction... however if the shortest transaction interval that will not cause buffer overflow in the app is about 100ms, then the highest voltage sampling rate of my envisioned oscilloscope can not be higher than 20Hz even at 8-bit resolution. And then i still did not try to save the data on the SD card inside the phone...

Yes, i know that from BLE4.2 we can change the MTU size and also AppInventor's BLE extension has that feature, however i would prefer using the default MTU size as many people have older phones.

I wonder why BLE performance is MUCH worse than expected? Is it my phone, Android 8.0 or Arduino's BLE implementation being too slow, or maybe AppInventor's code implementation is the bottleneck?
Any idea how could i reach shorter transaction intervals (goal would be around 1MTU each 20ms)?

Thanks in advance!