Any example of using IMU with XiaoBle using Wake-Up Interrupt

I’m trying to write a basic function that once the IMU sense the movement it send the interrupt single to the xiao ble sense. I followed older threads that using double tap which works, also leads to the datasheet for lsm6ds3tr-c.pdf. But no matter how I configure the Register it does not trigger the interput.
Here is the configuration:

writeRegister(LSM6DS3TR_ADDR, TAP_CFG, 0x8E);
writeRegister(LSM6DS3TR_ADDR, TAP_THS_6D, 0x43);
writeRegister(LSM6DS3TR_ADDR, TAP_CFG, 0x40);
writeRegister(LSM6DS3TR_ADDR, INT1_CTRL, 0x40);
writeRegister(LSM6DS3TR_ADDR, MD1_CFG, 0x4E);

Anyone has any idea what’s going on? or any working example?

Thanks in advance.

Hi there,

LOL, Someone didn’t read them all… It works and there is a motion & fall demo I have on here, that sends a “notify” via BLE to the central connected to the peripheral. based on the Thresholds and freefall interrupts. Also you must define the attach correctly, for the BSP you are using. Go look at those threads and come back, I can help you get it working. Easy Peasy. :+1:

HTH
GL :slight_smile: PJ :v:

The Double tap one is a wakeup demo , BTW :point_up:

actually, I did read all the threads about IMU with interrupt, I think it’s my bad that didnt explain the question in good way.
My Goal: to programming the system to using the min power while its in the idle status (no movement). So I plan to use IMU’s motion interrupt to wakeup the device, which try not use the IMU data checking in the loop() to save bit power.

The problem for me now is that so far I can only wake up the device by double tap detection which was listed on the forum few years ago, any other motion detection / tilt detection are not working.

I also check your example code in the
[Xiao nRF52840 Sense IMU Parked demo With SLEEP Interrupt button](https://Xiao nRF52840 Sense IMU Parked demo With SLEEP Interrupt button)
Which I run it on my xiaoblesense and found that the IMU interrupt also never gets trigged. Any idea how ?

Hi there,

Which BSP did you use? Some don’t work with the logical pin names and need the GPIO number. It’s documented as well. Wakeup can be any motion above a certain threshold or movement for a certain amount of time, Al controlled by the config registers. the one you list is a WAKE-UP demo… YMMV. there are others/more.

HTH
GL :slight_smile: PJ :v:

I’m using mbed 2.9.1. I strip down my code to pure imu and interrupt related but still no luck.

I past the code here for help.

#include "LSM6DS3.h"
#include "Wire.h"
#include <Arduino.h>

LSM6DS3 myIMU(I2C_MODE, 0x6A); // 

// interrupt pin set
const int interruptPin =  PIN_LSM6DS3TR_C_INT1; 

// interrupt flag
volatile bool motionDetected = false;

void setup() {
  // Serial Init
  Serial.begin(9600);
  delay(2500);
  // IMU LSM6DS3
   myIMU.settings.gyroEnabled = 1; // Gyro ON.
  if (myIMU.begin() != 0) {
    Serial.println("IMU initialization failed!");
    while (1);
  }
  Serial.println("IMU initialized.");

  configureIMU();  // not needed here, if already applied..
  
  // interrupt pin configure
  pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), motionInterrupt, RISING);
}

void loop() {
   if (motionDetected)
   {
      motionDetected = false;
      performTask();
   }

}

void motionInterrupt() {
  motionDetected = true;
}


void configureIMU()
{
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60);
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x8E);
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0x85);  // Changed the value to 0x01 for increased sensitivity was 85 Set tap threshold 8C,03 in demo
  //myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, tapThreshold);
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT_DUR2, 0x7F);  // Set Duration, Quiet and Shock time windows 7F
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x80); // Single & double-tap enabled (SINGLE_DOUBLE_TAP = 1)
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x00);  // Disable the double-tap interrupt

}

// wake up and run some tasks
void performTask() {

  Serial.println("Performing task...");
  delay(1000); 
}

Hi there,

So you were close , However I think you are conflating the two subjects and functions. Motion & Free Fall,or Motion and IMPACT is also possible. Keep looking at the threads for both Motion and FALL detection.
Try this sketch bellow, See if you get where you went off track.
BSP 2.9.1 was discovered by @msfujino that only worked if you Defined the GPIO pin Number the macro didn’t hook.
I tested this just now again, and it works great!
DOuble tap the can it will interrupt and increment the count, after the third Double-Tap it will sleep and wait for a tap to wake up. You can also have any motion wake it up as well if you change the code.

#include "LSM6DS3.h"
#include "Wire.h"
LSM6DS3 myIMU(I2C_MODE, 0x6A); // IMU
//#define int1Pin PIN_LSM6DS3TR_C_INT1
#define int1Pin P0_11 // <--- GPIO pin number 

const int ledPin = LED_BUILTIN; // set ledPin to on-board LED

uint8_t interruptCount = 0; // Amount of received interrupts
uint8_t prevInterruptCount = 0; // Interrupt Counter from last loop

void setup() {
  Serial.begin(9600);
  while ( !Serial ) delay(10);   // for nrf52840 with native usb
  pinMode(ledPin, OUTPUT); // use the LED as an output
  Wire.setClock(1000000);  // Set I2C to 1 MHz (Fast Mode Plus)
  Serial.println("Hello, I am awake!");
  myIMU.settings.gyroEnabled = 0; // Gyro currently not used, disabled to save power
  if (myIMU.begin() != 0) {
    Serial.println("IMU error");
  } else {
    Serial.println("IMU OK!");
  }

  setupDoubleTapInterrupt();

  pinMode(int1Pin, INPUT);
  attachInterrupt(digitalPinToInterrupt(int1Pin), int1ISR, RISING);
}

void loop() {
  setLED(false);
  Serial.print("Interrupt Counter: ");
  Serial.println(interruptCount);

  if (interruptCount > prevInterruptCount) {
    Serial.println("Interrupt received!");
  }
  prevInterruptCount = interruptCount;

  if (interruptCount >= 3) {
    // Trigger System OFF after 5 interrupts
    goToPowerOff();
  }

  delay(500);
}

void goToPowerOff() {
  Serial.println("Going to System OFF");
  setLED(true);
  setupDoubleTapInterrupt();
  delay(1000); // delay seems important to apply settings, before going to System OFF
  //Ensure interrupt pin from IMU is set to wake up device
  nrf_gpio_cfg_sense_input(digitalPinToInterrupt(int1Pin), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  //delay(2000);// Trigger System OFF
  NRF_POWER->SYSTEMOFF = 1;
}

void setupDoubleTapInterrupt() {
  uint8_t error = 0;
  uint8_t dataToWrite = 0;

  // Double Tap Config
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60); //* Acc = 416Hz (High-Performance mode)// Turn on the accelerometer
  // ODR_XL = 416 Hz, FS_XL = 2g
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x8E);// INTERRUPTS_ENABLE, SLOPE_FDS// Enable interrupts and tap detection on X, Y, Z-axis
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0x85);// Set tap threshold 8C
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT_DUR2, 0x7F);// Set Duration, Quiet and Shock time windows 7F
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x80);// Single & double-tap enabled (SINGLE_DOUBLE_TAP = 1)
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x08);// Double-tap interrupt driven to INT1 pin
}

void int1ISR() {
  interruptCount++;
}

  void setLED(bool on)
  {
  // data = 1 -> LED = On
  // data = 0 -> LED = Off
  digitalWrite(LED_BUILTIN, on ? HIGH : LOW);
  }

HTH
GL :slight_smile: PJ :v:

not sure which you are trying to accomplish, You can detect movement (motion) or you can detect Free-Fall, or you can detect Free-Fall with Impacts, also AWT(like on a mobile phone). You can use all with Interrupts and Wakeup can be achieved by the same as well as the “double-TAP”. I have successfully used every mode. High performance and low power modes work very well. There is some nuance involved with detecting and configuration. so ask what you don’t understand. :+1:

Serial output;

Hello, I am awake!
IMU OK!
Interrupt Counter: 0
Interrupt Counter: 0
Interrupt Counter: 0
Interrupt Counter: 0
Interrupt Counter: 0
Interrupt Counter: 0
Interrupt Counter: 0
Interrupt Counter: 0
Interrupt Counter: 0
Interrupt Counter: 1
Interrupt received!
Interrupt Counter: 1
Interrupt Counter: 1
Interrupt Counter: 1
Interrupt Counter: 2
Interrupt received!
Interrupt Counter: 2
Interrupt Counter: 2
Interrupt Counter: 2
Interrupt Counter: 3
Interrupt received!
Going to System OFF

Look here…

Here you go…

3 drops/Falls and a SLEEP…

Double-Tap (on the can) to Wake up! :stuck_out_tongue:

Enjoy

HTH
GL :slight_smile: PJ :v:

Code for video…

/*****************************************************************************/
// Code for 3 Falls and a SLEEP! video
// IMU Interrupt Example for XIAO BLE Sense
// This example shows how to configure LMSD6S3TR-C on XIAO BLE SENSE to interrupt
// on INT1 after a Free Fall event after 3 Free Falls it sleeps and "Double Tap" INT2 wakes it up again.
// tested with 2.9.0 - 2.9.3 
//poatched and toasted by pjg
/*******************************************************************************/

#include "LSM6DS3.h"
#include "Wire.h"
#include <U8x8lib.h>
LSM6DS3 myIMU(I2C_MODE, 0x6A); // IMU 
//#define int2Pin PIN_LSM6DS3TR_C_INT1
#define int2Pin P0_11 // <--- GPIO pin number 
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* clock=*/ PIN_WIRE_SCL, /* data=*/ PIN_WIRE_SDA, /* reset=*/ U8X8_PIN_NONE);
 // OLEDs without Reset of the Display

const int buttonPin = 1;     // the number of the pushbutton pin
int buttonState = 0;         // variable for reading the pushbutton status
int BuzzerPin = A3;
uint8_t interruptCount = 0; // Amount of received interrupts
uint8_t prevInterruptCount = 0; // Interrupt Counter from last loop
uint16_t detectCount = 0;

void setup() {
    Serial.begin(9600);
	  delay(1000); //relax...
	  Serial.println("Processor came out of reset.\n");
    u8x8.begin();
    u8x8.setFlipMode(1);   // set number from 1 to 3, the screen word will rotary 180
    
    pinMode(LED_BUILTIN, OUTPUT);// initialize the LED pin as an output:
    pinMode(buttonPin, INPUT_PULLUP);// initialize the pushbutton pin as an input:
    pinMode(BuzzerPin, OUTPUT);
    pinMode(LEDR, OUTPUT);
    pinMode(LEDG, OUTPUT);
    pinMode(LEDB, OUTPUT);
    setLedRGB(false, false, true); // set blue led

    myIMU.settings.gyroEnabled = 0; // Gyro currently not used, disabled to save power 
    if (myIMU.begin() != 0) {
        Serial.println("IMU error");
    } else {
        Serial.println("IMU OK!");
    }
    Serial.println("\nLSM6D3 'FREE FALL DEMO' compiled on "__DATE__ " at " __TIME__);
    //setupDoubleTapInterrupt();
    setupFreeFallInterrupt();
    pinMode(int2Pin, INPUT);
    attachInterrupt(digitalPinToInterrupt(int2Pin), int1ISR, RISING);
    u8x8.setFont(u8x8_font_8x13B_1x2_r);
    u8x8.clearDisplay();
    u8x8.setCursor(0, 0);
    u8x8.print("Free Fall Demo");
   Serial.print("\Iterrupt Counter: ");
   Serial.println(interruptCount);
}

void loop() {
    setLedRGB(false, false, true); // reset led to blue only
    u8x8.setCursor(0, 3);
    u8x8.print(interruptCount);
    //Serial.print("\Iterrupt Counter: ");
    //Serial.println(interruptCount);
uint8_t readDataByte = 0;
    //Read the wake-up source register
    myIMU.readRegister(&readDataByte, LSM6DS3_ACC_GYRO_WAKE_UP_SRC);
    //Mask off the FF_IA bit for free-fall detection
    readDataByte &= 0x20;
    if (readDataByte) {
        detectCount ++;
        Serial.print("Free fall detected!  ");
        Serial.println(detectCount);
    }
    delay(10);
    // if interrupt was received in this cycle
    if (interruptCount > prevInterruptCount) {
      Serial.println("\Interrupt received!");
      setLedRGB(false, true, false); // set green only
    }
    
    prevInterruptCount = interruptCount;
    
    if (interruptCount >= 3) {
      // Trigger System OFF after 5 interrupts
      goToPowerOff();
    }
    
    delay(500);
}


// -------------------- System ------------------------- //

void goToPowerOff() {
  setLedRGB(false, false, false);
  Serial.println("Going to System OFF");
  u8x8.clearDisplay();
  u8x8.setCursor(0, 3);
  u8x8.print("SLEEP");
  setupDoubleTapInterrupt(); // not needed here, if already applied..
  delay(1000); // delay seems important to apply settings, before going to System OFF
  //Ensure interrupt pin from IMU is set to wake up device
  nrf_gpio_cfg_sense_input(digitalPinToInterrupt(int2Pin), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  delay(2000); // Required delay
  // Trigger System OFF
  NRF_POWER->SYSTEMOFF = 1;
}

// -------------------- Interrupts ------------------------- //

void setupFreeFallInterrupt() {
  uint8_t errorAccumulator = 0;
  uint8_t dataToWrite = 0;  //Temporary variable

  //Setup the accelerometer******************************
  dataToWrite = 0; //Start Fresh!
  dataToWrite |= LSM6DS3_ACC_GYRO_BW_XL_200Hz;
  dataToWrite |= LSM6DS3_ACC_GYRO_FS_XL_2g;
  dataToWrite |= LSM6DS3_ACC_GYRO_ODR_XL_416Hz;

   // //Now, write the patched together data
   errorAccumulator += myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60 );
   //errorAccumulator += myIMU.readRegister(&dataToWrite, LSM6DS3_ACC_GYRO_CTRL4_C);
   //errorAccumulator += myIMU.writeRegister( LSM6DS3_ACC_GYRO_CTRL4_C, 0x10 );
   // Write 00h into WAKE_UP_DUR 
	 errorAccumulator += myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_DUR, 0x00 );
    // Set FF threshold (FF_THS[2:0] = 011b)
    // Set six samples event duration (FF_DUR[5:0] = 000110b)
    // Write 33h into FREE_FALL 
   errorAccumulator += myIMU.writeRegister(LSM6DS3_ACC_GYRO_FREE_FALL, 0x33);
    // FF interrupt driven to INT1 pin
    // Write 10h into MD1_CFG
	 errorAccumulator += myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x10 );
    // Also route to INT2 pin
    // Write 10h into MD1_CFG
	 errorAccumulator += myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD2_CFG, 0x10 );
	  // Latch interrupt
    // Write 01h into TAP_CFG 
   errorAccumulator += myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x81);
	
	if( errorAccumulator )
  {
	  Serial.println("Problem configuring the device.");
  }
  else
  {
	  Serial.println("Device O.K.");
  }	
}


void setupDoubleTapInterrupt() {
  uint8_t error = 0;
  uint8_t dataToWrite = 0;

  // Double Tap Config
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60); //* Acc = 416Hz (High-Performance mode)// Turn on the accelerometer
  // ODR_XL = 416 Hz, FS_XL = 2g
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x8E);// INTERRUPTS_ENABLE, SLOPE_FDS// Enable interrupts and tap detection on X, Y, Z-axis
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_THS_6D, 0x85);// Set tap threshold 8C
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_INT_DUR2, 0x7F);// Set Duration, Quiet and Shock time windows 7F
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x80);// Single & double-tap enabled (SINGLE_DOUBLE_TAP = 1)
  myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x08);// Double-tap interrupt driven to INT1 pin
}

void int1ISR()
{
  interruptCount++;
 ;
  
}

// -------------------- Utilities ------------------------- //

void setLedRGB(bool red, bool green, bool blue) {
  if (!blue) { digitalWrite(LEDB, HIGH); } else { digitalWrite(LEDB, LOW); }
  if (!green) { digitalWrite(LEDG, HIGH); } else { digitalWrite(LEDG, LOW); }
  if (!red) { digitalWrite(LEDR, HIGH); } else { digitalWrite(LEDR, LOW); }
}

Serial output…one cycle

IMU OK!

LSM6D3 'FREE FALL DEMO' compiled on Mar  2 2025 at 16:14:27
Device O.K.
Iterrupt Counter: 0
Free fall detected!  1
Interrupt received!
Free fall detected!  2
Interrupt received!
Free fall detected!  3
Interrupt received!
Going to System OFF
Processor came out of reset.

IMU OK!

LSM6D3 'FREE FALL DEMO' compiled on Mar  2 2025 at 16:14:27
Device O.K.
Iterrupt Counter: 0

Thanks for the reference, with some digging into the library from cp file it’s finally working!.
I post the configure here in case anyone want to check out:

void setupMotionInterrupt() {
   myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x60); //* Acc = 416Hz (High-Performance mode)// Turn on the accelerometer
    // Motion Detection (Wake-Up) on INT2
    myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x05); // ~250mg threshold
    myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_DUR, 0x0); // 1x ODR duration
    myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, LSM6DS3_ACC_GYRO_INT1_WU_ENABLED);     // Route WAKE_UP to INT1
}

1 Like

Hi there,

Awesome, Glad it was helpful, Please mark the Solution so others can find it also. :+1:

GL :slight_smile: PJ :v: