WIP: accelerometer activity tracking
This commit is contained in:
parent
4b490bed4c
commit
e435969b51
20
movement.c
20
movement.c
@ -78,6 +78,7 @@ void cb_tick(void);
|
|||||||
void cb_accelerometer_event(void);
|
void cb_accelerometer_event(void);
|
||||||
void cb_accelerometer_wake(void);
|
void cb_accelerometer_wake(void);
|
||||||
uint8_t stationary_minutes = 0;
|
uint8_t stationary_minutes = 0;
|
||||||
|
uint8_t active_minutes = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __EMSCRIPTEN__
|
#if __EMSCRIPTEN__
|
||||||
@ -155,23 +156,30 @@ static inline void _movement_disable_fast_tick_if_possible(void) {
|
|||||||
|
|
||||||
static void _movement_handle_top_of_minute(void) {
|
static void _movement_handle_top_of_minute(void) {
|
||||||
watch_date_time_t date_time = watch_rtc_get_date_time();
|
watch_date_time_t date_time = watch_rtc_get_date_time();
|
||||||
|
static const uint8_t stationary_minutes_for_sleep = 2;
|
||||||
|
|
||||||
#ifdef HAS_ACCELEROMETER
|
#ifdef HAS_ACCELEROMETER
|
||||||
if (stationary_minutes < 5) {
|
bool accelerometer_is_alseep = HAL_GPIO_A4_read();
|
||||||
// if the watch has been stationary for fewer than 5 minutes, find out if it's still stationary.
|
if (!accelerometer_is_alseep) active_minutes++;
|
||||||
if (HAL_GPIO_A4_read()) stationary_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);
|
printf("Stationary minutes: %d\n", stationary_minutes);
|
||||||
|
|
||||||
// does this mark five stationary minutes? and are we not already asleep?
|
// should we go to sleep? and are we not already asleep?
|
||||||
if (stationary_minutes == 5 && movement_state.le_mode_ticks != -1) {
|
if (stationary_minutes >= stationary_minutes_for_sleep && movement_state.le_mode_ticks != -1) {
|
||||||
// if so, enter low energy mode.
|
// if so, enter low energy mode.
|
||||||
printf("Entering low energy mode due to inactivity.\n");
|
printf("Entering low energy mode due to inactivity.\n");
|
||||||
movement_request_sleep();
|
movement_request_sleep();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log data every five minutes, and reset the active_minutes count.
|
||||||
if ((date_time.unit.minute % 5) == 0) {
|
if ((date_time.unit.minute % 5) == 0) {
|
||||||
_movement_log_data();
|
_movement_log_data();
|
||||||
|
active_minutes = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#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_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_set_range(LIS2DW_RANGE_2_G); // Application note AN5038 recommends 2g range
|
||||||
lis2dw_enable_sleep(); // allow acceleromter to sleep and wake on activity
|
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?
|
lis2dw_configure_6d_threshold(3); // 0-3 is 80, 70, 60, or 50 degrees. 50 is least precise, hopefully most sensitive?
|
||||||
|
|
||||||
// set up interrupts:
|
// set up interrupts:
|
||||||
|
|||||||
@ -34,16 +34,16 @@ movement_activity_data_point movement_activity_log[MOVEMENT_NUM_DATA_POINTS] = {
|
|||||||
// the absolute number of data points logged
|
// the absolute number of data points logged
|
||||||
uint32_t data_points = 0;
|
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.
|
// 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) {
|
void _movement_log_data(void) {
|
||||||
size_t pos = data_points % MOVEMENT_NUM_DATA_POINTS;
|
size_t pos = data_points % MOVEMENT_NUM_DATA_POINTS;
|
||||||
movement_activity_data_point data_point = {0};
|
movement_activity_data_point data_point = {0};
|
||||||
|
|
||||||
// Movement tracks stationary minutes when deciding whether to sleep.
|
// Movement tracks active minutes when deciding whether to sleep.
|
||||||
data_point.bit.stationary_minutes = stationary_minutes;
|
data_point.bit.active_minutes = active_minutes;
|
||||||
|
|
||||||
// orientation changes are counted in TC2. stash them in the data point...
|
// orientation changes are counted in TC2. stash them in the data point...
|
||||||
data_point.bit.orientation_changes = tc_count16_get_count(2);
|
data_point.bit.orientation_changes = tc_count16_get_count(2);
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uint32_t stationary_minutes: 3;
|
uint32_t active_minutes: 3;
|
||||||
uint32_t orientation_changes: 9;
|
uint32_t orientation_changes: 9;
|
||||||
uint32_t measured_temperature: 10;
|
uint32_t measured_temperature: 10;
|
||||||
uint32_t measured_light: 10;
|
uint32_t measured_light: 10;
|
||||||
|
|||||||
@ -37,6 +37,7 @@ const watch_face_t watch_faces[] = {
|
|||||||
activity_logging_face,
|
activity_logging_face,
|
||||||
voltage_face,
|
voltage_face,
|
||||||
days_since_face,
|
days_since_face,
|
||||||
|
accel_interrupt_count_face,
|
||||||
preferences_face,
|
preferences_face,
|
||||||
set_time_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.
|
* 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.
|
* 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. */
|
/* Custom hourly chime tune. Check movement_custom_signal_tunes.h for options. */
|
||||||
#define SIGNAL_TUNE_DEFAULT
|
#define SIGNAL_TUNE_DEFAULT
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
// hacky: we're just tapping into Movement's global state.
|
// hacky: we're just tapping into Movement's global state.
|
||||||
// we should make better API for this.
|
// 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) {
|
static void _accel_interrupt_count_face_update_display(accel_interrupt_count_state_t *state) {
|
||||||
(void) state;
|
(void) state;
|
||||||
@ -48,7 +48,7 @@ static void _accel_interrupt_count_face_update_display(accel_interrupt_count_sta
|
|||||||
|
|
||||||
// Orientation changes / active minutes
|
// Orientation changes / active minutes
|
||||||
uint16_t orientation_changes = tc_count16_get_count(2);
|
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);
|
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) {
|
if (*context_ptr == NULL) {
|
||||||
*context_ptr = malloc(sizeof(accel_interrupt_count_state_t));
|
*context_ptr = malloc(sizeof(accel_interrupt_count_state_t));
|
||||||
memset(*context_ptr, 0, 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
|
// update more quickly to catch changes, also to blink setting
|
||||||
movement_request_tick_frequency(4);
|
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) {
|
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, " ");
|
watch_display_text(WATCH_POSITION_BOTTOM, " ");
|
||||||
} else {
|
} else {
|
||||||
watch_display_text(WATCH_POSITION_TOP_RIGHT, " ");
|
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");
|
watch_display_float_with_best_effort(state->new_threshold * 0.03125, " G");
|
||||||
printf("%s\n", buf);
|
printf("%s\n", buf);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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");
|
watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "LOG", "AC");
|
||||||
sprintf(buf, "%2d", state->display_index);
|
sprintf(buf, "%2d", state->display_index);
|
||||||
watch_display_text(WATCH_POSITION_TOP_RIGHT, buf);
|
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);
|
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);
|
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;
|
buf[6] = 0;
|
||||||
watch_display_text(WATCH_POSITION_BOTTOM, buf);
|
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);
|
watch_display_text(WATCH_POSITION_TOP_RIGHT, buf);
|
||||||
|
|
||||||
state->data_dump_idx++;
|
state->data_dump_idx++;
|
||||||
|
|||||||
@ -129,6 +129,8 @@ static void _watch_disable_all_pins_except_rtc(void) {
|
|||||||
uint32_t config = RTC->MODE0.TAMPCTRL.reg;
|
uint32_t config = RTC->MODE0.TAMPCTRL.reg;
|
||||||
uint32_t portb_pins_to_disable = 0xFFFFFFFF;
|
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 there's an action set on RTC/IN[0], leave PB00 configured
|
||||||
if (config & RTC_TAMPCTRL_IN0ACT_Msk) portb_pins_to_disable &= 0xFFFFFFFE;
|
if (config & RTC_TAMPCTRL_IN0ACT_Msk) portb_pins_to_disable &= 0xFFFFFFFE;
|
||||||
// same with RTC/IN[1] and PB02
|
// 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) {
|
static void _watch_disable_all_peripherals_except_slcd(void) {
|
||||||
_watch_disable_tcc();
|
_watch_disable_tcc();
|
||||||
watch_disable_adc();
|
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
|
/// TODO: Actually disable all these peripherals! #SecondMovement
|
||||||
// watch_disable_i2c();
|
// watch_disable_i2c();
|
||||||
// SERCOM3->USART.CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE;
|
// SERCOM3->USART.CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE;
|
||||||
|
|||||||
@ -288,3 +288,7 @@ lis2dw_wakeup_source_t lis2dw_get_wakeup_source() {
|
|||||||
lis2dw_interrupt_source_t lis2dw_get_interrupt_source(void) {
|
lis2dw_interrupt_source_t lis2dw_get_interrupt_source(void) {
|
||||||
return (lis2dw_interrupt_source_t) watch_i2c_read8(LIS2DW_ADDRESS, LIS2DW_REG_ALL_INT_SRC);
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -375,4 +375,6 @@ lis2dw_interrupt_source_t lis2dw_get_interrupt_source(void);
|
|||||||
|
|
||||||
lis2dw_wakeup_source_t lis2dw_get_wakeup_source(void);
|
lis2dw_wakeup_source_t lis2dw_get_wakeup_source(void);
|
||||||
|
|
||||||
|
uint8_t lis2dw_get_wakeup_threshold(void);
|
||||||
|
|
||||||
#endif // LIS2DW_H
|
#endif // LIS2DW_H
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user