]> git.earman.xyz Git - sensor-watch.git/commitdiff
WIP: accelerometer activity tracking
authorJoey Castillo <joeycastillo@utexas.edu>
Sun, 20 Apr 2025 17:47:04 +0000 (13:47 -0400)
committerJoey Castillo <joeycastillo@utexas.edu>
Sun, 20 Apr 2025 17:47:04 +0000 (13:47 -0400)
movement.c
movement_activity.c
movement_activity.h
movement_config.h
watch-faces/demo/accel_interrupt_count_face.c
watch-faces/sensor/activity_logging_face.c
watch-library/hardware/watch/watch_deepsleep.c
watch-library/shared/driver/lis2dw.c
watch-library/shared/driver/lis2dw.h

index 21250ddf0ead3b9afa96c871b9a5f5b3c596cddd..dc96e284ca8b42a6e10fd2d9bcd33ae191c0e0fc 100644 (file)
@@ -78,6 +78,7 @@ void cb_tick(void);
 void cb_accelerometer_event(void);
 void cb_accelerometer_wake(void);
 uint8_t stationary_minutes = 0;
+uint8_t active_minutes = 0;
 #endif
 
 #if __EMSCRIPTEN__
@@ -155,23 +156,30 @@ static inline void _movement_disable_fast_tick_if_possible(void) {
 
 static void _movement_handle_top_of_minute(void) {
     watch_date_time_t date_time = watch_rtc_get_date_time();
+    static const uint8_t stationary_minutes_for_sleep = 2;
 
 #ifdef HAS_ACCELEROMETER
-    if (stationary_minutes < 5) {
-        // if the watch has been stationary for fewer than 5 minutes, find out if it's still stationary.
-        if (HAL_GPIO_A4_read()) stationary_minutes++;
+    bool accelerometer_is_alseep = HAL_GPIO_A4_read();
+    if (!accelerometer_is_alseep) active_minutes++;
+    printf("Active minutes: %d\n", active_minutes);
+
+    if (stationary_minutes < 2) {
+        // if the watch has been stationary for fewer minutes than the cutoff, find out if it's still stationary.
+        if (accelerometer_is_alseep) stationary_minutes++;
         printf("Stationary minutes: %d\n", stationary_minutes);
 
-        // does this mark five stationary minutes? and are we not already asleep?
-        if (stationary_minutes == 5 && movement_state.le_mode_ticks != -1) {
+        // should we go to sleep? and are we not already asleep?
+        if (stationary_minutes >= stationary_minutes_for_sleep && movement_state.le_mode_ticks != -1) {
             // if so, enter low energy mode.
             printf("Entering low energy mode due to inactivity.\n");
             movement_request_sleep();
         }
     }
 
+    // log data every five minutes, and reset the active_minutes count.
     if ((date_time.unit.minute % 5) == 0) {
         _movement_log_data();
+        active_minutes = 0;
     }
 #endif
 
@@ -718,7 +726,7 @@ void app_setup(void) {
             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
-            lis2dw_configure_wakeup_threshold(8);           // g threshold to wake up: (2 * FS / 64) where FS is "full scale" of ±2g.
+            lis2dw_configure_wakeup_threshold(32);          // g threshold to wake up: (THS * FS / 64) where FS is "full scale" of ±2g.
             lis2dw_configure_6d_threshold(3);               // 0-3 is 80, 70, 60, or 50 degrees. 50 is least precise, hopefully most sensitive?
 
             // set up interrupts:
index ea2d9d6fb13eab37bb9dc2dc0d1327ecf663f686..0b08c158e17bf2bd9cfef716d82ae824963a9d95 100644 (file)
@@ -34,16 +34,16 @@ movement_activity_data_point movement_activity_log[MOVEMENT_NUM_DATA_POINTS] = {
 // the absolute number of data points logged
 uint32_t data_points = 0;
 
-// hacky: we're just tapping into Movement's global state for stationary detection.
+// hacky: we're just tapping into Movement's global state for activity detection.
 // do we need better API for this? i'm less bothered now that it's all in Movement.
-extern uint8_t stationary_minutes;
+extern uint8_t active_minutes;
 
 void _movement_log_data(void) {
     size_t pos = data_points % MOVEMENT_NUM_DATA_POINTS;
     movement_activity_data_point data_point = {0};
 
-    // Movement tracks stationary minutes when deciding whether to sleep.
-    data_point.bit.stationary_minutes = stationary_minutes;
+    // Movement tracks active minutes when deciding whether to sleep.
+    data_point.bit.active_minutes = active_minutes;
 
     // orientation changes are counted in TC2. stash them in the data point...
     data_point.bit.orientation_changes = tc_count16_get_count(2);
index 68f0f2eec3c14bb5a0b6f52e12bf43cffe5b149d..358e66265ca4f3a31245c2cf9ad19c44c54a9399 100644 (file)
@@ -33,7 +33,7 @@
 
 typedef union {
     struct {
-        uint32_t stationary_minutes: 3;
+        uint32_t active_minutes: 3;
         uint32_t orientation_changes: 9;
         uint32_t measured_temperature: 10;
         uint32_t measured_light: 10;
index b738423818a9e5888dda76e5089158182985e0c6..2645af5e39aff4665c877788ff4fba88e59cc5c2 100644 (file)
@@ -37,6 +37,7 @@ const watch_face_t watch_faces[] = {
     activity_logging_face,
     voltage_face,
     days_since_face,
+    accel_interrupt_count_face,
     preferences_face,
     set_time_face,
 };
@@ -49,7 +50,7 @@ const watch_face_t watch_faces[] = {
  * Some folks also like to use this to hide the preferences and time set faces from the normal rotation.
  * If you don't want any faces to be excluded, set this to 0 and a long Mode press will have no effect.
  */
-#define MOVEMENT_SECONDARY_FACE_INDEX (MOVEMENT_NUM_FACES - 4)
+#define MOVEMENT_SECONDARY_FACE_INDEX (MOVEMENT_NUM_FACES - 5)
 
 /* Custom hourly chime tune. Check movement_custom_signal_tunes.h for options. */
 #define SIGNAL_TUNE_DEFAULT
index 53e1273c79ec51111b1f409322359557d317e143..c357191a0e2bc18a03cef65306e1d57343bef37d 100644 (file)
@@ -33,7 +33,7 @@
 
 // hacky: we're just tapping into Movement's global state.
 // we should make better API for this.
-extern uint8_t stationary_minutes;
+extern uint8_t active_minutes;
 
 static void _accel_interrupt_count_face_update_display(accel_interrupt_count_state_t *state) {
     (void) state;
@@ -48,7 +48,7 @@ static void _accel_interrupt_count_face_update_display(accel_interrupt_count_sta
 
     // Orientation changes / active minutes
     uint16_t orientation_changes = tc_count16_get_count(2);
-    sprintf(buf, "%-3u/%2d", orientation_changes > 999 ? 999 : orientation_changes, stationary_minutes);
+    sprintf(buf, "%-3u/%2d", orientation_changes > 999 ? 999 : orientation_changes, active_minutes);
     watch_display_text(WATCH_POSITION_BOTTOM, buf);
 }
 
@@ -57,9 +57,6 @@ void accel_interrupt_count_face_setup(uint8_t watch_face_index, void ** context_
     if (*context_ptr == NULL) {
         *context_ptr = malloc(sizeof(accel_interrupt_count_state_t));
         memset(*context_ptr, 0, sizeof(accel_interrupt_count_state_t));
-        accel_interrupt_count_state_t *state = (accel_interrupt_count_state_t *)*context_ptr;
-        /// TODO: hook up to movement methods for tracking threshold
-        state->threshold = 8;
     }
 }
 
@@ -71,6 +68,9 @@ void accel_interrupt_count_face_activate(void *context) {
 
     // update more quickly to catch changes, also to blink setting
     movement_request_tick_frequency(4);
+
+    // fetch current threshold from accelerometer
+    state->threshold = lis2dw_get_wakeup_threshold();
 }
 
 bool accel_interrupt_count_face_loop(movement_event_t event, void *context) {
@@ -88,7 +88,7 @@ bool accel_interrupt_count_face_loop(movement_event_t event, void *context) {
                         watch_display_text(WATCH_POSITION_BOTTOM, "      ");
                     } else {
                         watch_display_text(WATCH_POSITION_TOP_RIGHT, "  ");
-                        watch_display_text_with_fallback(WATCH_POSITION_TOP, "W_THS", "TH");
+                        watch_display_text_with_fallback(WATCH_POSITION_TOP, "WAKth", "TH");
                         watch_display_float_with_best_effort(state->new_threshold * 0.03125, " G");
                         printf("%s\n", buf);
                     }
index b662f80c6e5f47875b73c6d6c2e1d5f770bb4422..d540c3237539683b764824b58acab373de049a73 100644 (file)
@@ -76,7 +76,7 @@ static void _activity_logging_face_update_display(activity_logging_state_t *stat
         watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "LOG", "AC");
         sprintf(buf, "%2d", state->display_index);
         watch_display_text(WATCH_POSITION_TOP_RIGHT, buf);
-        sprintf(buf, "%-3u/%2d", data_points[pos].bit.orientation_changes > 999 ? 999 : data_points[pos].bit.orientation_changes, data_points[pos].bit.stationary_minutes);
+        sprintf(buf, "%-3u/%2d", data_points[pos].bit.orientation_changes > 999 ? 999 : data_points[pos].bit.orientation_changes, data_points[pos].bit.active_minutes);
         watch_display_text(WATCH_POSITION_BOTTOM, buf);
     }
 }
@@ -140,7 +140,7 @@ bool activity_logging_face_loop(movement_event_t event, void *context) {
                 sprintf(buf, "%3d%3d", data_points[pos].bit.measured_temperature - 300, data_points[pos].bit.orientation_changes > 999 ? 999 : data_points[pos].bit.orientation_changes);
                 buf[6] = 0;
                 watch_display_text(WATCH_POSITION_BOTTOM, buf);
-                sprintf(buf, "%2d", data_points[pos].bit.stationary_minutes);
+                sprintf(buf, "%2d", data_points[pos].bit.active_minutes);
                 watch_display_text(WATCH_POSITION_TOP_RIGHT, buf);
 
                 state->data_dump_idx++;
index 94d6548801d91350094b22c791fee0d2c431e6de..93312bb2c4af8ec744e4982678b494b53ad454c4 100644 (file)
@@ -129,6 +129,8 @@ static void _watch_disable_all_pins_except_rtc(void) {
     uint32_t config = RTC->MODE0.TAMPCTRL.reg;
     uint32_t portb_pins_to_disable = 0xFFFFFFFF;
 
+    /// FIXME: Watch library shouldn't be responsible for this, but ovement uses PB03 for orientation tracking. Keep it on.
+    portb_pins_to_disable &= 0xFFFFFFF7;
     // if there's an action set on RTC/IN[0], leave PB00 configured
     if (config & RTC_TAMPCTRL_IN0ACT_Msk) portb_pins_to_disable &= 0xFFFFFFFE;
     // same with RTC/IN[1] and PB02
@@ -150,7 +152,10 @@ static void _watch_disable_all_pins_except_rtc(void) {
 static void _watch_disable_all_peripherals_except_slcd(void) {
     _watch_disable_tcc();
     watch_disable_adc();
-    watch_disable_external_interrupts();
+    /// FIXME: I just disabled this next line since we need the EIC's event system connection to count orientation changes.
+    // The TODO item: need to power profile the impact of keeping EIC enabled, as well as the UI implications.
+    // watch_disable_external_interrupts();
+
     /// TODO: Actually disable all these peripherals! #SecondMovement
     // watch_disable_i2c();
     // SERCOM3->USART.CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE;
index 3db111b43ffd87a6267d40c855b5815f65ffb83e..e7ccf2d2aa2b783e347b818d0b3b19a0477fbcce 100644 (file)
@@ -288,3 +288,7 @@ lis2dw_wakeup_source_t lis2dw_get_wakeup_source() {
 lis2dw_interrupt_source_t lis2dw_get_interrupt_source(void) {
     return (lis2dw_interrupt_source_t) watch_i2c_read8(LIS2DW_ADDRESS, LIS2DW_REG_ALL_INT_SRC);
 }
+
+uint8_t lis2dw_get_wakeup_threshold(void) {
+    return watch_i2c_read8(LIS2DW_ADDRESS, LIS2DW_REG_WAKE_UP_THS) & 0b00111111;
+}
index 150ded1b817aaea26549c373788bdcecf721b214..57ff05b2ab75ed4ea9392d504b77052a26c06e97 100644 (file)
@@ -375,4 +375,6 @@ lis2dw_interrupt_source_t lis2dw_get_interrupt_source(void);
 
 lis2dw_wakeup_source_t lis2dw_get_wakeup_source(void);
 
+uint8_t lis2dw_get_wakeup_threshold(void);
+
 #endif // LIS2DW_H