XIAO nRF52840 Sense Free-Falls , Sleep & Double Tap Wake Up Demo

This is IMU Interrupt Example for XIAO BLE Sense on the Dev board.
It displays the interrupt count and Sleep.
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.

/*****************************************************************************/
// 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.
//  
//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

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); }
}
Using library Seeed Arduino LSM6DS3 at version 2.0.3 in folder: D:\Arduino_projects\libraries\Seeed_Arduino_LSM6DS3 
Using library Wire in folder: C:\Users\Dude\AppData\Local\Arduino15\packages\Seeeduino\hardware\mbed\2.9.0\libraries\Wire (legacy)
Using library U8g2 at version 2.34.22 in folder: D:\Arduino_projects\libraries\U8g2 
Using library SPI in folder: C:\Users\Dude\AppData\Local\Arduino15\packages\Seeeduino\hardware\mbed\2.9.0\libraries\SPI (legacy)
"C:\\Users\\Dude\\AppData\\Local\\Arduino15\\packages\\Seeeduino\\tools\\arm-none-eabi-gcc\\7-2017q4/bin/arm-none-eabi-size" -A "C:\\Users\\Dude\\AppData\\Local\\Temp\\arduino\\sketches\\8253AD941CB66F96DBB83652EDEB1561/Free_fal_demo_copy_20230829210527.ino.elf"
Sketch uses 101376 bytes (12%) of program storage space. Maximum is 811008 bytes.
Global variables use 46072 bytes (19%) of dynamic memory, leaving 191496 bytes for local variables. Maximum is 237568 bytes.

HTH
GL :slight_smile: PJ
It is possible to do Motion detection (movement) and Impact (drop detection) with Free-Fall all at the same time. Very cool. :sunglasses: :call_me_hand:

1 Like

FYI, SLEEP does NOT work correctly with Seeed nRF52 mbed-enabled Boards Version 2.9.2 FYI.
works perfectly with 2.9.0 & 2.9.1 Go figure.
HTH
GL :slight_smile:

1 Like

Hi Everyone,

I’m encountering an issue where my device fails to stay in deep sleep mode. Immediately after entering deep sleep, the MCU wakes up again, despite there being no motion detected. The reset reason displayed is: 09:49:13.485 → 65536 (0x10000).

Currently, nothing is connected to the MCU; I’m using just the bare XIAO NRF SENSE board.

My setup includes a XIAO NRF running mbed 2.9.0 and a double tap example. Any insights on what might be causing this behavior would be greatly appreciated.

Thank you!

/*****************************************************************************/
// 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 "Double Tap" was recognized.
// Additionally, the device goes into System OFF state, after 5 interrupts were
// received. Another "Double Tap" will wake up the device again.
// 
// Original by chuck
//poatched and toasted by pjg
/*******************************************************************************/

#include "LSM6DS3.h"
#include "Wire.h"

LSM6DS3 myIMU(I2C_MODE, 0x6A); // IMU 
#define int1Pin PIN_LSM6DS3TR_C_INT1




 // 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


void setup() {
    Serial.begin(9600);
	  delay(1000); //relax...
	  Serial.println("Processor came out of reset.\n");

    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!");
    }
    
    setupDoubleTapInterrupt();
    
    pinMode(int1Pin, INPUT);
    attachInterrupt(digitalPinToInterrupt(int1Pin), int1ISR, RISING);

/*
WATCHDOG TIMER FOR AUTO RESET

   NRF_WDT->CONFIG = (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos) | ( WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos);
    NRF_WDT->CRV = 60*32768; // 2 sec. timout
    NRF_WDT->TASKS_START = 1;

    NRF_WDT->RR[0] = WDT_RR_RR_Reload; //Reload watchdog register 0.
*/


}
int i = 0;
void loop() {
    delay(5000);

  int32_t reset_reason = 0;    
 reset_reason = NRF_POWER->RESETREAS;
 Serial.println(reset_reason);   


  if(i == 2)
  {
  NRF_POWER->RESETREAS = 0xFFFFFFFF;  // Write to clear the reset reasons

  }

    setLedRGB(false, false, true); // reset led to blue only

    Serial.print("\Iterrupt Counter: ");
    Serial.println(interruptCount);

    // 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 >= 5) {
      // Trigger System OFF after 5 interrupts
      goToPowerOff();
    }
    
    delay(500);
}


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

void goToPowerOff() {



  setLedRGB(false, false, false);
  Serial.println("Going to System OFF");

  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(int1Pin), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  // Trigger System OFF

   NRF_POWER->SYSTEMOFF = 1;
}

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

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); }
}```

HI there,
Just to clarify, The 2.9.2. does work so you can update your bsp to the latest.
This example is for freeFall, comment out *setupFreeFallInterrupt();*and uncomment the //setupDoubleTapInerrupt , to test the sleep portion 3) double Taps fast and it sleeps ALSO You forgot the delay…
for 2.9.2 you need to use the GPIO pin number for the wake up INT.
Works Perfectly as is with 2.9.1
HTH
GL :slight_smile: PJ :v:

a better example of ALL of it is here (3 button presses SLEEPS)

Thanks for your input.
Using the mbed version and the delay it is now working like a charm.

1 Like