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
- DFR1222 FireBeetle 2 ESP32-C5 ×1
- SEN0695 Fermion: BMI323+BMM350 9 DOF IMU Sensor ×1
Software Preparation
-
Download and install Arduino IDE: Download Arduino IDE
-
Download and install the DFRobot_BMI323 library: Download the DFRobot_BMI323 library
-
Download and install the DFRobot_BMM350 library: Download the DFRobot_BMM350 library
-
Library Installation Guide: View Installation Guide
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

Was this article helpful?
