Skip to content

Commit 725aeb8

Browse files
committed
fw: Add Motion Sensitivity setting and LSM6DSO mapping
Add an Asterix-only settings menu and preference (0-100%, default 100%) to control accelerometer shake sensitivity. Expose shell prefs and UI labels, and store the value in prefs_values. Introduce accel_manager_update_sensitivity and a driver API to set percent. Driver interpolates between board low/high wake thresholds (100% = most sensitive = low threshold), clamps values, logs changes, and reconfigures shake detection immediately. Signed-off-by: Joshua Jun <joshuajun@proton.me>
1 parent 9c68fd3 commit 725aeb8

7 files changed

Lines changed: 193 additions & 6 deletions

File tree

src/fw/apps/system_apps/settings/settings_system.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "settings_factory_reset.h"
1818
#include "settings_menu.h"
19+
#include "settings_option_menu.h"
1920
#include "settings_system.h"
2021
#include "settings_window.h"
2122

@@ -75,6 +76,9 @@ enum {
7576
DebuggingItemCoreDumpNow = 0,
7677
DebuggingItemCoreDumpShortcut,
7778
DebuggingItemALSThreshold,
79+
#if PLATFORM_ASTERIX
80+
DebuggingItemMotionSensitivity,
81+
#endif
7882
DebuggingItem_Count,
7983
};
8084

@@ -464,13 +468,61 @@ static void prv_als_threshold_menu_push(SettingsSystemData *data) {
464468
app_window_stack_push(&number_window->window, animated);
465469
}
466470

471+
// Motion Sensitivity Settings (Asterix/Obelix only)
472+
/////////////////////////////
473+
#if PLATFORM_ASTERIX
474+
static const uint8_t s_motion_sensitivity_values[] = { 10, 25, 40, 55, 70, 85, 100 };
475+
476+
static const char *s_motion_sensitivity_labels[] = {
477+
i18n_noop("Very Low"),
478+
i18n_noop("Low"),
479+
i18n_noop("Medium-Low"),
480+
i18n_noop("Medium"),
481+
i18n_noop("Medium-High"),
482+
i18n_noop("High"),
483+
i18n_noop("Very High")
484+
};
485+
486+
static int prv_motion_sensitivity_get_selection_index() {
487+
const uint8_t sensitivity = shell_prefs_get_motion_sensitivity();
488+
489+
// Find closest match
490+
for (int i = 0; i < (int)ARRAY_LENGTH(s_motion_sensitivity_values); i++) {
491+
if (sensitivity <= s_motion_sensitivity_values[i]) {
492+
return i;
493+
}
494+
}
495+
return ARRAY_LENGTH(s_motion_sensitivity_values) - 1;
496+
}
497+
498+
static void prv_motion_sensitivity_menu_select(OptionMenu *option_menu, int selection, void *context) {
499+
shell_prefs_set_motion_sensitivity(s_motion_sensitivity_values[selection]);
500+
app_window_stack_remove(&option_menu->window, true /* animated */);
501+
}
502+
503+
static void prv_motion_sensitivity_menu_push(SettingsSystemData *data) {
504+
int index = prv_motion_sensitivity_get_selection_index();
505+
const OptionMenuCallbacks callbacks = {
506+
.select = prv_motion_sensitivity_menu_select,
507+
};
508+
const char *title = i18n_noop("Motion Sensitivity");
509+
settings_option_menu_push(
510+
title, OptionMenuContentType_SingleLine, index, &callbacks,
511+
ARRAY_LENGTH(s_motion_sensitivity_labels),
512+
true /* icons_enabled */, s_motion_sensitivity_labels, data);
513+
}
514+
#endif
515+
467516
// Debug options window
468517
///////////////////////
469518

470519
static const char* s_debugging_titles[DebuggingItem_Count] = {
471520
[DebuggingItemCoreDumpNow] = i18n_noop("Bug report now"),
472521
[DebuggingItemCoreDumpShortcut] = i18n_noop("Bug shortcut"),
473522
[DebuggingItemALSThreshold] = i18n_noop("ALS Threshold"),
523+
#if PLATFORM_ASTERIX
524+
[DebuggingItemMotionSensitivity] = i18n_noop("Motion Sensitivity"),
525+
#endif
474526
};
475527

476528
static void prv_debugging_draw_row_callback(GContext* ctx, const Layer *cell_layer,
@@ -497,6 +549,11 @@ static void prv_debugging_draw_row_callback(GContext* ctx, const Layer *cell_lay
497549
"%"PRIu32, current_threshold);
498550
subtitle_text = data->als_threshold_buffer;
499551
}
552+
#if PLATFORM_ASTERIX
553+
else if (cell_index->row == DebuggingItemMotionSensitivity) {
554+
subtitle_text = i18n_get(s_motion_sensitivity_labels[prv_motion_sensitivity_get_selection_index()], data);
555+
}
556+
#endif
500557
menu_cell_basic_draw(ctx, cell_layer, title, subtitle_text, NULL);
501558
}
502559

@@ -528,6 +585,11 @@ static void prv_debugging_select_callback(MenuLayer *menu_layer,
528585
case DebuggingItemALSThreshold:
529586
prv_als_threshold_menu_push(data);
530587
break;
588+
#if PLATFORM_ASTERIX
589+
case DebuggingItemMotionSensitivity:
590+
prv_motion_sensitivity_menu_push(data);
591+
break;
592+
#endif
531593
default:
532594
WTF;
533595
}

src/fw/drivers/imu/lsm6dso/lsm6dso.c

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ static uint32_t s_tap_threshold = BOARD_CONFIG_ACCEL.accel_config.double_tap_thr
9292
static bool s_fifo_in_use = false; // true when we have enabled FIFO batching
9393
static uint32_t s_last_vibe_detected = 0;
9494

95+
// User-configured sensitivity percentage (0-100), where 100 = most sensitive
96+
// Default to 100% (maximum sensitivity) to maintain current behavior
97+
static uint8_t s_user_sensitivity_percent = 100;
98+
9599
// Error tracking and recovery
96100
static uint32_t s_i2c_error_count = 0;
97101
static uint32_t s_last_successful_read_ms = 0;
@@ -657,16 +661,36 @@ static void prv_lsm6dso_configure_shake(bool enable, bool sensitivity_high) {
657661
// Duration: increase a bit to reduce spurious triggers
658662
lsm6dso_wkup_dur_set(&lsm6dso_ctx, sensitivity_high ? 0 : 1);
659663

660-
// Threshold: derive from board config; clamp into 0..63
664+
// Threshold calculation:
665+
// - Board config provides Low (15) and High (64) thresholds
666+
// - sensitivity_high flag indicates stationary mode (use low threshold for any movement)
667+
// - s_user_sensitivity_percent (0-100) controls normal mode threshold
668+
// * 100% = most sensitive = use Low threshold (15)
669+
// * 50% = medium = interpolate between Low and High (~40)
670+
// * 0% = least sensitive = use High threshold (64)
671+
661672
uint32_t raw_high = BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdHigh];
662673
uint32_t raw_low = BOARD_CONFIG_ACCEL.accel_config.shake_thresholds[AccelThresholdLow];
663-
uint32_t raw = sensitivity_high ? raw_high : raw_low;
664-
// Increase sensitivity: scale threshold down (halve). Ensure at least 2 to avoid noise storms.
665-
raw = (raw + 1) / 2; // divide by 2 rounding up
674+
uint32_t raw;
675+
676+
if (sensitivity_high) {
677+
// Stationary mode: always use low threshold for maximum sensitivity
678+
raw = raw_low;
679+
} else {
680+
// Normal mode: interpolate based on user preference
681+
// Invert the percentage: 100% sensitive = low threshold, 0% sensitive = high threshold
682+
uint32_t inverted_percent = 100 - s_user_sensitivity_percent;
683+
raw = raw_low + ((raw_high - raw_low) * inverted_percent) / 100;
684+
}
685+
686+
// Clamp to valid range
666687
if (raw > 63) raw = 63; // lsm6dso wk_ths is 6 bits
667-
// Sanity fallback if 0 (avoid constant triggers) choose very low but non-zero
668-
if (raw == 0) raw = 2;
688+
if (raw < 2) raw = 2; // Avoid noise storms with very low thresholds
689+
669690
lsm6dso_wkup_threshold_set(&lsm6dso_ctx, (uint8_t)raw);
691+
692+
PBL_LOG(LOG_LEVEL_DEBUG, "LSM6DSO: Shake threshold set to %lu (sensitivity_high=%d, user_percent=%u)",
693+
raw, sensitivity_high, s_user_sensitivity_percent);
670694
}
671695

672696
static void prv_lsm6dso_interrupt_handler(bool *should_context_switch) {
@@ -1361,3 +1385,18 @@ void lsm6dso_get_diagnostics(Lsm6dsoDiagnostics *diagnostics) {
13611385

13621386
diagnostics->state_flags = flags;
13631387
}
1388+
1389+
void lsm6dso_set_sensitivity_percent(uint8_t percent) {
1390+
if (percent > 100) {
1391+
percent = 100; // Clamp to max
1392+
}
1393+
1394+
s_user_sensitivity_percent = percent;
1395+
1396+
// Reconfigure shake detection if it's currently enabled
1397+
if (s_lsm6dso_state.shake_detection_enabled) {
1398+
prv_lsm6dso_configure_shake(true, s_lsm6dso_state.shake_sensitivity_high);
1399+
}
1400+
1401+
PBL_LOG(LOG_LEVEL_INFO, "LSM6DSO: User sensitivity set to %u percent", percent);
1402+
}

src/fw/services/common/accel_manager.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,39 @@ void analytics_external_collect_accel_samples_received(void) {
400400
AnalyticsClient_System);
401401
}
402402

403+
// Update the motion sensitivity based on user preference (0-100%)
404+
// This is called by the preferences system when the user changes the setting
405+
void accel_manager_update_sensitivity(uint8_t sensitivity_percent) {
406+
// Sensitivity mapping:
407+
// - sensitivity_percent: 0-100 where higher = more sensitive
408+
// - For Asterix with LSM6DSO, this maps to the wake-up threshold
409+
// - Lower threshold = more sensitive (triggers on smaller movements)
410+
// - Higher threshold = less sensitive (requires larger movements to trigger)
411+
//
412+
// We'll map the user's percentage to a threshold multiplier:
413+
// - 100% (most sensitive) = use Low threshold (15)
414+
// - 50% (medium) = use mid-range (~40)
415+
// - 0% (least sensitive) = use High threshold (64)
416+
417+
#if PLATFORM_ASTERIX
418+
extern void lsm6dso_set_sensitivity_percent(uint8_t percent);
419+
mutex_lock_recursive(s_accel_manager_mutex);
420+
lsm6dso_set_sensitivity_percent(sensitivity_percent);
421+
mutex_unlock_recursive(s_accel_manager_mutex);
422+
423+
PBL_LOG(LOG_LEVEL_INFO, "Motion sensitivity updated to %u percent", sensitivity_percent);
424+
#else
425+
// For other platforms, fall back to binary high/low setting
426+
bool use_high_sensitivity = (sensitivity_percent >= 50);
427+
mutex_lock_recursive(s_accel_manager_mutex);
428+
accel_set_shake_sensitivity_high(use_high_sensitivity);
429+
mutex_unlock_recursive(s_accel_manager_mutex);
430+
431+
PBL_LOG(LOG_LEVEL_INFO, "Motion sensitivity updated to %u percent (using %s sensitivity)",
432+
sensitivity_percent, use_high_sensitivity ? "high" : "normal");
433+
#endif
434+
}
435+
403436
void accel_manager_init(void) {
404437
s_accel_manager_mutex = mutex_create_recursive();
405438

@@ -417,6 +450,15 @@ void accel_manager_init(void) {
417450
prv_shake_add_subscriber_cb(PebbleTask_KernelMain);
418451

419452
analytics_external_collect_accel_xyz_delta();
453+
454+
// Apply saved motion sensitivity preference for Asterix/Obelix
455+
// Only available in normal shell (not PRF)
456+
#if (PLATFORM_ASTERIX) && !defined(RECOVERY_FW)
457+
extern uint8_t shell_prefs_get_motion_sensitivity(void);
458+
uint8_t saved_sensitivity = shell_prefs_get_motion_sensitivity();
459+
accel_manager_update_sensitivity(saved_sensitivity);
460+
PBL_LOG(LOG_LEVEL_INFO, "Initialized motion sensitivity to %u percent", saved_sensitivity);
461+
#endif
420462
}
421463

422464
static void prv_copy_accel_sample_to_accel_data(AccelDriverSample const *accel_sample,

src/fw/services/common/accel_manager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,10 @@ bool gyro_manager_run_selftest(void);
9090
// event from any small movements
9191
void accel_enable_high_sensitivity(bool high_sensitivity);
9292

93+
// Update the motion sensitivity based on user preference (0-100%)
94+
// Only available on Asterix/Obelix platforms
95+
// 100 = most sensitive, 0 = least sensitive
96+
void accel_manager_update_sensitivity(uint8_t sensitivity_percent);
97+
9398
// lightweight call to determine if the watch is idle
9499
bool accel_is_idle(void);

src/fw/shell/normal/prefs.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ static uint16_t s_backlight_intensity; // default pulled from BOARD_CONFIGs in s
7575
#define PREF_KEY_BACKLIGHT_MOTION "lightMotion"
7676
static bool s_backlight_motion_enabled = true;
7777

78+
#define PREF_KEY_MOTION_SENSITIVITY "motionSensitivity"
79+
static uint8_t s_motion_sensitivity = 100; // Default to maximum sensitivity (100%)
80+
7881
#define PREF_KEY_BACKLIGHT_AMBIENT_THRESHOLD "lightAmbientThreshold"
7982
static uint32_t s_backlight_ambient_threshold = 0; // default set from board config in shell_prefs_init()
8083

@@ -275,6 +278,24 @@ static bool prv_set_s_backlight_motion_enabled(bool *enabled) {
275278
return true;
276279
}
277280

281+
static bool prv_set_s_motion_sensitivity(uint8_t *sensitivity) {
282+
// Clamp sensitivity to 0-100 range
283+
if (*sensitivity > 100) {
284+
s_motion_sensitivity = 100; // Reset to default if invalid
285+
return false;
286+
}
287+
s_motion_sensitivity = *sensitivity;
288+
289+
// Update accelerometer sensitivity in accel_manager
290+
// This applies the setting to the hardware
291+
#if PLATFORM_ASTERIX
292+
extern void accel_manager_update_sensitivity(uint8_t sensitivity);
293+
accel_manager_update_sensitivity(*sensitivity);
294+
#endif
295+
296+
return true;
297+
}
298+
278299
static bool prv_set_s_backlight_ambient_threshold(uint32_t *threshold) {
279300
// Validate and constrain the threshold
280301
if (*threshold > AMBIENT_LIGHT_LEVEL_MAX) {
@@ -859,6 +880,18 @@ void backlight_set_motion_enabled(bool enable) {
859880
prv_pref_set(PREF_KEY_BACKLIGHT_MOTION, &enable, sizeof(enable));
860881
}
861882

883+
uint8_t shell_prefs_get_motion_sensitivity(void) {
884+
return s_motion_sensitivity;
885+
}
886+
887+
void shell_prefs_set_motion_sensitivity(uint8_t sensitivity) {
888+
// Clamp to valid range
889+
if (sensitivity > 100) {
890+
sensitivity = 50;
891+
}
892+
prv_pref_set(PREF_KEY_MOTION_SENSITIVITY, &sensitivity, sizeof(sensitivity));
893+
}
894+
862895
uint32_t backlight_get_ambient_threshold(void) {
863896
return s_backlight_ambient_threshold;
864897
}

src/fw/shell/normal/prefs_values.h.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
PREFS_MACRO(PREF_KEY_BACKLIGHT_TIMEOUT_MS, s_backlight_timeout_ms)
99
PREFS_MACRO(PREF_KEY_BACKLIGHT_INTENSITY, s_backlight_intensity)
1010
PREFS_MACRO(PREF_KEY_BACKLIGHT_MOTION, s_backlight_motion_enabled)
11+
PREFS_MACRO(PREF_KEY_MOTION_SENSITIVITY, s_motion_sensitivity)
1112
PREFS_MACRO(PREF_KEY_BACKLIGHT_AMBIENT_THRESHOLD, s_backlight_ambient_threshold)
1213
PREFS_MACRO(PREF_KEY_STATIONARY, s_stationary_mode_enabled)
1314
PREFS_MACRO(PREF_KEY_DEFAULT_WORKER, s_default_worker)

src/fw/shell/prefs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ void backlight_set_intensity_percent(uint8_t intensity_percent);
8989
bool backlight_is_motion_enabled(void);
9090
void backlight_set_motion_enabled(bool enable);
9191

92+
// Motion sensitivity for accelerometer shake detection (0-100, lower = less sensitive)
93+
// Only available on platforms with LSM6DSO (Asterix, Obelix)
94+
uint8_t shell_prefs_get_motion_sensitivity(void);
95+
void shell_prefs_set_motion_sensitivity(uint8_t sensitivity);
96+
9297
// The backlight ambient light threshold setting
9398
uint32_t backlight_get_ambient_threshold(void);
9499
void backlight_set_ambient_threshold(uint32_t threshold);

0 commit comments

Comments
 (0)