Example Code for Arduino-Acquiring Motion Status and Heading Angle via I2C

This tutorial is designed to guide users in implementing motion state detection and heading angle (electronic compass) monitoring on the ESP32 using the BMI323 and BMM350 sensors.

Hardware Preparation

Software Preparation

Wiring Diagram

SEN0695-Motion Status wiring diagram

Connect the 9-axis IMU sensor to the ESP32-C5 as shown in the diagram. Core pin connections:

  • Sensor pin “3V3” → ESP32-C5 3.3V
  • Sensor pin “GND” → ESP32-C5 GND
  • Sensor pin “INT1” → ESP32-C5 GPIO27
  • Sensor pin “INT2” → ESP32-C5 GPIO26
  • Sensor I2C pin “SCL” → ESP32-C5 SCL (default GPIO10)
  • Sensor I2C pin “SDA” → ESP32-C5 SDA (default GPIO9)
  • I²C address pad configuration: Leave the I²C address pads of BMI323 and BMM350 open. In this state, the I²C address of BMI323 is 0x69, and the I²C address of BMM350 is 0x15 (factory default mode).

Sample Code

#include "DFRobot_BMI323.h"
#include "DFRobot_BMM350.h"

// I2C address definitions
#define BMI323_I2C_ADDR  0x69    
#define BMM350_I2C_ADDR  0x15    

// Interrupt pin definitions (modify according to actual wiring)
#define INT1_PIN         27       // BMI323 INT1 → GPIO27 (any motion)
#define INT2_PIN         26       // BMI323 INT2 → GPIO26 (no motion)

// Create sensor objects
DFRobot_BMI323 bmi323(&Wire, BMI323_I2C_ADDR);
DFRobot_BMM350_I2C bmm350(&Wire, BMM350_I2C_ADDR);

// Interrupt flags
volatile bool gAnyMotionFlag = false;
volatile bool gNoMotionFlag  = false;

// Motion state enumeration
enum MotionState {
  STATE_UNKNOWN,
  STATE_MOVING,
  STATE_STILL
};
MotionState currentState = STATE_UNKNOWN;

// Direction names array
const char* directionNames[] = {
  "North", "Northeast", "East", "Southeast",
  "South", "Southwest", "West", "Northwest"
};

// Interrupt service routines
void IRAM_ATTR interruptAnyMotion() {
  gAnyMotionFlag = true;
}

void IRAM_ATTR interruptNoMotion() {
  gNoMotionFlag = true;
}

/**
 * @brief Convert heading angle to direction name
 * @param heading Heading angle (0~360°)
 * @return Direction name string
 */
const char* getDirectionName(float heading) {
  int index = (int)((heading + 22.5f) / 45.0f) % 8;
  return directionNames[index];
}

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.println("\n=== BMI323 Motion Detector + BMM350 Compass ===");

  // ---------- Initialize BMI323 ----------
  Serial.print("Initializing BMI323... ");
  while (!bmi323.begin()) {
    Serial.println("Failed, retry in 1s");
    delay(1000);
  }
  Serial.println("OK");

  // Configure accelerometer
  if (!bmi323.configAccel(bmi323.eAccelODR50Hz, bmi323.eAccelRange8G, bmi323.eAccelModeNormal)) {
    Serial.println("Error: BMI323 accelerometer config failed!");
    while (1) delay(1000);
  }

  // Configure any-motion detection (INT1)
  struct bmi3_any_motion_config anyMotionCfg;
  anyMotionCfg.duration    = 9;    // 9 * 20ms = 180ms
  anyMotionCfg.slope_thres = 9;    // 9 * 1.953mg ≈ 17.6mg
  anyMotionCfg.acc_ref_up  = 1;    // Always update reference
  anyMotionCfg.hysteresis  = 5;    // 5 * 1.953mg ≈ 9.8mg
  anyMotionCfg.wait_time   = 4;    // 4 * 20ms = 80ms

  if (!bmi323.enableAnyMotionInt(anyMotionCfg, bmi323.eINT1, bmi323.eAxisXYZ)) {
    Serial.println("Error: Failed to enable any-motion interrupt!");
    while (1) delay(1000);
  }

  // Configure no-motion detection (INT2)
  struct bmi3_no_motion_config noMotionCfg;
  noMotionCfg.duration    = 9;    // 9 * 20ms = 180ms
  noMotionCfg.slope_thres = 9;    // 9 * 1.953mg ≈ 17.6mg
  noMotionCfg.acc_ref_up  = 1;    // Always update reference
  noMotionCfg.hysteresis  = 5;    // 5 * 1.953mg ≈ 9.8mg
  noMotionCfg.wait_time   = 5;    // 5 * 20ms = 100ms

  if (!bmi323.enableNoMotionInt(noMotionCfg, bmi323.eINT2, bmi323.eAxisXYZ)) {
    Serial.println("Error: Failed to enable no-motion interrupt!");
    while (1) delay(1000);
  }

  // Configure ESP32 external interrupts
  attachInterrupt(digitalPinToInterrupt(INT1_PIN), interruptAnyMotion, RISING);
  attachInterrupt(digitalPinToInterrupt(INT2_PIN), interruptNoMotion, RISING);

  // ---------- Initialize BMM350 ----------
  Serial.print("Initializing BMM350... ");
  while (bmm350.begin()) {   // begin() returns 0 on success, non-zero on failure
    Serial.println("Failed, retry in 1s");
    delay(1000);
  }
  Serial.println("OK");

  // Set operation mode to normal mode
  bmm350.setOperationMode(eBmm350NormalMode);

  // Set preset mode to high accuracy, data rate 25Hz
  bmm350.setPresetMode(BMM350_PRESETMODE_HIGHACCURACY, BMM350_DATA_RATE_25HZ);

  // Enable X, Y, Z axis measurements
  bmm350.setMeasurementXYZ();

  Serial.println("Setup complete! Monitoring motion and compass.\n");
}

void loop() {
  static unsigned long lastPrint = 0;
  unsigned long now = millis();

  // Handle motion interrupts
  if (gAnyMotionFlag) {
    gAnyMotionFlag = false;
    uint16_t status = bmi323.getIntStatus();
    if (status & BMI3_INT_STATUS_ANY_MOTION) {
      if (currentState != STATE_MOVING) {
        currentState = STATE_MOVING;
        float heading = bmm350.getCompassDegree();
        const char* dirName = getDirectionName(heading);
        Serial.print("[");
        Serial.print(now);
        Serial.print("] State: MOVING | Direction: ");
        Serial.print(dirName);
        Serial.print(" (");
        Serial.print(heading, 1);
        Serial.println("°)");
      }
    }
  }

  if (gNoMotionFlag) {
    gNoMotionFlag = false;
    uint16_t status = bmi323.getIntStatus();
    if (status & BMI3_INT_STATUS_NO_MOTION) {
      if (currentState != STATE_STILL) {
        currentState = STATE_STILL;
        float heading = bmm350.getCompassDegree();
        const char* dirName = getDirectionName(heading);
        Serial.print("[");
        Serial.print(now);
        Serial.print("] State: STILL | Direction: ");
        Serial.print(dirName);
        Serial.print(" (");
        Serial.print(heading, 1);
        Serial.println("°)");
      }
    }
  }

  // Output current state and direction every second
  if (now - lastPrint >= 1000) {
    lastPrint = now;
    float heading = bmm350.getCompassDegree();
    const char* dirName = getDirectionName(heading);
    const char* stateStr = (currentState == STATE_MOVING) ? "MOVING" : 
                           (currentState == STATE_STILL) ? "STILL" : "UNKNOWN";
    Serial.print("[");
    Serial.print(now);
    Serial.print("] State: ");
    Serial.print(stateStr);
    Serial.print(" | Direction: ");
    Serial.print(dirName);
    Serial.print(" (");
    Serial.print(heading, 1);
    Serial.println("°)");
  }

  delay(10);
}

Result

SEN0695-Motion Status Result

Was this article helpful?

TOP