Arduino Nano RP2040 Connect hangs/freezes when using IMU plus BLE

I am working an device that reads acceleration data from the IMU does some light processing of the data and sends status via BLE messages. I am struggling with using the IMU library and BLE library at the same time. I have put together a short demonstration program. I know the BLE connection logic needs a little work. I have been using the nRF Connect tool to test connectivity.

When only the TestBLE define is included this program can be connected to and changes in the characteristic can be observed.

When only the TestIMU define is included the results of the simple calculations can be seen on the CDC port.

When both the TestIMU and TestBLE defines are included the program waits until a BLE connection is made it then prints 10 to 12 IMU results and might send 1 BLE update but very shortly all output stops and the BLE is not connectable.

#define TestBLE
#define TestIMU

#include <Arduino_LSM6DSOX.h> // This lib has been modified, Sample rate and Fifo control
#include <ArduinoBLE.h>

const char* deviceServiceUuid = "1E598386-2B22-11E9-B210-D663BD873D93";
const char* deviceServiceCharacteristicUuidIndex = "1E598386-2B22-11E9-B210-D663BD873D91"; // Index

BLEService testService(deviceServiceUuid);
BLEUnsignedShortCharacteristic testCharIndex(deviceServiceCharacteristicUuidIndex, BLERead | BLENotify);

void setup() {
  Serial.begin(230400);
  while (!Serial);
  Serial.println("Started");

#ifdef TestIMU
  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }
  
  Wire.setClock(400000); // Increase I2C clock from 100kHz to 400kHz
#endif

#ifdef TestBLE
    if(!BLE.begin()) {
    Serial.println("starting BLE failes!");
    while(1) {}
  }
  BLE.setLocalName("test");
  BLE.setAdvertisedService(testService);
  testService.addCharacteristic(testCharIndex);
  BLE.addService(testService);

  BLE.advertise();
#endif
}

void loop() {
  int x, y, z;
  unsigned long lastCalc = 0;
  long sampCnt = 0;
  long sampCntMissed = 0;
  unsigned cnt = 0;
  long long sumY = 0;
#ifdef TestBLE
  BLEDevice central;


// Wait for central to connect
  central = BLE.central();
  while(central.connected() == false) {
    sampCnt++;
    if((sampCnt%100000) == 0) Serial.print(".");
    central = BLE.central();
  }
#endif

  sampCnt = 0;
  lastCalc = millis();

  while(1) {
#ifdef TestIMU
    if (IMU.accelerationAvailable()) {
      IMU.readAccelerationI(x, y, z);
      sampCnt ++;
      sumY += y;
    }
    else {
      sampCntMissed ++;
    }
#endif
    if((millis() - lastCalc) > 1000) {
      Serial.print(sampCnt); Serial.print(" : ");  
      Serial.print(sampCntMissed); Serial.print(" : ");  
      Serial.print(sumY); Serial.print(" : ");
      Serial.print(sumY/sampCnt); Serial.println("");
#ifdef TestBLE
      if (central.connected()) 
      {
        testCharIndex.writeValue(cnt);
        cnt ++;
      }
#endif
      sampCnt = 0;
      sampCntMissed = 0;
      sumY = 0;
      lastCalc += 1000;
    }
  }
}

Welcome to the forum.

Have a look into the example I posted in the following thread reply #2.

https://forum.arduino.cc/t/bluetooth-communications/902610/2

It contains a lot of useful tips on how to write a sketch for a BLE peripheral.

I recommend focusing on the following parts for your case.

  • separate sensor and BLE code
  • handle the connection via handlers
  • update the characteristics separately, do not worry about the connection in that part of the code
  • avoid any while loops, make sure loop() runs as often as possible
  • do not write to the characteristic too often, a BLE characteristic is not a pipe, if you write a new value before the client/central has read it the old value is lost

Have a look around the forum for the Nano 33 BLE and Sense. I have helped a few people with similar sketches. Ideally you want to use the data from the IMU and calculate some useful application-level value. That should be the basis for your characteristic. BLE has been designed for low bandwidth. So, sending raw data somewhere should be avoided.

If you must send raw data, make sure you use the smallest datatype possible for your application and combine the values for multiple samples. This will allow you to reduce the overhead and increase the bandwidth. It will require you to unpack the data on your central.

Here is a link to a post that might be useful as well reply #2.

https://forum.arduino.cc/t/improve-requesting-accelerometer-data-rate-through-ble/697921/2

The code presented works on the Nano 33 BLE Sense board (with a change of sensor libraries). It does not work on the Nano RP2040 Connect board.

I refactored my code to match the code pattern in your example. I tested the results at several points along the way. None of the changes seemed to change to results.

The results were very similar. The data starts printing as soon as the USB CDC connects. Some times it runs for awhile sometimes it stops quickly. During the times it continues to run it stop quickly after a BLE connection.

I then retested the new code on the Nano 33 Sensor and it works as expected.

I then tested a few things to see if I could reproduce other results from the original code. To get the sensor output to run continuously both the BLE setup and task had to be commented out. To get the BLE to function as expected the IMU if tree from the sensor task can be commented out.

To repeat, the refactored code as presented below hangs when run of the Nano RP2040 connect, within a few seconds of BLE connection activity.

#include <Arduino_LSM6DSOX.h>
#include <ArduinoBLE.h>

const char* deviceServiceUuid = "1E598386-2B22-11E9-B210-D663BD873D93";
const char* deviceServiceCharacteristicUuidIndex = "1E598386-2B22-11E9-B210-D663BD873D91"; // Index

BLEService testService(deviceServiceUuid);
BLEUnsignedShortCharacteristic testCharIndex(deviceServiceCharacteristicUuidIndex, BLERead | BLENotify);

typedef struct {
  long sampCnt;
  long sampCntMissed;
  long sumY;
  long avgY;
  bool updated = 0;  
} sensor_data_t;

sensor_data_t sensorData;

void setup() {
  Serial.begin(230400);
  while (!Serial);
  Serial.println("Started");

  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }

  Wire.setClock(400000); // Increase I2C clock from 100kHz to 400kHz

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

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

bool sensorTask() {
  int x, y, z;
  static unsigned long lastCalc = 0;
  static long sampCnt = 0;
  static long sampCntMissed = 0;
  static long long sumY = 0;

    if (IMU.accelerationAvailable()) {
      IMU.readAccelerationI(x, y, z);
      sampCnt ++;
      sumY += y;
    }
    else {
      sampCntMissed ++;
    }

    if((millis() - lastCalc) > 1000) {
      sensorData.sampCnt = sampCnt;
      sensorData.sampCntMissed = sampCntMissed;
      sensorData.sumY = sumY;
      sensorData.avgY = sumY / sampCnt;
      sensorData.updated = true;

      sampCnt = 0;
      sampCntMissed = 0;
      sumY = 0;
      lastCalc += 1000;
    }

  return sensorData.updated;
}

void printTask()
{
  Serial.print(sensorData.sampCnt); Serial.print(" : ");  
  Serial.print(sensorData.sampCntMissed); Serial.print(" : ");  
  Serial.print(sensorData.sumY); Serial.print(" : ");
  Serial.print(sensorData.avgY); Serial.println("");
}

bool setupBleMode() {
  if ( !BLE.begin() ) {
    return false;
  }
  BLE.setDeviceName("BLE Test");
  BLE.setLocalName("test");
  BLE.setAdvertisedService(testService);
  testService.addCharacteristic(testCharIndex);

  BLE.addService(testService);

  testCharIndex.writeValue(0);

  BLE.setEventHandler( BLEConnected, blePeripheralConnectHandler );
  BLE.setEventHandler( BLEDisconnected, blePeripheralDisconnectHandler );

  BLE.advertise();

  return true;
}

void bleTask() {
  const uint32_t BLE_UPDATE_INTERVAL = 10;
  static uint32_t previousMillis = 0;
  static  unsigned cnt = 0;
  uint32_t currentMillis = millis();
  BLEDevice central;

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

  if( sensorData.updated ) {  
    testCharIndex.writeValue(cnt);
    cnt ++;
    sensorData.updated = false;
  }
}

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


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

I had to change the readAccelerationI() to the original version because I do not have your library and your sketch has been running for more than 20 minutes without any issues.

I changed the Arduino_LSM6DSOX lib back to the original and modified the code to use the floating point version of readAcceleration.

I am using Arduino 1.8.19 on a Windows 10 machine. I have ArduinoBLE version 1.2.1 and Arduino_LSM6DSOX version 1.0.0.

On the original RP2040:
I have WiFiNINA firmware version 1.4.8.

I tried this on a second RP2040 that has WiFiNINA firmware version 1.4.7. This one warns of possible issue during the firmware version check.

I tried this with a different Android Device running the most recent version of nRF connect.

All the combination of Nano RP2040 and Droids running nRF connect act the same. The app runs until a BLE connection is attempted and then the Nano RP2040 stops printing and responding BLE.

I am wondering what is different between what you have test and what I am testing.

Does anyone have any idea what might be going on with this situation?