From e8461984d60a80841a5f4b219358cc20567114f8 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 12:58:14 -0400 Subject: launcher is now movement --- launcher/launcher.c | 218 --------------------- launcher/launcher.h | 89 --------- launcher/launcher_config.h | 18 -- launcher/make/.gitignore | 1 - launcher/make/Makefile | 32 --- launcher/widgets/clock/simple_clock_widget.c | 92 --------- launcher/widgets/clock/simple_clock_widget.h | 20 -- .../widgets/complications/pulseometer_widget.c | 87 -------- .../widgets/complications/pulseometer_widget.h | 24 --- launcher/widgets/settings/preferences_widget.c | 120 ------------ launcher/widgets/settings/preferences_widget.h | 18 -- launcher/widgets/settings/set_time_widget.c | 109 ----------- launcher/widgets/settings/set_time_widget.h | 18 -- movement/make/.gitignore | 1 + movement/make/Makefile | 32 +++ movement/movement.c | 218 +++++++++++++++++++++ movement/movement.h | 89 +++++++++ movement/movement_config.h | 18 ++ movement/widgets/clock/simple_clock_widget.c | 92 +++++++++ movement/widgets/clock/simple_clock_widget.h | 20 ++ .../widgets/complications/pulseometer_widget.c | 87 ++++++++ .../widgets/complications/pulseometer_widget.h | 24 +++ movement/widgets/settings/preferences_widget.c | 120 ++++++++++++ movement/widgets/settings/preferences_widget.h | 18 ++ movement/widgets/settings/set_time_widget.c | 109 +++++++++++ movement/widgets/settings/set_time_widget.h | 18 ++ 26 files changed, 846 insertions(+), 846 deletions(-) delete mode 100644 launcher/launcher.c delete mode 100644 launcher/launcher.h delete mode 100644 launcher/launcher_config.h delete mode 100755 launcher/make/.gitignore delete mode 100755 launcher/make/Makefile delete mode 100644 launcher/widgets/clock/simple_clock_widget.c delete mode 100644 launcher/widgets/clock/simple_clock_widget.h delete mode 100644 launcher/widgets/complications/pulseometer_widget.c delete mode 100644 launcher/widgets/complications/pulseometer_widget.h delete mode 100644 launcher/widgets/settings/preferences_widget.c delete mode 100644 launcher/widgets/settings/preferences_widget.h delete mode 100644 launcher/widgets/settings/set_time_widget.c delete mode 100644 launcher/widgets/settings/set_time_widget.h create mode 100755 movement/make/.gitignore create mode 100755 movement/make/Makefile create mode 100644 movement/movement.c create mode 100644 movement/movement.h create mode 100644 movement/movement_config.h create mode 100644 movement/widgets/clock/simple_clock_widget.c create mode 100644 movement/widgets/clock/simple_clock_widget.h create mode 100644 movement/widgets/complications/pulseometer_widget.c create mode 100644 movement/widgets/complications/pulseometer_widget.h create mode 100644 movement/widgets/settings/preferences_widget.c create mode 100644 movement/widgets/settings/preferences_widget.h create mode 100644 movement/widgets/settings/set_time_widget.c create mode 100644 movement/widgets/settings/set_time_widget.h diff --git a/launcher/launcher.c b/launcher/launcher.c deleted file mode 100644 index 7adb01a8..00000000 --- a/launcher/launcher.c +++ /dev/null @@ -1,218 +0,0 @@ -#include -#include -#include -#include "watch.h" -#include "launcher.h" -#include "launcher_config.h" - -LauncherState launcher_state; -void * widget_contexts[LAUNCHER_NUM_WIDGETS]; -const int32_t launcher_screensaver_deadlines[8] = {INT_MAX, 3600, 7200, 21600, 43200, 86400, 172800, 604800}; -LauncherEvent event; - -void cb_mode_btn_interrupt(); -void cb_light_btn_interrupt(); -void cb_alarm_btn_interrupt(); -void cb_alarm_btn_extwake(); -void cb_alarm_fired(); -void cb_tick(); - -static inline void _launcher_reset_screensaver_countdown() { - // for testing, make the timeout happen 60x faster. - launcher_state.screensaver_ticks = launcher_screensaver_deadlines[launcher_state.launcher_settings.bit.screensaver_interval] / 60; -} - -void launcher_request_tick_frequency(uint8_t freq) { - watch_rtc_disable_all_periodic_callbacks(); - launcher_state.subsecond = 0; - launcher_state.tick_frequency = freq; - watch_rtc_register_periodic_callback(cb_tick, freq); -} - -void launcher_illuminate_led() { - launcher_state.light_ticks = 3; -} - -void launcher_move_to_widget(uint8_t widget_index) { - launcher_state.widget_changed = true; - launcher_state.next_widget = widget_index; -} - -void launcher_move_to_next_widget() { - launcher_move_to_widget((launcher_state.current_widget + 1) % LAUNCHER_NUM_WIDGETS); -} - -void app_init() { - memset(&launcher_state, 0, sizeof(launcher_state)); - - launcher_state.launcher_settings.bit.led_green_color = 0xF; - launcher_state.launcher_settings.bit.button_should_sound = true; - launcher_state.launcher_settings.bit.screensaver_interval = 1; - _launcher_reset_screensaver_countdown(); -} - -void app_wake_from_deep_sleep() { - // This app does not support deep sleep mode. -} - -void app_setup() { - static bool is_first_launch = true; - - if (is_first_launch) { - for(uint8_t i = 0; i < LAUNCHER_NUM_WIDGETS; i++) { - widget_contexts[i] = NULL; - is_first_launch = false; - } - } - if (launcher_state.screensaver_ticks != -1) { - watch_disable_extwake_interrupt(BTN_ALARM); - watch_rtc_disable_alarm_callback(); - - watch_enable_external_interrupts(); - watch_register_interrupt_callback(BTN_MODE, cb_mode_btn_interrupt, INTERRUPT_TRIGGER_BOTH); - watch_register_interrupt_callback(BTN_LIGHT, cb_light_btn_interrupt, INTERRUPT_TRIGGER_BOTH); - watch_register_interrupt_callback(BTN_ALARM, cb_alarm_btn_interrupt, INTERRUPT_TRIGGER_BOTH); - - watch_enable_buzzer(); - watch_enable_leds(); - watch_enable_display(); - - launcher_request_tick_frequency(1); - - for(uint8_t i = 0; i < LAUNCHER_NUM_WIDGETS; i++) { - widgets[i].setup(&launcher_state.launcher_settings, &widget_contexts[i]); - } - - widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[0]); - event.subsecond = 0; - event.event_type = EVENT_ACTIVATE; - } -} - -void app_prepare_for_sleep() { -} - -void app_wake_from_sleep() { -} - -bool app_loop() { - if (launcher_state.widget_changed) { - if (launcher_state.launcher_settings.bit.button_should_sound) { - // low note for nonzero case, high note for return to widget 0 - watch_buzzer_play_note(launcher_state.next_widget ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 50); - } - widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - launcher_state.current_widget = launcher_state.next_widget; - watch_clear_display(); - widgets[launcher_state.current_widget].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - event.subsecond = 0; - event.event_type = EVENT_ACTIVATE; - launcher_state.widget_changed = false; - } - - // If the LED is off and should be on, turn it on - if (launcher_state.light_ticks > 0 && !launcher_state.led_on) { - watch_set_led_color(launcher_state.launcher_settings.bit.led_red_color ? (0xF | launcher_state.launcher_settings.bit.led_red_color << 4) : 0, - launcher_state.launcher_settings.bit.led_green_color ? (0xF | launcher_state.launcher_settings.bit.led_green_color << 4) : 0); - launcher_state.led_on = true; - - } - - // if the LED is on and should be off, turn it off - if (launcher_state.led_on && launcher_state.light_ticks == 0) { - // unless the user is holding down the LIGHT button, in which case, give them more time. - if (watch_get_pin_level(BTN_LIGHT)) { - launcher_state.light_ticks = 3; - } else { - watch_set_led_off(); - launcher_state.led_on = false; - } - } - - // if we have timed out of our screensaver countdown, enter screensaver mode. - if (launcher_state.screensaver_ticks == 0) { - launcher_state.screensaver_ticks = -1; - watch_date_time alarm_time; - alarm_time.reg = 0; - alarm_time.unit.second = 59; // after a match, the alarm fires at the next rising edge of CLK_RTC_CNT, so 59 seconds lets us update at :00 - watch_rtc_register_alarm_callback(cb_alarm_fired, alarm_time, ALARM_MATCH_SS); - watch_register_extwake_callback(BTN_ALARM, cb_alarm_btn_extwake, true); - event.event_type = EVENT_NONE; - event.subsecond = 0; - - // this is a little mini-runloop. - // as long as screensaver_ticks is -1 (i.e. screensaver is active), we wake up here, update the screen, and go right back to sleep. - while (launcher_state.screensaver_ticks == -1) { - event.event_type = EVENT_SCREENSAVER; - widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - watch_enter_shallow_sleep(true); - } - // as soon as screensaver_ticks is reset by the extwake handler, we bail out of the loop and reactivate ourselves. - event.event_type = EVENT_ACTIVATE; - // this is a hack tho: waking from shallow sleep, app_setup does get called, but it happens before we have reset our ticks. - // need to figure out if there's a better heuristic for determining how we woke up. - app_setup(); - } - - static bool can_sleep = true; - - if (event.event_type) { - event.subsecond = launcher_state.subsecond; - can_sleep = widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - event.event_type = EVENT_NONE; - event.subsecond = 0; - } - - return can_sleep && !launcher_state.led_on; -} - -LauncherEventType _figure_out_button_event(LauncherEventType button_down_event, uint8_t *down_timestamp) { - watch_date_time date_time = watch_rtc_get_date_time(); - if (*down_timestamp) { - uint8_t diff = ((61 + date_time.unit.second) - *down_timestamp) % 60; - *down_timestamp = 0; - if (diff > 1) return button_down_event + 2; - else return button_down_event + 1; - } else { - *down_timestamp = date_time.unit.second + 1; - return button_down_event; - } -} - -void cb_light_btn_interrupt() { - _launcher_reset_screensaver_countdown(); - event.event_type = _figure_out_button_event(EVENT_LIGHT_BUTTON_DOWN, &launcher_state.light_down_timestamp); -} - -void cb_mode_btn_interrupt() { - _launcher_reset_screensaver_countdown(); - event.event_type = _figure_out_button_event(EVENT_MODE_BUTTON_DOWN, &launcher_state.mode_down_timestamp); -} - -void cb_alarm_btn_interrupt() { - _launcher_reset_screensaver_countdown(); - event.event_type = _figure_out_button_event(EVENT_ALARM_BUTTON_DOWN, &launcher_state.alarm_down_timestamp); -} - -void cb_alarm_btn_extwake() { - // wake up! - _launcher_reset_screensaver_countdown(); -} - -void cb_alarm_fired() { - event.event_type = EVENT_SCREENSAVER; -} - -void cb_tick() { - event.event_type = EVENT_TICK; - watch_date_time date_time = watch_rtc_get_date_time(); - if (date_time.unit.second != launcher_state.last_second) { - if (launcher_state.light_ticks) launcher_state.light_ticks--; - if (launcher_state.launcher_settings.bit.screensaver_interval && launcher_state.screensaver_ticks > 0) launcher_state.screensaver_ticks--; - - launcher_state.last_second = date_time.unit.second; - launcher_state.subsecond = 0; - } else { - launcher_state.subsecond++; - } -} diff --git a/launcher/launcher.h b/launcher/launcher.h deleted file mode 100644 index 5c5315a3..00000000 --- a/launcher/launcher.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef LAUNCHER_H_ -#define LAUNCHER_H_ -#include -#include - -// TODO: none of this is implemented -typedef union { - struct { - uint32_t reserved : 3; - uint32_t clock_mode_24h : 1; // determines whether clock should use 12 or 24 hour mode. - uint32_t button_should_sound : 1; // if true, pressing a button emits a sound. - uint32_t signal_should_sound : 1; // if true, a double beep is played at the top of each hour. - uint32_t alarm_should_sound : 1; // if true, the alarm interrupt can match a time and play a song. - uint32_t alarm_minute : 6; // the minute of the alarm we want to match - uint32_t alarm_hour : 5; // the second of the alarm we want to match - uint32_t screensaver_interval : 3; // 0 to disable screensaver, or a screensaver activation interval. - uint32_t led_duration : 3; // how many seconds to shine the LED for, or 0 to disable it. - uint32_t led_red_color : 4; // for general purpose illumination, the red LED value (0-15) - uint32_t led_green_color : 4; // for general purpose illumination, the green LED value (0-15) - } bit; - uint32_t value; -} LauncherSettings; - -typedef enum { - EVENT_NONE = 0, // There is no event to report. - EVENT_ACTIVATE, // Your widget is entering the foreground. - EVENT_TICK, // Most common event type. Your widget is being called from the tick callback. - EVENT_SCREENSAVER, // Your widget is being asked to display its output for screensaver mode. - EVENT_LIGHT_BUTTON_DOWN, // The light button has been pressed, but not yet released. - EVENT_LIGHT_BUTTON_UP, // The light button was pressed and released. - EVENT_LIGHT_LONG_PRESS, // The light button was held for >2 seconds, and released. - EVENT_MODE_BUTTON_DOWN, // The mode button has been pressed, but not yet released. - EVENT_MODE_BUTTON_UP, // The mode button was pressed and released. - EVENT_MODE_LONG_PRESS, // The mode button was held for >2 seconds, and released. - EVENT_ALARM_BUTTON_DOWN, // The alarm button has been pressed, but not yet released. - EVENT_ALARM_BUTTON_UP, // The alarm button was pressed and released. - EVENT_ALARM_LONG_PRESS, // The alarm button was held for >2 seconds, and released. -} LauncherEventType; - -typedef struct { - uint8_t event_type; - uint8_t subsecond; -} LauncherEvent; - -typedef void (*launcher_widget_setup)(LauncherSettings *settings, void ** context_ptr); -typedef void (*launcher_widget_activate)(LauncherSettings *settings, void *context); -typedef bool (*launcher_widget_loop)(LauncherEvent event, LauncherSettings *settings, void *context); -typedef void (*launcher_widget_resign)(LauncherSettings *settings, void *context); - -typedef struct { - launcher_widget_setup setup; - launcher_widget_activate activate; - launcher_widget_loop loop; - launcher_widget_resign resign; -} WatchWidget; - -typedef struct { - // properties stored in BACKUP register - LauncherSettings launcher_settings; - - // transient properties - int16_t current_widget; - int16_t next_widget; - bool widget_changed; - - // LED stuff - uint8_t light_ticks; - bool led_on; - - // button tracking for long press - uint8_t light_down_timestamp; - uint8_t mode_down_timestamp; - uint8_t alarm_down_timestamp; - - // screensaver countdown - int32_t screensaver_ticks; - - // stuff for subsecond tracking - uint8_t tick_frequency; - uint8_t last_second; - uint8_t subsecond; -} LauncherState; - -void launcher_move_to_widget(uint8_t widget_index); -void launcher_move_to_next_widget(); -void launcher_illuminate_led(); -void launcher_request_tick_frequency(uint8_t freq); - -#endif // LAUNCHER_H_ diff --git a/launcher/launcher_config.h b/launcher/launcher_config.h deleted file mode 100644 index ff656350..00000000 --- a/launcher/launcher_config.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef LAUNCHER_CONFIG_H_ -#define LAUNCHER_CONFIG_H_ - -#include "simple_clock_widget.h" -#include "preferences_widget.h" -#include "set_time_widget.h" -#include "pulseometer_widget.h" - -#define LAUNCHER_NUM_WIDGETS 3 - -WatchWidget widgets[LAUNCHER_NUM_WIDGETS] = { - simple_clock_widget, - preferences_widget, - set_time_widget, -}; - - -#endif // LAUNCHER_CONFIG_H_ diff --git a/launcher/make/.gitignore b/launcher/make/.gitignore deleted file mode 100755 index 3722ac63..00000000 --- a/launcher/make/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build/ diff --git a/launcher/make/Makefile b/launcher/make/Makefile deleted file mode 100755 index a1059bbd..00000000 --- a/launcher/make/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# Leave this line at the top of the file; it has all the watch library sources and includes. -TOP = ../.. -include $(TOP)/make.mk - -# If you add any other subdirectories with header files you wish to include, add them after ../ -# Note that you will need to add a backslash at the end of any line you wish to continue, i.e. -# INCLUDES += \ -# -I../ \ -# -I../drivers/ \ -# -I../widgets/fitness/ -INCLUDES += \ - -I../ \ - -I../widgets/ \ - -I../widgets/clock/ \ - -I../widgets/settings/ \ - -I../widgets/complications/ \ - -# If you add any other source files you wish to compile, add them after ../app.c -# Note that you will need to add a backslash at the end of any line you wish to continue, i.e. -# SRCS += \ -# ../launcher.c \ -# ../drivers/lis2dh.c \ -# ../widgets/fitness/step_count_widget.c -SRCS += \ - ../launcher.c \ - ../widgets/clock/simple_clock_widget.c \ - ../widgets/settings/preferences_widget.c \ - ../widgets/settings/set_time_widget.c \ - ../widgets/complications/pulseometer_widget.c \ - -# Leave this line at the bottom of the file; it has all the targets for making your project. -include $(TOP)/rules.mk diff --git a/launcher/widgets/clock/simple_clock_widget.c b/launcher/widgets/clock/simple_clock_widget.c deleted file mode 100644 index 7fe0183d..00000000 --- a/launcher/widgets/clock/simple_clock_widget.c +++ /dev/null @@ -1,92 +0,0 @@ -#include -#include "simple_clock_widget.h" -#include "watch.h" - -void simple_clock_widget_setup(LauncherSettings *settings, void ** context_ptr) { - (void) settings; - // the only context we need is the timestamp of the previous tick. - if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint32_t)); -} - -void simple_clock_widget_activate(LauncherSettings *settings, void *context) { - if (settings->bit.clock_mode_24h) { - watch_set_indicator(WATCH_INDICATOR_24H); - } - watch_set_colon(); - // this ensures that none of the timestamp fields will match, so we can re-render them all. - *((uint32_t *)context) = 0xFFFFFFFF; -} - -bool simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { - printf("simple_clock_widget_loop\n"); - const char weekdays[7][3] = {"SA", "SU", "MO", "TU", "WE", "TH", "FR"}; - char buf[11]; - uint8_t pos; - - watch_date_time date_time; - uint32_t previous_date_time; - switch (event.event_type) { - case EVENT_ACTIVATE: - case EVENT_TICK: - case EVENT_SCREENSAVER: - date_time = watch_rtc_get_date_time(); - previous_date_time = *((uint32_t *)context); - *((uint32_t *)context) = date_time.reg; - - if (date_time.reg >> 6 == previous_date_time >> 6 && event.event_type != EVENT_SCREENSAVER) { - // everything before seconds is the same, don't waste cycles setting those segments. - pos = 8; - sprintf(buf, "%02d", date_time.unit.second); - } else if (date_time.reg >> 12 == previous_date_time >> 12 && event.event_type != EVENT_SCREENSAVER) { - // everything before minutes is the same. - pos = 6; - sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second); - } else { - // other stuff changed; let's do it all. - if (!settings->bit.clock_mode_24h) { - // if we are in 12 hour mode, do some cleanup. - if (date_time.unit.hour < 12) { - watch_clear_indicator(WATCH_INDICATOR_PM); - } else { - watch_set_indicator(WATCH_INDICATOR_PM); - } - date_time.unit.hour %= 12; - if (date_time.unit.hour == 0) date_time.unit.hour = 12; - } - pos = 0; - if (event.event_type == EVENT_SCREENSAVER) { - sprintf(buf, "%s%2d%2d%02d ", weekdays[simple_clock_widget_get_weekday(date_time.unit.year, date_time.unit.month, date_time.unit.day)], date_time.unit.day, date_time.unit.hour, date_time.unit.minute); - } else { - sprintf(buf, "%s%2d%2d%02d%02d", weekdays[simple_clock_widget_get_weekday(date_time.unit.year, date_time.unit.month, date_time.unit.day)], date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); - } - } - watch_display_string(buf, pos); - break; - case EVENT_MODE_BUTTON_UP: - launcher_move_to_next_widget(); - return false; - case EVENT_LIGHT_BUTTON_UP: - launcher_illuminate_led(); - break; - case EVENT_ALARM_BUTTON_UP: - break; - default: - break; - } - - return true; -} - -void simple_clock_widget_resign(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; -} - -uint8_t simple_clock_widget_get_weekday(uint16_t year, uint16_t month, uint16_t day) { - year += 20; - if (month <= 2) { - month += 12; - year--; - } - return (day + 13 * (month + 1) / 5 + year + year / 4 + 525) % 7; -} diff --git a/launcher/widgets/clock/simple_clock_widget.h b/launcher/widgets/clock/simple_clock_widget.h deleted file mode 100644 index 739c942b..00000000 --- a/launcher/widgets/clock/simple_clock_widget.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef SIMPLE_CLOCK_WIDGET_H_ -#define SIMPLE_CLOCK_WIDGET_H_ - -#include "launcher.h" - -void simple_clock_widget_setup(LauncherSettings *settings, void ** context_ptr); -void simple_clock_widget_activate(LauncherSettings *settings, void *context); -bool simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void simple_clock_widget_resign(LauncherSettings *settings, void *context); - -uint8_t simple_clock_widget_get_weekday(uint16_t day, uint16_t month, uint16_t year); - -#define simple_clock_widget { \ - simple_clock_widget_setup, \ - simple_clock_widget_activate, \ - simple_clock_widget_loop, \ - simple_clock_widget_resign, \ -} - -#endif // FAKE_WIDGET_H_ \ No newline at end of file diff --git a/launcher/widgets/complications/pulseometer_widget.c b/launcher/widgets/complications/pulseometer_widget.c deleted file mode 100644 index 7b3f3efe..00000000 --- a/launcher/widgets/complications/pulseometer_widget.c +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include "pulseometer_widget.h" -#include "watch.h" - -#define PULSOMETER_WIDGET_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.) -#define PULSOMETER_WIDGET_FREQUENCY (1 << PULSOMETER_WIDGET_FREQUENCY_FACTOR) - -void pulseometer_widget_setup(LauncherSettings *settings, void ** context_ptr) { - (void) settings; - if (*context_ptr == NULL) *context_ptr = malloc(sizeof(PulsometerState)); -} - -void pulseometer_widget_activate(LauncherSettings *settings, void *context) { - (void) settings; - memset(context, 0, sizeof(PulsometerState)); -} - -bool pulseometer_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { - printf("pulseometer_widget_loop\n"); - (void) settings; - PulsometerState *pulsometer_state = (PulsometerState *)context; - char buf[14]; - switch (event.event_type) { - case EVENT_TICK: - if (pulsometer_state->pulse == 0 && !pulsometer_state->measuring) { - switch (pulsometer_state->ticks % 5) { - case 0: - watch_display_string(" Hold ", 2); - break; - case 1: - watch_display_string(" Alarn", 4); - break; - case 2: - watch_display_string("+ Count ", 0); - break; - case 3: - watch_display_string(" 30Beats ", 0); - break; - case 4: - watch_clear_display(); - break; - } - pulsometer_state->ticks = (pulsometer_state->ticks + 1) % 5; - } else { - if (pulsometer_state->measuring && pulsometer_state->ticks) { - pulsometer_state->pulse = (int16_t)((30.0 * ((float)(60 << PULSOMETER_WIDGET_FREQUENCY_FACTOR) / (float)pulsometer_state->ticks)) + 0.5); - } - if (pulsometer_state->pulse > 240) { - watch_display_string(" Hi", 0); - } else if (pulsometer_state->pulse < 40) { - watch_display_string(" Lo", 0); - } else { - sprintf(buf, " %-3dbpn", pulsometer_state->pulse); - watch_display_string(buf, 0); - } - if (pulsometer_state->measuring) pulsometer_state->ticks++; - } - return false; - case EVENT_MODE_BUTTON_UP: - launcher_move_to_next_widget(); - return false; - case EVENT_LIGHT_BUTTON_UP: - launcher_illuminate_led(); - break; - case EVENT_ALARM_BUTTON_DOWN: - pulsometer_state->ticks = 0; - pulsometer_state->pulse = 0xFFFF; - pulsometer_state->measuring = true; - launcher_request_tick_frequency(PULSOMETER_WIDGET_FREQUENCY); - break; - case EVENT_ALARM_BUTTON_UP: - case EVENT_ALARM_LONG_PRESS: - pulsometer_state->measuring = false; - launcher_request_tick_frequency(1); - break; - default: - break; - } - - return true; -} - -void pulseometer_widget_resign(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; -} diff --git a/launcher/widgets/complications/pulseometer_widget.h b/launcher/widgets/complications/pulseometer_widget.h deleted file mode 100644 index 5b18d7f3..00000000 --- a/launcher/widgets/complications/pulseometer_widget.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef PULSEOMETER_WIDGET_H_ -#define PULSEOMETER_WIDGET_H_ - -#include "launcher.h" - -typedef struct { - bool measuring; - int16_t pulse; - int16_t ticks; -} PulsometerState; - -void pulseometer_widget_setup(LauncherSettings *settings, void ** context_ptr); -void pulseometer_widget_activate(LauncherSettings *settings, void *context); -bool pulseometer_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void pulseometer_widget_resign(LauncherSettings *settings, void *context); - -#define pulseometer_widget { \ - pulseometer_widget_setup, \ - pulseometer_widget_activate, \ - pulseometer_widget_loop, \ - pulseometer_widget_resign, \ -} - -#endif // PULSEOMETER_WIDGET_H_ \ No newline at end of file diff --git a/launcher/widgets/settings/preferences_widget.c b/launcher/widgets/settings/preferences_widget.c deleted file mode 100644 index 1618a72c..00000000 --- a/launcher/widgets/settings/preferences_widget.c +++ /dev/null @@ -1,120 +0,0 @@ -#include -#include "preferences_widget.h" -#include "watch.h" - -#define PREFERENCES_WIDGET_NUM_PREFEFENCES (5) -const char preferences_widget_titles[PREFERENCES_WIDGET_NUM_PREFEFENCES][11] = {"CL ", "Bt Beep ", "SC ", "Lt grn ", "Lt red "}; - -void preferences_widget_setup(LauncherSettings *settings, void ** context_ptr) { - (void) settings; - if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); -} - -void preferences_widget_activate(LauncherSettings *settings, void *context) { - (void) settings; - *((uint8_t *)context) = 0; - launcher_request_tick_frequency(4); // we need to manually blink some pixels -} - -bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { - printf("preferences_widget_loop\n"); - uint8_t current_page = *((uint8_t *)context); - switch (event.event_type) { - case EVENT_MODE_BUTTON_UP: - watch_set_led_off(); - launcher_move_to_next_widget(); - return false; - case EVENT_LIGHT_BUTTON_UP: - current_page = (current_page + 1) % PREFERENCES_WIDGET_NUM_PREFEFENCES; - *((uint8_t *)context) = current_page; - break; - case EVENT_ALARM_BUTTON_UP: - switch (current_page) { - case 0: - settings->bit.clock_mode_24h = !(settings->bit.clock_mode_24h); - break; - case 1: - settings->bit.button_should_sound = !(settings->bit.button_should_sound); - break; - case 2: - settings->bit.screensaver_interval = settings->bit.screensaver_interval + 1; - break; - case 3: - settings->bit.led_green_color = settings->bit.led_green_color + 1; - break; - case 4: - settings->bit.led_red_color = settings->bit.led_red_color + 1; - break; - } - break; - default: - break; - } - - watch_display_string((char *)preferences_widget_titles[current_page], 0); - - if (event.subsecond % 2) return current_page <= 2; - char buf[3]; - switch (current_page) { - case 0: - if (settings->bit.clock_mode_24h) watch_display_string("24h", 4); - else watch_display_string("12h", 4); - break; - case 1: - if (settings->bit.button_should_sound) watch_display_string("y", 9); - else watch_display_string("n", 9); - break; - case 2: - switch (settings->bit.screensaver_interval) { - case 0: - watch_display_string(" never", 4); - break; - case 1: - watch_display_string("1 hour", 4); - break; - case 2: - watch_display_string("2 hour", 4); - break; - case 3: - watch_display_string("6 hour", 4); - break; - case 4: - watch_display_string("12 hr", 4); - break; - case 5: - watch_display_string(" 1 day", 4); - break; - case 6: - watch_display_string(" 2 day", 4); - break; - case 7: - watch_display_string(" 7 day", 4); - break; - } - break; - case 3: - sprintf(buf, "%2d", settings->bit.led_green_color); - watch_display_string(buf, 8); - break; - case 4: - sprintf(buf, "%2d", settings->bit.led_red_color); - watch_display_string(buf, 8); - break; - } - - if (current_page > 2) { - watch_set_led_color(settings->bit.led_red_color ? (0xF | settings->bit.led_red_color << 4) : 0, - settings->bit.led_green_color ? (0xF | settings->bit.led_green_color << 4) : 0); - return false; - } - - watch_set_led_off(); - return true; -} - -void preferences_widget_resign(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; - watch_set_led_off(); - launcher_request_tick_frequency(1); -} diff --git a/launcher/widgets/settings/preferences_widget.h b/launcher/widgets/settings/preferences_widget.h deleted file mode 100644 index 11a189f8..00000000 --- a/launcher/widgets/settings/preferences_widget.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PREFERENCES_WIDGET_H_ -#define PREFERENCES_WIDGET_H_ - -#include "launcher.h" - -void preferences_widget_setup(LauncherSettings *settings, void ** context_ptr); -void preferences_widget_activate(LauncherSettings *settings, void *context); -bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void preferences_widget_resign(LauncherSettings *settings, void *context); - -#define preferences_widget { \ - preferences_widget_setup, \ - preferences_widget_activate, \ - preferences_widget_loop, \ - preferences_widget_resign, \ -} - -#endif // PREFERENCES_WIDGET_H_ \ No newline at end of file diff --git a/launcher/widgets/settings/set_time_widget.c b/launcher/widgets/settings/set_time_widget.c deleted file mode 100644 index d8cfb9fc..00000000 --- a/launcher/widgets/settings/set_time_widget.c +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include "set_time_widget.h" -#include "watch.h" - -#define SET_TIME_WIDGET_NUM_SETTINGS (6) -const char set_time_widget_titles[SET_TIME_WIDGET_NUM_SETTINGS][3] = {"HR", "MN", "SE", "YR", "MO", "DA"}; - -void set_time_widget_setup(LauncherSettings *settings, void ** context_ptr) { - (void) settings; - if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); -} - -void set_time_widget_activate(LauncherSettings *settings, void *context) { - (void) settings; - *((uint8_t *)context) = 0; - launcher_request_tick_frequency(4); -} - -bool set_time_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { - uint8_t current_page = *((uint8_t *)context); - const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31}; - watch_date_time date_time = watch_rtc_get_date_time(); - - switch (event.event_type) { - case EVENT_MODE_BUTTON_UP: - launcher_move_to_next_widget(); - return false; - case EVENT_LIGHT_BUTTON_UP: - current_page = (current_page + 1) % SET_TIME_WIDGET_NUM_SETTINGS; - *((uint8_t *)context) = current_page; - break; - case EVENT_ALARM_BUTTON_UP: - switch (current_page) { - case 0: // hour - date_time.unit.hour = (date_time.unit.hour + 1) % 24; - break; - case 1: // minute - date_time.unit.minute = (date_time.unit.minute + 1) % 60; - break; - case 2: // second - date_time.unit.second = 0; - break; - case 3: // year - // only allow 2021-2030. fix this sometime next decade - date_time.unit.year = ((date_time.unit.year % 10) + 1); - break; - case 4: // month - date_time.unit.month = (date_time.unit.month % 12) + 1; - break; - case 5: // day - date_time.unit.day = date_time.unit.day + 1; - // can't set to the 29th on a leap year. if it's february 29, set to 11:59 on the 28th. - // and it should roll over. - if (date_time.unit.day > days_in_month[date_time.unit.month - 1]) { - date_time.unit.day = 1; - } - break; - } - watch_rtc_set_date_time(date_time); - break; - default: - break; - } - - char buf[11]; - if (current_page < 3) { - watch_set_colon(); - if (settings->bit.clock_mode_24h) { - watch_set_indicator(WATCH_INDICATOR_24H); - sprintf(buf, "%s %2d%02d%02d", set_time_widget_titles[current_page], date_time.unit.hour, date_time.unit.minute, date_time.unit.second); - } else { - sprintf(buf, "%s %2d%02d%02d", set_time_widget_titles[current_page], (date_time.unit.hour % 12) ? (date_time.unit.hour % 12) : 12, date_time.unit.minute, date_time.unit.second); - if (date_time.unit.hour > 12) watch_set_indicator(WATCH_INDICATOR_PM); - else watch_clear_indicator(WATCH_INDICATOR_PM); - } - } else { - watch_clear_colon(); - watch_clear_indicator(WATCH_INDICATOR_24H); - watch_clear_indicator(WATCH_INDICATOR_PM); - sprintf(buf, "%s %2d%02d%02d", set_time_widget_titles[current_page], date_time.unit.year + 20, date_time.unit.month, date_time.unit.day); - } - if (event.subsecond % 2) { - switch (current_page) { - case 0: - case 3: - buf[4] = buf[5] = ' '; - break; - case 1: - case 4: - buf[6] = buf[7] = ' '; - break; - case 2: - case 5: - buf[8] = buf[9] = ' '; - break; - } - } - - watch_display_string(buf, 0); - - return true; -} - -void set_time_widget_resign(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; - watch_set_led_off(); - launcher_request_tick_frequency(1); -} diff --git a/launcher/widgets/settings/set_time_widget.h b/launcher/widgets/settings/set_time_widget.h deleted file mode 100644 index dc492dce..00000000 --- a/launcher/widgets/settings/set_time_widget.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef SET_TIME_WIDGET_H_ -#define SET_TIME_WIDGET_H_ - -#include "launcher.h" - -void set_time_widget_setup(LauncherSettings *settings, void ** context_ptr); -void set_time_widget_activate(LauncherSettings *settings, void *context); -bool set_time_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void set_time_widget_resign(LauncherSettings *settings, void *context); - -#define set_time_widget { \ - set_time_widget_setup, \ - set_time_widget_activate, \ - set_time_widget_loop, \ - set_time_widget_resign, \ -} - -#endif // SET_TIME_WIDGET_H_ diff --git a/movement/make/.gitignore b/movement/make/.gitignore new file mode 100755 index 00000000..3722ac63 --- /dev/null +++ b/movement/make/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/movement/make/Makefile b/movement/make/Makefile new file mode 100755 index 00000000..35b80079 --- /dev/null +++ b/movement/make/Makefile @@ -0,0 +1,32 @@ +# Leave this line at the top of the file; it has all the watch library sources and includes. +TOP = ../.. +include $(TOP)/make.mk + +# If you add any other subdirectories with header files you wish to include, add them after ../ +# Note that you will need to add a backslash at the end of any line you wish to continue, i.e. +# INCLUDES += \ +# -I../ \ +# -I../drivers/ \ +# -I../widgets/fitness/ +INCLUDES += \ + -I../ \ + -I../widgets/ \ + -I../widgets/clock/ \ + -I../widgets/settings/ \ + -I../widgets/complications/ \ + +# If you add any other source files you wish to compile, add them after ../app.c +# Note that you will need to add a backslash at the end of any line you wish to continue, i.e. +# SRCS += \ +# ../movement.c \ +# ../drivers/lis2dh.c \ +# ../widgets/fitness/step_count_widget.c +SRCS += \ + ../movement.c \ + ../widgets/clock/simple_clock_widget.c \ + ../widgets/settings/preferences_widget.c \ + ../widgets/settings/set_time_widget.c \ + ../widgets/complications/pulseometer_widget.c \ + +# Leave this line at the bottom of the file; it has all the targets for making your project. +include $(TOP)/rules.mk diff --git a/movement/movement.c b/movement/movement.c new file mode 100644 index 00000000..9c15833c --- /dev/null +++ b/movement/movement.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include "watch.h" +#include "movement.h" +#include "movement_config.h" + +LauncherState movement_state; +void * widget_contexts[MOVEMENT_NUM_WIDGETS]; +const int32_t movement_screensaver_deadlines[8] = {INT_MAX, 3600, 7200, 21600, 43200, 86400, 172800, 604800}; +LauncherEvent event; + +void cb_mode_btn_interrupt(); +void cb_light_btn_interrupt(); +void cb_alarm_btn_interrupt(); +void cb_alarm_btn_extwake(); +void cb_alarm_fired(); +void cb_tick(); + +static inline void _movement_reset_screensaver_countdown() { + // for testing, make the timeout happen 60x faster. + movement_state.screensaver_ticks = movement_screensaver_deadlines[movement_state.movement_settings.bit.screensaver_interval] / 60; +} + +void movement_request_tick_frequency(uint8_t freq) { + watch_rtc_disable_all_periodic_callbacks(); + movement_state.subsecond = 0; + movement_state.tick_frequency = freq; + watch_rtc_register_periodic_callback(cb_tick, freq); +} + +void movement_illuminate_led() { + movement_state.light_ticks = 3; +} + +void movement_move_to_widget(uint8_t widget_index) { + movement_state.widget_changed = true; + movement_state.next_widget = widget_index; +} + +void movement_move_to_next_widget() { + movement_move_to_widget((movement_state.current_widget + 1) % MOVEMENT_NUM_WIDGETS); +} + +void app_init() { + memset(&movement_state, 0, sizeof(movement_state)); + + movement_state.movement_settings.bit.led_green_color = 0xF; + movement_state.movement_settings.bit.button_should_sound = true; + movement_state.movement_settings.bit.screensaver_interval = 1; + _movement_reset_screensaver_countdown(); +} + +void app_wake_from_deep_sleep() { + // This app does not support deep sleep mode. +} + +void app_setup() { + static bool is_first_launch = true; + + if (is_first_launch) { + for(uint8_t i = 0; i < MOVEMENT_NUM_WIDGETS; i++) { + widget_contexts[i] = NULL; + is_first_launch = false; + } + } + if (movement_state.screensaver_ticks != -1) { + watch_disable_extwake_interrupt(BTN_ALARM); + watch_rtc_disable_alarm_callback(); + + watch_enable_external_interrupts(); + watch_register_interrupt_callback(BTN_MODE, cb_mode_btn_interrupt, INTERRUPT_TRIGGER_BOTH); + watch_register_interrupt_callback(BTN_LIGHT, cb_light_btn_interrupt, INTERRUPT_TRIGGER_BOTH); + watch_register_interrupt_callback(BTN_ALARM, cb_alarm_btn_interrupt, INTERRUPT_TRIGGER_BOTH); + + watch_enable_buzzer(); + watch_enable_leds(); + watch_enable_display(); + + movement_request_tick_frequency(1); + + for(uint8_t i = 0; i < MOVEMENT_NUM_WIDGETS; i++) { + widgets[i].setup(&movement_state.movement_settings, &widget_contexts[i]); + } + + widgets[0].activate(&movement_state.movement_settings, widget_contexts[0]); + event.subsecond = 0; + event.event_type = EVENT_ACTIVATE; + } +} + +void app_prepare_for_sleep() { +} + +void app_wake_from_sleep() { +} + +bool app_loop() { + if (movement_state.widget_changed) { + if (movement_state.movement_settings.bit.button_should_sound) { + // low note for nonzero case, high note for return to widget 0 + watch_buzzer_play_note(movement_state.next_widget ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 50); + } + widgets[movement_state.current_widget].resign(&movement_state.movement_settings, widget_contexts[movement_state.current_widget]); + movement_state.current_widget = movement_state.next_widget; + watch_clear_display(); + widgets[movement_state.current_widget].activate(&movement_state.movement_settings, widget_contexts[movement_state.current_widget]); + event.subsecond = 0; + event.event_type = EVENT_ACTIVATE; + movement_state.widget_changed = false; + } + + // If the LED is off and should be on, turn it on + if (movement_state.light_ticks > 0 && !movement_state.led_on) { + watch_set_led_color(movement_state.movement_settings.bit.led_red_color ? (0xF | movement_state.movement_settings.bit.led_red_color << 4) : 0, + movement_state.movement_settings.bit.led_green_color ? (0xF | movement_state.movement_settings.bit.led_green_color << 4) : 0); + movement_state.led_on = true; + + } + + // if the LED is on and should be off, turn it off + if (movement_state.led_on && movement_state.light_ticks == 0) { + // unless the user is holding down the LIGHT button, in which case, give them more time. + if (watch_get_pin_level(BTN_LIGHT)) { + movement_state.light_ticks = 3; + } else { + watch_set_led_off(); + movement_state.led_on = false; + } + } + + // if we have timed out of our screensaver countdown, enter screensaver mode. + if (movement_state.screensaver_ticks == 0) { + movement_state.screensaver_ticks = -1; + watch_date_time alarm_time; + alarm_time.reg = 0; + alarm_time.unit.second = 59; // after a match, the alarm fires at the next rising edge of CLK_RTC_CNT, so 59 seconds lets us update at :00 + watch_rtc_register_alarm_callback(cb_alarm_fired, alarm_time, ALARM_MATCH_SS); + watch_register_extwake_callback(BTN_ALARM, cb_alarm_btn_extwake, true); + event.event_type = EVENT_NONE; + event.subsecond = 0; + + // this is a little mini-runloop. + // as long as screensaver_ticks is -1 (i.e. screensaver is active), we wake up here, update the screen, and go right back to sleep. + while (movement_state.screensaver_ticks == -1) { + event.event_type = EVENT_SCREENSAVER; + widgets[movement_state.current_widget].loop(event, &movement_state.movement_settings, widget_contexts[movement_state.current_widget]); + watch_enter_shallow_sleep(true); + } + // as soon as screensaver_ticks is reset by the extwake handler, we bail out of the loop and reactivate ourselves. + event.event_type = EVENT_ACTIVATE; + // this is a hack tho: waking from shallow sleep, app_setup does get called, but it happens before we have reset our ticks. + // need to figure out if there's a better heuristic for determining how we woke up. + app_setup(); + } + + static bool can_sleep = true; + + if (event.event_type) { + event.subsecond = movement_state.subsecond; + can_sleep = widgets[movement_state.current_widget].loop(event, &movement_state.movement_settings, widget_contexts[movement_state.current_widget]); + event.event_type = EVENT_NONE; + event.subsecond = 0; + } + + return can_sleep && !movement_state.led_on; +} + +LauncherEventType _figure_out_button_event(LauncherEventType button_down_event, uint8_t *down_timestamp) { + watch_date_time date_time = watch_rtc_get_date_time(); + if (*down_timestamp) { + uint8_t diff = ((61 + date_time.unit.second) - *down_timestamp) % 60; + *down_timestamp = 0; + if (diff > 1) return button_down_event + 2; + else return button_down_event + 1; + } else { + *down_timestamp = date_time.unit.second + 1; + return button_down_event; + } +} + +void cb_light_btn_interrupt() { + _movement_reset_screensaver_countdown(); + event.event_type = _figure_out_button_event(EVENT_LIGHT_BUTTON_DOWN, &movement_state.light_down_timestamp); +} + +void cb_mode_btn_interrupt() { + _movement_reset_screensaver_countdown(); + event.event_type = _figure_out_button_event(EVENT_MODE_BUTTON_DOWN, &movement_state.mode_down_timestamp); +} + +void cb_alarm_btn_interrupt() { + _movement_reset_screensaver_countdown(); + event.event_type = _figure_out_button_event(EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp); +} + +void cb_alarm_btn_extwake() { + // wake up! + _movement_reset_screensaver_countdown(); +} + +void cb_alarm_fired() { + event.event_type = EVENT_SCREENSAVER; +} + +void cb_tick() { + event.event_type = EVENT_TICK; + watch_date_time date_time = watch_rtc_get_date_time(); + if (date_time.unit.second != movement_state.last_second) { + if (movement_state.light_ticks) movement_state.light_ticks--; + if (movement_state.movement_settings.bit.screensaver_interval && movement_state.screensaver_ticks > 0) movement_state.screensaver_ticks--; + + movement_state.last_second = date_time.unit.second; + movement_state.subsecond = 0; + } else { + movement_state.subsecond++; + } +} diff --git a/movement/movement.h b/movement/movement.h new file mode 100644 index 00000000..025a5aa6 --- /dev/null +++ b/movement/movement.h @@ -0,0 +1,89 @@ +#ifndef MOVEMENT_H_ +#define MOVEMENT_H_ +#include +#include + +// TODO: none of this is implemented +typedef union { + struct { + uint32_t reserved : 3; + uint32_t clock_mode_24h : 1; // determines whether clock should use 12 or 24 hour mode. + uint32_t button_should_sound : 1; // if true, pressing a button emits a sound. + uint32_t signal_should_sound : 1; // if true, a double beep is played at the top of each hour. + uint32_t alarm_should_sound : 1; // if true, the alarm interrupt can match a time and play a song. + uint32_t alarm_minute : 6; // the minute of the alarm we want to match + uint32_t alarm_hour : 5; // the second of the alarm we want to match + uint32_t screensaver_interval : 3; // 0 to disable screensaver, or a screensaver activation interval. + uint32_t led_duration : 3; // how many seconds to shine the LED for, or 0 to disable it. + uint32_t led_red_color : 4; // for general purpose illumination, the red LED value (0-15) + uint32_t led_green_color : 4; // for general purpose illumination, the green LED value (0-15) + } bit; + uint32_t value; +} LauncherSettings; + +typedef enum { + EVENT_NONE = 0, // There is no event to report. + EVENT_ACTIVATE, // Your widget is entering the foreground. + EVENT_TICK, // Most common event type. Your widget is being called from the tick callback. + EVENT_SCREENSAVER, // Your widget is being asked to display its output for screensaver mode. + EVENT_LIGHT_BUTTON_DOWN, // The light button has been pressed, but not yet released. + EVENT_LIGHT_BUTTON_UP, // The light button was pressed and released. + EVENT_LIGHT_LONG_PRESS, // The light button was held for >2 seconds, and released. + EVENT_MODE_BUTTON_DOWN, // The mode button has been pressed, but not yet released. + EVENT_MODE_BUTTON_UP, // The mode button was pressed and released. + EVENT_MODE_LONG_PRESS, // The mode button was held for >2 seconds, and released. + EVENT_ALARM_BUTTON_DOWN, // The alarm button has been pressed, but not yet released. + EVENT_ALARM_BUTTON_UP, // The alarm button was pressed and released. + EVENT_ALARM_LONG_PRESS, // The alarm button was held for >2 seconds, and released. +} LauncherEventType; + +typedef struct { + uint8_t event_type; + uint8_t subsecond; +} LauncherEvent; + +typedef void (*movement_widget_setup)(LauncherSettings *settings, void ** context_ptr); +typedef void (*movement_widget_activate)(LauncherSettings *settings, void *context); +typedef bool (*movement_widget_loop)(LauncherEvent event, LauncherSettings *settings, void *context); +typedef void (*movement_widget_resign)(LauncherSettings *settings, void *context); + +typedef struct { + movement_widget_setup setup; + movement_widget_activate activate; + movement_widget_loop loop; + movement_widget_resign resign; +} WatchWidget; + +typedef struct { + // properties stored in BACKUP register + LauncherSettings movement_settings; + + // transient properties + int16_t current_widget; + int16_t next_widget; + bool widget_changed; + + // LED stuff + uint8_t light_ticks; + bool led_on; + + // button tracking for long press + uint8_t light_down_timestamp; + uint8_t mode_down_timestamp; + uint8_t alarm_down_timestamp; + + // screensaver countdown + int32_t screensaver_ticks; + + // stuff for subsecond tracking + uint8_t tick_frequency; + uint8_t last_second; + uint8_t subsecond; +} LauncherState; + +void movement_move_to_widget(uint8_t widget_index); +void movement_move_to_next_widget(); +void movement_illuminate_led(); +void movement_request_tick_frequency(uint8_t freq); + +#endif // MOVEMENT_H_ diff --git a/movement/movement_config.h b/movement/movement_config.h new file mode 100644 index 00000000..49f342ca --- /dev/null +++ b/movement/movement_config.h @@ -0,0 +1,18 @@ +#ifndef MOVEMENT_CONFIG_H_ +#define MOVEMENT_CONFIG_H_ + +#include "simple_clock_widget.h" +#include "preferences_widget.h" +#include "set_time_widget.h" +#include "pulseometer_widget.h" + +#define MOVEMENT_NUM_WIDGETS 3 + +WatchWidget widgets[MOVEMENT_NUM_WIDGETS] = { + simple_clock_widget, + preferences_widget, + set_time_widget, +}; + + +#endif // MOVEMENT_CONFIG_H_ diff --git a/movement/widgets/clock/simple_clock_widget.c b/movement/widgets/clock/simple_clock_widget.c new file mode 100644 index 00000000..22218860 --- /dev/null +++ b/movement/widgets/clock/simple_clock_widget.c @@ -0,0 +1,92 @@ +#include +#include "simple_clock_widget.h" +#include "watch.h" + +void simple_clock_widget_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + // the only context we need is the timestamp of the previous tick. + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint32_t)); +} + +void simple_clock_widget_activate(LauncherSettings *settings, void *context) { + if (settings->bit.clock_mode_24h) { + watch_set_indicator(WATCH_INDICATOR_24H); + } + watch_set_colon(); + // this ensures that none of the timestamp fields will match, so we can re-render them all. + *((uint32_t *)context) = 0xFFFFFFFF; +} + +bool simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + printf("simple_clock_widget_loop\n"); + const char weekdays[7][3] = {"SA", "SU", "MO", "TU", "WE", "TH", "FR"}; + char buf[11]; + uint8_t pos; + + watch_date_time date_time; + uint32_t previous_date_time; + switch (event.event_type) { + case EVENT_ACTIVATE: + case EVENT_TICK: + case EVENT_SCREENSAVER: + date_time = watch_rtc_get_date_time(); + previous_date_time = *((uint32_t *)context); + *((uint32_t *)context) = date_time.reg; + + if (date_time.reg >> 6 == previous_date_time >> 6 && event.event_type != EVENT_SCREENSAVER) { + // everything before seconds is the same, don't waste cycles setting those segments. + pos = 8; + sprintf(buf, "%02d", date_time.unit.second); + } else if (date_time.reg >> 12 == previous_date_time >> 12 && event.event_type != EVENT_SCREENSAVER) { + // everything before minutes is the same. + pos = 6; + sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second); + } else { + // other stuff changed; let's do it all. + if (!settings->bit.clock_mode_24h) { + // if we are in 12 hour mode, do some cleanup. + if (date_time.unit.hour < 12) { + watch_clear_indicator(WATCH_INDICATOR_PM); + } else { + watch_set_indicator(WATCH_INDICATOR_PM); + } + date_time.unit.hour %= 12; + if (date_time.unit.hour == 0) date_time.unit.hour = 12; + } + pos = 0; + if (event.event_type == EVENT_SCREENSAVER) { + sprintf(buf, "%s%2d%2d%02d ", weekdays[simple_clock_widget_get_weekday(date_time.unit.year, date_time.unit.month, date_time.unit.day)], date_time.unit.day, date_time.unit.hour, date_time.unit.minute); + } else { + sprintf(buf, "%s%2d%2d%02d%02d", weekdays[simple_clock_widget_get_weekday(date_time.unit.year, date_time.unit.month, date_time.unit.day)], date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); + } + } + watch_display_string(buf, pos); + break; + case EVENT_MODE_BUTTON_UP: + movement_move_to_next_widget(); + return false; + case EVENT_LIGHT_BUTTON_UP: + movement_illuminate_led(); + break; + case EVENT_ALARM_BUTTON_UP: + break; + default: + break; + } + + return true; +} + +void simple_clock_widget_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} + +uint8_t simple_clock_widget_get_weekday(uint16_t year, uint16_t month, uint16_t day) { + year += 20; + if (month <= 2) { + month += 12; + year--; + } + return (day + 13 * (month + 1) / 5 + year + year / 4 + 525) % 7; +} diff --git a/movement/widgets/clock/simple_clock_widget.h b/movement/widgets/clock/simple_clock_widget.h new file mode 100644 index 00000000..3bf4c9a3 --- /dev/null +++ b/movement/widgets/clock/simple_clock_widget.h @@ -0,0 +1,20 @@ +#ifndef SIMPLE_CLOCK_WIDGET_H_ +#define SIMPLE_CLOCK_WIDGET_H_ + +#include "movement.h" + +void simple_clock_widget_setup(LauncherSettings *settings, void ** context_ptr); +void simple_clock_widget_activate(LauncherSettings *settings, void *context); +bool simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void simple_clock_widget_resign(LauncherSettings *settings, void *context); + +uint8_t simple_clock_widget_get_weekday(uint16_t day, uint16_t month, uint16_t year); + +#define simple_clock_widget { \ + simple_clock_widget_setup, \ + simple_clock_widget_activate, \ + simple_clock_widget_loop, \ + simple_clock_widget_resign, \ +} + +#endif // FAKE_WIDGET_H_ \ No newline at end of file diff --git a/movement/widgets/complications/pulseometer_widget.c b/movement/widgets/complications/pulseometer_widget.c new file mode 100644 index 00000000..6384685a --- /dev/null +++ b/movement/widgets/complications/pulseometer_widget.c @@ -0,0 +1,87 @@ +#include +#include +#include "pulseometer_widget.h" +#include "watch.h" + +#define PULSOMETER_WIDGET_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.) +#define PULSOMETER_WIDGET_FREQUENCY (1 << PULSOMETER_WIDGET_FREQUENCY_FACTOR) + +void pulseometer_widget_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(PulsometerState)); +} + +void pulseometer_widget_activate(LauncherSettings *settings, void *context) { + (void) settings; + memset(context, 0, sizeof(PulsometerState)); +} + +bool pulseometer_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + printf("pulseometer_widget_loop\n"); + (void) settings; + PulsometerState *pulsometer_state = (PulsometerState *)context; + char buf[14]; + switch (event.event_type) { + case EVENT_TICK: + if (pulsometer_state->pulse == 0 && !pulsometer_state->measuring) { + switch (pulsometer_state->ticks % 5) { + case 0: + watch_display_string(" Hold ", 2); + break; + case 1: + watch_display_string(" Alarn", 4); + break; + case 2: + watch_display_string("+ Count ", 0); + break; + case 3: + watch_display_string(" 30Beats ", 0); + break; + case 4: + watch_clear_display(); + break; + } + pulsometer_state->ticks = (pulsometer_state->ticks + 1) % 5; + } else { + if (pulsometer_state->measuring && pulsometer_state->ticks) { + pulsometer_state->pulse = (int16_t)((30.0 * ((float)(60 << PULSOMETER_WIDGET_FREQUENCY_FACTOR) / (float)pulsometer_state->ticks)) + 0.5); + } + if (pulsometer_state->pulse > 240) { + watch_display_string(" Hi", 0); + } else if (pulsometer_state->pulse < 40) { + watch_display_string(" Lo", 0); + } else { + sprintf(buf, " %-3dbpn", pulsometer_state->pulse); + watch_display_string(buf, 0); + } + if (pulsometer_state->measuring) pulsometer_state->ticks++; + } + return false; + case EVENT_MODE_BUTTON_UP: + movement_move_to_next_widget(); + return false; + case EVENT_LIGHT_BUTTON_UP: + movement_illuminate_led(); + break; + case EVENT_ALARM_BUTTON_DOWN: + pulsometer_state->ticks = 0; + pulsometer_state->pulse = 0xFFFF; + pulsometer_state->measuring = true; + movement_request_tick_frequency(PULSOMETER_WIDGET_FREQUENCY); + break; + case EVENT_ALARM_BUTTON_UP: + case EVENT_ALARM_LONG_PRESS: + pulsometer_state->measuring = false; + movement_request_tick_frequency(1); + break; + default: + break; + } + + return true; +} + +void pulseometer_widget_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} diff --git a/movement/widgets/complications/pulseometer_widget.h b/movement/widgets/complications/pulseometer_widget.h new file mode 100644 index 00000000..e5947660 --- /dev/null +++ b/movement/widgets/complications/pulseometer_widget.h @@ -0,0 +1,24 @@ +#ifndef PULSEOMETER_WIDGET_H_ +#define PULSEOMETER_WIDGET_H_ + +#include "movement.h" + +typedef struct { + bool measuring; + int16_t pulse; + int16_t ticks; +} PulsometerState; + +void pulseometer_widget_setup(LauncherSettings *settings, void ** context_ptr); +void pulseometer_widget_activate(LauncherSettings *settings, void *context); +bool pulseometer_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void pulseometer_widget_resign(LauncherSettings *settings, void *context); + +#define pulseometer_widget { \ + pulseometer_widget_setup, \ + pulseometer_widget_activate, \ + pulseometer_widget_loop, \ + pulseometer_widget_resign, \ +} + +#endif // PULSEOMETER_WIDGET_H_ \ No newline at end of file diff --git a/movement/widgets/settings/preferences_widget.c b/movement/widgets/settings/preferences_widget.c new file mode 100644 index 00000000..c4537329 --- /dev/null +++ b/movement/widgets/settings/preferences_widget.c @@ -0,0 +1,120 @@ +#include +#include "preferences_widget.h" +#include "watch.h" + +#define PREFERENCES_WIDGET_NUM_PREFEFENCES (5) +const char preferences_widget_titles[PREFERENCES_WIDGET_NUM_PREFEFENCES][11] = {"CL ", "Bt Beep ", "SC ", "Lt grn ", "Lt red "}; + +void preferences_widget_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); +} + +void preferences_widget_activate(LauncherSettings *settings, void *context) { + (void) settings; + *((uint8_t *)context) = 0; + movement_request_tick_frequency(4); // we need to manually blink some pixels +} + +bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + printf("preferences_widget_loop\n"); + uint8_t current_page = *((uint8_t *)context); + switch (event.event_type) { + case EVENT_MODE_BUTTON_UP: + watch_set_led_off(); + movement_move_to_next_widget(); + return false; + case EVENT_LIGHT_BUTTON_UP: + current_page = (current_page + 1) % PREFERENCES_WIDGET_NUM_PREFEFENCES; + *((uint8_t *)context) = current_page; + break; + case EVENT_ALARM_BUTTON_UP: + switch (current_page) { + case 0: + settings->bit.clock_mode_24h = !(settings->bit.clock_mode_24h); + break; + case 1: + settings->bit.button_should_sound = !(settings->bit.button_should_sound); + break; + case 2: + settings->bit.screensaver_interval = settings->bit.screensaver_interval + 1; + break; + case 3: + settings->bit.led_green_color = settings->bit.led_green_color + 1; + break; + case 4: + settings->bit.led_red_color = settings->bit.led_red_color + 1; + break; + } + break; + default: + break; + } + + watch_display_string((char *)preferences_widget_titles[current_page], 0); + + if (event.subsecond % 2) return current_page <= 2; + char buf[3]; + switch (current_page) { + case 0: + if (settings->bit.clock_mode_24h) watch_display_string("24h", 4); + else watch_display_string("12h", 4); + break; + case 1: + if (settings->bit.button_should_sound) watch_display_string("y", 9); + else watch_display_string("n", 9); + break; + case 2: + switch (settings->bit.screensaver_interval) { + case 0: + watch_display_string(" never", 4); + break; + case 1: + watch_display_string("1 hour", 4); + break; + case 2: + watch_display_string("2 hour", 4); + break; + case 3: + watch_display_string("6 hour", 4); + break; + case 4: + watch_display_string("12 hr", 4); + break; + case 5: + watch_display_string(" 1 day", 4); + break; + case 6: + watch_display_string(" 2 day", 4); + break; + case 7: + watch_display_string(" 7 day", 4); + break; + } + break; + case 3: + sprintf(buf, "%2d", settings->bit.led_green_color); + watch_display_string(buf, 8); + break; + case 4: + sprintf(buf, "%2d", settings->bit.led_red_color); + watch_display_string(buf, 8); + break; + } + + if (current_page > 2) { + watch_set_led_color(settings->bit.led_red_color ? (0xF | settings->bit.led_red_color << 4) : 0, + settings->bit.led_green_color ? (0xF | settings->bit.led_green_color << 4) : 0); + return false; + } + + watch_set_led_off(); + return true; +} + +void preferences_widget_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; + watch_set_led_off(); + movement_request_tick_frequency(1); +} diff --git a/movement/widgets/settings/preferences_widget.h b/movement/widgets/settings/preferences_widget.h new file mode 100644 index 00000000..3d463027 --- /dev/null +++ b/movement/widgets/settings/preferences_widget.h @@ -0,0 +1,18 @@ +#ifndef PREFERENCES_WIDGET_H_ +#define PREFERENCES_WIDGET_H_ + +#include "movement.h" + +void preferences_widget_setup(LauncherSettings *settings, void ** context_ptr); +void preferences_widget_activate(LauncherSettings *settings, void *context); +bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void preferences_widget_resign(LauncherSettings *settings, void *context); + +#define preferences_widget { \ + preferences_widget_setup, \ + preferences_widget_activate, \ + preferences_widget_loop, \ + preferences_widget_resign, \ +} + +#endif // PREFERENCES_WIDGET_H_ \ No newline at end of file diff --git a/movement/widgets/settings/set_time_widget.c b/movement/widgets/settings/set_time_widget.c new file mode 100644 index 00000000..9464eb5b --- /dev/null +++ b/movement/widgets/settings/set_time_widget.c @@ -0,0 +1,109 @@ +#include +#include "set_time_widget.h" +#include "watch.h" + +#define SET_TIME_WIDGET_NUM_SETTINGS (6) +const char set_time_widget_titles[SET_TIME_WIDGET_NUM_SETTINGS][3] = {"HR", "MN", "SE", "YR", "MO", "DA"}; + +void set_time_widget_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); +} + +void set_time_widget_activate(LauncherSettings *settings, void *context) { + (void) settings; + *((uint8_t *)context) = 0; + movement_request_tick_frequency(4); +} + +bool set_time_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + uint8_t current_page = *((uint8_t *)context); + const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31}; + watch_date_time date_time = watch_rtc_get_date_time(); + + switch (event.event_type) { + case EVENT_MODE_BUTTON_UP: + movement_move_to_next_widget(); + return false; + case EVENT_LIGHT_BUTTON_UP: + current_page = (current_page + 1) % SET_TIME_WIDGET_NUM_SETTINGS; + *((uint8_t *)context) = current_page; + break; + case EVENT_ALARM_BUTTON_UP: + switch (current_page) { + case 0: // hour + date_time.unit.hour = (date_time.unit.hour + 1) % 24; + break; + case 1: // minute + date_time.unit.minute = (date_time.unit.minute + 1) % 60; + break; + case 2: // second + date_time.unit.second = 0; + break; + case 3: // year + // only allow 2021-2030. fix this sometime next decade + date_time.unit.year = ((date_time.unit.year % 10) + 1); + break; + case 4: // month + date_time.unit.month = (date_time.unit.month % 12) + 1; + break; + case 5: // day + date_time.unit.day = date_time.unit.day + 1; + // can't set to the 29th on a leap year. if it's february 29, set to 11:59 on the 28th. + // and it should roll over. + if (date_time.unit.day > days_in_month[date_time.unit.month - 1]) { + date_time.unit.day = 1; + } + break; + } + watch_rtc_set_date_time(date_time); + break; + default: + break; + } + + char buf[11]; + if (current_page < 3) { + watch_set_colon(); + if (settings->bit.clock_mode_24h) { + watch_set_indicator(WATCH_INDICATOR_24H); + sprintf(buf, "%s %2d%02d%02d", set_time_widget_titles[current_page], date_time.unit.hour, date_time.unit.minute, date_time.unit.second); + } else { + sprintf(buf, "%s %2d%02d%02d", set_time_widget_titles[current_page], (date_time.unit.hour % 12) ? (date_time.unit.hour % 12) : 12, date_time.unit.minute, date_time.unit.second); + if (date_time.unit.hour > 12) watch_set_indicator(WATCH_INDICATOR_PM); + else watch_clear_indicator(WATCH_INDICATOR_PM); + } + } else { + watch_clear_colon(); + watch_clear_indicator(WATCH_INDICATOR_24H); + watch_clear_indicator(WATCH_INDICATOR_PM); + sprintf(buf, "%s %2d%02d%02d", set_time_widget_titles[current_page], date_time.unit.year + 20, date_time.unit.month, date_time.unit.day); + } + if (event.subsecond % 2) { + switch (current_page) { + case 0: + case 3: + buf[4] = buf[5] = ' '; + break; + case 1: + case 4: + buf[6] = buf[7] = ' '; + break; + case 2: + case 5: + buf[8] = buf[9] = ' '; + break; + } + } + + watch_display_string(buf, 0); + + return true; +} + +void set_time_widget_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; + watch_set_led_off(); + movement_request_tick_frequency(1); +} diff --git a/movement/widgets/settings/set_time_widget.h b/movement/widgets/settings/set_time_widget.h new file mode 100644 index 00000000..363fd571 --- /dev/null +++ b/movement/widgets/settings/set_time_widget.h @@ -0,0 +1,18 @@ +#ifndef SET_TIME_WIDGET_H_ +#define SET_TIME_WIDGET_H_ + +#include "movement.h" + +void set_time_widget_setup(LauncherSettings *settings, void ** context_ptr); +void set_time_widget_activate(LauncherSettings *settings, void *context); +bool set_time_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void set_time_widget_resign(LauncherSettings *settings, void *context); + +#define set_time_widget { \ + set_time_widget_setup, \ + set_time_widget_activate, \ + set_time_widget_loop, \ + set_time_widget_resign, \ +} + +#endif // SET_TIME_WIDGET_H_ -- cgit v1.2.3