Fall detection logic

Hi, I'm working on this project: fall detection for the elderly. The basic concept is that when an elderly person falls, the watch detects the fall and sends a message to the caretaker. This part focuses on fall detection. The attached code is the code for fall detection. I can't simulate the fall scenario with this logic. Please advise on the logic written. thank you.

#include <Wire.h>         // I2C communication  
#include "BMA250.h"       // Accelerometer library
#include <TinyScreen.h>   // TinyScreen library


// Accelerometer variables
BMA250 accel_sensor;
int x, y, z;
float accelMagnitude;

// Fall detection thresholds
float impactThreshold = 2.5;
float freeFallThreshold = 0.5;
float recoveryThreshold = 1.0;
unsigned long recoveryDelay = 1000;

// Fall detection states
enum FallState { IDLE, IMPACT_DETECTED, FREE_FALL_DETECTED };
FallState fallState = IDLE;

unsigned long impactTime = 0;
unsigned long freeFallTime = 0;

// Setup Serial for different boards
#if defined(ARDUINO_ARCH_SAMD)
TinyScreen display = TinyScreen(TinyScreenDefault);
#define SerialMonitorInterface SerialUSB
#else
TinyScreen display = TinyScreen(TinyScreenDefault);
#define SerialMonitorInterface Serial
#endif

const FONT_INFO& font10pt = thinPixel7_10ptFontInfo;

void setup() {
    SerialMonitorInterface.begin(115200);
    Wire.begin();

    SerialMonitorInterface.println("Initializing BMA250...");
    accel_sensor.begin(BMA250_range_2g, BMA250_update_time_64ms);

    // Initialize TinyScreen
    display.begin();
    display.setBrightness(15);   // Adjust brightness (0-15)
    display.setFlip(true);       // Adjust orientation if necessary
    display.clearScreen();
}

void loop() {
    accel_sensor.read();  // Read accelerometer data
    x = accel_sensor.X;
    y = accel_sensor.Y;
    z = accel_sensor.Z;

    if (x == -1 && y == -1 && z == -1) {
        SerialMonitorInterface.println("ERROR: Invalid data.");
        return;
    }

    accelMagnitude = sqrt(sq(x) + sq(y) + sq(z));  // Calculate magnitude

    switch (fallState) {
        case IDLE:
            if (accelMagnitude > impactThreshold) {
                SerialMonitorInterface.println("Impact detected!");
                impactTime = millis();
                fallState = IMPACT_DETECTED;
            }
            break;

        case IMPACT_DETECTED:
            if (accelMagnitude < freeFallThreshold) {
                SerialMonitorInterface.println("Free fall detected!");
                freeFallTime = millis();
                fallState = FREE_FALL_DETECTED;
            } else if (millis() - impactTime > recoveryDelay) {
                SerialMonitorInterface.println("False alarm, resetting to IDLE.");
                fallState = IDLE;
            }
            break;

        case FREE_FALL_DETECTED:
            if (accelMagnitude < recoveryThreshold && (millis() - freeFallTime) > 500) {
                SerialMonitorInterface.println("Fall confirmed!");
                displayFallDetected();  // Show message on TinyScreen
                fallState = IDLE;
            } else if (millis() - freeFallTime > recoveryDelay) {
                SerialMonitorInterface.println("False alarm, resetting to IDLE.");
                fallState = IDLE;
            }
            break;
    }
    
    showSerial();  // Debugging output
    delay(250);    // Adjust delay as necessary
}

void displayFallDetected() {
    display.clearScreen();
    display.setFont(font10pt);  // Choose a suitable font
    display.setCursor(10, 20);          // Set position (x, y)
    display.print("Fall Detected!");            // Refresh the screen to show the message
}

void showSerial() {
    SerialMonitorInterface.print("X = ");
    SerialMonitorInterface.print(x);
    SerialMonitorInterface.print("  Y = ");
    SerialMonitorInterface.print(y);
    SerialMonitorInterface.print("  Z = ");
    SerialMonitorInterface.print(z);
    SerialMonitorInterface.print("  Accel Magnitude = ");
    SerialMonitorInterface.println(accelMagnitude);
}

Background information:
BMA250: TinyCircuits-BMA250-library
I'm using TinyZero Processor Board
TinyScreen TinyShield for display

Review the logic in the switch/case series. It doesn't make much sense to me. I suggest to discard it and test the value of accelMagnitude directly.

As discussed in the BMA250 data sheet, the sensor itself is capable of free fall and shock detection (most modern accelerometers do).

1 Like
#include <Wire.h>         // I2C communication  
#include "BMA250.h"       // Accelerometer library
#include <TinyScreen.h>   // TinyScreen library


// Accelerometer variables
BMA250 accel_sensor;
int x, y, z;
float accelMagnitude;

// Fall detection thresholds
float impactThreshold = 2.5;
float freeFallThreshold = 0.5;
float recoveryThreshold = 1.0;
unsigned long recoveryDelay = 1000;

// Fall detection states
enum FallState { IDLE, IMPACT_DETECTED, FREE_FALL_DETECTED };
FallState fallState = IDLE;

unsigned long impactTime = 0;
unsigned long freeFallTime = 0;

// Setup Serial for different boards
#if defined(ARDUINO_ARCH_SAMD)
TinyScreen display = TinyScreen(TinyScreenDefault);
#define SerialMonitorInterface SerialUSB
#else
TinyScreen display = TinyScreen(TinyScreenDefault);
#define SerialMonitorInterface Serial
#endif

const FONT_INFO& font10pt = thinPixel7_10ptFontInfo;

void setup() {
    SerialMonitorInterface.begin(115200);
    Wire.begin();

    SerialMonitorInterface.println("Initializing BMA250...");
    accel_sensor.begin(BMA250_range_2g, BMA250_update_time_64ms);

    // Initialize TinyScreen
    display.begin();
    display.setBrightness(15);   // Adjust brightness (0-15)
    display.setFlip(true);       // Adjust orientation if necessary
    display.clearScreen();
}

void loop() {
    // Read accelerometer data
    accel_sensor.read();
    x = accel_sensor.X;
    y = accel_sensor.Y;
    z = accel_sensor.Z;

    // Handle invalid data
    if (x == -1 && y == -1 && z == -1) {
        SerialMonitorInterface.println("ERROR: Invalid data.");
        return;
    }

    // Calculate acceleration magnitude
    accelMagnitude = sqrt(sq(x) + sq(y) + sq(z)) / 256.0; // Normalize to g

    // Log the current readings
    showSerial();

    // Directly test the acceleration magnitude
    if (accelMagnitude > impactThreshold) {
        SerialMonitorInterface.println("Impact detected!");
    } else if (accelMagnitude < freeFallThreshold) {
        SerialMonitorInterface.println("Free fall detected!");
        displayFallDetected();
    } else if (accelMagnitude < recoveryThreshold) {
        SerialMonitorInterface.println("Recovering...");
    } else {
        SerialMonitorInterface.println("No fall detected.");
    }

    delay(250); // Delay to manage loop speed
}


void displayFallDetected() {
    display.clearScreen();
    display.setFont(font10pt);  // Choose a suitable font
    display.setCursor(10, 20);          // Set position (x, y)
    display.print("Fall Detected!");            // Refresh the screen to show the message
}

void showSerial() {
    SerialMonitorInterface.print("X = ");
    SerialMonitorInterface.print(x);
    SerialMonitorInterface.print("  Y = ");
    SerialMonitorInterface.print(y);
    SerialMonitorInterface.print("  Z = ");
    SerialMonitorInterface.print(z);
    SerialMonitorInterface.print("  Accel Magnitude = ");
    SerialMonitorInterface.println(accelMagnitude);
}

is this ok? I'm still confused how to set the Fall detection thresholds. when boards is on the table, it reads recovering. if you shake the the board it displays free detected. please advice on this. thank you.

I'm still confused how to set the Fall detection thresholds.

So am I. What is the reasoning behind or evidence for these mystery numbers? What units do they have?

// Fall detection thresholds
float impactThreshold = 2.5;
float freeFallThreshold = 0.5;
float recoveryThreshold = 1.0;

Check the sensor data sheet to see what thresholds the sensor uses for fall and impact detection.

Have you done any experiments to determine what the sensor library reports during free fall and impact?

impactThreshold (2.5g): Detects a sudden impact or shock.
freeFallThreshold (0.5g): Detects free fall with minimal acceleration.
recoveryThreshold (1.0g): Ensures the device is stable after a fall.

#include <Wire.h>         // I2C communication  
#include "BMA250.h"       // Accelerometer library
#include <TinyScreen.h>   // TinyScreen library

// Accelerometer variables
BMA250 accel_sensor;
int x, y, z;
float accelMagnitude;

// Fall detection thresholds
float impactThreshold = 2.5;
float freeFallThreshold = 0.5;
float recoveryThreshold = 1.0;

// Flag to halt accelerometer reading after fall
bool fallDetected = false;

// Setup Serial for different boards
#if defined(ARDUINO_ARCH_SAMD)
TinyScreen display = TinyScreen(TinyScreenDefault);
#define SerialMonitorInterface SerialUSB
#else
TinyScreen display = TinyScreen(TinyScreenDefault);
#define SerialMonitorInterface Serial
#endif

const FONT_INFO& font10pt = thinPixel7_10ptFontInfo;

void displayFallDetected();
void showSerial();

void setup() {
    SerialMonitorInterface.begin(115200);
    Wire.begin();

    SerialMonitorInterface.println("Initializing BMA250...");
    accel_sensor.begin(BMA250_range_2g, BMA250_update_time_64ms);

    // Initialize TinyScreen
    display.begin();
    display.setBrightness(15);   // Adjust brightness (0-15)
    display.setFlip(true);       // Adjust orientation if necessary
    display.clearScreen();
}

void loop() {
    if (fallDetected) {
        SerialMonitorInterface.println("Fall detected! Stopping accelerometer.");
        displayFallDetected();

        // Check for user response
        if (display.getButtons(TSButtonLowerLeft)) { // Yes (help required)
            SerialMonitorInterface.println("Yes, need help!");
            display.clearScreen();
            display.setFont(font10pt);
            display.setCursor(10, 10);
            display.print("Sending Help...");
        } 
        if (display.getButtons(TSButtonLowerRight)) { // No (cancel)
            SerialMonitorInterface.println("No help needed.");
            display.clearScreen();
            display.setFont(font10pt);
            display.setCursor(10, 10);
            display.print("Help Canceled.");
            fallDetected = false;
            display.clearScreen();
            return;
        }

        delay(10000); // Wait for 10 seconds

        // Restart accelerometer after timeout
        SerialMonitorInterface.println("Restarting accelerometer...");
        accel_sensor.begin(BMA250_range_2g, BMA250_update_time_64ms);
        fallDetected = false;
        display.clearScreen();
        return;
    }

    // Read accelerometer data
    accel_sensor.read();
    x = accel_sensor.X;
    y = accel_sensor.Y;
    z = accel_sensor.Z;

    // Handle invalid data
    if (x == -1 && y == -1 && z == -1) {
        SerialMonitorInterface.println("ERROR: Invalid data.");
        return;
    }

    // Calculate acceleration magnitude
    accelMagnitude = sqrt(sq(x) + sq(y) + sq(z)) / 256.0;

    // Display readings
    showSerial();

    // Check thresholds for fall detection
    if (accelMagnitude > impactThreshold) {
        SerialMonitorInterface.println("Impact detected!");
        fallDetected = true;
    } else if (accelMagnitude < freeFallThreshold) {
        SerialMonitorInterface.println("Free fall detected!");
        fallDetected = true;
    } else if (accelMagnitude < recoveryThreshold) {
        SerialMonitorInterface.println("Recovering...");
    } else {
        SerialMonitorInterface.println("No fall detected.");
    }

    delay(250); // Delay to manage loop speed
}

void displayFallDetected() {
    display.clearScreen();
    display.setFont(font10pt);
    display.setCursor(10, 10);
    display.print("Fall Detected!");
    display.setCursor(10, 20);
    display.print("Do you need help?");
    display.setCursor(0, 45);
    display.print("-> Yes");
    display.setCursor(70, 45);
    display.print("No <-");
}

void showSerial() {
    SerialMonitorInterface.print("X = ");
    SerialMonitorInterface.print(x);
    SerialMonitorInterface.print("  Y = ");
    SerialMonitorInterface.print(y);
    SerialMonitorInterface.print("  Z = ");
    SerialMonitorInterface.print(z);
    SerialMonitorInterface.print("  Accel Magnitude = ");
    SerialMonitorInterface.println(accelMagnitude);
}

this code currently detects fall and then displays "Fall Detected! Do you need help? Yes No". if we click yes it displays "Sending help" if no, goes back to normal. problem now is when I cleck the button, it doesn't read the input. no reaction when the button is pressed. i have set timer to 10s, after it goes back to normal. Please advice on this. thank you.

Is this code you have got from elsewhere ??

Why not just detect a sudden acceleration followed by no movement ?

I would start by logging data and then examine the result of simulated falls . Then write some code that detects this signature of a fall.

Is this a school task ?

yes, it is a project. i got a base code for accelerometer from tinycircuits. From there, i developed this using chatgpt. this is my first time doing this.

So basically you have no clue about the code.

Take a step back and read and understand the datasheet and see if you can write yourself code to use the built in capabilities to detect free fall or a shock

Apparently so, and also unable to answer reasonable questions from forum members.

1 Like

Have you studied how running and walking also fit those parameters? Or sitting down on a hard chair or bench? Or setting down on the toilet? Or falling into bed?
Will your code be able to differentiate a damaging fall from a rolling fall?

You can’t shortcut with AI and copied code - you need to know something of it , otherwise you are wasting your time and likely to give up .
If you know nothing , this is too complex as a start project .

There are few shortcuts in life …

If this a school, then what have you been taught this far ??

The OP seems to have failed in the critical thinking class. How can a watch know the difference between falling and hammering a nail?

Through AI/ML training. Sometimes it does ask you if you fell though,

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