TinyML and BLE for activity tracking - memory issues?

Hi everyone,

I’m new to Arduino and have been working on a project using the Seeed Xiao BLE Sense, where I’m currently stuck. My goal is to use it as a wearable activity tracker that classifies activities using tinyML and then sends this data via BLE to an app.

I’ve managed to get the BLE connection working with the app and am able to read IMU data in real time, but when I try to expand on this and to activate TensorFlow (tflInterpreter->AllocateTensors();), then the board bugs.
What I mean by this is that the beginning of the code runs but when I try to allocate tensors, then it crashes. The blue LED light turns on as planned but the board does not appear in a bluetooth search anymore, and in the Arduino IDE the board isn’t even listed anymore in ports.

I think it has something to do with memory use but am not sure. So far I’ve left the loop part without ML code and am trying to get setup to work. Code below, any help appreciated. Thanks a lot!

REDIRECT_STDOUT_TO(Serial);

#include <ArduinoBLE.h>
#include <LSM6DS3.h>
#include <Wire.h>

#include <TensorFlowLite.h>
#include <tensorflow/lite/micro/all_ops_resolver.h>
#include <tensorflow/lite/micro/micro_error_reporter.h>
#include <tensorflow/lite/micro/micro_interpreter.h>
#include <tensorflow/lite/schema/schema_generated.h>
#include <tensorflow/lite/version.h>
#include "model.h"

#define BLENAME                       "Tracker"
#define SERVICE_UUID                  "270a0000-f38d-4433-867b-ad0cef704438"
#define ACC_CHAR_UUID                 "270a0001-f38d-4433-867b-ad0cef704438"
#define GYR_CHAR_UUID                 "270a0002-f38d-4433-867b-ad0cef704438"
#define ML_CHAR_UUID                  "270a0003-f38d-4433-867b-ad0cef704438"
#define SamplingRate                 100   //Hz
#define Duration                      1 //seconds
#define NUM_GESTURES (sizeof(GESTURES) / sizeof(GESTURES[0]))

BLEService bleService(SERVICE_UUID); // Bluetooth® Low Energy LED Service
BLEStringCharacteristic AccelCharacteristic(ACC_CHAR_UUID, BLERead | BLENotify, 1024);
BLEStringCharacteristic GyroCharacteristic(GYR_CHAR_UUID, BLERead | BLENotify, 1024);
BLEStringCharacteristic MLCharacteristic(ML_CHAR_UUID, BLERead | BLENotify, 1024);

LSM6DS3 myIMU(I2C_MODE, 0x6A);    //I2C device address 0x6A
float aX, aY, aZ, gX, gY, gZ;
const float accelerationThreshold = 2.0; // threshold of significant in G's
int numSamples = 100;
int samplesRead = numSamples;
String AccelData ="";
String GyroData ="";
String MLData="";

// global variables used for TensorFlow Lite (Micro)
tflite::MicroErrorReporter tflErrorReporter;

// pull in all the TFLM ops, you can remove this line and
// only pull in the TFLM ops you need, if would like to reduce
// the compiled size of the sketch.
tflite::AllOpsResolver tflOpsResolver;

const tflite::Model* tflModel = nullptr;
tflite::MicroInterpreter* tflInterpreter = nullptr;
TfLiteTensor* tflInputTensor = nullptr;
TfLiteTensor* tflOutputTensor = nullptr;

// Create a static memory buffer for TFLM, the size may need to
// be adjusted based on the model you are using
constexpr int tensorArenaSize = 8 * 1024;
uint8_t tensorArena[tensorArenaSize];

// array to map gesture index to a name
const char* GESTURES[] = {
    "Walking",
    "Eating",
    "Sitting",
    "Trotting",
    "Lying",
    "Playing",
    "Standing",
};


void setup() {
  Serial.begin(9600);
  
  // begin initialization
  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");
    while (1);
  }

  // set advertised local name and service UUID:
  BLE.setLocalName(BLENAME);
  BLE.setDeviceName(BLENAME);
  BLE.setAdvertisedService(bleService);

  // add the characteristic to the service
  bleService.addCharacteristic(AccelCharacteristic);
  bleService.addCharacteristic(GyroCharacteristic);
  bleService.addCharacteristic(MLCharacteristic);
  
  // add service
  BLE.addService(bleService);

  BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
  BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
  
  // start advertising
  BLE.advertise();

  Serial.println("BLE LED Peripheral");  
  // set LED pin to output mode
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, LOW);

  if (myIMU.begin() != 0) {
  Serial.println("Device error");
  } else {
  Serial.println("IMU ready");
  }

  // get the TFL representation of the model byte array
  tflModel = tflite::GetModel(model);
  if (tflModel->version() != TFLITE_SCHEMA_VERSION) {
    Serial.println("Model schema mismatch!");
    while (1);
  }

  // Create an interpreter to run the model
  tflInterpreter = new tflite::MicroInterpreter(tflModel, tflOpsResolver, tensorArena, tensorArenaSize, &tflErrorReporter);

  // Allocate memory for the model's input and output tensors
  //tflInterpreter->AllocateTensors();

  // Get pointers for the model's input and output tensors
  //tflInputTensor = tflInterpreter->input(0);
  //tflOutputTensor = tflInterpreter->output(0);
}


void loop() {
  // listen for Bluetooth® Low Energy peripherals to connect:
  BLEDevice central = BLE.central();
  AccelData = String(myIMU.readFloatAccelX(), 3)+","+String(myIMU.readFloatAccelY(), 3)+","+String(myIMU.readFloatAccelZ(), 3);
  GyroData = String(myIMU.readFloatGyroX(), 3)+","+String(myIMU.readFloatGyroY(), 3)+","+String(myIMU.readFloatGyroZ(), 3);
  delay(50);

  if (central) {
    AccelCharacteristic.writeValue(AccelData.c_str());
    GyroCharacteristic.writeValue(GyroData.c_str());
  }
  
}

void blePeripheralConnectHandler(BLEDevice central) {
  // central connected event handler
  Serial.print("Connected event, central: ");
  Serial.println(central.address());
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, LOW);
  digitalWrite(LEDB, HIGH);

}

void blePeripheralDisconnectHandler(BLEDevice central) {
  // central disconnected event handler
  Serial.print("Disconnected event, central: ");
  Serial.println(central.address());
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, LOW);
}