LSM6DS3 Free-fall detection not working

Hello there,
I am trying to detect a fall with my XIAO nRF52840. It has an onboard IMU (LSM6DS3).

I used this sample code from another forum here which worked fine for double tap detection. However I need a free-fall detection.

So I went through the whole LSM6DS3 documentation and came up with this code here:

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

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

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

void setup() {
    Serial.begin(9600);
    
    while (!Serial) {
    ;  // wait for serial port to connect. Needed for native USB port only
    }

    Serial.println("--- START ---");


    pinMode(LED_RED, OUTPUT);
    pinMode(LED_GREEN, OUTPUT);
    pinMode(LED_BLUE, OUTPUT);
    setLedRGB(false, false, true); // set blue led

    myIMU.settings.accelEnabled = 1;
    myIMU.settings.tempEnabled = 1;
    myIMU.settings.gyroEnabled = 1; // Gyro currently not used, disabled to save power 
    
    if (myIMU.begin() != 0) {
        Serial.println("IMU error");
    } else {
        Serial.println("IMU OK!");
    }
    
    setupFreeFallInterrupt();
    
    pinMode(int1Pin, INPUT);
    attachInterrupt(digitalPinToInterrupt(int1Pin), int1ISR, RISING);
}

void loop() {
    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");
  setupFreeFallInterrupt(); // not needed here, if already applied..
  delay(100); // 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 setupFreeFallInterrupt() {
  uint8_t error = 0;
  uint8_t dataToWrite = 0;

  dataToWrite |= LSM6DS3_ACC_GYRO_BW_XL_200Hz; // 0000 0001  200Hz
  dataToWrite |= LSM6DS3_ACC_GYRO_FS_XL_2g;    // 0000 0000  2g
  dataToWrite |= LSM6DS3_ACC_GYRO_ODR_XL_416Hz;// 0110 0000  516

  error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, dataToWrite);

  // p.61
  // enable linear acceleration sensor
  error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL9_XL, 0x38);

  // Wakeup source (p.62)
  // 00100000 x20
  // 00101111 x2f
  error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_SRC, 0x2f);

  // TAP_CFG (58h) Register (page 77)
  // Latch interrupt - Write 01h into TAP_CFG 
  error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x01); // LATCHED
  
  //MD1_CFG (5Eh) Functions routing on INT1 register (page 80)
  // Free fall
  error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x10);   // 00010000

  //p.79 -> default values
  error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_DUR, 0x00); 

  // p.80 -> 
  // 00110 :
  // 011 : 312mg threshold
  error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_FREE_FALL, 0x33); // 00110 011
  //error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_FREE_FALL, 0x30); // 00110 000   156mg
  //error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_FREE_FALL, 0x00); // 00000 000 156mg
  //error += myIMU.writeRegister(LSM6DS3_ACC_GYRO_FREE_FALL, 0x07); // 00000 111 500mg
  
  if (error) {
	  Serial.println("Problem configuring the device.");
  } else {
	  Serial.println("Device O.K.");
  }	
}

void int1ISR()
{
  interruptCount++;
}

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

void setLedRGB(bool red, bool green, bool blue) {
  if (!blue) { digitalWrite(LED_BLUE, HIGH); } else { digitalWrite(LED_BLUE, LOW); }
  if (!green) { digitalWrite(LED_GREEN, HIGH); } else { digitalWrite(LED_GREEN, LOW); }
  if (!red) { digitalWrite(LED_RED, HIGH); } else { digitalWrite(LED_RED, LOW); }
}

however now that I (assumed I) configured for free fall I don't get any interrupts anymore.
I guess something is missing / wrong in the setupFreeFallInterrupt() function but what?
Does anyone have an idea? I admit, I'm not very experienced in these registers. Maybe I missed something?

Thanks for any help!

How are you testing the free fall detection?

The sensor needs to be in some approximation of free fall to trigger a free fall interrupt.

1 Like

Well, not sure how else I could test it.
I hold the device and let it fall (on a soft ground) so I would expect it to trigger a free fall.
Do you mean there have to be some other conditions (e.g. a specific orientation) or so?

Your test is reasonable. However, what does the data sheet say about how long it takes the sensor to detect and react to free fall conditions?

Note that variables shared with interrupt routines must be declared "volatile", or they may not be updated as you expect. Unless you do so, the compiler can arbitrarily choose to load a copy instead of the updated variable.

uint8_t interruptCount = 0; // Amount of received interrupts
1 Like

Thank you for your help.
Well, the interruptCount worked on the doubleTap experiment so I decided not to change anything here. But I can give it a try.

Regarding the wakeup time, I'm not sure how to configure this.
There is a register for it but I don't understand the configuration of it and what to enter there.

image

There is also the freefall register where I can select a threshold.

image

As you can see from my code I was experimenting with it setting some different values. I understood the mg means "milli g force" so basically "how much force is still ok to be interpreted as free fall". Thus a higher value (e.g. 111 for 500mg) should be better. Still nothing seems to work.

So yea, if you can help me with the interpretation of teh WAKE_UP_DUR register, please feel free.

The documentation states:

FF_DUR
Free fall duration event. Default: 0

So do I enter a value there? Like "millis to fall before it causes the event" or something? But what value would be resonable here? The documentation does not state much.

Sometimes interrupts do work as expected if you forget to declare "volatile", but other times they do not. Ignore advice at your own risk, especially since the volatile declaration has no negative consequences in this case.

The documentation of the free fall register settings in the LSM6DS3 data sheet is pathetic. You should look on the ST web site (LSM6 product pages) for an application note that gives some actually useful information. Other than that, I can only suggest to experiment.

My guess is that the duration is "minimum time for free fall to be detected before interrupt is triggered".

I don't see where you are enabling the free fall interrupt. See registers MD1_CFG and MD2_CFG.

I doubt you need to enable the gyro for free fall detection.

1 Like

Thanks for the reply. I tried to fix things but still can't make it work. Volatile did not help either sadly.

I even tried to fix the example code from the library but it also does not give any response.

Any idea what might be wrong with that one perhaps?

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

#define CLEAR_STEP      true
#define NOT_CLEAR_STEP  false

//Create a instance of class LSM6DS3
LSM6DS3 lsm6ds3(I2C_MODE, 0x6A);    //I2C device address 0x6A
uint16_t detectCount = 0;

void setup() {
    Serial.begin(9600);
    while (!Serial);
    if (lsm6ds3.begin() != 0) {
        Serial.println("Device error");
    } else {
        Serial.println("Device OK!");
    }

    if (0 != config_free_fall_detect()) {
        Serial.println("Fail to configure!");
    } else {
        Serial.println("Success to Configure!");
    }
}

void loop() {
    uint8_t readDataByte = 0;
    //Read the wake-up source register
    lsm6ds3.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);
}

int config_free_fall_detect(void) {
    uint8_t error = 0;
    uint8_t dataToWrite = 0;

    dataToWrite |= LSM6DS3_ACC_GYRO_BW_XL_200Hz;
    dataToWrite |= LSM6DS3_ACC_GYRO_FS_XL_2g;
    dataToWrite |= LSM6DS3_ACC_GYRO_ODR_XL_416Hz;

    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, dataToWrite);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_DUR, 0x00);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_FREE_FALL, 0x33);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x10);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_MD2_CFG, 0x10);
    error += lsm6ds3.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x01);

    return error;
}

Hi there, So I figured it out...What you forgot in the code you used.
Kinda funny too.
I'll post the solution over on the other Forum and you can repost the fix here. Please do it's a good lesson to all of us wanna be coders. :wink:
HTH
GL :slight_smile: PJ

here is the output when I toss it in the air.

Device OK!
Success to Configure!
Free fall detected!  1
Free fall detected!  2
Free fall detected!  3
Free fall detected!  4
Free fall detected!  5
Free fall detected!  6
Free fall detected!  7
Free fall detected!  8
Free fall detected!  9
Free fall detected!  10
1 Like

Thanks again for your hint.

So for anyone else having this issue you may have a look here

for additional info. But in short you need to enable the TIMER_EN flag in the TAP_CFG register.

I added a pull request in the sample code of seeedstudio. Once this is accepted the examples from their website should work again!

Thanks to everyone for your support!

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