Skip to content

Fall Detection Documentation

Oskar Öberg edited this page Sep 3, 2026 · 37 revisions

This document explains how fall detection works on Flic Duo and how to use it from flic2lib.

Abstract

Fall detection continuously monitors Flic Duo's built-in accelerometer for the broad pattern of a fall: a low-G period that indicates falling, followed by a high-G impact. When the configured conditions are met, the SDK reports state updates through the button delegate and delivers accelerometer data for the fall event.

License

Note that commercial use of the Fall Detection feature requires a valid license.

To purchase a license, please contact sales.

Compatibility

Fall Detection is only supported on Flic Duo.

Assets

The Fall Detection feature can be enabled through flic2lib found here:

iOS:

https://github.com/50ButtonsEach/flic2lib-ios

Android:

https://github.com/50ButtonsEach/flic2lib-android

A sample project demonstrating how to implement the Fall Detection feature can be found here:

https://github.com/50ButtonsEach/flic-duo-features-sample-ios

How the Algorithm Works

Fall detection is threshold-based. The device watches the acceleration magnitude and moves through these phases:

  1. The Duo enters the low-G state when the average acceleration magnitude stays below lowGThresholdMg for at least lowGDurationMs.
  2. Once in the low-G state, the Duo waits for an impact. The impact must happen within highGTimeoutMs.
  3. The impact is accepted when the acceleration magnitude stays at or above highGThresholdMg for highGTimeWindowMs. This smoothing window filters out very short spikes while still accepting impact-like motion.
  4. When the impact condition is met, fall detection is triggered and the SDK sends FLICButtonFallDetectionStateTriggered.
  5. The SDK then collects accelerometer data from before the impact and reports FLICButtonFallDetectionStatePreFallDataCollected when that pre-fall data is available.
  6. The Duo continues recording after the trigger for postEventRecordDurationMs, then the SDK reports FLICButtonFallDetectionStateCompleted when both pre-fall and post-fall data has been collected.

highGTimeoutMs is the transition window between the falling phase and impact. A longer timeout can detect falls where the impact is delayed or less abrupt, but it can also increase the probability of false positives.

Graph over the configuration parameters for Fall Detection

image

Running Fall Detection Indefinitely

To keep the Fall Detection feature running indefinitely it needs to be re-enabled in each buttonIsReady callback. Since the Fall Detection feature requires the connection to be active, combining it with alwaysReconnect=true and re-enabling the Fall Detection configuration on each buttonIsReady will ensure that Fall Detection is running indefinitely.

Important Disclaimer about Swipe Killing

On iOS it is important to inform users not so swipe-kill your application, as this is considered by iOS that you are no longer interested in events for this application, thus the app will not be woken up by either a Fall Detected event or a button press.

Wearing the Duo

Fall detection performs best when the Duo is worn on a lanyard around the neck. The lanyard should not be too loose, and the Duo should preferably be worn under the shirt. This keeps the device close to the body and reduces extra movement that can affect fall detection.

image

Battery Impact

Fall detection starts and uses the accelerometer continuously while it is active, so it affects battery performance. Using alwaysReconnect: true can also increase battery consumption because the Duo will keep advertising for reconnects after a lost connection. This is recommended for fall detection reliability, but apps should account for the battery tradeoff.

Enabling Fall Detection

Enable fall detection after the button is connected and ready, for example from buttonIsReady(_:). Do this every time the button becomes ready because the fall detection configuration is only valid for one session.

import flic2lib

func buttonIsReady(_ flicButton: FLICButton) {
    let config = FLICButtonFallDetectionConfig(
        lowGThresholdMg: 700,
        lowGDurationMs: 500,
        highGTimeoutMs: 650,
        highGThresholdMg: 3500,
        highGTimeWindowMs: 50,
        postEventRecordDurationMs: 2000,
        fullScaleSelection: .fourG
    )

    flicButton.enableFallDetection(with: config, alwaysReconnect: true) { result in
        if result != .success {
            print("Failed to enable fall detection: \(result)")
        }
    }
}

Use alwaysReconnect: true for fall detection. This enables always-reconnect advertising, which tells the device to always try to reconnect, even if no button is pressed. This is required since fall detection event can happen without a button press, which is normally the event which starts the connection attempt.

Firmware Check

Since most Duos will not come with the Fall Detection firmware installed, it is important to handle Duos that has not yet updated to the new firmware. To ensure a good user experience, please check the firmwareRevision property on FLICButton and make sure it is at or above 20. If the phone has access to the internet, the firmware is already being downloaded at this point so just make sure to refresh the UI on the next connect.

Suggested Configuration

This can be seen as a medium sensitive configuration to detect falls but reject everyday activities like jogging or light jumping.

Parameter Value
lowGThresholdMg 700
lowGDurationMs 500
highGTimeoutMs 650
highGThresholdMg 3500
highGTimeWindowMs 50
postEventRecordDurationMs 2000
fullScaleSelection .fourG
alwaysReconnect true

In Swift, these values map to FLICButtonFallDetectionConfig as:

let config = FLICButtonFallDetectionConfig(
    lowGThresholdMg: 700,
    lowGDurationMs: 500,
    highGTimeoutMs: 650,
    highGThresholdMg: 3500,
    highGTimeWindowMs: 50,
    postEventRecordDurationMs: 2000,
    fullScaleSelection: .fourG
)

Configuration fields:

Property Meaning
lowGThresholdMg Acceleration magnitude threshold, in mg, used to enter the low-G state.
lowGDurationMs Minimum time the average acceleration magnitude must remain below lowGThresholdMg.
highGTimeoutMs Maximum time between entering the low-G state and detecting impact. Longer values allow more transition time but can increase false positives.
highGThresholdMg Acceleration magnitude threshold, in mg, used to detect impact.
highGTimeWindowMs Time window where acceleration must stay at or above highGThresholdMg for the impact to be accepted.
postEventRecordDurationMs Duration, in milliseconds, to keep recording accelerometer samples after a fall has been triggered.
fullScaleSelection Accelerometer full-scale range selection used while fall detection is active. Use .twoG, .fourG, .eightG, or .sixteenG.

Receiving Fall Detection Updates

Fall detection updates are reported to the button delegate:

func button(_ button: FLICButton, didUpdateFallDetection event: FLICButtonFallDetectionEvent)

FLICButtonFallDetectionEvent contains the current state and any accelerometer data collected so far:

class FLICButtonFallDetectionEvent: NSObject {
    var state: FLICButtonFallDetectionState { get }
    var preFallSampleRate: UInt16 { get }
    var preFallExpectedSampleCount: UInt16 { get }
    var preFallAccelerometerData: FLICButtonAccelerometerData { get }
    var postFallSampleRate: UInt16 { get }
    var postFallExpectedSampleCount: UInt16 { get }
    var postFallAccelerometerData: FLICButtonAccelerometerData { get }
}

The event state is one of:

enum FLICButtonFallDetectionState {
    case triggered
    case preFallDataCollected
    case completed
    case disabled
}

The states update as data is collected:

State Meaning
.triggered A high impact was detected after the low-G condition. Treat this as a potential fall.
.preFallDataCollected The accelerometer data up until the impact has been downloaded and is available in preFallAccelerometerData.
.completed Both pre-fall and post-fall accelerometer data has been collected.
.disabled Fall detection was disabled before the current collection finished.

Example delegate handling with buzzer feedback:

func button(_ button: FLICButton, didUpdateFallDetection event: FLICButtonFallDetectionEvent) {
    switch (event.state) {
    case .triggered:
        let notes = [
            FLICButtonBuzzerNote(hz: 2200, duration: 200),
            FLICButtonBuzzerNote(hz: 0, duration: 100),
            FLICButtonBuzzerNote(hz: 2200, duration: 200),
        ]
        button.playBuzzerSound(notes)

    case .preFallDataCollected:
        print(
            "Pre-fall samples: \(event.preFallAccelerometerData.points.count)/" +
            "\(event.preFallExpectedSampleCount) at \(event.preFallSampleRate) Hz"
        )

    case .completed:
        print(
            "Post-fall samples: \(event.postFallAccelerometerData.points.count)/" +
            "\(event.postFallExpectedSampleCount) at \(event.postFallSampleRate) Hz"
        )

    case .disabled:
        print("Fall detection disabled")

    @unknown default:
        break
    }
}

It is recommended to play a buzzer sound when .triggered is received. This alerts the user immediately and gives them an opportunity to cancel your app's alert or escalation flow before post-fall recording completes.

Filtering out False Positives

Minimizing false positives can be done in a few different steps. What we recommend is to configure the device to be a bit more sensitive than required, to avoid false negatives, and to examine the accelerometer data to try to rule out false alerts.

One important aspect is to give the user the option to cancel the alert. We strongly recommend using the Buzzer to notify the user that a fall event is detected and give the user some time to cancel the event.

When analyzing the accelerometer data, we recommend looking for the following patterns to determine the severity of the incident.

  1. If the post-event data is showing little or no movement, this can mean that the person has passed out
  2. Comparing the initial orientation with the last orientation can determine if the person was standing up before the event, and lying down at the end of the event. This can be used to distinguish a person (violently) sitting down in a couch and someone laying on the ground. Note that the way the person is waring the device will affect what patterns to look for.

Disabling Fall Detection

Call disableFallDetection(_:) when the app no longer needs fall detection:

button.disableFallDetection(true)

Pass true if you also want to disable always-reconnect advertising that was enabled for fall detection. This is recommended for normal operation when not using Fall Detection.

Important Limitations and Disclaimer

Fall detection should be thoroughly tested in the customer’s intended real-world environment before deployment. The configuration may need to be calibrated for the specific use case, wearer, placement, activity pattern, and response process. Customers should review and analyze the accelerometer data and event behavior from the device to determine what action is appropriate for their application.

Fall detection should be treated as a complement to a personal alarm button, not as a replacement for it. Pressing the button remains the primary and most reliable way for a user to communicate that they need help. Fall detection is intended to provide an additional signal in situations where the user may be unable to press the button.

Fall detection may fail to trigger, may trigger late, or may produce false positives. Real-world performance can be affected by many factors, including configuration parameters, how the Duo is worn, Bluetooth range, radio interference, phone operating system behavior, lost connections, battery level, and whether the host device is available and running correctly.

Shortcut Labs AB does not guarantee that fall detection will detect every fall or emergency. The customer is responsible for validating the feature, setting suitable parameters, interpreting the data, and deciding how their application should respond. Shortcut Labs accepts no responsibility for harm, loss, or damage resulting from a fall detection event not being triggered, being delayed, or being interpreted incorrectly.

Contact

For questions and tech support, please use this form:

https://flic.io/business#contact

Clone this wiki locally