Seeed xiao bluetooth comunication increasing sampling rate

Hi guys, I have 2 seedxiaosense and I am using one as peripheral and the other as central. The peripheral is reading the data and is sending the data by BLE to the central. I am reading 20 packets every seconds but I would like to increase the sampling frequency.

Each packet is a string as you can see from the code in “PERIPHERAL”.

do you know if there is an immediate way that I do not see other than change the data format ??

PHERIPHERAL

#include <Wire.h>
#include "SPI.h"
#include <SparkFun_MS5803_I2C.h>
#include <SparkFun_Bio_Sensor_Hub_Library.h>
#include <bluefruit.h>
#include <Adafruit_LittleFS.h>
#include <InternalFileSystem.h>
#include "LSM6DS3.h"

//#####################################################################################################################
//BLE and data transmission Settings 
#define FAST  
#ifdef FAST
  #define CONN_PARAM  6         // connection interval *1.25mS, min 6
  #define DATA_NUM    244       // max 244
#else
  #define CONN_PARAM  16        // connection interval *1.25mS, default 16
  #define DATA_NUM    20        // default 20
#endif 
union unionData {               // Union for data type conversion
  uint32_t  dataBuff32[DATA_NUM/4];
  uint8_t   dataBuff8[DATA_NUM];
};
union unionData ud;
uint16_t conn;					        // connect handle
bool connectedFlag = false;     // set by connect callback
// Custum Service and Characteristic
// 55c40000-8682-4dd1-be0c-40588193b485 for example
#define customService_UUID(val) (const uint8_t[]) { \
    0x85, 0xB4, 0x93, 0x81, 0x58, 0x40, 0x0C, 0xBE, \
    0xD1, 0x4D, (uint8_t)(val & 0xff), (uint8_t)(val >> 8), 0x00, 0x00, 0xC4, 0x55 }

BLEService        customService        (customService_UUID(0x0000));
BLECharacteristic customCharacteristic   (customService_UUID(0x0030));

BLEDfu  bledfu;
BLEDis  bledis;
BLEUart bleuart;
BLEBas  blebas;
//#####################################################################################################################
//Sensor Settings
MS5803 sensor(ADDRESS_HIGH);
double pressure_baseline = 0;

// Reset pin, MFIO pin for PPG
int resPin = 1;
int mfioPin = 2;
// Takes address, reset pin, and MFIO pin.
SparkFun_Bio_Sensor_Hub bioHub(resPin, mfioPin); 

LSM6DS3 myIMU(I2C_MODE, 0x6A);
float GyroX = 0.0;
float GyroY= 0.0;
float GyroZ= 0.0;
float AccX = 0.0;
float AccY = 0.0;
float AccZ = 0.0;
bioData body;  

// Function prototypes
void setupBLE();
void startAdv();
void getValues();
void PressureSetup();
void updateBaseline();
void sensorsSetup();

bool isCollectingData = false; // for keeping track of state (collecting | not collecting);
bool setupok = false;

void setup() {
  delay(2000);
  Serial.begin(115200);
  delay(2000);

  // Pin initialization
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  pinMode(LED_BLUE, OUTPUT); 

  digitalWrite(LED_RED, HIGH);
  digitalWrite(LED_GREEN, HIGH);
  digitalWrite(LED_BLUE, HIGH);
  // BLE 
  setupBLE();

  //Sensors
  sensorsSetup();

  
}

void loop(){
  if (connectedFlag) {  // If connected to the central
    if (setupok) {
      getValues();     
      //delay(5);
    } else {
      Serial.println("Setup not complete, skipping VALUES");
    }
  } else {
    Serial.println("Not connected, waiting for connection.");
  }

  if(!setupok && connectedFlag){
    sensorsSetup();
  }
}
//#############################################################################################################
// SENSOR FUNCTIONS
void getValues() {
  // pressure sensor reading
  float temperature_c = sensor.getTemperature(CELSIUS, ADC_512);
  float temperature_f = sensor.getTemperature(FAHRENHEIT, ADC_512);
  float pressure_abs = sensor.getPressure(ADC_4096); //- pressure_baseline; The baseline is acquired direclty in processing
  pressure_abs *= 100;
  pressure_abs = (int)pressure_abs;
  pressure_abs = (double)pressure_abs / 100;

  //PPG sensor Reading
  body = bioHub.readBpm();    

  //Accelerometer sensor readings
  GyroX=myIMU.readFloatGyroX();
  GyroY=myIMU.readFloatGyroY();
  GyroZ=myIMU.readFloatGyroZ();
  AccX = myIMU.readFloatAccelX();
  AccY = myIMU.readFloatAccelY();
  AccZ = myIMU.readFloatAccelZ();

  // formatting data into single data structure to send over charateristic in string/byte array
  //original
  char dataPacket[3];
  memset(dataPacket, 0, sizeof(dataPacket));
  //snprintf(dataPacket, sizeof(dataPacket), "V%.2f,%d,%d,%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f", pressure_abs, body.heartRate, body.oxygen, body.status, GyroX, GyroY, GyroZ, AccX, AccY, AccZ);
  //
  snprintf(dataPacket, sizeof(dataPacket), "V%.2f", pressure_abs);
  //try

  // Write the data to the BLE characteristic
        if (!customCharacteristic.notify((uint8_t*)dataPacket, sizeof(dataPacket) - 1)) {
      Serial.println("Failed to send notification");
    } else {
      Serial.println("Sensors Values Sent");
    }
  bleuart.print("Data Packet: "); // sends data packet to BlueFruit LE app
  bleuart.println(dataPacket);
}

void PressureSetup() {
  
  //digitalWrite(LED_RED, LOW);
  Wire.begin();
  sensor.reset();
  sensor.begin();
  Serial.println("Pressure setup okay.");
  // int numberOfSamples = 120;
  // unsigned long previousMillis = 0; // Stores the last time the update occurred
  // const long interval = 1000 / 12; // Interval at which to sample (1000ms/12) // 100hZ
  // unsigned long countdownStart = millis();
  // int countdownTime = 10;
  // char dataPacket[128]; // to send base line to ui
  // memset(dataPacket, 0, sizeof(dataPacket));
  
  // for (int index = 0; index < numberOfSamples;) { // Remove the increment from here
  //   unsigned long currentMillis = millis();
  //   // update countdown every  1 sec
  //   if (currentMillis - countdownStart >= 1000) { 
  //     countdownStart = currentMillis;
  //     countdownTime--;
  //     Serial.print(countdownTime);
  //     Serial.println("seconds until baseline aquired");
  //   }
  //   // sample pressure at 12hz intervals
  //   if (currentMillis - previousMillis >= interval) { 
  //     previousMillis = currentMillis; // Save the last time you sampled the pressure
  //     pressure_baseline += sensor.getPressure(ADC_4096);  // Sample the pressure
  //     bleuart.println(pressure_baseline);
  //     snprintf(dataPacket, sizeof(dataPacket), "B%.2f", pressure_baseline);
  //     // Write the data to the BLE characteristic
  //     if (!customCharacteristic.notify((uint8_t*)dataPacket, sizeof(dataPacket) - 1)) {
  //     Serial.println("Failed to send notification");
  //   } else {
  //     Serial.println("A baseline Value has been sent");
  //   }

  //     index++; 
  //   } 
  // }
  // pressure_baseline /= numberOfSamples;

  // bleuart.println("Baseline has been acquired (100Hz): ");
  // bleuart.println(pressure_baseline);
  // snprintf(dataPacket, sizeof(dataPacket), "B%.2f", pressure_baseline);
  // // Write the data to the BLE characteristic
  // if (!customCharacteristic.notify((uint8_t*)dataPacket, sizeof(dataPacket) - 1)) {
  //     Serial.println("Failed to send notification");
  //   } else {
  //     Serial.println("The Final Baseline Value has been sent.");
  //   }
  // Serial.print("Baseline has been acquired (100Hz): ");
  // Serial.println(pressure_baseline);
  // Serial.println("----------------------------------------------------------------------------------");
  // digitalWrite(LED_RED, HIGH);
  // Serial.println("Sensor Setup Started.");
}

void ppgSetup(){
  int result = bioHub.begin();
  if (result == 0) //Zero errors!
    Serial.println("Sensor started!");
  else
    Serial.println("Could not communicate with the sensor!");
 
  Serial.println("Configuring Sensor...."); 
  int error = bioHub.configBpm(MODE_TWO); // Configuring just the BPM settings. 
  if(error == 0){ // Zero errors
    Serial.println("Sensor configured.");
  }
  else {
    Serial.println("Error configuring sensor.");
    Serial.print("Error: "); 
    Serial.println(error); 
  }
}

void accSetup(){
  if (myIMU.begin() != 0) {
    Serial.println("Device error");
  }
  //Serial.println("Calibraiton of the ccelerometer done");
  Serial.println("Accelerometer okay!");
}

void sensorsSetup(){
  Serial.println("Sensor Setup Started.");
  Serial.println("Pressure Sensor...");
  PressureSetup();
  Serial.println("PPG...");
  ppgSetup();
  Serial.println("Accelerometer...");
  accSetup();
  Serial.println("Sensor Setup Ended");
  setupok = true;
}
//#############################################################################################################
// BLE FUNCTIONS
void connect_callback(uint16_t conn_handle)
{
  connectedFlag = false;
  
  Serial.print("【connect_callback】 conn_Handle : ");
  Serial.println(conn_handle, HEX);
  conn = conn_handle;

  // Get the reference to current connection
  BLEConnection* connection = Bluefruit.Connection(conn_handle);

  // request to chamge parameters
#ifdef FAST
  connection->requestPHY();                     // PHY 2MHz (2Mbit/sec moduration) 1 --> 2
  delay(1000);                                  // delay a bit for request to complete
  //Serial.println(connection->getPHY());
  connection->requestDataLengthUpdate();        // data length  27 --> 251
#endif  
  connection->requestMtuExchange(DATA_NUM + 3); // MTU 23 --> 247
  connection->requestConnectionParameter(CONN_PARAM);   // connection interval (*1.25mS)   
  delay(1000);                                  // delay a bit for request to complete  
  Serial.println();
  Serial.print("PHY ----------> "); Serial.println(connection->getPHY());
  Serial.print("Data length --> "); Serial.println(connection->getDataLength());
  Serial.print("MTU ----------> "); Serial.println(connection->getMtu());
  Serial.print("Interval -----> "); Serial.println(connection->getConnectionInterval());      

  char central_name[32] = { 0 };
  connection->getPeerName(central_name, sizeof(central_name));
  Serial.print("【connect_callback】 Connected to ");
  Serial.println(central_name);

  connectedFlag = true;
}

void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
  (void) conn_handle;
  (void) reason;

  Serial.print("【disconnect_callback】 reason = 0x");
  Serial.println(reason, HEX);
  connectedFlag = false;
  setupok = false;
}

void setupBLE()
{

  Serial.print("Start BLUETOOTH Initialization");
  // Initialization of Bruefruit class
  Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
  Bluefruit.configUuid128Count(15);

  Bluefruit.begin();
  #ifdef FAST  
    Bluefruit.setTxPower(0);    // Central is close, 0dBm
  #else
    Bluefruit.setTxPower(4);    // default +4dBm
  #endif  
  Bluefruit.setConnLedInterval(50);
  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
  // Bluefruit.Periph.setConnInterval(6, 12);    // 7.5 - 15mS (effects unknown)

  // Custom Service Settings
  customService.begin();
  customCharacteristic.setProperties(CHR_PROPS_NOTIFY);
  customCharacteristic.setFixedLen(DATA_NUM);
  customCharacteristic.begin();

  // Advertisement Settings
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addService(customService);
  Bluefruit.ScanResponse.addName();
  
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setIntervalMS(20, 153);     // fast mode 20mS, slow mode 153mS
  Bluefruit.Advertising.setFastTimeout(30);         // fast mode 30 sec
  Bluefruit.Advertising.start(0);                   // 0 = Don't stop advertising after n seconds

  Serial.println("End of BLUETOOTH initialization");

  // Connecting to Central
  Serial.println("Connecting to Central ................");
  while(!Bluefruit.connected()) {
    Serial.print("."); delay(100);
  }
  Serial.println();
  Serial.println("Connected to Central");
  delay(5000);
}
//#############################################################################################################

CENTRAL

#include <Wire.h>
#include <SPI.h>
#include <bluefruit.h>

//***********************************************************************************************************
#define FAST                    // compiler option : parameters setting for fast transmission
                                // if default setting, comment out
#define MAX_PACKET  200         // maximum number of data packets allowed by FIFO
#define FIFO_SIZE DATA_NUM * MAX_PACKET  // FIFO size, push and pop on a packet-by-packet basis
#ifdef FAST
  #define DATA_NUM  244         // number of bytes in data packet
#else
  #define DATA_NUM  20          // number of bytes in data packet
#endif
//***********************************************************************************************************

union unionData {               // Union for data type conversion
  uint32_t  dataBuff32[DATA_NUM/4];
  uint8_t   dataBuff8[DATA_NUM];
};
union unionData ud; 

uint16_t conn;                  // connect handle
bool notifyFlag = false;        // set by notify callback
bool connectedFlag = false;     // set by connect callback

// Custom Service and Characteristic
// 55c40000-8682-4dd1-be0c-40588193b485 for example
#define customService_UUID(val) (const uint8_t[]) { \
    0x85, 0xB4, 0x93, 0x81, 0x58, 0x40, 0x0C, 0xBE, \
    0xD1, 0x4D, (uint8_t)(val & 0xff), (uint8_t)(val >> 8), 0x00, 0x00, 0xC4, 0x55 }

BLEClientService        customService       (customService_UUID(0x0000));
BLEClientCharacteristic customCharacteristic  (customService_UUID(0x0030));

unsigned long startTime = 0;
unsigned long packetCount = 0;
unsigned long totalDataReceived = 0; // in bytes
const unsigned long interval = 2000; // 10 seconds

void setup() {
    delay(2000);
    Serial.begin(115200);
    delay(2000);

    // LED initialization
    pinMode(LED_RED, OUTPUT);
    pinMode(LED_GREEN, OUTPUT);
    pinMode(LED_BLUE, OUTPUT);

    Serial.println("------ Central Board ------");
    Serial.println("Start Initialization");
    
    // Initialization of Bluefruit
    Bluefruit.configCentralBandwidth(BANDWIDTH_MAX);  
    Bluefruit.begin(0, 1);
    Bluefruit.setName("XIAO BLE Central");

    // Custom Service Settings
    customService.begin();
    customCharacteristic.setNotifyCallback(data_notify_callback);
    customCharacteristic.begin();

    // Blue LED blinking interval setting
    Bluefruit.setConnLedInterval(100);

    // Callbacks
    Bluefruit.Central.setDisconnectCallback(disconnect_callback);
    Bluefruit.Central.setConnectCallback(connect_callback);
    
    // Scanner settings
    Bluefruit.Scanner.setRxCallback(scan_callback);
    Bluefruit.Scanner.restartOnDisconnect(true);
    Bluefruit.Scanner.setIntervalMS(100, 50);    
    Bluefruit.Scanner.filterUuid(customService.uuid);
    Bluefruit.Scanner.useActiveScan(false);
    Bluefruit.Scanner.start(0);                  

    Serial.println("End of initialization");
    startTime = millis();
}

void loop() {
    unsigned long currentTime = millis();

    if (currentTime - startTime >= interval) {
        // Calculate the sampling rate (packets per second)
        float samplingRate = (float)packetCount / (interval / 1000.0);

        // Calculate the bit rate (bits per second)
        float bitRate = (totalDataReceived * 8) / (interval / 1000.0);

        Serial.print("Sampling Rate: ");
        Serial.print(samplingRate);
        Serial.println(" packets/second");

        Serial.print("Bit Rate: ");
        Serial.print(bitRate);
        Serial.println(" bits/second");

        // Reset counters and timer
        packetCount = 0;
        totalDataReceived = 0;
        startTime = millis();
    }
}

//*************************************************************************************************************************************************************************************
// THE CONNECTION IS ESTABLISHED 
//*************************************************************************************************************************************************************************************
// FUNCTIONS FOR CONNECTION with the peripheral 
void scan_callback(ble_gap_evt_adv_report_t* report)
{
  Bluefruit.Central.connect(report);
}

void connect_callback(uint16_t conn_handle){
    conn = conn_handle;
    connectedFlag = false;
    if (!customService.discover(conn_handle))
    {
        Serial.println("【connect_callback】 Service not found, disconnecting!");
        Bluefruit.disconnect(conn_handle);
        return;
    }
    if (!customCharacteristic.discover())
    {
        Serial.println("【connect_callback】 Characteristic not found, disconnecting!");
        Bluefruit.disconnect(conn_handle);
        return;
    }

    // After discovering the characteristic, enable notifications
    if (!customCharacteristic.enableNotify()) {
        Serial.println("Failed to enable notifications");
    } else {
        Serial.println("Notifications enabled");
    }

    connectedFlag = true;
    Serial.println("【connect_callback】connected");
}

void disconnect_callback(uint16_t conn_handle, uint8_t reason){
  (void) conn_handle;
  (void) reason;

  Serial.print("【disconnect_callback】 Disconnected, reason = 0x");
  Serial.println(reason, HEX);
  connectedFlag = false;
}
//*************************************************************************************************************************************************************************************
//*************************************************************************************************************************************************************************************

// Callback when new data is available on the characteristic
void data_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len) {
    // Count the packets and accumulate the total data received
    packetCount++;
    totalDataReceived += len;
    
    // Process the received data
    sendDataThroughSerial((char*)data, len);
}

// Function to send data through the serial port
void sendDataThroughSerial(char* data, uint16_t len) {
    //Serial.print(data);  // This sends the string up to the null terminator
    //Serial.println();
}

You can lower the CONN_PARAM value to decrease the connection interval further. It will allow for more frequent data transmission.

Hi there,

As @liaifat85 :star: states , lower it. for me and others lurking…here is why and what it does.

  • Connection interval

The time between data transfer events between the central and peripheral devices. The connection interval is set by the application, and the devices use it to decide how often to communicate. Lower connection intervals increase speed and decrease latency, but they also increase power consumption.

HTH
GL :slight_smile: PJ :v:

I setup two,
Gets pretty good just chilling…

Not connected, waiting for connection.
Not connected, waiting for connection.
Not connected, waiting for connection.[BLE   ] BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP : Conn Handle = 0
【connect_callback】 Connected to XIAO BLE Central

Sensor Setup Started.
Pressure Sensor...
Pressure setup okay.
PPG...
Could not communicate with the sensor!
Configuring Sensor....
Error configuring sensor.
Error: 255
Accelerometer...
Accelerometer okay!
Sensor Setup Ended
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTC_EVT_HVX : Conn Handle = 0
[BLE   ] BLE_GATTC_EVT_HVX : Conn Handle = 0
Sampling Rate: 22.50 packets/second
Bit Rate: 43920.00 bits/second
[BLE   ] BLE_GATTC_EVT_HVX : Conn Handle = 0
[BLE   ] BLE_GATTC_EVT_HVX : Conn Handle = 0
[BLE   ] BLE_GATTC_EVT_HVX : Conn Handle = 0
[BLE   ] BLE_GATTC_EVT_HVX : Conn Handle = 0
[BLE   ] BLE_GATTC_EVT_HVX : Conn Handle = 0

Hi! Thanks for the response! @PJ_Glasso @liaifat85

I try to put 2

#define FAST
#ifdef FAST
#define CONN_PARAM 2 // connection interval *1.25mS, min 6
#define DATA_NUM 244 // max 244
#else
#define CONN_PARAM 16 // connection interval *1.25mS, default 16
#define DATA_NUM 20 // default 20
#endif

but then the output of the peripheral is like this:
PHY ----------> 2
Data length → 251
MTU ----------> 247
Interval -----> 16

so, now the interval is no more 6, neither 2 but it is 16. And the output of the central is still:

Sampling Rate: 19.00 packets/second
Bit Rate: 37088.00 bits/second
Sampling Rate: 19.00 packets/second
Bit Rate: 37088.00 bits/second

any suggestion? do I need to change anything else?

Ideally I would like to obtain a sampling rate close to 50Hz.

Hi there,

So I loaded up both codes with two devices , appears to work.
When I use Nrf-Connect for DT and the Dongle, I’m able to connect to it and turn on notify
and switch the conn params to 2M_PHY and it echo’s that to serial port… MAN the DATA comes FLYING in… Have a LOOK then at the counter and serial output, it almost chokes the Windows side with dongle but it does keep up.

I changed the params for connection and looked at the serial output on the xiao.

Testing R G B LED - RED, GREEN, BLUE... 
Testing R G B LED - RED, End of BLUETOOTH initialization
Connecting to Central ................
.................................................................[BLE   ] BLE_GAP_EVT_CONNECTED : Conn Handle = 0
[GAP   ] MAC = C8:65:29:7E:60:0C, Type = 1, Resolved = 0
[GAP   ] Conn Interval = 7.50 ms, Latency = 0, Supervisor Timeout = 4000 ms
【connect_callback】 conn_Handle : 0

Connected to Central
[BLE   ] BLE_GATTC_EVT_EXCHANGE_MTU_RSP : Conn Handle = 0
[GAP   ] ATT MTU is changed to 247

PHY ----------> 1
Data length --> 27
MTU ----------> 247
Interval -----> 6
[BLE   ] BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP : Conn Handle = 0
【connect_callback】 Connected to nRF5x
[BLE   ] BLE_GATTS_EVT_SYS_ATTR_MISSING : Conn Handle = 0
Sensor Setup Started.
Pressure Sensor...
Pressure setup okay.
PPG...
Could not communicate with the sensor!
Configuring Sensor....
Error configuring sensor.
Error: 255
Accelerometer...
Accelerometer okay!
Sensor Setup Ended
BSP Library : 1.1.8
Bootloader  : s140 7.3.0
Serial No   : B3F98A0809BF2DCA

--------- SoftDevice Config ---------
Max UUID128     : 15
ATTR Table Size : 4096
Service Changed : 1
Peripheral Connect Setting
  - Max MTU         : 247
  - Event Length    : 100
  - HVN Queue Size  : 3
  - WrCmd Queue Size: 1

--------- BLE Settings ---------
Name            : XIAO nRF52840 Sense
Max Connections : Peripheral = 1, Central = 0 
Address         : C0:E3:88:19:09:4E (Static)
TX Power        : 0 dBm
Conn Intervals  : min = 20.00 ms, max = 30.00 ms
Conn Timeout    : 2000 ms
Peripheral Paired Devices: 

Failed to send notification
Failed to send notification
Failed to send notification
[BLE   ] BLE_GATTS_EVT_WRITE : Conn Handle = 0
[GATTS ] attr's cccd = 0x0001
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent
[BLE   ] BLE_GATTS_EVT_HVN_TX_COMPLETE : Conn Handle = 0
[CHR   ] Notify 2 bytes
Sensors Values Sent

I would adjust the params on the Desktop setup change them on the fly, You should see the difference. The Supervisory time out also once the 2PHY is connected should be lowered and you may need to also adjust on the Central as well Once you shift to FAST mode.

HTH
GL :slight_smile: PJ :v:

Great example of why having the $9 dongle to test with and NRF_connect for Desktops.
:+1:

Hi @PJ_Glasso !
thanks again. I am trying to learn .

so are you suggesting downloading nRF connect for desktop BLE? is that right?


so I have:

  • seed xiao (central) (COM1)
  • seed xiao(peripheral) (COM2)
  • NRF dongle
  • NRFconnect for desktop open

and then what I do?


Ideally in my project I would like to have just the 2 seed xiao boards not also the dongle attached to my laptop.
With the code I have right now I can not achieve higher sampling frequency and if I change the CONN_PARAM form 6 to 2 the arduino sets the CONN_PARAM automatically at 16. Do you know why?

if I change the CONN_PARAM form 6 to 2 the arduino sets the CONN_PARAM automatically at 16.

  // Write the data to the BLE characteristic
        if (!customCharacteristic.notify((uint8_t*)dataPacket, sizeof(dataPacket) - 1)) {

I think the set value is too small and has been corrected to the default value of 16, but unfortunately I can’t remember where I saw the minimum value of 6.

It looks like you are preparing data on this line, why don’t you find out how many seconds the data is ready?
Maybe a simpler sketch (with fixed packet data, without moving the sensor) would solve the problem faster and check the throughput.

1 Like

you are right.

This is the code without the sensors collecting data, I just send a constant string of values:

so this is the peripheral code

#include <Wire.h>
#include "SPI.h"
#include <SparkFun_MS5803_I2C.h>
#include <SparkFun_Bio_Sensor_Hub_Library.h>
#include <bluefruit.h>
#include <Adafruit_LittleFS.h>
#include <InternalFileSystem.h>
#include "LSM6DS3.h"

//#####################################################################################################################
//BLE and data transmission Settings 
#define FAST  
#ifdef FAST
  #define CONN_PARAM  6         // connection interval *1.25mS, min 6
  #define DATA_NUM    244       // max 244
#else
  #define CONN_PARAM  16        // connection interval *1.25mS, default 16
  #define DATA_NUM    20        // default 20
#endif 
union unionData {               // Union for data type conversion
  uint32_t  dataBuff32[DATA_NUM/4];
  uint8_t   dataBuff8[DATA_NUM];
};
union unionData ud;
uint16_t conn;					        // connect handle
bool connectedFlag = false;     // set by connect callback
// Custum Service and Characteristic
// 55c40000-8682-4dd1-be0c-40588193b485 for example
#define customService_UUID(val) (const uint8_t[]) { \
    0x85, 0xB4, 0x93, 0x81, 0x58, 0x40, 0x0C, 0xBE, \
    0xD1, 0x4D, (uint8_t)(val & 0xff), (uint8_t)(val >> 8), 0x00, 0x00, 0xC4, 0x55 }

BLEService        customService        (customService_UUID(0x0000));
BLECharacteristic customCharacteristic   (customService_UUID(0x0030));

BLEDfu  bledfu;
BLEDis  bledis;
BLEUart bleuart;
BLEBas  blebas;
//#####################################################################################################################
float GyroX = 3330.0;
float GyroY = 3330.0;
float GyroZ = 3330.0;
float AccX  = 3330.0;
float AccY  = 3330.0;
float AccZ  = 3330.0;

bool isCollectingData = false; // for keeping track of state (collecting | not collecting);

void setup() {
  delay(2000);
  Serial.begin(115200);
  delay(2000);

  // Pin initialization
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  pinMode(LED_BLUE, OUTPUT); 

  digitalWrite(LED_RED, HIGH);
  digitalWrite(LED_GREEN, HIGH);
  digitalWrite(LED_BLUE, HIGH);
  
  // BLE 
  setupBLE();

}

void loop(){
  if (connectedFlag) {  // If connected to the central
    sendValues();  
  } else {
    Serial.println("Not connected, waiting for connection.");
  }
}
//#############################################################################################################
// SENSOR FUNCTIONS
void sendValues() {
  float pressure_abs = 400.00;
  int heartRate = 1000;
  int oxygen = 1000;
  int status = 1;

  char dataPacket[80];
  memset(dataPacket, 0, sizeof(dataPacket));
  snprintf(dataPacket, sizeof(dataPacket), "V%.2f,%d,%d,%d,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f", pressure_abs, heartRate, oxygen, status, GyroX, GyroY, GyroZ, AccX, AccY, AccZ);

  // Write the data to the BLE characteristic
  if (!customCharacteristic.notify((uint8_t*)dataPacket, sizeof(dataPacket) - 1)) {
      Serial.println("Failed to send notification");
  } else {
      Serial.println("Sensors Values Sent");
  }
  bleuart.print("Data Packet: "); // sends data packet to BlueFruit LE app
  bleuart.println(dataPacket);
}

//#############################################################################################################
// BLE FUNCTIONS
void connect_callback(uint16_t conn_handle)
{
  connectedFlag = false;
  
  Serial.print("【connect_callback】 conn_Handle : ");
  Serial.println(conn_handle, HEX);
  conn = conn_handle;

  // Get the reference to current connection
  BLEConnection* connection = Bluefruit.Connection(conn_handle);

  // request to chamge parameters
#ifdef FAST
  connection->requestPHY();                              // PHY 2MHz (2Mbit/sec moduration) 1 --> 2
  delay(1000);                                           // delay a bit for request to complete
  //Serial.println(connection->getPHY());
  connection->requestDataLengthUpdate();                 // data length  27 --> 251
#endif  
  connection->requestMtuExchange(DATA_NUM + 3);          // MTU 23 --> 247
  connection->requestConnectionParameter(CONN_PARAM);    // connection interval (*1.25mS)   
  delay(1000);                                           // delay a bit for request to complete  
  Serial.println();
  Serial.print("PHY ----------> "); Serial.println(connection->getPHY());
  Serial.print("Data length --> "); Serial.println(connection->getDataLength());
  Serial.print("MTU ----------> "); Serial.println(connection->getMtu());
  Serial.print("Interval -----> "); Serial.println(connection->getConnectionInterval());      

  char central_name[32] = { 0 };
  connection->getPeerName(central_name, sizeof(central_name));
  Serial.print("【connect_callback】 Connected to ");
  Serial.println(central_name);

  connectedFlag = true;
}

void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
  (void) conn_handle;
  (void) reason;

  Serial.print("【disconnect_callback】 reason = 0x");
  Serial.println(reason, HEX);
  connectedFlag = false;
  
}

void setupBLE()
{

  Serial.print("Start BLUETOOTH Initialization");
  // Initialization of Bruefruit class
  Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
  Bluefruit.configUuid128Count(15);

  Bluefruit.begin();
  #ifdef FAST  
    Bluefruit.setTxPower(0);    // Central is close, 0dBm
  #else
    Bluefruit.setTxPower(4);    // default +4dBm
  #endif  
  Bluefruit.setConnLedInterval(50);
  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

  // Custom Service Settings
  customService.begin();
  customCharacteristic.setProperties(CHR_PROPS_NOTIFY);
  customCharacteristic.setFixedLen(DATA_NUM);
  customCharacteristic.begin();

  // Advertisement Settings
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addService(customService);
  Bluefruit.ScanResponse.addName();
  
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setIntervalMS(20, 153);     // fast mode 20mS, slow mode 153mS
  Bluefruit.Advertising.setFastTimeout(30);         // fast mode 30 sec
  Bluefruit.Advertising.start(0);                   // 0 = Don't stop advertising after n seconds

  Serial.println("End of BLUETOOTH initialization");

  // Connecting to Central
  Serial.println("Connecting to Central ................");
  while(!Bluefruit.connected()) {
    Serial.print("."); delay(100);
  }
  Serial.println();
  Serial.println("Connected to Central");
  delay(5000);
}
//#############################################################################################################



while here is the CENTRAL code:

#include <Wire.h>
#include <SPI.h>
#include <bluefruit.h>

//***********************************************************************************************************
#define FAST                    // compiler option : parameters setting for fast transmission
                                // if default setting, comment out
#define MAX_PACKET  200         // maximum number of data packets allowed by FIFO
#define FIFO_SIZE DATA_NUM * MAX_PACKET  // FIFO size, push and pop on a packet-by-packet basis
#ifdef FAST
  #define DATA_NUM  244         // number of bytes in data packet
#else
  #define DATA_NUM  20          // number of bytes in data packet
#endif
//***********************************************************************************************************

union unionData {               // Union for data type conversion
  uint32_t  dataBuff32[DATA_NUM/4];
  uint8_t   dataBuff8[DATA_NUM];
};
union unionData ud; 

uint16_t conn;                  // connect handle
bool notifyFlag = false;        // set by notify callback
bool connectedFlag = false;     // set by connect callback

// Custom Service and Characteristic
// 55c40000-8682-4dd1-be0c-40588193b485 for example
#define customService_UUID(val) (const uint8_t[]) { \
    0x85, 0xB4, 0x93, 0x81, 0x58, 0x40, 0x0C, 0xBE, \
    0xD1, 0x4D, (uint8_t)(val & 0xff), (uint8_t)(val >> 8), 0x00, 0x00, 0xC4, 0x55 }

BLEClientService        customService       (customService_UUID(0x0000));
BLEClientCharacteristic customCharacteristic  (customService_UUID(0x0030));

unsigned long startTime = 0;
unsigned long packetCount = 0;
unsigned long totalDataReceived = 0; // in bytes
const unsigned long interval = 2000; // 10 seconds

void setup() {
    delay(2000);
    Serial.begin(115200);
    delay(2000);

    // LED initialization
    pinMode(LED_RED, OUTPUT);
    pinMode(LED_GREEN, OUTPUT);
    pinMode(LED_BLUE, OUTPUT);

    Serial.println("------ Central Board ------");
    Serial.println("Start Initialization");
    
    // Initialization of Bluefruit
    Bluefruit.configCentralBandwidth(BANDWIDTH_MAX);  
    Bluefruit.begin(0, 1);
    Bluefruit.setName("XIAO BLE Central");

    // Custom Service Settings
    customService.begin();
    customCharacteristic.setNotifyCallback(data_notify_callback);
    customCharacteristic.begin();
    

    // Blue LED blinking interval setting
    Bluefruit.setConnLedInterval(100);

    // Callbacks
    Bluefruit.Central.setDisconnectCallback(disconnect_callback);
    Bluefruit.Central.setConnectCallback(connect_callback);
    
    // Scanner settings
    Bluefruit.Scanner.setRxCallback(scan_callback);
    Bluefruit.Scanner.restartOnDisconnect(true);
    Bluefruit.Scanner.setIntervalMS(100, 50);    
    Bluefruit.Scanner.filterUuid(customService.uuid);
    Bluefruit.Scanner.useActiveScan(false);
    Bluefruit.Scanner.start(0);                  

    Serial.println("End of initialization");
    startTime = millis();
}

void loop() {
    unsigned long currentTime = millis();

    if (currentTime - startTime >= interval) {
        // Calculate the sampling rate (packets per second)
        float samplingRate = (float)packetCount / (interval / 1000.0);

        // Calculate the bit rate (bits per second)
        float bitRate = (totalDataReceived * 8) / (interval / 1000.0);

        Serial.print("Sampling Rate: ");
        Serial.print(samplingRate);
        Serial.println(" packets/second");

        Serial.print("Bit Rate: ");
        Serial.print(bitRate);
        Serial.println(" bits/second");

        // Reset counters and timer
        packetCount = 0;
        totalDataReceived = 0;
        startTime = millis();
    }
}

//*************************************************************************************************************************************************************************************
// THE CONNECTION IS ESTABLISHED 
//*************************************************************************************************************************************************************************************
// FUNCTIONS FOR CONNECTION with the peripheral 
void scan_callback(ble_gap_evt_adv_report_t* report)
{
  Bluefruit.Central.connect(report);
}

void connect_callback(uint16_t conn_handle){
    conn = conn_handle;
    connectedFlag = false;
    if (!customService.discover(conn_handle))
    {
        Serial.println("【connect_callback】 Service not found, disconnecting!");
        Bluefruit.disconnect(conn_handle);
        return;
    }
    if (!customCharacteristic.discover())
    {
        Serial.println("【connect_callback】 Characteristic not found, disconnecting!");
        Bluefruit.disconnect(conn_handle);
        return;
    }

    // After discovering the characteristic, enable notifications
    if (!customCharacteristic.enableNotify()) {
        Serial.println("Failed to enable notifications");
    } else {
        Serial.println("Notifications enabled");
    }

    connectedFlag = true;
    Serial.println("【connect_callback】connected");
}

void disconnect_callback(uint16_t conn_handle, uint8_t reason){
  (void) conn_handle;
  (void) reason;

  Serial.print("【disconnect_callback】 Disconnected, reason = 0x");
  Serial.println(reason, HEX);
  connectedFlag = false;
}
//*************************************************************************************************************************************************************************************
//*************************************************************************************************************************************************************************************

// Callback when new data is available on the characteristic
void data_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len) {
    // Count the packets and accumulate the total data received
    packetCount++;
    totalDataReceived += len;
    
    // Process the received data
    sendDataThroughSerial((char*)data, len);
}

// Function to send data through the serial port
void sendDataThroughSerial(char* data, uint16_t len) {
    Serial.print(data);  // This sends the string up to the null terminator
    Serial.println();
}

the sampling rate is:

00:24:18.378 → Sampling Rate: 533.00 packets/second

00:24:18.378 → Bit Rate: 1040416.00 bits/second

so it is a problem related to data collection not related to ble comunications

1 Like

I’m glad you found the cause.
Print() also eats up a lot of time and should be minimized.

1 Like

Hi There,

Smash Hit, @msfujino… When I was testing I thought Too many serial port info…He needs the LED status Light You use…:+1: In Nrf_connect it has it set to 6.5 default , fyi.

GL :slight_smile: PJ :v:

yes the bottleneck was this guy:
"float pressure_abs = sensor.getPressure(ADC_4096); " the fact that the resolution is so high provokes loosing time

thanks for the support @PJ_Glasso
:slight_smile:

Hi there,

No worries, Lots of smart folks here to help, even when the Spammers get a free test day pass…here. LOL :face_with_peeking_eye:
But your on it now so keep going and great Job! :+1:

GL :slight_smile: PJ :v: