[arduino ble nano] data loss during bluetooth transmission

I am using the BLE nano 33 IOT. I am trying to capture and transmit the data of the ECG sensor to the smartphone using ble notify characteristic.

However I am able to acquire the sample rate of 1000/second using serial communication. I am trying to send the same number of samples (1000 samples/second) by bluetooth low energy. I am trying to iterate the issues in bullet points.

  1. When I connect ble scanner app to bluetooth of ble nano IOT, it transmits ecg data of sample rate of 1000.

  2. When I connect the scanner app to bluetooth of ble nano IOT and press the notify button, the sample rate of ecg gets reduced up to 300.

Can you please help me in troubleshooting this issue. I want to send 1000 Samples per second while pressing notify data on ble scanner app.

That is not how BLE works. The peripheral does not transmit data as it wishes. The peripheral is a server and offers services to clients. They decide how often they want to read the characteristic. All the peripheral does is update the characteristic. That does not cause any data to be send. Only when the client reads a characteristic the data is send. That is why notifications are there to inform the client that the data on the peripheral has changed.

BLE was not designed as serial replacement. BLE was designed for low power not bandwidth. What do you want to do with the data on your phone? BLE was designed for smart sensors that would process the data and then send smart data to a client e.g., a few values per second.

To get the maximum number of values over BLE you need to choose the smallest data type for your application and then create a compound characteristic to send multiple values in one packet. The data packet size is a bit less than 30 bytes.

Can you post your code?

I am trying to develop a ecg patch which sends real time raw values of ecg to the android smartphone and then smartphone to do necessary processing of anomaly detection and GUI interface.

generally in order to identify different waveform accurately, sample rate of greater than 500 is required. So in case of serial, i can able to achieve this. but in case of bluetooth number of data samples are reduced. I am sharing my code here. Please let me know if anything could be done to share all samples via bluetooth.

#include <ArduinoBLE.h>
BLEService batteryService("180d");
BLEIntCharacteristic batteryLevelChar("1801",  BLENotify);

void setup() {
  Serial.begin(9600);
  BLE.begin();
  analogReadResolution(12);
  BLE.setLocalName("HOPS_CARDIO");
  BLE.setAdvertisedService(batteryService); // add the service UUID
  batteryService.addCharacteristic(batteryLevelChar); // add the battery level characteristic
  BLE.addService(batteryService); // Add the battery service
  BLE.advertise();
  Serial.println("Bluetooth device active, waiting for connections...");
}

void loop() {
  // wait for a BLE central
  BLEDevice central = BLE.central();
  if (central) {
    while (central.connected()) {
      updateBatteryLevel();
    }
    Serial.print("Disconnected from central: ");
    Serial.println(central.address());
  }
}

void updateBatteryLevel() {
  int sensorValue = analogRead(A1);
  Serial.println(sensorValue);
  batteryLevelChar.writeValue(sensorValue);
  delayMicroseconds(1);
}
1 Like

Do you already have your own app on the Android phone? How do you write the app e.g., what tools are you using and what programming language? You will need to be able to separate a bunch of bytes into the individual sample values.

Can you confirm you need the 12-bit resolution?

Do you have a link to the ECG sensor datasheet? What kind of signal do you get from the sensor? Using delayMicroseconds with analogRead will not give you equidistant samples. That makes signal analysis kind of impossible. You would need to use a timer to run the A/D conversion automatically.

I am troubleshooting on the application downloaded from the

Link for the ECG sensor : SparkFun Single Lead Heart Rate Monitor - AD8232 - SEN-12650 - SparkFun Electronics

Yes, we do require 12bit resolution, However, even with 10 bit resolution we were facing the same issue.

Actually, I was checking the maximum sample rate that i can achieve by reducing the delay time. In different trials, with the delay of 1 microseconds i was able to get sampling rate of 3000 in serial. so I continued in that direction. While I have less knowledge of A/D conversion using timer, I haven't tried it yet.

Here is an example that combines multiple samples into a single characteristic. You will need to separate the samples in your app. I chose the number of samples to fill a standard BLE data packet.
I also included a packetCounter to allow your application to detect when a data packet was lost.
It adds one byte to the characteristic and counts from 0-255 continuously.

/*
  This example creates a BLE peripheral with a service that stores multiple samples in a characteristic.
  The characteristic contains a packetCounter to allow the receiver to detect missing packets on the
  application layer.

  The circuit:
  - Arduino Nano 33 BLE

  You can use a generic BLE central app, like LightBlue (iOS and Android) or
  nRF Connect (Android), to interact with the services and characteristics
  created in this sketch.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>


//----------------------------------------------------------------------------------------------------------------------
// BLE UUIDs
//----------------------------------------------------------------------------------------------------------------------

#define BLE_UUID_CARDIO_SERVICE                   "991F0000-7774-3332-15F5-90324778E1BF"
#define BLE_UUID_CARDIO                           "991F0001-7774-3332-15F5-90324778E1BF"

//----------------------------------------------------------------------------------------------------------------------
// BLE
//----------------------------------------------------------------------------------------------------------------------

#define NUMBER_OF_SAMPLES   12

union cardio_data_u
{
  struct __attribute__( ( packed ) )
  {
    uint16_t values[NUMBER_OF_SAMPLES];
    uint8_t packetCounter = 0;
  };
  uint8_t bytes[ NUMBER_OF_SAMPLES * sizeof( uint16_t ) + sizeof packetCounter ];
};

typedef struct
{
  cardio_data_u data;
  uint32_t index = 0;
  bool updated = false;
} cardio_data_t;

cardio_data_t cardioData;

BLEService cardioService( BLE_UUID_CARDIO_SERVICE );
BLECharacteristic cardioCharacteristic( BLE_UUID_CARDIO, BLERead | BLENotify, sizeof cardioData.data.bytes );

#define BLE_DEVICE_NAME                           "Arduino Nano 33 BLE"
#define BLE_LOCAL_NAME                            "Arduino Nano 33 BLE"

//----------------------------------------------------------------------------------------------------------------------
// APP & I/O
//----------------------------------------------------------------------------------------------------------------------

#define BLE_LED_PIN                               LED_BUILTIN
#define CARDIO_SENSOR_PIN                         A0


void setup()
{
  Serial.begin( 9600 );
  while ( !Serial );

  pinMode( BLE_LED_PIN, OUTPUT );

  if ( !setupBleMode() )
  {
    Serial.println( "Failed to initialize BLE!" );
    while ( 1 );
  }
  else
  {
    Serial.println( "BLE initialized. Waiting for clients to connect." );
  }
}


void loop()
{
  bleTask();
  if ( sensorTask() )
  {
    printTask();
  }
}


bool sensorTask()
{
  const uint32_t SENSOR_UPDATE_INTERVAL = 1;
  static uint32_t previousMillis = 0;
//  static uint16_t counter = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis < SENSOR_UPDATE_INTERVAL )
  {
    return false;
  }
  previousMillis = currentMillis;

//  cardioData.data.values[cardioData.index] = counter;
//  counter = ( counter + 1 ) % 1000;
  cardioData.data.values[cardioData.index] = analogRead( CARDIO_SENSOR_PIN );
  cardioData.index = ( cardioData.index + 1 ) % NUMBER_OF_SAMPLES;
  if ( cardioData.index != 0 )
  {
    return false;
  }

  cardioData.updated = true;
  return true;
}


void printTask()
{
  Serial.print( cardioData.data.packetCounter );
  Serial.print( "\t" );

  for ( uint32_t i = 0; i < NUMBER_OF_SAMPLES; i++ )
  {
    Serial.print( cardioData.data.values[i] );
    if ( i < NUMBER_OF_SAMPLES - 1 )
    {
      Serial.print( "\t" );
    }
  }
  Serial.println();
}


bool setupBleMode()
{
  if ( !BLE.begin() )
  {
    return false;
  }

  // set advertised local name and service UUID
  BLE.setDeviceName( BLE_DEVICE_NAME );
  BLE.setLocalName( BLE_LOCAL_NAME );
  BLE.setAdvertisedService( cardioService );

  // BLE add characteristics
  cardioService.addCharacteristic( cardioCharacteristic );

  // add service
  BLE.addService( cardioService );

  // set the initial value for the characeristic
  cardioCharacteristic.writeValue( cardioData.data.bytes, sizeof cardioData.data.bytes );

  // set BLE event handlers
  BLE.setEventHandler( BLEConnected, blePeripheralConnectHandler );
  BLE.setEventHandler( BLEDisconnected, blePeripheralDisconnectHandler );

  // start advertising
  BLE.advertise();

  return true;
}


void bleTask()
{
  const uint32_t BLE_UPDATE_INTERVAL = 10;
  static uint32_t previousMillis = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis >= BLE_UPDATE_INTERVAL )
  {
    previousMillis = currentMillis;
    BLE.poll();
  }

  if ( cardioData.updated )
  {
    cardioCharacteristic.writeValue( cardioData.data.bytes, sizeof cardioData.data.bytes );
    cardioData.data.packetCounter++;
    cardioData.updated = false;
  }
}

void blePeripheralConnectHandler( BLEDevice central )
{
  digitalWrite( BLE_LED_PIN, HIGH );
  Serial.print( F( "Connected to central: " ) );
  Serial.println( central.address() );
}


void blePeripheralDisconnectHandler( BLEDevice central )
{
  digitalWrite( BLE_LED_PIN, LOW );
  Serial.print( F( "Disconnected from central: " ) );
  Serial.println( central.address() );
}

If you have any questions please let me know.
There is another counter in the code commented out. I used that during debugging instead of sending analog data.

1 Like

Thank you for the effort & support. I am going to implement this code. Will let you know in short time.

Thank you Klaus_k. your solution is working in my case.

@Klaus_K how would you parse this struct using swift. I have a very similar program on Arduino and I am trying to receive this data on my app.

1 Like

@boggy17 Welcome to the forum

I have not used Swift myself, but when I googled "swift parsing array of bytes" I found some pages on reddit and stackoverflow that discuss how to do this. I recommend you have a look around and try some of the solution. The code did not look too complicated.

Next time I recommend you create a new post. That will give you a better chance to get a fast answer from other users.

1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.