From 69b8048790c9f5ad02e01534e9885552f20f4fbc Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 26 May 2025 10:51:11 -0400 Subject: [PATCH] power down accelerometer unless a watch face requests it --- movement.c | 29 +++++++++++++++++-- movement.h | 7 +++++ .../sensor/accelerometer_status_face.c | 13 ++++++++- .../sensor/accelerometer_status_face.h | 1 + watch-faces/sensor/activity_logging_face.c | 2 ++ 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/movement.c b/movement.c index 561925ce..a767fa35 100644 --- a/movement.c +++ b/movement.c @@ -40,7 +40,6 @@ #include "shell.h" #include "utz.h" #include "zones.h" -#include "lis2dw.h" #include "tc.h" #include "evsys.h" #include "delay.h" @@ -539,7 +538,7 @@ bool movement_disable_tap_detection_if_available(void) { if (movement_state.has_lis2dw) { // Ramp data rate back down to the usual lowest rate to save power. lis2dw_set_low_noise_mode(false); - lis2dw_set_data_rate(LIS2DW_DATA_RATE_LOWEST); + lis2dw_set_data_rate(movement_state.accelerometer_background_rate); lis2dw_set_mode(LIS2DW_MODE_LOW_POWER); // ...disable Z axis (not sure if this is needed, does this save power?)... lis2dw_configure_tap_threshold(0, 0, 0, 0); @@ -550,6 +549,24 @@ bool movement_disable_tap_detection_if_available(void) { return false; } +lis2dw_data_rate_t movement_get_accelerometer_background_rate(void) { + if (movement_state.has_lis2dw) return movement_state.accelerometer_background_rate; + else return LIS2DW_DATA_RATE_POWERDOWN; +} + +bool movement_set_accelerometer_background_rate(lis2dw_data_rate_t new_rate) { + if (movement_state.has_lis2dw) { + if (movement_state.accelerometer_background_rate != new_rate) { + lis2dw_set_data_rate(new_rate); + movement_state.accelerometer_background_rate = new_rate; + + return true; + } + } + + return false; +} + float movement_get_temperature(void) { float temperature_c = (float)0xFFFFFFFF; @@ -702,7 +719,6 @@ void app_setup(void) { lis2dw_set_mode(LIS2DW_MODE_LOW_POWER); // select low power (not high performance) mode lis2dw_set_low_power_mode(LIS2DW_LP_MODE_1); // lowest power mode, 12-bit lis2dw_set_low_noise_mode(false); // low noise mode raises power consumption slightly; we don't need it - lis2dw_set_data_rate(LIS2DW_DATA_RATE_LOWEST); // sample at 1.6 Hz, lowest rate available lis2dw_enable_stationary_motion_detection(); // stationary/motion detection mode keeps the data rate at 1.6 Hz even in sleep lis2dw_set_range(LIS2DW_RANGE_2_G); // Application note AN5038 recommends 2g range lis2dw_enable_sleep(); // allow acceleromter to sleep and wake on activity @@ -730,7 +746,14 @@ void app_setup(void) { // but it will only fire once tap recognition is enabled. watch_register_interrupt_callback(HAL_GPIO_A3_pin(), cb_accelerometer_event, INTERRUPT_TRIGGER_RISING); + // Enable the interrupts... lis2dw_enable_interrupts(); + + // ...and power down the accelerometer to save energy. This means the interrupts we just configured won't fire. + // Tap detection will ramp up sesing and make use of the A3 interrupt. + // If a watch face wants to check in on the A4 pin, it can call movement_set_accelerometer_background_rate + lis2dw_set_data_rate(LIS2DW_DATA_RATE_POWERDOWN); + movement_state.accelerometer_background_rate = LIS2DW_DATA_RATE_POWERDOWN; } else { watch_disable_i2c(); } diff --git a/movement.h b/movement.h index 5626385a..dbee3830 100644 --- a/movement.h +++ b/movement.h @@ -28,6 +28,7 @@ #include #include "watch.h" #include "utz.h" +#include "lis2dw.h" /// @brief A struct that allows a watch face to report its state back to Movement. typedef struct { @@ -291,6 +292,8 @@ typedef struct { // boolean set if accelerometer is detected bool has_lis2dw; + // data rate for background accelerometer sensing + lis2dw_data_rate_t accelerometer_background_rate; } movement_state_t; void movement_move_to_face(uint8_t watch_face_index); @@ -373,6 +376,10 @@ void movement_set_alarm_enabled(bool value); bool movement_enable_tap_detection_if_available(void); bool movement_disable_tap_detection_if_available(void); +// gets and sets the accelerometer data rate in the background +lis2dw_data_rate_t movement_get_accelerometer_background_rate(void); +bool movement_set_accelerometer_background_rate(lis2dw_data_rate_t new_rate); + // If the board has a temperature sensor, this function will give you the temperature in degrees celsius. // If the board has multiple temperature sensors, it will use the most accurate one available. // If the board has no temperature sensors, it will return 0xFFFFFFFF. diff --git a/watch-faces/sensor/accelerometer_status_face.c b/watch-faces/sensor/accelerometer_status_face.c index a358a8b3..df443300 100644 --- a/watch-faces/sensor/accelerometer_status_face.c +++ b/watch-faces/sensor/accelerometer_status_face.c @@ -54,6 +54,13 @@ void accelerometer_status_face_setup(uint8_t watch_face_index, void ** context_p void accelerometer_status_face_activate(void *context) { accel_interrupt_count_state_t *state = (accel_interrupt_count_state_t *)context; + // get the current data rate from Movement + state->old_rate = movement_get_accelerometer_background_rate(); + if (state->old_rate == LIS2DW_DATA_RATE_POWERDOWN) { + // if accelerometer was powered down, power it up at the lowest possible rate + movement_set_accelerometer_background_rate(LIS2DW_DATA_RATE_LOWEST); + } + // never in settings mode at the start state->is_setting = false; @@ -126,5 +133,9 @@ bool accelerometer_status_face_loop(movement_event_t event, void *context) { } void accelerometer_status_face_resign(void *context) { - (void) context; + accel_interrupt_count_state_t *state = (accel_interrupt_count_state_t *)context; + + // restore old data rate. This only does something if old_rate was POWERDOWN. + // If the old rate was anything other than POWERDOWN, we didn't change anything and this will be a no-op. + movement_set_accelerometer_background_rate(state->old_rate); } diff --git a/watch-faces/sensor/accelerometer_status_face.h b/watch-faces/sensor/accelerometer_status_face.h index 2b152d5f..2e15c510 100644 --- a/watch-faces/sensor/accelerometer_status_face.h +++ b/watch-faces/sensor/accelerometer_status_face.h @@ -38,6 +38,7 @@ typedef struct { uint8_t new_threshold; uint8_t threshold; + lis2dw_data_rate_t old_rate; bool is_setting; } accel_interrupt_count_state_t; diff --git a/watch-faces/sensor/activity_logging_face.c b/watch-faces/sensor/activity_logging_face.c index 1fdfd9b4..3d5eb1cf 100644 --- a/watch-faces/sensor/activity_logging_face.c +++ b/watch-faces/sensor/activity_logging_face.c @@ -73,6 +73,8 @@ void activity_logging_face_setup(uint8_t watch_face_index, void ** context_ptr) if (*context_ptr == NULL) { *context_ptr = malloc(sizeof(activity_logging_state_t)); memset(*context_ptr, 0, sizeof(activity_logging_state_t)); + // At first run, tell Movement to run the accelerometer in the background. It will now run at this rate forever. + movement_set_accelerometer_background_rate(LIS2DW_DATA_RATE_LOWEST); } }