MEMS Accelerometer Free Fall Detection

Micro Electro Mechanical Systems (MEMS) accelerometers have been available since the 1990’s. MEMS creates a microscopic mechanical sensing structures that is coupled with microelectronic circuits to measure physical parameters such as acceleration. One of the first applications of the silicon based MEMS accelerometer was to protect hard drives (HDD) media when being dropped while rotating. When free fall was detected the HDD head would be moved to a safe zone to protect the media.

When a MEMS accelerometer is in free fall all acceleration values move towards zero. The figures below help to understand why this is true (Figures from Physical StackExchange). When in free fall the frame and mass are both affected by the same acceleration so the force acting on the spring nears zero. For a MEMS accelerometer the measured acceleration nears zero.

An inexpensive MEMS accelerometer is the LIS3DH. This unit is a 3-axis accelerometer with scalable ranges (±2g/±4g/±8g/±16g), supports multiple data rates, is low power, has three 10-bit ADCs, and interfaces over I2C or SPI. This accelerometer can also generate interrupts for tap, double-tap, orientation changes, and freefall detection. These accelerometers are available on breakout boards from multiple vendor such as Adafruit and Sparkfun.

To configure the LIS3DH for free fall detection a threshold level in g’s and duration are programmed in device registers. The design tip DT0100 shows an example of setting the level to ~350mg with a duration of 30ms. For the interrupt to occur a level of 350mg or less must be detected on all three axis accelerometers for a minimum of 30ms before the interrupt occurs as shown in the figure below from the ST design note.

For this example I used an Adafruit LIS3DH breakout board. Adafruit does a nice job of providing libraries for their products. The Adafruit LIS3DH library unfortunately does not provide a free fall interrupt method. Normally I would inherit the library object and create my own methods but the library made key communication variables private, so this wasn’t an option without modifying the library. My client’s application did not required any other processing so I created code to replicate the free fall interrupt function in the Arduino loop().

The code uses the Adafruit sensors_event_t type, which returns the accelerometer values in m/s2. To match the design tip the free fall threshold would be set to 3.432 m/s2 (1g = 9.08665 m/s2). With a free fall threshold of 350mg, I found it too easy to enter free fall so I reduced the value to about 50mg, which worked well. When all three axis meet the free fall detection threshold, the free fall duration is checked. If the amount of time exceeds the free fall duration then a LED is turned on (the client turned a servo motor) indicating the unit is in free fall. If the free fall threshold isn’t met, then the duration time is updated to the current time using millis().

Below is the sample code emulating the LIS3DH free fall detection.

// Free Fall Detection
// Adafruit LIS3DH Accelerometer with SPI
// Adapted from Adafruit library acceldemo.ino

#include <SPI.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>

// Hardware Pins
#define LIS3DH_CS 10  // SPI device select
#define LED_PIN   4   // LED output pin to indicate freefall active high

// Operating Parameters
#define FREEFALL_ACCEL_THRESHOLD  0.5 // freefall threshold in m/s^2, 1g = 9.80665 m/s^2
#define FREEFALL_TIME_THRESHOLD   30  // freefall time threshold in ms

// hardware SPI for accelerometer
Adafruit_LIS3DH lis = Adafruit_LIS3DH(LIS3DH_CS); // accel object
sensors_event_t event;                            // sensor data to evaluate for freefall

// timer for free fall
uint32_t freeFallTime;           // timer for consecutive freefall detections

void setup(void) {
  while (!Serial) delay(10);     // will pause Zero, Leonardo, etc until serial console opens

  // setup output control pin for LED
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  if (! lis.begin()) {
    Serial.println("Couldnt start");
    while (1) yield();

  lis.setRange(LIS3DH_RANGE_2_G);           // set to 2G range
  lis.setDataRate(LIS3DH_DATARATE_400_HZ);  // sample at fastest rate (2.5ms)

  Serial.println("Free Fall Detection with LIS3DH");

  freeFallTime = millis();

void loop() {

  lis.getEvent(&event);   // get latest data

  // detect free fall
  if(abs(event.acceleration.x) <= FREEFALL_ACCEL_THRESHOLD &&
     abs(event.acceleration.y) <= FREEFALL_ACCEL_THRESHOLD &&
     abs(event.acceleration.z) <= FREEFALL_ACCEL_THRESHOLD) {

    if((millis() - freeFallTime) >= FREEFALL_TIME_THRESHOLD) {
      digitalWrite(LED_PIN, HIGH);
  else { // not in freefall reset timer
    freeFallTime = millis();