IMU Nano 33 BLE Rev 2 Sensor- Bluetooth Capability and Serial Monitor

Hello, I am part of a university group project and am working on trying to use the bluetooth aspect of the IMU Nano 33 BLE Rev 2 sensor to have the serial monitor data outputted, while the IMU is battery powered. I am running into a lot of errors and problems when trying to do this, if anyone is able to give any tips or advice based on my code. I can give more details too about what the code is doing if that helps, thank you!

#include "Arduino_BMI270_BMM150.h"

// Constants
#define MINIMUM_TILT 20    // Positive threshold for tilt detection in degrees
#define MAXIMUM_TILT -20   // Negative threshold for tilt detection in degrees
#define WAIT_TIME 1000     // How often to run the code (in milliseconds)
#define HAPTIC_PIN 3       // D3 pin for motor
#define BUTTON_PIN 5       // D5 pin for calibration button
#define PWM_FREQUENCY 1    // Frequency of the sine wave (how often we update the PWM value)
#define TILT_THRESHOLD_DURATION 5000 // Time (ms) the tilt condition must be sustained to trigger haptic

// Variables for accelerometer data
float ax, ay, az;
float baselineAX, baselineAY, baselineAZ;
float baselineAXangle, baselineAYangle, baselineAZangle;
bool calibrated = false;

// Timing variables
unsigned long previousMillis = 0;
unsigned long previousSineWaveMillis = 0;
unsigned long tiltStartTime = 0;

// Haptic motor and tilt tracking
bool tiltTimerRunning = false;
bool hapticActive = false;

// Sine wave control
float sineWaveValue = 0;
float frequency = 50;  // Hz for the sine wave
const float pi = 3.14159;

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

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

  // Pin configurations
  pinMode(HAPTIC_PIN, OUTPUT);
  digitalWrite(HAPTIC_PIN, LOW);  // Motor off initially

  pinMode(BUTTON_PIN, INPUT);
  digitalWrite(BUTTON_PIN, LOW); // Button off initially

  Serial.println("Place the board at the desired angle and press the button to calibrate.");

  previousMillis = millis();
}

void loop() {
  buttonStateHandler();

  if (!calibrated || !IMU.accelerationAvailable() || digitalRead(BUTTON_PIN) == HIGH)
    return;  // Skip everything if not calibrated or button pressed

  unsigned long currentTime = millis();

  if (currentTime - previousMillis < WAIT_TIME)
    return;  // Wait until the next scheduled check

  previousMillis = currentTime;

  // Read accelerometer values
  IMU.readAcceleration(ax, ay, az);

  float angleAX = atan2(ax, sqrt(ay * ay + az * az)) * 180 / PI;
  float angleAY = atan2(ay, sqrt(ax * ax + az * az)) * 180 / PI;
  float angleAZ = atan2(az, sqrt(ax * ax + ay * ay)) * 180 / PI;

  float correctedAX = angleAX - baselineAXangle;
  float correctedAY = angleAY - baselineAYangle;
  float correctedAZ = angleAZ - baselineAZangle;

  Serial.print("Y= ");
  Serial.print(correctedAY);
  Serial.println(" degrees");

  Serial.print("Z= ");
  Serial.print(correctedAZ);
  Serial.println(" degrees");

  // Detect if we are tilted beyond thresholds
  bool isTilted = (correctedAY > MINIMUM_TILT || correctedAY < MAXIMUM_TILT ||
                   correctedAZ > MINIMUM_TILT || correctedAZ < MAXIMUM_TILT);

  if (isTilted) {
    if (!tiltTimerRunning) {
      tiltTimerRunning = true;
      tiltStartTime = currentTime;
      Serial.println("Tilt detected! Timer started.");
    }

    // Check how long we've been tilted
    if (tiltTimerRunning && (currentTime - tiltStartTime >= TILT_THRESHOLD_DURATION)) {
      // Tilt sustained for 5 seconds, activate haptics
      if (!hapticActive) {
        Serial.println("Tilt sustained for 5 seconds! Haptic motor ON.");
        hapticActive = true;
      }
      runHapticMotor(currentTime);
    } else {
      // Not past 5 seconds yet
      Serial.print("Holding tilt for ");
      Serial.print((currentTime - tiltStartTime) / 1000);
      Serial.println(" seconds...");
      analogWrite(HAPTIC_PIN, 0);  // Ensure motor is off until timer expires
    }

  } else {
    // Tilt condition ended before timer expired -> reset
    if (tiltTimerRunning || hapticActive) {
      Serial.println("Tilt ended. Resetting timer and turning motor off.");
    }
    tiltTimerRunning = false;
    hapticActive = false;
    analogWrite(HAPTIC_PIN, 0);  // Turn off motor immediately
  }
}

void buttonStateHandler() {
  int buttonState = digitalRead(BUTTON_PIN);

  if (buttonState == HIGH) {
    calibrateAccelerometer();
    calibrated = true;
  }
}

void runHapticMotor(unsigned long currentTime) {
  if (currentTime - previousSineWaveMillis >= (1000 / PWM_FREQUENCY)) {
    previousSineWaveMillis = currentTime;

    sineWaveValue = sin(2 * pi * frequency * currentTime / 1000.0);
    int pwmValue = map(sineWaveValue, -1, 1, 0, 255);

    analogWrite(HAPTIC_PIN, pwmValue);
    Serial.print("PWM: ");
    Serial.println(pwmValue);
  }
}

void calibrateAccelerometer() {
  float sumAX = 0, sumAY = 0, sumAZ = 0;
  int samples = 500;

  analogWrite(HAPTIC_PIN, 0);  // Turn off motor during calibration

  Serial.println("Calibrating Accelerometer... Hold the board steady.");

  for (int i = 0; i < samples; i++) {
    while (!IMU.accelerationAvailable());
    IMU.readAcceleration(ax, ay, az);
    sumAX += ax;
    sumAY += ay;
    sumAZ += az;
    delay(10);
  }

  // Store baseline offset
  baselineAX = sumAX / samples;
  baselineAY = sumAY / samples;
  baselineAZ = sumAZ / samples;

  // Calculate tilt angles of baseline values in degrees
  baselineAXangle = atan2(baselineAX, sqrt(baselineAY * baselineAY + baselineAZ * baselineAZ)) * 180 / PI;
  baselineAYangle = atan2(baselineAY, sqrt(baselineAX * baselineAX + baselineAZ * baselineAZ)) * 180 / PI;
  baselineAZangle = atan2(baselineAZ, sqrt(baselineAX * baselineAX + baselineAY * baselineAY)) * 180 / PI;

  Serial.println("Calibration complete!");
  Serial.print("Baseline X: "); Serial.println(baselineAXangle, 3);
  Serial.print("Baseline Y: "); Serial.println(baselineAYangle, 3);
  Serial.print("Baseline Z: "); Serial.println(baselineAZangle, 3);

  // Provide calibration feedback via haptic motor (short buzz)
  unsigned long currentTime = millis();
  previousSineWaveMillis = currentTime;

  sineWaveValue = sin(2 * pi * frequency * currentTime / 1000.0);
  int pwmValue = map(sineWaveValue, -1, 1, 0, 255);

  analogWrite(HAPTIC_PIN, pwmValue);
  delay(100);
  analogWrite(HAPTIC_PIN, 0);
  delay(100);
  analogWrite(HAPTIC_PIN, pwmValue);
  delay(100);
  analogWrite(HAPTIC_PIN, 0);
}

Please explain what you mean by "serial monitor data" and how Bluetooth will be used.

Thanks for your response! By serial monitor data, I mean that we are currently having 2 measured angles outputted every second the code is running in the Arduino serial monitor when the IMU is connected to the computer.

I was curious if it was possible to utilize bluetooth in some way to have this data be sent via bluetooth when the IMU will be connected to battery power, not my computer, so that we are still able to see the data being outputted even when we do not have the connection to the arduino serial monitor.

Do you mean that you want to use Bluetooth to send these two angles somewhere? If so, where?

  Serial.print("Y= ");
  Serial.print(correctedAY);
  Serial.println(" degrees");

  Serial.print("Z= ");
  Serial.print(correctedAZ);
  Serial.println(" degrees");

Incidentally, the equations in the code are not correct for tilt angles. Where did those equations come from?

Only two steady state tilt angles can be measured by an IMU, and one set of correct equations is given in this tutorial:

(There are several different definitions for tilt angles, depending on the order of operations and other considerations).

Thanks for the resource, will definitely look into the angle formula we are using. And yes, we ideally would like to be able to have those serial print angles outputted to a device via Bluetooth, since the IMU will be battery powered and not directly connected to the computer. We just weren't sure if that is possible to do.

The Nano BLE Sense Rev 2 was created with the idea of sending sensor data elsewhere by Bluetooth, but unfortunately, the Arduino company does not seem to have time to produce quality tutorials or examples on how to use it. There are a few posts on this forum but few seem to make progress.

Adafruit does a much, much better job of teaching their customers how to use their products, so have a look at the Feather radio modules, and the getting started tutorials, particularly the Feather nRF52840 Sense.

Is this what you want to send by BLE?

Serial.print("Y= ");
  Serial.print(correctedAY);
  Serial.println(" degrees");

  Serial.print("Z= ");
  Serial.print(correctedAZ);
  Serial.println(" degrees");

It's simple to send the floats as integers (after multiplication by 100,1000,ect) or as text after using sprintf().

The Arduino BLE library example for "Battery Monitor" shows how to send an integer.

It is also possible to send the 8 bytes of the 2 float array, but it is less easy to troubleshoot and debug.

yes, this is what we want to send by BLE, i will look into that example. thank you!

thanks for your help!

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