fix(firmware): fall detection false positives + 4MB flash support (#263, #265)

Issue #263: Default fall_thresh raised from 2.0 to 15.0 rad/s² — normal
walking produces accelerations of 2.5-5.0 which triggered constant false
"Fall Detected" alerts. Added consecutive-frame requirement (3 frames)
and 5-second cooldown debounce to prevent alert storms.

Issue #265: Added partitions_4mb.csv and sdkconfig.defaults.4mb for
ESP32-S3 boards with 4MB flash (e.g. SuperMini). OTA slots are 1.856MB
each, fitting the ~978KB firmware binary with room to spare.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv 2026-03-15 09:44:23 -04:00
parent 1d4af7c757
commit 8afd76da20
6 changed files with 79 additions and 7 deletions

View File

@ -244,6 +244,10 @@ static uint32_t s_frame_count;
/** Previous phase velocity for fall detection (acceleration). */
static float s_prev_phase_velocity;
/** Fall detection debounce state (issue #263). */
static uint8_t s_fall_consec_count; /**< Consecutive frames above threshold. */
static int64_t s_fall_last_alert_us; /**< Timestamp of last fall alert (debounce). */
/** Adaptive calibration state. */
static bool s_calibrated;
static float s_calib_sum;
@ -689,7 +693,7 @@ static void process_frame(const edge_ring_slot_t *slot)
}
s_presence_detected = (s_presence_score > threshold);
/* --- Step 10: Fall detection (phase acceleration) --- */
/* --- Step 10: Fall detection (phase acceleration + debounce, issue #263) --- */
if (s_history_len >= 3) {
uint16_t i0 = (s_history_idx + EDGE_PHASE_HISTORY_LEN - 1) % EDGE_PHASE_HISTORY_LEN;
uint16_t i1 = (s_history_idx + EDGE_PHASE_HISTORY_LEN - 2) % EDGE_PHASE_HISTORY_LEN;
@ -697,10 +701,26 @@ static void process_frame(const edge_ring_slot_t *slot)
float accel = fabsf(velocity - s_prev_phase_velocity);
s_prev_phase_velocity = velocity;
s_fall_detected = (accel > s_cfg.fall_thresh);
if (s_fall_detected) {
ESP_LOGW(TAG, "Fall detected! accel=%.4f > thresh=%.4f",
accel, s_cfg.fall_thresh);
if (accel > s_cfg.fall_thresh) {
s_fall_consec_count++;
} else {
s_fall_consec_count = 0;
}
/* Require EDGE_FALL_CONSEC_MIN consecutive frames above threshold,
* plus a cooldown period to prevent alert storms. */
int64_t now_us = esp_timer_get_time();
int64_t cooldown_us = (int64_t)EDGE_FALL_COOLDOWN_MS * 1000;
if (s_fall_consec_count >= EDGE_FALL_CONSEC_MIN
&& (now_us - s_fall_last_alert_us) >= cooldown_us)
{
s_fall_detected = true;
s_fall_last_alert_us = now_us;
s_fall_consec_count = 0;
ESP_LOGW(TAG, "Fall detected! accel=%.4f > thresh=%.4f (consec=%u)",
accel, s_cfg.fall_thresh, EDGE_FALL_CONSEC_MIN);
} else if (s_fall_consec_count == 0) {
s_fall_detected = false;
}
}
@ -850,6 +870,8 @@ esp_err_t edge_processing_init(const edge_config_t *cfg)
s_latest_rssi = 0;
s_frame_count = 0;
s_prev_phase_velocity = 0.0f;
s_fall_consec_count = 0;
s_fall_last_alert_us = 0;
s_last_vitals_send_us = 0;
s_has_prev_iq = false;
s_prev_iq_len = 0;

View File

@ -42,6 +42,10 @@
#define EDGE_CALIB_FRAMES 1200 /**< Frames for adaptive calibration (~60s at 20 Hz). */
#define EDGE_CALIB_SIGMA_MULT 3.0f /**< Threshold = mean + 3*sigma of ambient. */
/* ---- Fall detection ---- */
#define EDGE_FALL_COOLDOWN_MS 5000 /**< Minimum ms between fall alerts (debounce). */
#define EDGE_FALL_CONSEC_MIN 3 /**< Consecutive frames above threshold to trigger. */
/* ---- SPSC ring buffer slot ---- */
typedef struct {
uint8_t iq_data[EDGE_MAX_IQ_BYTES]; /**< Raw I/Q bytes from CSI callback. */

View File

@ -61,7 +61,7 @@ void nvs_config_load(nvs_config_t *cfg)
#ifdef CONFIG_EDGE_FALL_THRESH
cfg->fall_thresh = (float)CONFIG_EDGE_FALL_THRESH / 1000.0f;
#else
cfg->fall_thresh = 2.0f;
cfg->fall_thresh = 15.0f; /* Default raised from 2.0 — see issue #263. */
#endif
cfg->vital_window = 256;
#ifdef CONFIG_EDGE_VITAL_INTERVAL_MS

View File

@ -0,0 +1,15 @@
# ESP32-S3 CSI Node — 4MB flash partition table (issue #265)
# For boards with 4MB flash (e.g. ESP32-S3 SuperMini 4MB).
# Binary is ~978KB so each OTA slot is 1.875MB — plenty of room.
#
# Usage: copy to partitions_display.csv OR set in sdkconfig:
# CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_4mb.csv"
# CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
# CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
#
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,
otadata, data, ota, 0xF000, 0x2000,
phy_init, data, phy, 0x11000, 0x1000,
ota_0, app, ota_0, 0x20000, 0x1D0000,
ota_1, app, ota_1, 0x1F0000, 0x1D0000,
Can't render this file because it contains an unexpected character in line 6 and column 44.

View File

@ -168,7 +168,9 @@ def main():
parser.add_argument("--edge-tier", type=int, choices=[0, 1, 2],
help="Edge processing tier: 0=off, 1=stats, 2=vitals")
parser.add_argument("--pres-thresh", type=int, help="Presence detection threshold (default: 50)")
parser.add_argument("--fall-thresh", type=int, help="Fall detection threshold (default: 500)")
parser.add_argument("--fall-thresh", type=int, help="Fall detection threshold in milli-units "
"(value/1000 = rad/s²). Default: 15000 → 15.0 rad/s². "
"Raise to reduce false positives in high-traffic areas.")
parser.add_argument("--vital-win", type=int, help="Phase history window in frames (default: 300)")
parser.add_argument("--vital-int", type=int, help="Vitals packet interval in ms (default: 1000)")
parser.add_argument("--subk-count", type=int, help="Top-K subcarrier count (default: 32)")

View File

@ -0,0 +1,29 @@
# ESP32-S3 CSI Node — 4MB Flash SDK Configuration (issue #265)
# For boards with 4MB flash (e.g. ESP32-S3 SuperMini 4MB).
#
# Build: cp sdkconfig.defaults.4mb sdkconfig.defaults && idf.py set-target esp32s3 && idf.py build
# Or: idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults.4mb" set-target esp32s3 && idf.py build
CONFIG_IDF_TARGET="esp32s3"
# 4MB flash partition table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_4mb.csv"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
# Compiler: optimize for size (critical for 4MB)
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
# CSI support
CONFIG_ESP_WIFI_CSI_ENABLED=y
# Disable display support to save flash (ADR-045 display requires 8MB)
# CONFIG_DISPLAY_ENABLE is not set
# Reduce logging to save flash
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
CONFIG_LWIP_SO_RCVBUF=y
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192