From efde4190f1c1d989530d1bd555739dffa1664da5 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Fri, 18 Feb 2022 16:34:52 -0500 Subject: movement: WIP app for capturing accelerometer data --- movement/make/Makefile | 1 + movement/movement_config.h | 3 +- movement/movement_faces.h | 1 + .../sensor/accelerometer_data_acquisition_face.c | 304 +++++++++++++++++++++ .../sensor/accelerometer_data_acquisition_face.h | 97 +++++++ 5 files changed, 404 insertions(+), 2 deletions(-) create mode 100644 movement/watch_faces/sensor/accelerometer_data_acquisition_face.c create mode 100644 movement/watch_faces/sensor/accelerometer_data_acquisition_face.h (limited to 'movement') diff --git a/movement/make/Makefile b/movement/make/Makefile index 9231d5ce..c1808d68 100755 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -50,6 +50,7 @@ SRCS += \ ../watch_faces/complication/countdown_face.c \ ../watch_faces/complication/blinky_face.c \ ../watch_faces/complication/moon_phase_face.c \ + ../watch_faces/sensor/accelerometer_data_acquisition_face.c \ # New watch faces go above this line. # Leave this line at the bottom of the file; it has all the targets for making your project. diff --git a/movement/movement_config.h b/movement/movement_config.h index e68fcc9a..2969de55 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -28,9 +28,8 @@ #include "movement_faces.h" const watch_face_t watch_faces[] = { + accelerometer_data_acquisition_face, simple_clock_face, - beats_face, - voltage_face, preferences_face, set_time_face, }; diff --git a/movement/movement_faces.h b/movement/movement_faces.h index ae94efa7..e4ef102b 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -45,6 +45,7 @@ #include "countdown_face.h" #include "blinky_face.h" #include "moon_phase_face.h" +#include "accelerometer_data_acquisition_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c new file mode 100644 index 00000000..d16ea0d3 --- /dev/null +++ b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.c @@ -0,0 +1,304 @@ +/* + * MIT License + * + * Copyright (c) 2022 Joey Castillo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "accelerometer_data_acquisition_face.h" + +static const char activity_types[][3] = { + "ID", // Idle + "SL", // Sleeping + "WH", // Washing Hands + "WA", // Walking + "JO", // Jogging + "RU", // Running + "BI", // Biking + "HI", // Hiking + "EL", // Elliptical + "SU", // Stairs Up + "SD", // Stairs Down + "WL", // Weight Lifting + "TE", // Testing +}; + +static void update(accelerometer_data_acquisition_state_t *state); +static void update_settings(accelerometer_data_acquisition_state_t *state); +static void advance_current_setting(accelerometer_data_acquisition_state_t *state); +static void start_reading(accelerometer_data_acquisition_state_t *state); +static void continue_reading(accelerometer_data_acquisition_state_t *state); +static void finish_reading(accelerometer_data_acquisition_state_t *state); + +void accelerometer_data_acquisition_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + (void) watch_face_index; + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(accelerometer_data_acquisition_state_t)); + memset(*context_ptr, 0, sizeof(accelerometer_data_acquisition_state_t)); + accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)*context_ptr; + state->beep_with_countdown = true; + state->countdown_length = 10; + } + // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep. +} + +void accelerometer_data_acquisition_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context; + state->next_available_page = 123; +} + +bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + (void) settings; + accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + case EVENT_TICK: + switch (state->mode) { + case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE: + update(state); + break; + case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN: + if (state->countdown_ticks > 0) { + state->countdown_ticks--; + if (state->countdown_ticks == 0) { + // at zero, begin reading + state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING; + state->reading_ticks = 15; + // also beep if the user asked for it + if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C6, 75); + start_reading(state); + } else if (state->countdown_ticks < 3) { + // beep for last two ticks before reading + if (state->beep_with_countdown) watch_buzzer_play_note(BUZZER_NOTE_C5, 75); + } + } + update(state); + break; + case ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING: + if (state->reading_ticks > 0) { + state->reading_ticks--; + if (state->reading_ticks > 0) { + continue_reading(state); + } else { + finish_reading(state); + state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE; + watch_buzzer_play_note(BUZZER_NOTE_C4, 125); + watch_buzzer_play_note(BUZZER_NOTE_REST, 50); + watch_buzzer_play_note(BUZZER_NOTE_C4, 125); + } + } + update(state); + break; + case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS: + update_settings(state); + break; + } + break; + case EVENT_MODE_BUTTON_UP: + movement_move_to_next_face(); + break; + case EVENT_LIGHT_BUTTON_UP: + switch (state->mode) { + case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE: + state->activity_type_index = (state->activity_type_index + 1) % (sizeof(activity_types) / sizeof(activity_types[0])); + update(state); + break; + case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS: + state->settings_page++; + if (state->settings_page > ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT) { + state->settings_page = 0; + state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE; + update(state); + } else { + update_settings(state); + } + break; + default: + break; + } + break; + case EVENT_LIGHT_LONG_PRESS: + movement_illuminate_led(); + break; + case EVENT_ALARM_BUTTON_UP: + printf("Alarm up! Mode is %d\n", state->mode); + switch (state->mode) { + case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE: + state->countdown_ticks = state->countdown_length; + printf("Setting countdown ticks to %d\n", state->countdown_ticks); + state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN; + printf("and mode to %d\n", state->mode); + update(state); + break; + case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN: + // cancel countdown + state->countdown_ticks = 0; + state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE; + update(state); + break; + case ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS: + advance_current_setting(state); + update_settings(state); + break; + default: + break; + } + break; + case EVENT_ALARM_LONG_PRESS: + printf("Alarm long\n"); + if (state->mode == ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE) { + state->repeat_ticks = 0; + state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS; + update_settings(state); + } + break; + default: + break; + } + + // return true if the watch can enter standby mode. If you are PWM'ing an LED or buzzing the buzzer here, + // you should return false since the PWM driver does not operate in standby mode. + return true; +} + +void accelerometer_data_acquisition_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + accelerometer_data_acquisition_state_t *state = (accelerometer_data_acquisition_state_t *)context; + if (state->reading_ticks) { + state->reading_ticks = 0; + finish_reading(state); + } + state->mode = ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE; + state->settings_page = 0; + state->countdown_ticks = 0; + state->repeat_ticks = 0; + state->reading_ticks = 0; +} + +static void update(accelerometer_data_acquisition_state_t *state) { + char buf[14]; + uint8_t ticks = 0; + switch (state->mode) { + case ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE: + ticks = state->countdown_length; + break; + case ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN: + ticks = state->countdown_ticks; + break; + case ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING: + ticks = state->reading_ticks; + break; + default: + ticks = 0; + break; + } + sprintf(buf, "%s%2dre%2d#o", + activity_types[state->activity_type_index], + ticks, + (8192 - state->next_available_page) / 82); + watch_display_string(buf, 0); + + watch_set_colon(); + + // special case: display full if full + if (state->next_available_page < 0) watch_display_string(" FUL", 6); + + // Bell if beep enabled + if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL); + else watch_clear_indicator(WATCH_INDICATOR_BELL); + + // Signal if sensing + if (state->reading_ticks) watch_set_indicator(WATCH_INDICATOR_SIGNAL); + else watch_clear_indicator(WATCH_INDICATOR_SIGNAL); + + // LAP if repeating + if (state->repeat_ticks) watch_set_indicator(WATCH_INDICATOR_LAP); + else watch_clear_indicator(WATCH_INDICATOR_LAP); + +} + +static void update_settings(accelerometer_data_acquisition_state_t *state) { + printf("TODO: Settings screen\n"); + char buf[12]; + watch_clear_colon(); + if (state->beep_with_countdown) watch_set_indicator(WATCH_INDICATOR_BELL); + else watch_clear_indicator(WATCH_INDICATOR_BELL); + switch (state->settings_page) { + case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND: + sprintf(buf, "SO Beep %c", state->beep_with_countdown ? 'Y' : 'N'); + watch_display_string(buf, 0); + break; + case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY: + sprintf(buf, "DL %2d SeC", state->countdown_length); + watch_display_string(buf, 0); + break; + case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT: + watch_display_string("rE no1n&p", 0); + break; + } +} + +static void advance_current_setting(accelerometer_data_acquisition_state_t *state) { + switch (state->settings_page) { + case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND: + state->beep_with_countdown = !state->beep_with_countdown; + break; + case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY: + // this is so lazy, i'm sorry + if (state->countdown_length == 1) state->countdown_length = 3; + else if (state->countdown_length == 3) state->countdown_length = 10; + else if (state->countdown_length == 10) state->countdown_length = 30; + else state->countdown_length = 1; + break; + case ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT: + // TODO: repeat setting + break; + } +} + +static void start_reading(accelerometer_data_acquisition_state_t *state) { + (void) state; + printf("TODO: Activate I2C bus and turn on accelerometer\n"); + printf("TODO: Write header\n"); +} + +static void continue_reading(accelerometer_data_acquisition_state_t *state) { + printf("TODO: Write one second of data\n"); + for(uint8_t j = 0; j < 25; j++) { + state->pos++; + if (state->pos >= 32) { + state->next_available_page++; + state->pos = 0; + } + } +} + +static void finish_reading(accelerometer_data_acquisition_state_t *state) { + if (state->pos != 0) { + state->next_available_page++; + state->pos = 0; + } + printf("TODO: Turn off accelerometer and disable I2C bus\n"); +} diff --git a/movement/watch_faces/sensor/accelerometer_data_acquisition_face.h b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.h new file mode 100644 index 00000000..a8c74ccc --- /dev/null +++ b/movement/watch_faces/sensor/accelerometer_data_acquisition_face.h @@ -0,0 +1,97 @@ +/* + * MIT License + * + * Copyright (c) 2022 Joey Castillo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ACCELEROMETER_DATA_ACQUISITION_FACE_H_ +#define ACCELEROMETER_DATA_ACQUISITION_FACE_H_ + +#include "movement.h" + +#define ACCELEROMETER_DATA_ACQUISITION_INVALID ((uint64_t)(0b11)) // all bits are 1 when the flash is erased +#define ACCELEROMETER_DATA_ACQUISITION_HEADER ((uint64_t)(0b10)) +#define ACCELEROMETER_DATA_ACQUISITION_DATA ((uint64_t)(0b01)) +#define ACCELEROMETER_DATA_ACQUISITION_DELETED ((uint64_t)(0b00)) // You can always write a 0 to any 1 bit + +typedef union { + struct { + int16_t temperature_and_type : 16; // high two bits are record type, val & 0xFFF is temperature + int8_t char1 : 8; + int8_t char2 : 8; + int32_t timestamp : 32; + } header; + struct { + int16_t x_accel_and_type : 16; // high two bits are record type, val & 0x3FFF is x_accel + int16_t y_accel : 16; + int16_t z_accel : 16; + int32_t counter : 16; + } data; + uint64_t value; +} accelerometer_data_acquisition_record_t; + +typedef enum { + ACCELEROMETER_DATA_ACQUISITION_MODE_IDLE, + ACCELEROMETER_DATA_ACQUISITION_MODE_COUNTDOWN, + ACCELEROMETER_DATA_ACQUISITION_MODE_SENSING, + ACCELEROMETER_DATA_ACQUISITION_MODE_SETTINGS, +} accelerometer_data_acquisition_mode_t; + +typedef enum { + ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_SOUND, + ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_DELAY, + ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_REPEAT, + // ACCELEROMETER_DATA_ACQUISITION_SETTINGS_PAGE_NAME, +} accelerometer_data_acquisition_settings_page_t; + +typedef struct { + // mode + accelerometer_data_acquisition_mode_t mode; + accelerometer_data_acquisition_settings_page_t settings_page; + // current settings + uint8_t activity_type_index; // active activity type + bool beep_with_countdown; // should we beep at the countdown + uint8_t countdown_length; // how many seconds to count down + uint16_t repeat_interval; // how many seconds to wait for a repeat + // info about the flash chip + int16_t next_available_page; + // transient properties + uint8_t countdown_ticks; + uint8_t repeat_ticks; + uint8_t reading_ticks; + accelerometer_data_acquisition_record_t records[32]; + uint16_t pos; +} accelerometer_data_acquisition_state_t; + +void accelerometer_data_acquisition_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void accelerometer_data_acquisition_face_activate(movement_settings_t *settings, void *context); +bool accelerometer_data_acquisition_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void accelerometer_data_acquisition_face_resign(movement_settings_t *settings, void *context); + +#define accelerometer_data_acquisition_face ((const watch_face_t){ \ + accelerometer_data_acquisition_face_setup, \ + accelerometer_data_acquisition_face_activate, \ + accelerometer_data_acquisition_face_loop, \ + accelerometer_data_acquisition_face_resign, \ + NULL, \ +}) + +#endif // ACCELEROMETER_DATA_ACQUISITION_FACE_H_ -- cgit v1.2.3