From 624ff19580e87915575118ca5ab4f6c33d811003 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Tue, 28 Sep 2021 01:06:37 -0400 Subject: wip: late-night untested launcher app musings --- launcher/launcher.c | 142 +++++++++++++++++++++++++++++++++++++++++ launcher/launcher.h | 74 +++++++++++++++++++++ launcher/launcher_config.h | 13 ++++ launcher/make/.gitignore | 1 + launcher/make/Makefile | 26 ++++++++ launcher/widgets/fake_widget.c | 24 +++++++ launcher/widgets/fake_widget.h | 18 ++++++ 7 files changed, 298 insertions(+) create mode 100644 launcher/launcher.c create mode 100644 launcher/launcher.h create mode 100644 launcher/launcher_config.h create mode 100755 launcher/make/.gitignore create mode 100755 launcher/make/Makefile create mode 100644 launcher/widgets/fake_widget.c create mode 100644 launcher/widgets/fake_widget.h diff --git a/launcher/launcher.c b/launcher/launcher.c new file mode 100644 index 00000000..1ad0b9da --- /dev/null +++ b/launcher/launcher.c @@ -0,0 +1,142 @@ +#include +#include +#include "watch.h" +#include "launcher.h" +#include "launcher_config.h" + +LauncherState launcher_state; +void * widget_contexts[LAUNCHER_NUM_WIDGETS]; + +void app_init() { + memset(&launcher_state, 0, sizeof(launcher_state)); +} + +void app_wake_from_deep_sleep() { + // This app does not support deep sleep mode. +} + +void app_setup() { + watch_enable_external_interrupts(); + watch_register_interrupt_callback(BTN_MODE, cb_mode_pressed, INTERRUPT_TRIGGER_BOTH); + watch_register_interrupt_callback(BTN_LIGHT, cb_light_pressed, INTERRUPT_TRIGGER_BOTH); + watch_register_interrupt_callback(BTN_ALARM, cb_alarm_pressed, INTERRUPT_TRIGGER_BOTH); + + watch_enable_buzzer(); + watch_enable_leds(); + watch_enable_display(); + + watch_register_tick_callback(cb_tick); + + for(uint8_t i = 0; i < LAUNCHER_NUM_WIDGETS; i++) { + widgets[i].setup(&launcher_state.launcherSettings, widget_contexts[i]); + } +} + +void app_prepare_for_sleep() { +} + +void app_wake_from_sleep() { +} + +LauncherEvent event; + +bool app_loop() { + // play a beep if the widget has changed in response to a user's press of the MODE button + if (launcher_state.widget_changed) { + // low note for nonzero case, high note for return to clock + watch_buzzer_play_note(launcher_state.current_widget ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 100); + 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_green(); + 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 (event) { + event = 0; + bool can_sleep = widgets[launcher_state.current_widget].loop(event, &launcher_state.launcherSettings, widget_contexts[launcher_state.current_widget]); + if (can_sleep) return true; + + event = EVENT_LOOP; + return false; + } + + return true; +} + +void move_to_next_widget() { + launcher_state.widget_changed = true; + widgets[launcher_state.current_widget].enter_background(&launcher_state.launcherSettings, widget_contexts[launcher_state.current_widget]); + launcher_state.current_widget = (launcher_state.current_widget + 1) % LAUNCHER_NUM_WIDGETS; + widgets[launcher_state.current_widget].enter_foreground(&launcher_state.launcherSettings, widget_contexts[launcher_state.current_widget]); +} + +void move_to_first_widget() { + launcher_state.widget_changed = true; + widgets[launcher_state.current_widget].enter_background(&launcher_state.launcherSettings, widget_contexts[launcher_state.current_widget]); + launcher_state.current_widget = 0; + widgets[0].enter_foreground(&launcher_state.launcherSettings, widget_contexts[0]); +} + +void illuminate_led() { + launcher_state.light_ticks = 3; +} + +void cb_light_pressed() { + struct calendar_date_time date_time; + watch_get_date_time(&date_time); + if (launcher_state.light_down_timestamp) { + uint8_t diff = (61 + date_time.time.sec) - launcher_state.light_down_timestamp; + if (diff > 1) event = EVENT_LIGHT_LONG_PRESS; + else event = EVENT_LIGHT_BUTTON_UP; + launcher_state.light_down_timestamp = 0; + } else { + launcher_state.light_down_timestamp = date_time.time.sec + 1; + event = EVENT_LIGHT_BUTTON_DOWN; + } +} + +void cb_mode_pressed() { + struct calendar_date_time date_time; + watch_get_date_time(&date_time); + if (launcher_state.mode_down_timestamp) { + uint8_t diff = (61 + date_time.time.sec) - launcher_state.mode_down_timestamp; + if (diff > 1) event = EVENT_MODE_LONG_PRESS; + else event = EVENT_MODE_BUTTON_UP; + launcher_state.mode_down_timestamp = 0; + } else { + launcher_state.mode_down_timestamp = date_time.time.sec + 1; + event = EVENT_MODE_BUTTON_DOWN; + } +} + +void cb_alarm_pressed() { + struct calendar_date_time date_time; + watch_get_date_time(&date_time); + if (launcher_state.alarm_down_timestamp) { + uint8_t diff = (61 + date_time.time.sec) - launcher_state.alarm_down_timestamp; + if (diff > 1) event = EVENT_ALARM_LONG_PRESS; + else event = EVENT_ALARM_BUTTON_UP; + launcher_state.alarm_down_timestamp = 0; + } else { + launcher_state.alarm_down_timestamp = date_time.time.sec + 1; + event = EVENT_ALARM_BUTTON_DOWN; + } +} + +void cb_tick() { + event = EVENT_TICK; +} diff --git a/launcher/launcher.h b/launcher/launcher.h new file mode 100644 index 00000000..888d1adb --- /dev/null +++ b/launcher/launcher.h @@ -0,0 +1,74 @@ +#ifndef LAUNCHER_H_ +#define LAUNCHER_H_ +#include +#include + +// TODO: none of this is implemented +typedef union { + struct { + uint32_t reserved : 1; + uint32_t clock_mode_24h : 1; // determines whether display should use 12 or 24 hour mode. + 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 plays a song. + uint32_t note_index : 7; // the index of the tone to play, or 0x7F for no tone. + uint32_t snapback_enabled : 1; // if true, snaps back to the main screen after 5 minutes + uint32_t sleep_interval : 3; // 0 to disable sleep, or a number of days to sleep after. + uint32_t sleep_blanks_screen : 1; // blank screen or display "SLEEP" when asleep + uint32_t led_red_color : 8; // for general purpose illumination, the red LED value + uint32_t led_green_color : 8; // for general purpose illumination, the green LED value + } bit; + uint32_t value; +} LauncherSettings; + +typedef enum LauncherEvent { + 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_LOOP, // The app did not sleep, and is going into another invocation of the run loop. + 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. +} LauncherEvent; + +typedef void (*launcher_widget_setup)(LauncherSettings *settings, void ** context_ptr); +typedef void (*launcher_widget_enter_foreground)(LauncherSettings *settings, void *context); +typedef bool (*launcher_widget_loop)(LauncherEvent event, LauncherSettings *settings, void *context); +typedef void (*launcher_widget_enter_background)(LauncherSettings *settings, void *context); + +typedef struct WatchWidget { + launcher_widget_setup setup; + launcher_widget_enter_foreground enter_foreground; + launcher_widget_loop loop; + launcher_widget_enter_background enter_background; +} WatchWidget; + +typedef struct LauncherState { + // properties stored in BACKUP register + LauncherSettings launcherSettings; + + // transient properties + int16_t current_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; +} LauncherState; + +void cb_mode_pressed(); +void cb_light_pressed(); +void cb_alarm_pressed(); +void cb_tick(); + +#endif // LAUNCHER_H_ diff --git a/launcher/launcher_config.h b/launcher/launcher_config.h new file mode 100644 index 00000000..cb820a7e --- /dev/null +++ b/launcher/launcher_config.h @@ -0,0 +1,13 @@ +#ifndef LAUNCHER_CONFIG_H_ +#define LAUNCHER_CONFIG_H_ + +#include "fake_widget.h" + +#define LAUNCHER_NUM_WIDGETS 1 + +WatchWidget widgets[LAUNCHER_NUM_WIDGETS] = { + fake_widget +}; + + +#endif // LAUNCHER_CONFIG_H_ diff --git a/launcher/make/.gitignore b/launcher/make/.gitignore new file mode 100755 index 00000000..3722ac63 --- /dev/null +++ b/launcher/make/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/launcher/make/Makefile b/launcher/make/Makefile new file mode 100755 index 00000000..dce33f45 --- /dev/null +++ b/launcher/make/Makefile @@ -0,0 +1,26 @@ +# 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../utils/ +INCLUDES += \ + -I../ \ + -I../widgets/ \ + +# 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 += \ +# ../app.c \ +# ../drivers/bmp280.c \ +# ../utils/temperature.c +SRCS += \ + ../launcher.c \ + ../widgets/fake_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/fake_widget.c b/launcher/widgets/fake_widget.c new file mode 100644 index 00000000..15e3cf2a --- /dev/null +++ b/launcher/widgets/fake_widget.c @@ -0,0 +1,24 @@ +#include "fake_widget.h" + +void fake_widget_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + *context_ptr = NULL; +} + +void fake_widget_enter_foreground(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} + +bool fake_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + (void) event; + (void) settings; + (void) context; + + return true; +} + +void fake_widget_enter_background(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} diff --git a/launcher/widgets/fake_widget.h b/launcher/widgets/fake_widget.h new file mode 100644 index 00000000..5a4b33db --- /dev/null +++ b/launcher/widgets/fake_widget.h @@ -0,0 +1,18 @@ +#ifndef FAKE_WIDGET_H_ +#define FAKE_WIDGET_H_ + +#include "launcher.h" + +void fake_widget_setup(LauncherSettings *settings, void ** context_ptr); +void fake_widget_enter_foreground(LauncherSettings *settings, void *context); +bool fake_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void fake_widget_enter_background(LauncherSettings *settings, void *context); + +#define fake_widget { \ + fake_widget_setup, \ + fake_widget_enter_foreground, \ + fake_widget_loop, \ + fake_widget_enter_background, \ +} + +#endif // FAKE_WIDGET_H_ \ No newline at end of file -- cgit v1.2.3 From 399caa2582a4c139bd37ea0ce4480d06cfa53914 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 3 Oct 2021 12:31:51 -0400 Subject: more wip launcher stuff --- launcher/launcher.c | 37 +++++++++++++++++++++---------------- launcher/launcher.h | 33 +++++++++++++++++---------------- launcher/widgets/fake_widget.c | 6 +++--- launcher/widgets/fake_widget.h | 12 +++++++----- 4 files changed, 48 insertions(+), 40 deletions(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 1ad0b9da..26fec93e 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -7,8 +7,19 @@ LauncherState launcher_state; void * widget_contexts[LAUNCHER_NUM_WIDGETS]; +void launcher_request_tick_frequency(uint8_t freq) { + watch_rtc_disable_all_periodic_callbacks(); + watch_rtc_register_periodic_callback(cb_tick, freq); +} + +void launcher_illuminate_led() { + launcher_state.light_ticks = 3; +} + + void app_init() { memset(&launcher_state, 0, sizeof(launcher_state)); + launcher_state.launcher_settings.bit.led_green_color = 0xF; } void app_wake_from_deep_sleep() { @@ -25,10 +36,10 @@ void app_setup() { watch_enable_leds(); watch_enable_display(); - watch_register_tick_callback(cb_tick); + launcher_request_tick_frequency(1); for(uint8_t i = 0; i < LAUNCHER_NUM_WIDGETS; i++) { - widgets[i].setup(&launcher_state.launcherSettings, widget_contexts[i]); + widgets[i].setup(&launcher_state.launcher_settings, widget_contexts[i]); } } @@ -50,7 +61,7 @@ bool app_loop() { // 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_green(); + watch_set_led_color(launcher_state.launcher_settings.bit.led_red_color, launcher_state.launcher_settings.bit.led_green_color); launcher_state.led_on = true; } @@ -67,32 +78,26 @@ bool app_loop() { if (event) { event = 0; - bool can_sleep = widgets[launcher_state.current_widget].loop(event, &launcher_state.launcherSettings, widget_contexts[launcher_state.current_widget]); - if (can_sleep) return true; - - event = EVENT_LOOP; - return false; + widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); } + if (launcher_state.led_on) return false; + return true; } void move_to_next_widget() { launcher_state.widget_changed = true; - widgets[launcher_state.current_widget].enter_background(&launcher_state.launcherSettings, widget_contexts[launcher_state.current_widget]); + widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); launcher_state.current_widget = (launcher_state.current_widget + 1) % LAUNCHER_NUM_WIDGETS; - widgets[launcher_state.current_widget].enter_foreground(&launcher_state.launcherSettings, widget_contexts[launcher_state.current_widget]); + widgets[launcher_state.current_widget].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); } void move_to_first_widget() { launcher_state.widget_changed = true; - widgets[launcher_state.current_widget].enter_background(&launcher_state.launcherSettings, widget_contexts[launcher_state.current_widget]); + widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); launcher_state.current_widget = 0; - widgets[0].enter_foreground(&launcher_state.launcherSettings, widget_contexts[0]); -} - -void illuminate_led() { - launcher_state.light_ticks = 3; + widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[0]); } void cb_light_pressed() { diff --git a/launcher/launcher.h b/launcher/launcher.h index 888d1adb..bc8d9191 100644 --- a/launcher/launcher.h +++ b/launcher/launcher.h @@ -6,16 +6,16 @@ // TODO: none of this is implemented typedef union { struct { - uint32_t reserved : 1; - uint32_t clock_mode_24h : 1; // determines whether display should use 12 or 24 hour mode. + uint32_t clock_mode_24h : 1; // determines whether clock should use 12 or 24 hour mode. 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 plays a song. - uint32_t note_index : 7; // the index of the tone to play, or 0x7F for no tone. - uint32_t snapback_enabled : 1; // if true, snaps back to the main screen after 5 minutes - uint32_t sleep_interval : 3; // 0 to disable sleep, or a number of days to sleep after. - uint32_t sleep_blanks_screen : 1; // blank screen or display "SLEEP" when asleep - uint32_t led_red_color : 8; // for general purpose illumination, the red LED value - uint32_t led_green_color : 8; // for general purpose illumination, the green LED value + 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 note_index : 4; // the index of the tone to play on button press, or 0xF for no tone. + 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; @@ -24,7 +24,6 @@ typedef enum LauncherEvent { 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_LOOP, // The app did not sleep, and is going into another invocation of the run loop. 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. @@ -37,20 +36,22 @@ typedef enum LauncherEvent { } LauncherEvent; typedef void (*launcher_widget_setup)(LauncherSettings *settings, void ** context_ptr); -typedef void (*launcher_widget_enter_foreground)(LauncherSettings *settings, void *context); -typedef bool (*launcher_widget_loop)(LauncherEvent event, LauncherSettings *settings, void *context); -typedef void (*launcher_widget_enter_background)(LauncherSettings *settings, void *context); +typedef void (*launcher_widget_activate)(LauncherSettings *settings, void *context); +typedef void (*launcher_widget_loop)(LauncherEvent event, LauncherSettings *settings, void *context); +typedef void (*launcher_widget_resign)(LauncherSettings *settings, void *context); typedef struct WatchWidget { + char widget_name[11]; + bool snapback_enabled; launcher_widget_setup setup; - launcher_widget_enter_foreground enter_foreground; + launcher_widget_activate activate; launcher_widget_loop loop; - launcher_widget_enter_background enter_background; + launcher_widget_resign resign; } WatchWidget; typedef struct LauncherState { // properties stored in BACKUP register - LauncherSettings launcherSettings; + LauncherSettings launcher_settings; // transient properties int16_t current_widget; diff --git a/launcher/widgets/fake_widget.c b/launcher/widgets/fake_widget.c index 15e3cf2a..70b6918c 100644 --- a/launcher/widgets/fake_widget.c +++ b/launcher/widgets/fake_widget.c @@ -5,12 +5,12 @@ void fake_widget_setup(LauncherSettings *settings, void ** context_ptr) { *context_ptr = NULL; } -void fake_widget_enter_foreground(LauncherSettings *settings, void *context) { +void fake_widget_activate(LauncherSettings *settings, void *context) { (void) settings; (void) context; } -bool fake_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +void fake_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { (void) event; (void) settings; (void) context; @@ -18,7 +18,7 @@ bool fake_widget_loop(LauncherEvent event, LauncherSettings *settings, void *con return true; } -void fake_widget_enter_background(LauncherSettings *settings, void *context) { +void fake_widget_resign(LauncherSettings *settings, void *context) { (void) settings; (void) context; } diff --git a/launcher/widgets/fake_widget.h b/launcher/widgets/fake_widget.h index 5a4b33db..470cdf99 100644 --- a/launcher/widgets/fake_widget.h +++ b/launcher/widgets/fake_widget.h @@ -4,15 +4,17 @@ #include "launcher.h" void fake_widget_setup(LauncherSettings *settings, void ** context_ptr); -void fake_widget_enter_foreground(LauncherSettings *settings, void *context); -bool fake_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void fake_widget_enter_background(LauncherSettings *settings, void *context); +void fake_widget_activate(LauncherSettings *settings, void *context); +void fake_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void fake_widget_resign(LauncherSettings *settings, void *context); #define fake_widget { \ + "WI dGIt01", \ + true, \ fake_widget_setup, \ - fake_widget_enter_foreground, \ + fake_widget_activate, \ fake_widget_loop, \ - fake_widget_enter_background, \ + fake_widget_resign, \ } #endif // FAKE_WIDGET_H_ \ No newline at end of file -- cgit v1.2.3 From 95ea714d61b058f2b25aea6c01021cb3b3b2c3be Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 3 Oct 2021 14:26:17 -0400 Subject: basic launcher switching between two widgets --- launcher/launcher.c | 90 +++++++++++++++++++--------------------- launcher/launcher.h | 8 ++-- launcher/launcher_config.h | 8 ++-- launcher/make/Makefile | 3 +- launcher/widgets/fake_widget.c | 24 ----------- launcher/widgets/fake_widget.h | 20 --------- launcher/widgets/fake_widget_1.c | 32 ++++++++++++++ launcher/widgets/fake_widget_1.h | 19 +++++++++ launcher/widgets/fake_widget_2.c | 32 ++++++++++++++ launcher/widgets/fake_widget_2.h | 19 +++++++++ 10 files changed, 155 insertions(+), 100 deletions(-) delete mode 100644 launcher/widgets/fake_widget.c delete mode 100644 launcher/widgets/fake_widget.h create mode 100644 launcher/widgets/fake_widget_1.c create mode 100644 launcher/widgets/fake_widget_1.h create mode 100644 launcher/widgets/fake_widget_2.c create mode 100644 launcher/widgets/fake_widget_2.h diff --git a/launcher/launcher.c b/launcher/launcher.c index 26fec93e..ff10cf6e 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -7,6 +7,11 @@ LauncherState launcher_state; void * widget_contexts[LAUNCHER_NUM_WIDGETS]; +void cb_mode_pressed(); +void cb_light_pressed(); +void cb_alarm_pressed(); +void cb_tick(); + void launcher_request_tick_frequency(uint8_t freq) { watch_rtc_disable_all_periodic_callbacks(); watch_rtc_register_periodic_callback(cb_tick, freq); @@ -16,10 +21,26 @@ void launcher_illuminate_led() { launcher_state.light_ticks = 3; } +void launcher_move_to_next_widget() { + launcher_state.widget_changed = true; + widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + launcher_state.current_widget = (launcher_state.current_widget + 1) % LAUNCHER_NUM_WIDGETS; + widgets[launcher_state.current_widget].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + watch_display_string(widgets[launcher_state.current_widget].widget_name, 0); +} + +void launcher_move_to_first_widget() { + launcher_state.widget_changed = true; + widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + launcher_state.current_widget = 0; + widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[0]); + watch_display_string(widgets[launcher_state.current_widget].widget_name, 0); +} void app_init() { memset(&launcher_state, 0, sizeof(launcher_state)); launcher_state.launcher_settings.bit.led_green_color = 0xF; + launcher_state.launcher_settings.bit.led_red_color = 0x0; } void app_wake_from_deep_sleep() { @@ -41,6 +62,8 @@ void app_setup() { for(uint8_t i = 0; i < LAUNCHER_NUM_WIDGETS; i++) { widgets[i].setup(&launcher_state.launcher_settings, widget_contexts[i]); } + + launcher_move_to_first_widget(); } void app_prepare_for_sleep() { @@ -61,8 +84,10 @@ bool app_loop() { // 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, launcher_state.launcher_settings.bit.led_green_color); + 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 @@ -77,71 +102,42 @@ bool app_loop() { } if (event) { - event = 0; widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + event = 0; } + if (launcher_state.led_on) return false; return true; } -void move_to_next_widget() { - launcher_state.widget_changed = true; - widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - launcher_state.current_widget = (launcher_state.current_widget + 1) % LAUNCHER_NUM_WIDGETS; - widgets[launcher_state.current_widget].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); -} - -void move_to_first_widget() { - launcher_state.widget_changed = true; - widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - launcher_state.current_widget = 0; - widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[0]); +LauncherEvent _figure_out_button_event(LauncherEvent 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_pressed() { - struct calendar_date_time date_time; - watch_get_date_time(&date_time); - if (launcher_state.light_down_timestamp) { - uint8_t diff = (61 + date_time.time.sec) - launcher_state.light_down_timestamp; - if (diff > 1) event = EVENT_LIGHT_LONG_PRESS; - else event = EVENT_LIGHT_BUTTON_UP; - launcher_state.light_down_timestamp = 0; - } else { - launcher_state.light_down_timestamp = date_time.time.sec + 1; - event = EVENT_LIGHT_BUTTON_DOWN; - } + event = _figure_out_button_event(EVENT_LIGHT_BUTTON_DOWN, &launcher_state.light_down_timestamp); } void cb_mode_pressed() { - struct calendar_date_time date_time; - watch_get_date_time(&date_time); - if (launcher_state.mode_down_timestamp) { - uint8_t diff = (61 + date_time.time.sec) - launcher_state.mode_down_timestamp; - if (diff > 1) event = EVENT_MODE_LONG_PRESS; - else event = EVENT_MODE_BUTTON_UP; - launcher_state.mode_down_timestamp = 0; - } else { - launcher_state.mode_down_timestamp = date_time.time.sec + 1; - event = EVENT_MODE_BUTTON_DOWN; - } + event = _figure_out_button_event(EVENT_MODE_BUTTON_DOWN, &launcher_state.mode_down_timestamp); } void cb_alarm_pressed() { - struct calendar_date_time date_time; - watch_get_date_time(&date_time); - if (launcher_state.alarm_down_timestamp) { - uint8_t diff = (61 + date_time.time.sec) - launcher_state.alarm_down_timestamp; - if (diff > 1) event = EVENT_ALARM_LONG_PRESS; - else event = EVENT_ALARM_BUTTON_UP; - launcher_state.alarm_down_timestamp = 0; - } else { - launcher_state.alarm_down_timestamp = date_time.time.sec + 1; - event = EVENT_ALARM_BUTTON_DOWN; - } + event = _figure_out_button_event(EVENT_ALARM_BUTTON_DOWN, &launcher_state.alarm_down_timestamp); } void cb_tick() { event = EVENT_TICK; + if (launcher_state.light_ticks) launcher_state.light_ticks--; } diff --git a/launcher/launcher.h b/launcher/launcher.h index bc8d9191..7a794545 100644 --- a/launcher/launcher.h +++ b/launcher/launcher.h @@ -42,7 +42,6 @@ typedef void (*launcher_widget_resign)(LauncherSettings *settings, void *context typedef struct WatchWidget { char widget_name[11]; - bool snapback_enabled; launcher_widget_setup setup; launcher_widget_activate activate; launcher_widget_loop loop; @@ -67,9 +66,8 @@ typedef struct LauncherState { uint8_t alarm_down_timestamp; } LauncherState; -void cb_mode_pressed(); -void cb_light_pressed(); -void cb_alarm_pressed(); -void cb_tick(); +void launcher_move_to_next_widget(); +void launcher_move_to_first_widget(); +void launcher_illuminate_led(); #endif // LAUNCHER_H_ diff --git a/launcher/launcher_config.h b/launcher/launcher_config.h index cb820a7e..db9d1be1 100644 --- a/launcher/launcher_config.h +++ b/launcher/launcher_config.h @@ -1,12 +1,14 @@ #ifndef LAUNCHER_CONFIG_H_ #define LAUNCHER_CONFIG_H_ -#include "fake_widget.h" +#include "fake_widget_1.h" +#include "fake_widget_2.h" -#define LAUNCHER_NUM_WIDGETS 1 +#define LAUNCHER_NUM_WIDGETS 2 WatchWidget widgets[LAUNCHER_NUM_WIDGETS] = { - fake_widget + fake_widget_1, + fake_widget_2, }; diff --git a/launcher/make/Makefile b/launcher/make/Makefile index dce33f45..f02c1853 100755 --- a/launcher/make/Makefile +++ b/launcher/make/Makefile @@ -20,7 +20,8 @@ INCLUDES += \ # ../utils/temperature.c SRCS += \ ../launcher.c \ - ../widgets/fake_widget.c \ + ../widgets/fake_widget_1.c \ + ../widgets/fake_widget_2.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/fake_widget.c b/launcher/widgets/fake_widget.c deleted file mode 100644 index 70b6918c..00000000 --- a/launcher/widgets/fake_widget.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "fake_widget.h" - -void fake_widget_setup(LauncherSettings *settings, void ** context_ptr) { - (void) settings; - *context_ptr = NULL; -} - -void fake_widget_activate(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; -} - -void fake_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { - (void) event; - (void) settings; - (void) context; - - return true; -} - -void fake_widget_resign(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; -} diff --git a/launcher/widgets/fake_widget.h b/launcher/widgets/fake_widget.h deleted file mode 100644 index 470cdf99..00000000 --- a/launcher/widgets/fake_widget.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef FAKE_WIDGET_H_ -#define FAKE_WIDGET_H_ - -#include "launcher.h" - -void fake_widget_setup(LauncherSettings *settings, void ** context_ptr); -void fake_widget_activate(LauncherSettings *settings, void *context); -void fake_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void fake_widget_resign(LauncherSettings *settings, void *context); - -#define fake_widget { \ - "WI dGIt01", \ - true, \ - fake_widget_setup, \ - fake_widget_activate, \ - fake_widget_loop, \ - fake_widget_resign, \ -} - -#endif // FAKE_WIDGET_H_ \ No newline at end of file diff --git a/launcher/widgets/fake_widget_1.c b/launcher/widgets/fake_widget_1.c new file mode 100644 index 00000000..aa4af2c2 --- /dev/null +++ b/launcher/widgets/fake_widget_1.c @@ -0,0 +1,32 @@ +#include "fake_widget_1.h" + +void fake_widget_1_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + // *context_ptr = NULL; +} + +void fake_widget_1_activate(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} + +void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + (void) settings; + (void) context; + + switch (event) { + case EVENT_MODE_BUTTON_UP: + launcher_move_to_next_widget(); + break; + case EVENT_LIGHT_BUTTON_UP: + launcher_illuminate_led(); + break; + default: + break; + } +} + +void fake_widget_1_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} diff --git a/launcher/widgets/fake_widget_1.h b/launcher/widgets/fake_widget_1.h new file mode 100644 index 00000000..00db80cb --- /dev/null +++ b/launcher/widgets/fake_widget_1.h @@ -0,0 +1,19 @@ +#ifndef FAKE_WIDGET_1_H_ +#define FAKE_WIDGET_1_H_ + +#include "launcher.h" + +void fake_widget_1_setup(LauncherSettings *settings, void ** context_ptr); +void fake_widget_1_activate(LauncherSettings *settings, void *context); +void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void fake_widget_1_resign(LauncherSettings *settings, void *context); + +#define fake_widget_1 { \ + "W1 d get01", \ + fake_widget_1_setup, \ + fake_widget_1_activate, \ + fake_widget_1_loop, \ + fake_widget_1_resign, \ +} + +#endif // FAKE_WIDGET_1_H_ \ No newline at end of file diff --git a/launcher/widgets/fake_widget_2.c b/launcher/widgets/fake_widget_2.c new file mode 100644 index 00000000..4ce00ce2 --- /dev/null +++ b/launcher/widgets/fake_widget_2.c @@ -0,0 +1,32 @@ +#include "fake_widget_2.h" + +void fake_widget_2_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + *context_ptr = NULL; +} + +void fake_widget_2_activate(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} + +void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + (void) settings; + (void) context; + + switch (event) { + case EVENT_MODE_BUTTON_UP: + launcher_move_to_next_widget(); + break; + case EVENT_LIGHT_BUTTON_UP: + launcher_illuminate_led(); + break; + default: + break; + } +} + +void fake_widget_2_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} diff --git a/launcher/widgets/fake_widget_2.h b/launcher/widgets/fake_widget_2.h new file mode 100644 index 00000000..31bf30bc --- /dev/null +++ b/launcher/widgets/fake_widget_2.h @@ -0,0 +1,19 @@ +#ifndef FAKE_WIDGET_2_H_ +#define FAKE_WIDGET_2_H_ + +#include "launcher.h" + +void fake_widget_2_setup(LauncherSettings *settings, void ** context_ptr); +void fake_widget_2_activate(LauncherSettings *settings, void *context); +void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void fake_widget_2_resign(LauncherSettings *settings, void *context); + +#define fake_widget_2 { \ + "W1 d get02", \ + fake_widget_2_setup, \ + fake_widget_2_activate, \ + fake_widget_2_loop, \ + fake_widget_2_resign, \ +} + +#endif // FAKE_WIDGET_2_H_ \ No newline at end of file -- cgit v1.2.3 From 8d5280a34f1159d8af0277ba04b7e1cb9c8bd7ac Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 3 Oct 2021 18:48:55 -0400 Subject: slcd: add function to blank entire screen --- watch-library/watch/watch_slcd.c | 6 ++++++ watch-library/watch/watch_slcd.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/watch-library/watch/watch_slcd.c b/watch-library/watch/watch_slcd.c index ba9d12b2..c1e24308 100644 --- a/watch-library/watch/watch_slcd.c +++ b/watch-library/watch/watch_slcd.c @@ -162,6 +162,12 @@ inline void watch_clear_pixel(uint8_t com, uint8_t seg) { slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(com, seg)); } +void watch_clear_display() { + SLCD->SDATAL0.reg = 0; + SLCD->SDATAL1.reg = 0; + SLCD->SDATAL2.reg = 0; +} + void watch_display_character(uint8_t character, uint8_t position) { // handle lowercase 7 if needed if (character == '7' && (position == 4 || position == 6)) character = '&'; diff --git a/watch-library/watch/watch_slcd.h b/watch-library/watch/watch_slcd.h index 4f5a617c..5a54b72e 100644 --- a/watch-library/watch/watch_slcd.h +++ b/watch-library/watch/watch_slcd.h @@ -69,6 +69,10 @@ void watch_set_pixel(uint8_t com, uint8_t seg); */ void watch_clear_pixel(uint8_t com, uint8_t seg); +/** @brief Clears all segments of the display, including incicators and the colon. + */ +void watch_clear_display(); + /** @brief Displays a string at the given position, starting from the top left. There are ten digits. A space in any position will clear that digit. * @param string A null-terminated string. -- cgit v1.2.3 From 27df51d40a29baea8c50a25ed2345b1293a5089c Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 3 Oct 2021 18:49:21 -0400 Subject: launcher app with clock and preferences menu --- launcher/launcher.c | 41 +++++---- launcher/launcher.h | 14 ++- launcher/launcher_config.h | 6 +- launcher/make/Makefile | 4 + launcher/widgets/clock/simple_clock_widget.c | 86 ++++++++++++++++++ launcher/widgets/clock/simple_clock_widget.h | 20 ++++ launcher/widgets/fake_widget_1.c | 10 +- launcher/widgets/fake_widget_1.h | 3 +- launcher/widgets/fake_widget_2.c | 8 +- launcher/widgets/fake_widget_2.h | 3 +- launcher/widgets/settings/preferences_widget.c | 121 +++++++++++++++++++++++++ launcher/widgets/settings/preferences_widget.h | 18 ++++ 12 files changed, 304 insertions(+), 30 deletions(-) create mode 100644 launcher/widgets/clock/simple_clock_widget.c create mode 100644 launcher/widgets/clock/simple_clock_widget.h create mode 100644 launcher/widgets/settings/preferences_widget.c create mode 100644 launcher/widgets/settings/preferences_widget.h diff --git a/launcher/launcher.c b/launcher/launcher.c index ff10cf6e..8dc38199 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -13,7 +13,10 @@ void cb_alarm_pressed(); void cb_tick(); void launcher_request_tick_frequency(uint8_t freq) { + // FIXME: there is an issue where after changing tick frequencies on a widget switch, something glitchy happens on the next one. watch_rtc_disable_all_periodic_callbacks(); + launcher_state.subsecond = 0; + launcher_state.tick_frequency = freq; watch_rtc_register_periodic_callback(cb_tick, freq); } @@ -21,26 +24,24 @@ void launcher_illuminate_led() { launcher_state.light_ticks = 3; } -void launcher_move_to_next_widget() { +void launcher_move_to_widget(uint8_t widget_index) { launcher_state.widget_changed = true; widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - launcher_state.current_widget = (launcher_state.current_widget + 1) % LAUNCHER_NUM_WIDGETS; + launcher_state.current_widget = widget_index; + watch_clear_display(); widgets[launcher_state.current_widget].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - watch_display_string(widgets[launcher_state.current_widget].widget_name, 0); + widgets[launcher_state.current_widget].loop(EVENT_ACTIVATE, &launcher_state.launcher_settings, launcher_state.subsecond, widget_contexts[launcher_state.current_widget]); } -void launcher_move_to_first_widget() { - launcher_state.widget_changed = true; - widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - launcher_state.current_widget = 0; - widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[0]); - watch_display_string(widgets[launcher_state.current_widget].widget_name, 0); +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.led_red_color = 0x0; + watch_date_time date_time = watch_rtc_get_date_time(); + watch_rtc_set_date_time(date_time); } void app_wake_from_deep_sleep() { @@ -60,10 +61,10 @@ void app_setup() { 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[i].setup(&launcher_state.launcher_settings, &widget_contexts[i]); } - launcher_move_to_first_widget(); + launcher_move_to_widget(0); } void app_prepare_for_sleep() { @@ -77,8 +78,8 @@ LauncherEvent event; bool app_loop() { // play a beep if the widget has changed in response to a user's press of the MODE button if (launcher_state.widget_changed) { - // low note for nonzero case, high note for return to clock - watch_buzzer_play_note(launcher_state.current_widget ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 100); + // low note for nonzero case, high note for return to widget 0 + watch_buzzer_play_note(launcher_state.current_widget ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 50); launcher_state.widget_changed = false; } @@ -102,7 +103,7 @@ bool app_loop() { } if (event) { - widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, launcher_state.subsecond, widget_contexts[launcher_state.current_widget]); event = 0; } @@ -139,5 +140,13 @@ void cb_alarm_pressed() { void cb_tick() { event = EVENT_TICK; - if (launcher_state.light_ticks) launcher_state.light_ticks--; + 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--; + + 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 index 7a794545..e49575e7 100644 --- a/launcher/launcher.h +++ b/launcher/launcher.h @@ -6,12 +6,13 @@ // 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 note_index : 4; // the index of the tone to play on button press, or 0xF for no tone. 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) @@ -37,11 +38,10 @@ typedef enum LauncherEvent { typedef void (*launcher_widget_setup)(LauncherSettings *settings, void ** context_ptr); typedef void (*launcher_widget_activate)(LauncherSettings *settings, void *context); -typedef void (*launcher_widget_loop)(LauncherEvent event, LauncherSettings *settings, void *context); +typedef void (*launcher_widget_loop)(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context); typedef void (*launcher_widget_resign)(LauncherSettings *settings, void *context); typedef struct WatchWidget { - char widget_name[11]; launcher_widget_setup setup; launcher_widget_activate activate; launcher_widget_loop loop; @@ -64,10 +64,16 @@ typedef struct LauncherState { uint8_t light_down_timestamp; uint8_t mode_down_timestamp; uint8_t alarm_down_timestamp; + + // 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_move_to_first_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 index db9d1be1..9bc43c47 100644 --- a/launcher/launcher_config.h +++ b/launcher/launcher_config.h @@ -1,12 +1,16 @@ #ifndef LAUNCHER_CONFIG_H_ #define LAUNCHER_CONFIG_H_ +#include "simple_clock_widget.h" +#include "preferences_widget.h" #include "fake_widget_1.h" #include "fake_widget_2.h" -#define LAUNCHER_NUM_WIDGETS 2 +#define LAUNCHER_NUM_WIDGETS 4 WatchWidget widgets[LAUNCHER_NUM_WIDGETS] = { + simple_clock_widget, + preferences_widget, fake_widget_1, fake_widget_2, }; diff --git a/launcher/make/Makefile b/launcher/make/Makefile index f02c1853..4fba0351 100755 --- a/launcher/make/Makefile +++ b/launcher/make/Makefile @@ -11,6 +11,8 @@ include $(TOP)/make.mk INCLUDES += \ -I../ \ -I../widgets/ \ + -I../widgets/clock/ \ + -I../widgets/settings/ \ # 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. @@ -20,6 +22,8 @@ INCLUDES += \ # ../utils/temperature.c SRCS += \ ../launcher.c \ + ../widgets/clock/simple_clock_widget.c \ + ../widgets/settings/preferences_widget.c \ ../widgets/fake_widget_1.c \ ../widgets/fake_widget_2.c \ diff --git a/launcher/widgets/clock/simple_clock_widget.c b/launcher/widgets/clock/simple_clock_widget.c new file mode 100644 index 00000000..51c6c9c6 --- /dev/null +++ b/launcher/widgets/clock/simple_clock_widget.c @@ -0,0 +1,86 @@ +#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. + *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; +} + +void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context) { + printf("simple_clock_widget_loop\n"); + (void) subsecond; + 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) { + case EVENT_TICK: + case EVENT_ACTIVATE: + 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) { + // 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) { + // 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; + 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; + case EVENT_LIGHT_BUTTON_UP: + launcher_illuminate_led(); + break; + case EVENT_ALARM_BUTTON_UP: + break; + default: + break; + } +} + +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 new file mode 100644 index 00000000..459721b9 --- /dev/null +++ b/launcher/widgets/clock/simple_clock_widget.h @@ -0,0 +1,20 @@ +#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); +void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, 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/fake_widget_1.c b/launcher/widgets/fake_widget_1.c index aa4af2c2..15ffcbd2 100644 --- a/launcher/widgets/fake_widget_1.c +++ b/launcher/widgets/fake_widget_1.c @@ -1,8 +1,9 @@ #include "fake_widget_1.h" +#include "watch.h" void fake_widget_1_setup(LauncherSettings *settings, void ** context_ptr) { (void) settings; - // *context_ptr = NULL; + *context_ptr = NULL; } void fake_widget_1_activate(LauncherSettings *settings, void *context) { @@ -10,14 +11,17 @@ void fake_widget_1_activate(LauncherSettings *settings, void *context) { (void) context; } -void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context) { + printf("fake_widget_1_loop\n"); (void) settings; + (void) subsecond; (void) context; + watch_display_string("W1 d get01", 0); switch (event) { case EVENT_MODE_BUTTON_UP: launcher_move_to_next_widget(); - break; + return; case EVENT_LIGHT_BUTTON_UP: launcher_illuminate_led(); break; diff --git a/launcher/widgets/fake_widget_1.h b/launcher/widgets/fake_widget_1.h index 00db80cb..18180156 100644 --- a/launcher/widgets/fake_widget_1.h +++ b/launcher/widgets/fake_widget_1.h @@ -5,11 +5,10 @@ void fake_widget_1_setup(LauncherSettings *settings, void ** context_ptr); void fake_widget_1_activate(LauncherSettings *settings, void *context); -void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context); void fake_widget_1_resign(LauncherSettings *settings, void *context); #define fake_widget_1 { \ - "W1 d get01", \ fake_widget_1_setup, \ fake_widget_1_activate, \ fake_widget_1_loop, \ diff --git a/launcher/widgets/fake_widget_2.c b/launcher/widgets/fake_widget_2.c index 4ce00ce2..f3ffc14b 100644 --- a/launcher/widgets/fake_widget_2.c +++ b/launcher/widgets/fake_widget_2.c @@ -1,4 +1,5 @@ #include "fake_widget_2.h" +#include "watch.h" void fake_widget_2_setup(LauncherSettings *settings, void ** context_ptr) { (void) settings; @@ -10,14 +11,17 @@ void fake_widget_2_activate(LauncherSettings *settings, void *context) { (void) context; } -void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context) { + printf("fake_widget_2_loop\n"); (void) settings; + (void) subsecond; (void) context; + watch_display_string("W1 d get02", 0); switch (event) { case EVENT_MODE_BUTTON_UP: launcher_move_to_next_widget(); - break; + return; case EVENT_LIGHT_BUTTON_UP: launcher_illuminate_led(); break; diff --git a/launcher/widgets/fake_widget_2.h b/launcher/widgets/fake_widget_2.h index 31bf30bc..1f6bd503 100644 --- a/launcher/widgets/fake_widget_2.h +++ b/launcher/widgets/fake_widget_2.h @@ -5,11 +5,10 @@ void fake_widget_2_setup(LauncherSettings *settings, void ** context_ptr); void fake_widget_2_activate(LauncherSettings *settings, void *context); -void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context); void fake_widget_2_resign(LauncherSettings *settings, void *context); #define fake_widget_2 { \ - "W1 d get02", \ fake_widget_2_setup, \ fake_widget_2_activate, \ fake_widget_2_loop, \ diff --git a/launcher/widgets/settings/preferences_widget.c b/launcher/widgets/settings/preferences_widget.c new file mode 100644 index 00000000..5877c237 --- /dev/null +++ b/launcher/widgets/settings/preferences_widget.c @@ -0,0 +1,121 @@ +#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; + *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 +} + +void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context) { + (void) settings; + (void) context; + printf("preferences_widget_loop\n"); + uint8_t current_page = *((uint8_t *)context); + switch (event) { + case EVENT_MODE_BUTTON_UP: + launcher_move_to_next_widget(); + return; + 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_clear_display(); + watch_display_string((char *)preferences_widget_titles[current_page], 0); + if (current_page > 2) { + // this is a hack, launcher should be able to illumate with a custom color. + launcher_illuminate_led(); + 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); + } else { + watch_set_led_off(); + } + + if (subsecond % 2) return; + 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; + } +} + +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 new file mode 100644 index 00000000..fcbcd0d6 --- /dev/null +++ b/launcher/widgets/settings/preferences_widget.h @@ -0,0 +1,18 @@ +#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); +void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, 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 -- cgit v1.2.3 From a6d8a098507ed13537e17e51bb545dcadb579d40 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 3 Oct 2021 19:15:47 -0400 Subject: avoid calling deactivate on widget 0 before it's activated --- launcher/launcher.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 8dc38199..0d4fbe86 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -64,7 +64,8 @@ void app_setup() { widgets[i].setup(&launcher_state.launcher_settings, &widget_contexts[i]); } - launcher_move_to_widget(0); + widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + widgets[0].loop(EVENT_ACTIVATE, &launcher_state.launcher_settings, 0, widget_contexts[launcher_state.current_widget]); } void app_prepare_for_sleep() { -- cgit v1.2.3 From 64485b4255271dca8d6d38a91fa9ace610bf7222 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 3 Oct 2021 19:16:14 -0400 Subject: implement beep / silent preference --- launcher/launcher.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 0d4fbe86..77dc303c 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -40,6 +40,7 @@ void launcher_move_to_next_widget() { 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; watch_date_time date_time = watch_rtc_get_date_time(); watch_rtc_set_date_time(date_time); } @@ -80,7 +81,9 @@ bool app_loop() { // play a beep if the widget has changed in response to a user's press of the MODE button if (launcher_state.widget_changed) { // low note for nonzero case, high note for return to widget 0 - watch_buzzer_play_note(launcher_state.current_widget ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 50); + if (launcher_state.launcher_settings.bit.button_should_sound) { + watch_buzzer_play_note(launcher_state.current_widget ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 50); + } launcher_state.widget_changed = false; } -- cgit v1.2.3 From 4300dff61631143f794f186cadb222bdf5f75a06 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 3 Oct 2021 20:37:15 -0400 Subject: launcher: first crack at low power 'screensaver' mode --- apps/Sensor Watch Starter Project/app.c | 2 +- launcher/launcher.c | 85 +++++++++++++++++++++------- launcher/launcher.h | 4 ++ launcher/widgets/clock/simple_clock_widget.c | 13 +++-- watch-library/watch/watch_deepsleep.c | 7 +-- watch-library/watch/watch_deepsleep.h | 15 ++--- 6 files changed, 84 insertions(+), 42 deletions(-) diff --git a/apps/Sensor Watch Starter Project/app.c b/apps/Sensor Watch Starter Project/app.c index ff5ed53d..ae5aa6e8 100644 --- a/apps/Sensor Watch Starter Project/app.c +++ b/apps/Sensor Watch Starter Project/app.c @@ -157,7 +157,7 @@ bool app_loop() { delay_ms(250); // nap time :) - watch_enter_shallow_sleep(NULL); + watch_enter_shallow_sleep(false); // we just woke up; wait a moment again for the user's finger to be off the button... delay_ms(250); diff --git a/launcher/launcher.c b/launcher/launcher.c index 77dc303c..0f8f7ead 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -1,19 +1,27 @@ #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}; -void cb_mode_pressed(); -void cb_light_pressed(); -void cb_alarm_pressed(); +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) { - // FIXME: there is an issue where after changing tick frequencies on a widget switch, something glitchy happens on the next one. watch_rtc_disable_all_periodic_callbacks(); launcher_state.subsecond = 0; launcher_state.tick_frequency = freq; @@ -39,10 +47,11 @@ void launcher_move_to_next_widget() { 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; - watch_date_time date_time = watch_rtc_get_date_time(); - watch_rtc_set_date_time(date_time); + launcher_state.launcher_settings.bit.screensaver_interval = 1; + _launcher_reset_screensaver_countdown(); } void app_wake_from_deep_sleep() { @@ -50,23 +59,27 @@ void app_wake_from_deep_sleep() { } void app_setup() { - watch_enable_external_interrupts(); - watch_register_interrupt_callback(BTN_MODE, cb_mode_pressed, INTERRUPT_TRIGGER_BOTH); - watch_register_interrupt_callback(BTN_LIGHT, cb_light_pressed, INTERRUPT_TRIGGER_BOTH); - watch_register_interrupt_callback(BTN_ALARM, cb_alarm_pressed, INTERRUPT_TRIGGER_BOTH); + if (launcher_state.screensaver_ticks != -1) { + watch_disable_extwake_interrupt(BTN_ALARM); - watch_enable_buzzer(); - watch_enable_leds(); - watch_enable_display(); + 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); - launcher_request_tick_frequency(1); + watch_enable_buzzer(); + watch_enable_leds(); + watch_enable_display(); - for(uint8_t i = 0; i < LAUNCHER_NUM_WIDGETS; i++) { - widgets[i].setup(&launcher_state.launcher_settings, &widget_contexts[i]); - } + 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[launcher_state.current_widget]); - widgets[0].loop(EVENT_ACTIVATE, &launcher_state.launcher_settings, 0, widget_contexts[launcher_state.current_widget]); + widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + widgets[0].loop(EVENT_ACTIVATE, &launcher_state.launcher_settings, 0, widget_contexts[launcher_state.current_widget]); + } } void app_prepare_for_sleep() { @@ -106,6 +119,19 @@ bool app_loop() { } } + // 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); + widgets[launcher_state.current_widget].loop(EVENT_SCREENSAVER, &launcher_state.launcher_settings, 0, widget_contexts[launcher_state.current_widget]); + event = EVENT_SCREENSAVER; + watch_enter_shallow_sleep(true); + } + if (event) { widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, launcher_state.subsecond, widget_contexts[launcher_state.current_widget]); event = 0; @@ -130,23 +156,38 @@ LauncherEvent _figure_out_button_event(LauncherEvent button_down_event, uint8_t } } -void cb_light_pressed() { +void cb_light_btn_interrupt() { + _launcher_reset_screensaver_countdown(); event = _figure_out_button_event(EVENT_LIGHT_BUTTON_DOWN, &launcher_state.light_down_timestamp); } -void cb_mode_pressed() { +void cb_mode_btn_interrupt() { + _launcher_reset_screensaver_countdown(); event = _figure_out_button_event(EVENT_MODE_BUTTON_DOWN, &launcher_state.mode_down_timestamp); } -void cb_alarm_pressed() { +void cb_alarm_btn_interrupt() { + _launcher_reset_screensaver_countdown(); event = _figure_out_button_event(EVENT_ALARM_BUTTON_DOWN, &launcher_state.alarm_down_timestamp); } +void cb_alarm_btn_extwake() { + _launcher_reset_screensaver_countdown(); + // this is a hack: waking from shallow sleep, app_setup does get called, but it happens before we reset our ticks. + // need to figure out if there's a better heuristic for determining how we woke up. + app_setup(); +} + +void cb_alarm_fired() { + event = EVENT_SCREENSAVER; +} + void cb_tick() { event = 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; diff --git a/launcher/launcher.h b/launcher/launcher.h index e49575e7..09b33dbb 100644 --- a/launcher/launcher.h +++ b/launcher/launcher.h @@ -25,6 +25,7 @@ typedef enum LauncherEvent { 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. @@ -65,6 +66,9 @@ typedef struct LauncherState { 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; diff --git a/launcher/widgets/clock/simple_clock_widget.c b/launcher/widgets/clock/simple_clock_widget.c index 51c6c9c6..29a53b41 100644 --- a/launcher/widgets/clock/simple_clock_widget.c +++ b/launcher/widgets/clock/simple_clock_widget.c @@ -27,17 +27,18 @@ void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, u watch_date_time date_time; uint32_t previous_date_time; switch (event) { - case EVENT_TICK: 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) { + if (date_time.reg >> 6 == previous_date_time >> 6 && event != 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) { + } else if (date_time.reg >> 12 == previous_date_time >> 12 && event != EVENT_SCREENSAVER) { // everything before minutes is the same. pos = 6; sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second); @@ -54,7 +55,11 @@ void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, u if (date_time.unit.hour == 0) date_time.unit.hour = 12; } pos = 0; - 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); + if (event == 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; diff --git a/watch-library/watch/watch_deepsleep.c b/watch-library/watch/watch_deepsleep.c index b7da82ee..9ca53db0 100644 --- a/watch-library/watch/watch_deepsleep.c +++ b/watch-library/watch/watch_deepsleep.c @@ -151,11 +151,8 @@ void _watch_disable_all_peripherals_except_slcd() { MCLK->APBCMASK.reg &= ~MCLK_APBCMASK_SERCOM3; } -void watch_enter_shallow_sleep(char *message) { - if (message != NULL) { - watch_display_string(" ", 0); - watch_display_string(message, 0); - } else { +void watch_enter_shallow_sleep(bool display_on) { + if (!display_on) { slcd_sync_deinit(&SEGMENT_LCD_0); hri_mclk_clear_APBCMASK_SLCD_bit(SLCD); } diff --git a/watch-library/watch/watch_deepsleep.h b/watch-library/watch/watch_deepsleep.h index 3dc428d0..84825f00 100644 --- a/watch-library/watch/watch_deepsleep.h +++ b/watch-library/watch/watch_deepsleep.h @@ -81,13 +81,8 @@ uint32_t watch_get_backup_data(uint8_t reg); * the LCD. You can wake from this mode by pressing the ALARM button, if you have an registered an * external wake callback on the ALARM button. When your app wakes from this shallow sleep mode, your * app_setup method will be called, since this function will have disabled things you set up. - * @param message Either NULL, or a string representing a message to display while in shallow sleep mode. If - * this parameter is NULL, the screen will be blanked out, and this function will disable the - * SLCD peripheral for additional power savings. If the message is non-NULL, it will replace - * any text on the screen, and will be displayed at position 0 (so you should pad out the beginning - * of the string with spaces if you wish for the message to appear on line 2, i.e. " SLEEP"). - * Also note that this function will NOT clear any indicator segments that you have set. This is - * by design, in case you wish to leave an indicator lit in sleep mode. + * @param display_on if true, leaves the LCD on to display whatever content was on-screen. If false, disables + * the segment LCD controller for additional power savings. * @details This shallow sleep mode is not the lowest power mode available (see watch_enter_deep_sleep), but * it has the benefit of retaining your application state and being able to wake from the ALARM button. * It also provides an option for displaying a message to the user when asleep. Note that whether you @@ -96,10 +91,10 @@ uint32_t watch_get_backup_data(uint8_t reg); * * Power consumption in shallow sleep mode varies a bit with the battery voltage and the temperature, * but at 3 V and 25~30° C you can roughly estimate: - * * < 12µA current draw with the LCD controller on (message != NULL) - * * < 6µA current draw with the LCD controller off (message == NULL) + * * < 12µA current draw with the LCD controller on + * * < 6µA current draw with the LCD controller off */ -void watch_enter_shallow_sleep(char *message); +void watch_enter_shallow_sleep(bool display_on); /** @brief Enters the SAM L22's lowest-power mode, BACKUP. * @details This function does some housekeeping before entering BACKUP mode. It first disables all -- cgit v1.2.3 From a6115de94d977da69a6a81f93a92706e76324a37 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sun, 3 Oct 2021 23:39:52 -0400 Subject: disable screensaver interrupt on wake --- launcher/launcher.c | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/launcher.c b/launcher/launcher.c index 0f8f7ead..120e045b 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -61,6 +61,7 @@ void app_wake_from_deep_sleep() { void app_setup() { 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); -- cgit v1.2.3 From 51e2f0d77d95a6e5c3b1426fbc7874c3a59a5e00 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 00:37:58 -0400 Subject: event type / subsecond refactor --- launcher/launcher.c | 34 ++++++++++++++------------ launcher/launcher.h | 14 +++++++++-- launcher/widgets/clock/simple_clock_widget.c | 11 ++++----- launcher/widgets/clock/simple_clock_widget.h | 2 +- launcher/widgets/fake_widget_1.c | 5 ++-- launcher/widgets/fake_widget_1.h | 2 +- launcher/widgets/fake_widget_2.c | 5 ++-- launcher/widgets/fake_widget_2.h | 2 +- launcher/widgets/settings/preferences_widget.c | 6 ++--- launcher/widgets/settings/preferences_widget.h | 2 +- 10 files changed, 47 insertions(+), 36 deletions(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 120e045b..4211e1ac 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -8,6 +8,7 @@ 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(); @@ -38,7 +39,9 @@ void launcher_move_to_widget(uint8_t widget_index) { launcher_state.current_widget = widget_index; watch_clear_display(); widgets[launcher_state.current_widget].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - widgets[launcher_state.current_widget].loop(EVENT_ACTIVATE, &launcher_state.launcher_settings, launcher_state.subsecond, widget_contexts[launcher_state.current_widget]); + event.value = 0; + event.bit.event_type = EVENT_ACTIVATE; + widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); } void launcher_move_to_next_widget() { @@ -79,7 +82,8 @@ void app_setup() { } widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - widgets[0].loop(EVENT_ACTIVATE, &launcher_state.launcher_settings, 0, widget_contexts[launcher_state.current_widget]); + event.value = 0; + event.bit.event_type = EVENT_ACTIVATE; } } @@ -89,8 +93,6 @@ void app_prepare_for_sleep() { void app_wake_from_sleep() { } -LauncherEvent event; - bool app_loop() { // play a beep if the widget has changed in response to a user's press of the MODE button if (launcher_state.widget_changed) { @@ -128,14 +130,16 @@ bool app_loop() { 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); - widgets[launcher_state.current_widget].loop(EVENT_SCREENSAVER, &launcher_state.launcher_settings, 0, widget_contexts[launcher_state.current_widget]); - event = EVENT_SCREENSAVER; + event.value = 0; + event.bit.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); } - if (event) { - widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, launcher_state.subsecond, widget_contexts[launcher_state.current_widget]); - event = 0; + if (event.bit.event_type) { + event.bit.subsecond = launcher_state.subsecond; + widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + event.value = 0; } @@ -144,7 +148,7 @@ bool app_loop() { return true; } -LauncherEvent _figure_out_button_event(LauncherEvent button_down_event, uint8_t *down_timestamp) { +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; @@ -159,17 +163,17 @@ LauncherEvent _figure_out_button_event(LauncherEvent button_down_event, uint8_t void cb_light_btn_interrupt() { _launcher_reset_screensaver_countdown(); - event = _figure_out_button_event(EVENT_LIGHT_BUTTON_DOWN, &launcher_state.light_down_timestamp); + event.bit.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 = _figure_out_button_event(EVENT_MODE_BUTTON_DOWN, &launcher_state.mode_down_timestamp); + event.bit.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 = _figure_out_button_event(EVENT_ALARM_BUTTON_DOWN, &launcher_state.alarm_down_timestamp); + event.bit.event_type = _figure_out_button_event(EVENT_ALARM_BUTTON_DOWN, &launcher_state.alarm_down_timestamp); } void cb_alarm_btn_extwake() { @@ -180,11 +184,11 @@ void cb_alarm_btn_extwake() { } void cb_alarm_fired() { - event = EVENT_SCREENSAVER; + event.bit.event_type = EVENT_SCREENSAVER; } void cb_tick() { - event = EVENT_TICK; + event.bit.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--; diff --git a/launcher/launcher.h b/launcher/launcher.h index 09b33dbb..d6ebbc2c 100644 --- a/launcher/launcher.h +++ b/launcher/launcher.h @@ -21,7 +21,7 @@ typedef union { uint32_t value; } LauncherSettings; -typedef enum LauncherEvent { +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. @@ -35,11 +35,21 @@ typedef enum LauncherEvent { 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 union { + struct { + uint32_t event_type : 8; + uint32_t subsecond : 8; + uint32_t reserved : 16; + } bit; + uint32_t value; } LauncherEvent; + typedef void (*launcher_widget_setup)(LauncherSettings *settings, void ** context_ptr); typedef void (*launcher_widget_activate)(LauncherSettings *settings, void *context); -typedef void (*launcher_widget_loop)(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context); +typedef void (*launcher_widget_loop)(LauncherEvent event, LauncherSettings *settings, void *context); typedef void (*launcher_widget_resign)(LauncherSettings *settings, void *context); typedef struct WatchWidget { diff --git a/launcher/widgets/clock/simple_clock_widget.c b/launcher/widgets/clock/simple_clock_widget.c index 29a53b41..427793f3 100644 --- a/launcher/widgets/clock/simple_clock_widget.c +++ b/launcher/widgets/clock/simple_clock_widget.c @@ -17,16 +17,15 @@ void simple_clock_widget_activate(LauncherSettings *settings, void *context) { *((uint32_t *)context) = 0xFFFFFFFF; } -void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context) { +void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { printf("simple_clock_widget_loop\n"); - (void) subsecond; 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) { + switch (event.bit.event_type) { case EVENT_ACTIVATE: case EVENT_TICK: case EVENT_SCREENSAVER: @@ -34,11 +33,11 @@ void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, u previous_date_time = *((uint32_t *)context); *((uint32_t *)context) = date_time.reg; - if (date_time.reg >> 6 == previous_date_time >> 6 && event != EVENT_SCREENSAVER) { + if (date_time.reg >> 6 == previous_date_time >> 6 && event.bit.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_SCREENSAVER) { + } else if (date_time.reg >> 12 == previous_date_time >> 12 && event.bit.event_type != EVENT_SCREENSAVER) { // everything before minutes is the same. pos = 6; sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second); @@ -55,7 +54,7 @@ void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, u if (date_time.unit.hour == 0) date_time.unit.hour = 12; } pos = 0; - if (event == EVENT_SCREENSAVER) { + if (event.bit.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); diff --git a/launcher/widgets/clock/simple_clock_widget.h b/launcher/widgets/clock/simple_clock_widget.h index 459721b9..42b0d3c5 100644 --- a/launcher/widgets/clock/simple_clock_widget.h +++ b/launcher/widgets/clock/simple_clock_widget.h @@ -5,7 +5,7 @@ void simple_clock_widget_setup(LauncherSettings *settings, void ** context_ptr); void simple_clock_widget_activate(LauncherSettings *settings, void *context); -void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context); +void 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); diff --git a/launcher/widgets/fake_widget_1.c b/launcher/widgets/fake_widget_1.c index 15ffcbd2..65324095 100644 --- a/launcher/widgets/fake_widget_1.c +++ b/launcher/widgets/fake_widget_1.c @@ -11,14 +11,13 @@ void fake_widget_1_activate(LauncherSettings *settings, void *context) { (void) context; } -void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context) { +void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context) { printf("fake_widget_1_loop\n"); (void) settings; - (void) subsecond; (void) context; watch_display_string("W1 d get01", 0); - switch (event) { + switch (event.bit.event_type) { case EVENT_MODE_BUTTON_UP: launcher_move_to_next_widget(); return; diff --git a/launcher/widgets/fake_widget_1.h b/launcher/widgets/fake_widget_1.h index 18180156..24704f23 100644 --- a/launcher/widgets/fake_widget_1.h +++ b/launcher/widgets/fake_widget_1.h @@ -5,7 +5,7 @@ void fake_widget_1_setup(LauncherSettings *settings, void ** context_ptr); void fake_widget_1_activate(LauncherSettings *settings, void *context); -void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context); +void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context); void fake_widget_1_resign(LauncherSettings *settings, void *context); #define fake_widget_1 { \ diff --git a/launcher/widgets/fake_widget_2.c b/launcher/widgets/fake_widget_2.c index f3ffc14b..a6eafb7b 100644 --- a/launcher/widgets/fake_widget_2.c +++ b/launcher/widgets/fake_widget_2.c @@ -11,14 +11,13 @@ void fake_widget_2_activate(LauncherSettings *settings, void *context) { (void) context; } -void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context) { +void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context) { printf("fake_widget_2_loop\n"); (void) settings; - (void) subsecond; (void) context; watch_display_string("W1 d get02", 0); - switch (event) { + switch (event.bit.event_type) { case EVENT_MODE_BUTTON_UP: launcher_move_to_next_widget(); return; diff --git a/launcher/widgets/fake_widget_2.h b/launcher/widgets/fake_widget_2.h index 1f6bd503..945e28fa 100644 --- a/launcher/widgets/fake_widget_2.h +++ b/launcher/widgets/fake_widget_2.h @@ -5,7 +5,7 @@ void fake_widget_2_setup(LauncherSettings *settings, void ** context_ptr); void fake_widget_2_activate(LauncherSettings *settings, void *context); -void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context); +void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context); void fake_widget_2_resign(LauncherSettings *settings, void *context); #define fake_widget_2 { \ diff --git a/launcher/widgets/settings/preferences_widget.c b/launcher/widgets/settings/preferences_widget.c index 5877c237..d4abeb8e 100644 --- a/launcher/widgets/settings/preferences_widget.c +++ b/launcher/widgets/settings/preferences_widget.c @@ -16,12 +16,12 @@ void preferences_widget_activate(LauncherSettings *settings, void *context) { launcher_request_tick_frequency(4); // we need to manually blink some pixels } -void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context) { +void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { (void) settings; (void) context; printf("preferences_widget_loop\n"); uint8_t current_page = *((uint8_t *)context); - switch (event) { + switch (event.bit.event_type) { case EVENT_MODE_BUTTON_UP: launcher_move_to_next_widget(); return; @@ -63,7 +63,7 @@ void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, ui watch_set_led_off(); } - if (subsecond % 2) return; + if (event.bit.subsecond % 2) return; char buf[3]; switch (current_page) { case 0: diff --git a/launcher/widgets/settings/preferences_widget.h b/launcher/widgets/settings/preferences_widget.h index fcbcd0d6..e2e27fc7 100644 --- a/launcher/widgets/settings/preferences_widget.h +++ b/launcher/widgets/settings/preferences_widget.h @@ -5,7 +5,7 @@ void preferences_widget_setup(LauncherSettings *settings, void ** context_ptr); void preferences_widget_activate(LauncherSettings *settings, void *context); -void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, uint8_t subsecond, void *context); +void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context); void preferences_widget_resign(LauncherSettings *settings, void *context); #define preferences_widget { \ -- cgit v1.2.3 From c1ec7734c910d1eef74ebc3647dc084eeb43bbcf Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 09:51:49 -0400 Subject: launcher: let widgets prevent sleep --- launcher/launcher.c | 26 ++++++++++++-------------- launcher/launcher.h | 3 ++- launcher/widgets/clock/simple_clock_widget.c | 6 ++++-- launcher/widgets/clock/simple_clock_widget.h | 2 +- launcher/widgets/fake_widget_1.c | 6 ++++-- launcher/widgets/fake_widget_1.h | 2 +- launcher/widgets/fake_widget_2.c | 6 ++++-- launcher/widgets/fake_widget_2.h | 2 +- launcher/widgets/settings/preferences_widget.c | 24 +++++++++++++----------- launcher/widgets/settings/preferences_widget.h | 2 +- 10 files changed, 43 insertions(+), 36 deletions(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 4211e1ac..20fd7e59 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -35,13 +35,7 @@ void launcher_illuminate_led() { void launcher_move_to_widget(uint8_t widget_index) { launcher_state.widget_changed = true; - widgets[launcher_state.current_widget].resign(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - launcher_state.current_widget = widget_index; - watch_clear_display(); - widgets[launcher_state.current_widget].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); - event.value = 0; - event.bit.event_type = EVENT_ACTIVATE; - widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + launcher_state.next_widget = widget_index; } void launcher_move_to_next_widget() { @@ -94,12 +88,17 @@ void app_wake_from_sleep() { } bool app_loop() { - // play a beep if the widget has changed in response to a user's press of the MODE button if (launcher_state.widget_changed) { - // low note for nonzero case, high note for return to widget 0 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.current_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.value = 0; + event.bit.event_type = EVENT_ACTIVATE; launcher_state.widget_changed = false; } @@ -136,16 +135,15 @@ bool app_loop() { watch_enter_shallow_sleep(true); } + bool can_sleep = true; + if (event.bit.event_type) { event.bit.subsecond = launcher_state.subsecond; - widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + can_sleep = widgets[launcher_state.current_widget].loop(event, &launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); event.value = 0; } - - if (launcher_state.led_on) return false; - - return true; + return can_sleep && !launcher_state.led_on; } LauncherEventType _figure_out_button_event(LauncherEventType button_down_event, uint8_t *down_timestamp) { diff --git a/launcher/launcher.h b/launcher/launcher.h index d6ebbc2c..97c56545 100644 --- a/launcher/launcher.h +++ b/launcher/launcher.h @@ -49,7 +49,7 @@ typedef union { typedef void (*launcher_widget_setup)(LauncherSettings *settings, void ** context_ptr); typedef void (*launcher_widget_activate)(LauncherSettings *settings, void *context); -typedef void (*launcher_widget_loop)(LauncherEvent event, 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 WatchWidget { @@ -65,6 +65,7 @@ typedef struct LauncherState { // transient properties int16_t current_widget; + int16_t next_widget; bool widget_changed; // LED stuff diff --git a/launcher/widgets/clock/simple_clock_widget.c b/launcher/widgets/clock/simple_clock_widget.c index 427793f3..83b5a713 100644 --- a/launcher/widgets/clock/simple_clock_widget.c +++ b/launcher/widgets/clock/simple_clock_widget.c @@ -17,7 +17,7 @@ void simple_clock_widget_activate(LauncherSettings *settings, void *context) { *((uint32_t *)context) = 0xFFFFFFFF; } -void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +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]; @@ -64,7 +64,7 @@ void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, v break; case EVENT_MODE_BUTTON_UP: launcher_move_to_next_widget(); - return; + return false; case EVENT_LIGHT_BUTTON_UP: launcher_illuminate_led(); break; @@ -73,6 +73,8 @@ void simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, v default: break; } + + return true; } void simple_clock_widget_resign(LauncherSettings *settings, void *context) { diff --git a/launcher/widgets/clock/simple_clock_widget.h b/launcher/widgets/clock/simple_clock_widget.h index 42b0d3c5..739c942b 100644 --- a/launcher/widgets/clock/simple_clock_widget.h +++ b/launcher/widgets/clock/simple_clock_widget.h @@ -5,7 +5,7 @@ void simple_clock_widget_setup(LauncherSettings *settings, void ** context_ptr); void simple_clock_widget_activate(LauncherSettings *settings, void *context); -void simple_clock_widget_loop(LauncherEvent event, 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); diff --git a/launcher/widgets/fake_widget_1.c b/launcher/widgets/fake_widget_1.c index 65324095..bdc964e2 100644 --- a/launcher/widgets/fake_widget_1.c +++ b/launcher/widgets/fake_widget_1.c @@ -11,7 +11,7 @@ void fake_widget_1_activate(LauncherSettings *settings, void *context) { (void) context; } -void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +bool fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context) { printf("fake_widget_1_loop\n"); (void) settings; (void) context; @@ -20,13 +20,15 @@ void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *c switch (event.bit.event_type) { case EVENT_MODE_BUTTON_UP: launcher_move_to_next_widget(); - return; + return false; case EVENT_LIGHT_BUTTON_UP: launcher_illuminate_led(); break; default: break; } + + return true; } void fake_widget_1_resign(LauncherSettings *settings, void *context) { diff --git a/launcher/widgets/fake_widget_1.h b/launcher/widgets/fake_widget_1.h index 24704f23..d033b49a 100644 --- a/launcher/widgets/fake_widget_1.h +++ b/launcher/widgets/fake_widget_1.h @@ -5,7 +5,7 @@ void fake_widget_1_setup(LauncherSettings *settings, void ** context_ptr); void fake_widget_1_activate(LauncherSettings *settings, void *context); -void fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context); +bool fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context); void fake_widget_1_resign(LauncherSettings *settings, void *context); #define fake_widget_1 { \ diff --git a/launcher/widgets/fake_widget_2.c b/launcher/widgets/fake_widget_2.c index a6eafb7b..46772222 100644 --- a/launcher/widgets/fake_widget_2.c +++ b/launcher/widgets/fake_widget_2.c @@ -11,7 +11,7 @@ void fake_widget_2_activate(LauncherSettings *settings, void *context) { (void) context; } -void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +bool fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context) { printf("fake_widget_2_loop\n"); (void) settings; (void) context; @@ -20,13 +20,15 @@ void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *c switch (event.bit.event_type) { case EVENT_MODE_BUTTON_UP: launcher_move_to_next_widget(); - return; + return false; case EVENT_LIGHT_BUTTON_UP: launcher_illuminate_led(); break; default: break; } + + return true; } void fake_widget_2_resign(LauncherSettings *settings, void *context) { diff --git a/launcher/widgets/fake_widget_2.h b/launcher/widgets/fake_widget_2.h index 945e28fa..cf01969d 100644 --- a/launcher/widgets/fake_widget_2.h +++ b/launcher/widgets/fake_widget_2.h @@ -5,7 +5,7 @@ void fake_widget_2_setup(LauncherSettings *settings, void ** context_ptr); void fake_widget_2_activate(LauncherSettings *settings, void *context); -void fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context); +bool fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context); void fake_widget_2_resign(LauncherSettings *settings, void *context); #define fake_widget_2 { \ diff --git a/launcher/widgets/settings/preferences_widget.c b/launcher/widgets/settings/preferences_widget.c index d4abeb8e..878b775b 100644 --- a/launcher/widgets/settings/preferences_widget.c +++ b/launcher/widgets/settings/preferences_widget.c @@ -16,15 +16,16 @@ void preferences_widget_activate(LauncherSettings *settings, void *context) { launcher_request_tick_frequency(4); // we need to manually blink some pixels } -void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { (void) settings; (void) context; printf("preferences_widget_loop\n"); uint8_t current_page = *((uint8_t *)context); switch (event.bit.event_type) { case EVENT_MODE_BUTTON_UP: + watch_set_led_off(); launcher_move_to_next_widget(); - return; + return false; case EVENT_LIGHT_BUTTON_UP: current_page = (current_page + 1) % PREFERENCES_WIDGET_NUM_PREFEFENCES; *((uint8_t *)context) = current_page; @@ -54,16 +55,8 @@ void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, vo watch_clear_display(); watch_display_string((char *)preferences_widget_titles[current_page], 0); - if (current_page > 2) { - // this is a hack, launcher should be able to illumate with a custom color. - launcher_illuminate_led(); - 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); - } else { - watch_set_led_off(); - } - if (event.bit.subsecond % 2) return; + if (event.bit.subsecond % 2) return current_page <= 2; char buf[3]; switch (current_page) { case 0: @@ -111,6 +104,15 @@ void preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, vo 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) { diff --git a/launcher/widgets/settings/preferences_widget.h b/launcher/widgets/settings/preferences_widget.h index e2e27fc7..11a189f8 100644 --- a/launcher/widgets/settings/preferences_widget.h +++ b/launcher/widgets/settings/preferences_widget.h @@ -5,7 +5,7 @@ void preferences_widget_setup(LauncherSettings *settings, void ** context_ptr); void preferences_widget_activate(LauncherSettings *settings, void *context); -void preferences_widget_loop(LauncherEvent event, 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 { \ -- cgit v1.2.3 From 7f8973a8cc55e37cd8e2d59f7eab2def0045d2a6 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 10:48:04 -0400 Subject: slcd: add more special cases --- watch-library/watch/watch_slcd.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/watch-library/watch/watch_slcd.c b/watch-library/watch/watch_slcd.c index c1e24308..fc8c255a 100644 --- a/watch-library/watch/watch_slcd.c +++ b/watch-library/watch/watch_slcd.c @@ -114,8 +114,8 @@ static const uint8_t Character_Set[] = 0b01010000, // r 0b01101101, // s 0b01111000, // t - 0b01100010, // u (appears as superscript to work in more positions) - 0b01100010, // v (appears as superscript to work in more positions) + 0b01100010, // u (appears in (u)pper half to work in more positions) + 0b00011100, // v (looks like u but in the lower half) 0b10111110, // w (only works in position 0) 0b01111110, // x 0b01101110, // y @@ -169,8 +169,16 @@ void watch_clear_display() { } void watch_display_character(uint8_t character, uint8_t position) { - // handle lowercase 7 if needed - if (character == '7' && (position == 4 || position == 6)) character = '&'; + // special cases for positions 4 and 6 + if (position == 4 || position == 6) { + if (character == '7') character = '&'; // "lowercase" 7 + if (character == 'v') character = 'u'; // bottom segment duplicated, so show in top half + if (character == 'J') character = 'j'; // same + } else if (position != 4 && position != 6) { + if (character == 'u') character = 'v'; // we can use the bottom segment; move to lower half + if (character == 'j') character = 'J'; // same but just display a normal J + } + if (position == 0) slcd_sync_seg_off(&SEGMENT_LCD_0, SLCD_SEGID(0, 15)); // clear funky ninth segment uint64_t segmap = Segment_Map[position]; uint64_t segdata = Character_Set[character - 0x20]; @@ -189,7 +197,8 @@ void watch_display_character(uint8_t character, uint8_t position) { segmap = segmap >> 8; segdata = segdata >> 1; } - if (character == 'T' && position == 1) slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(1, 12)); + if (character == 'T' && position == 1) slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(1, 12)); // add descender + else if (position == 0 && (character == 'B' || character == 'D')) slcd_sync_seg_on(&SEGMENT_LCD_0, SLCD_SEGID(0, 15)); // add funky ninth segment } void watch_display_string(char *string, uint8_t position) { -- cgit v1.2.3 From 0546859073a3e57ed724e4eb1d741a438a175638 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 11:09:11 -0400 Subject: launcher: improvements to screensaver mode --- launcher/launcher.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 20fd7e59..43921106 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -130,9 +130,19 @@ bool app_loop() { 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.value = 0; - event.bit.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); + + // 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.bit.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.bit.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(); } bool can_sleep = true; @@ -175,10 +185,8 @@ void cb_alarm_btn_interrupt() { } void cb_alarm_btn_extwake() { + // wake up! _launcher_reset_screensaver_countdown(); - // this is a hack: waking from shallow sleep, app_setup does get called, but it happens before we reset our ticks. - // need to figure out if there's a better heuristic for determining how we woke up. - app_setup(); } void cb_alarm_fired() { -- cgit v1.2.3 From 0b60edb2659dfacf729a29f7aea7dab2bfa22160 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 11:57:29 -0400 Subject: launcher: fix typo --- launcher/launcher.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 43921106..38a13b5c 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -75,7 +75,7 @@ void app_setup() { widgets[i].setup(&launcher_state.launcher_settings, &widget_contexts[i]); } - widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[launcher_state.current_widget]); + widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[0]); event.value = 0; event.bit.event_type = EVENT_ACTIVATE; } -- cgit v1.2.3 From 74e866250920cb35b4575e0cdad47f63d4ae871d Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 11:57:57 -0400 Subject: launcher: track can_sleep between runloop invocations --- launcher/launcher.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 38a13b5c..d6883567 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -145,7 +145,7 @@ bool app_loop() { app_setup(); } - bool can_sleep = true; + static bool can_sleep = true; if (event.bit.event_type) { event.bit.subsecond = launcher_state.subsecond; -- cgit v1.2.3 From 95e95bb980d39c57544195715d508b6ccaee054d Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 11:59:08 -0400 Subject: launcher: fix flickering in preferences widget --- launcher/widgets/settings/preferences_widget.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/launcher/widgets/settings/preferences_widget.c b/launcher/widgets/settings/preferences_widget.c index 878b775b..7e583c81 100644 --- a/launcher/widgets/settings/preferences_widget.c +++ b/launcher/widgets/settings/preferences_widget.c @@ -3,7 +3,7 @@ #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"}; +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; @@ -17,8 +17,6 @@ void preferences_widget_activate(LauncherSettings *settings, void *context) { } bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { - (void) settings; - (void) context; printf("preferences_widget_loop\n"); uint8_t current_page = *((uint8_t *)context); switch (event.bit.event_type) { @@ -53,7 +51,6 @@ bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, vo break; } - watch_clear_display(); watch_display_string((char *)preferences_widget_titles[current_page], 0); if (event.bit.subsecond % 2) return current_page <= 2; -- cgit v1.2.3 From 110c81cbed94340f54fa890af94523f7a6135493 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 14:11:39 -0400 Subject: preferences widget: fix alignment of 'never' --- launcher/widgets/settings/preferences_widget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/widgets/settings/preferences_widget.c b/launcher/widgets/settings/preferences_widget.c index 7e583c81..79508ab3 100644 --- a/launcher/widgets/settings/preferences_widget.c +++ b/launcher/widgets/settings/preferences_widget.c @@ -67,7 +67,7 @@ bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, vo case 2: switch (settings->bit.screensaver_interval) { case 0: - watch_display_string("never", 4); + watch_display_string(" never", 4); break; case 1: watch_display_string("1 hour", 4); -- cgit v1.2.3 From 58cf2e75856c8a3aaee9619835bff16e97026a86 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 14:12:15 -0400 Subject: launcher: add widget for setting the time --- launcher/launcher_config.h | 6 +- launcher/make/Makefile | 1 + launcher/widgets/settings/set_time_widget.c | 109 ++++++++++++++++++++++++++++ launcher/widgets/settings/set_time_widget.h | 18 +++++ 4 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 launcher/widgets/settings/set_time_widget.c create mode 100644 launcher/widgets/settings/set_time_widget.h diff --git a/launcher/launcher_config.h b/launcher/launcher_config.h index 9bc43c47..ab0f6ea3 100644 --- a/launcher/launcher_config.h +++ b/launcher/launcher_config.h @@ -3,16 +3,16 @@ #include "simple_clock_widget.h" #include "preferences_widget.h" +#include "set_time_widget.h" #include "fake_widget_1.h" #include "fake_widget_2.h" -#define LAUNCHER_NUM_WIDGETS 4 +#define LAUNCHER_NUM_WIDGETS 3 WatchWidget widgets[LAUNCHER_NUM_WIDGETS] = { simple_clock_widget, preferences_widget, - fake_widget_1, - fake_widget_2, + set_time_widget, }; diff --git a/launcher/make/Makefile b/launcher/make/Makefile index 4fba0351..2f35f58c 100755 --- a/launcher/make/Makefile +++ b/launcher/make/Makefile @@ -24,6 +24,7 @@ SRCS += \ ../launcher.c \ ../widgets/clock/simple_clock_widget.c \ ../widgets/settings/preferences_widget.c \ + ../widgets/settings/set_time_widget.c \ ../widgets/fake_widget_1.c \ ../widgets/fake_widget_2.c \ diff --git a/launcher/widgets/settings/set_time_widget.c b/launcher/widgets/settings/set_time_widget.c new file mode 100644 index 00000000..c65459ab --- /dev/null +++ b/launcher/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; + *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.bit.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.bit.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 new file mode 100644 index 00000000..dc492dce --- /dev/null +++ b/launcher/widgets/settings/set_time_widget.h @@ -0,0 +1,18 @@ +#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_ -- cgit v1.2.3 From 83192b3a580c19eb694ca7951650db87eeb582ad Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 4 Oct 2021 15:27:29 -0400 Subject: launcher: fix high beep on return to clock --- launcher/launcher.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index d6883567..160b8d2a 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -91,7 +91,7 @@ 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.current_widget ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 50); + 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; -- cgit v1.2.3 From 8372e37bea5313a131054081e84fe7e9a45f32a9 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Tue, 5 Oct 2021 15:55:34 -0400 Subject: fix memory leak when waking from screensaver mode --- launcher/launcher.c | 8 ++++++++ launcher/widgets/clock/simple_clock_widget.c | 2 +- launcher/widgets/settings/preferences_widget.c | 2 +- launcher/widgets/settings/set_time_widget.c | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 160b8d2a..7d87c58a 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -56,6 +56,14 @@ void app_wake_from_deep_sleep() { } 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(); diff --git a/launcher/widgets/clock/simple_clock_widget.c b/launcher/widgets/clock/simple_clock_widget.c index 83b5a713..0c89cbc2 100644 --- a/launcher/widgets/clock/simple_clock_widget.c +++ b/launcher/widgets/clock/simple_clock_widget.c @@ -5,7 +5,7 @@ void simple_clock_widget_setup(LauncherSettings *settings, void ** context_ptr) { (void) settings; // the only context we need is the timestamp of the previous tick. - *context_ptr = malloc(sizeof(uint32_t)); + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint32_t)); } void simple_clock_widget_activate(LauncherSettings *settings, void *context) { diff --git a/launcher/widgets/settings/preferences_widget.c b/launcher/widgets/settings/preferences_widget.c index 79508ab3..40e0ac50 100644 --- a/launcher/widgets/settings/preferences_widget.c +++ b/launcher/widgets/settings/preferences_widget.c @@ -7,7 +7,7 @@ const char preferences_widget_titles[PREFERENCES_WIDGET_NUM_PREFEFENCES][11] = { void preferences_widget_setup(LauncherSettings *settings, void ** context_ptr) { (void) settings; - *context_ptr = malloc(sizeof(uint8_t)); + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); } void preferences_widget_activate(LauncherSettings *settings, void *context) { diff --git a/launcher/widgets/settings/set_time_widget.c b/launcher/widgets/settings/set_time_widget.c index c65459ab..c7949a4f 100644 --- a/launcher/widgets/settings/set_time_widget.c +++ b/launcher/widgets/settings/set_time_widget.c @@ -7,7 +7,7 @@ const char set_time_widget_titles[SET_TIME_WIDGET_NUM_SETTINGS][3] = {"HR", "MN" void set_time_widget_setup(LauncherSettings *settings, void ** context_ptr) { (void) settings; - *context_ptr = malloc(sizeof(uint8_t)); + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); } void set_time_widget_activate(LauncherSettings *settings, void *context) { -- cgit v1.2.3 From 0f349cb52e7436303f61a3dd2dde2f40d3dc05c3 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Tue, 5 Oct 2021 17:58:49 -0400 Subject: add pulseometer widget --- launcher/launcher_config.h | 1 + launcher/make/Makefile | 2 + .../widgets/complications/pulseometer_widget.c | 83 ++++++++++++++++++++++ .../widgets/complications/pulseometer_widget.h | 24 +++++++ 4 files changed, 110 insertions(+) create mode 100644 launcher/widgets/complications/pulseometer_widget.c create mode 100644 launcher/widgets/complications/pulseometer_widget.h diff --git a/launcher/launcher_config.h b/launcher/launcher_config.h index ab0f6ea3..5280b989 100644 --- a/launcher/launcher_config.h +++ b/launcher/launcher_config.h @@ -4,6 +4,7 @@ #include "simple_clock_widget.h" #include "preferences_widget.h" #include "set_time_widget.h" +#include "pulseometer_widget.h" #include "fake_widget_1.h" #include "fake_widget_2.h" diff --git a/launcher/make/Makefile b/launcher/make/Makefile index 2f35f58c..c6d18137 100755 --- a/launcher/make/Makefile +++ b/launcher/make/Makefile @@ -13,6 +13,7 @@ INCLUDES += \ -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. @@ -25,6 +26,7 @@ SRCS += \ ../widgets/clock/simple_clock_widget.c \ ../widgets/settings/preferences_widget.c \ ../widgets/settings/set_time_widget.c \ + ../widgets/complications/pulseometer_widget.c \ ../widgets/fake_widget_1.c \ ../widgets/fake_widget_2.c \ diff --git a/launcher/widgets/complications/pulseometer_widget.c b/launcher/widgets/complications/pulseometer_widget.c new file mode 100644 index 00000000..3df1cb44 --- /dev/null +++ b/launcher/widgets/complications/pulseometer_widget.c @@ -0,0 +1,83 @@ +#include +#include +#include "pulseometer_widget.h" +#include "watch.h" + +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]; + // starts at index 15 + const uint8_t pulse_lookup[] = {240, 225, 212, 200, 189, 180, 171, 164, 157, 150, 144, 138, 133, 129, 124, 120, 116, 113, 109, 106, 103, 100, 97, 95, 92, 90, 88, 86, 84, 82, 80, 78, 77, 75, 73, 72, 71, 69, 68, 67, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 55, 54, 53, 52, 51, 51, 50, 49, 49, 48, 47, 47, 46, 46, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, 40, 40}; + switch (event.bit.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++; + } else { + if (pulsometer_state->ticks < 15) { + watch_display_string(" Lo", 0); + } else if (pulsometer_state->ticks > 91) { + watch_display_string(" Hi", 0); + } else { + if (pulsometer_state->measuring) pulsometer_state->pulse = pulse_lookup[pulsometer_state->ticks - 15]; + 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->measuring = true; + launcher_request_tick_frequency(2); + 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 new file mode 100644 index 00000000..5b18d7f3 --- /dev/null +++ b/launcher/widgets/complications/pulseometer_widget.h @@ -0,0 +1,24 @@ +#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 -- cgit v1.2.3 From 12ee9221354c863bddb21a076dd3e3bdface3c67 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Tue, 5 Oct 2021 18:01:26 -0400 Subject: remove fake / placeholder widgets --- launcher/launcher_config.h | 2 -- launcher/make/Makefile | 10 ++++------ launcher/widgets/fake_widget_1.c | 37 ------------------------------------- launcher/widgets/fake_widget_1.h | 18 ------------------ launcher/widgets/fake_widget_2.c | 37 ------------------------------------- launcher/widgets/fake_widget_2.h | 18 ------------------ 6 files changed, 4 insertions(+), 118 deletions(-) delete mode 100644 launcher/widgets/fake_widget_1.c delete mode 100644 launcher/widgets/fake_widget_1.h delete mode 100644 launcher/widgets/fake_widget_2.c delete mode 100644 launcher/widgets/fake_widget_2.h diff --git a/launcher/launcher_config.h b/launcher/launcher_config.h index 5280b989..ff656350 100644 --- a/launcher/launcher_config.h +++ b/launcher/launcher_config.h @@ -5,8 +5,6 @@ #include "preferences_widget.h" #include "set_time_widget.h" #include "pulseometer_widget.h" -#include "fake_widget_1.h" -#include "fake_widget_2.h" #define LAUNCHER_NUM_WIDGETS 3 diff --git a/launcher/make/Makefile b/launcher/make/Makefile index c6d18137..a1059bbd 100755 --- a/launcher/make/Makefile +++ b/launcher/make/Makefile @@ -7,7 +7,7 @@ include $(TOP)/make.mk # INCLUDES += \ # -I../ \ # -I../drivers/ \ -# -I../utils/ +# -I../widgets/fitness/ INCLUDES += \ -I../ \ -I../widgets/ \ @@ -18,17 +18,15 @@ INCLUDES += \ # 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 += \ -# ../app.c \ -# ../drivers/bmp280.c \ -# ../utils/temperature.c +# ../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 \ - ../widgets/fake_widget_1.c \ - ../widgets/fake_widget_2.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/fake_widget_1.c b/launcher/widgets/fake_widget_1.c deleted file mode 100644 index bdc964e2..00000000 --- a/launcher/widgets/fake_widget_1.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "fake_widget_1.h" -#include "watch.h" - -void fake_widget_1_setup(LauncherSettings *settings, void ** context_ptr) { - (void) settings; - *context_ptr = NULL; -} - -void fake_widget_1_activate(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; -} - -bool fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context) { - printf("fake_widget_1_loop\n"); - (void) settings; - (void) context; - watch_display_string("W1 d get01", 0); - - switch (event.bit.event_type) { - case EVENT_MODE_BUTTON_UP: - launcher_move_to_next_widget(); - return false; - case EVENT_LIGHT_BUTTON_UP: - launcher_illuminate_led(); - break; - default: - break; - } - - return true; -} - -void fake_widget_1_resign(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; -} diff --git a/launcher/widgets/fake_widget_1.h b/launcher/widgets/fake_widget_1.h deleted file mode 100644 index d033b49a..00000000 --- a/launcher/widgets/fake_widget_1.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FAKE_WIDGET_1_H_ -#define FAKE_WIDGET_1_H_ - -#include "launcher.h" - -void fake_widget_1_setup(LauncherSettings *settings, void ** context_ptr); -void fake_widget_1_activate(LauncherSettings *settings, void *context); -bool fake_widget_1_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void fake_widget_1_resign(LauncherSettings *settings, void *context); - -#define fake_widget_1 { \ - fake_widget_1_setup, \ - fake_widget_1_activate, \ - fake_widget_1_loop, \ - fake_widget_1_resign, \ -} - -#endif // FAKE_WIDGET_1_H_ \ No newline at end of file diff --git a/launcher/widgets/fake_widget_2.c b/launcher/widgets/fake_widget_2.c deleted file mode 100644 index 46772222..00000000 --- a/launcher/widgets/fake_widget_2.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "fake_widget_2.h" -#include "watch.h" - -void fake_widget_2_setup(LauncherSettings *settings, void ** context_ptr) { - (void) settings; - *context_ptr = NULL; -} - -void fake_widget_2_activate(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; -} - -bool fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context) { - printf("fake_widget_2_loop\n"); - (void) settings; - (void) context; - watch_display_string("W1 d get02", 0); - - switch (event.bit.event_type) { - case EVENT_MODE_BUTTON_UP: - launcher_move_to_next_widget(); - return false; - case EVENT_LIGHT_BUTTON_UP: - launcher_illuminate_led(); - break; - default: - break; - } - - return true; -} - -void fake_widget_2_resign(LauncherSettings *settings, void *context) { - (void) settings; - (void) context; -} diff --git a/launcher/widgets/fake_widget_2.h b/launcher/widgets/fake_widget_2.h deleted file mode 100644 index cf01969d..00000000 --- a/launcher/widgets/fake_widget_2.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FAKE_WIDGET_2_H_ -#define FAKE_WIDGET_2_H_ - -#include "launcher.h" - -void fake_widget_2_setup(LauncherSettings *settings, void ** context_ptr); -void fake_widget_2_activate(LauncherSettings *settings, void *context); -bool fake_widget_2_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void fake_widget_2_resign(LauncherSettings *settings, void *context); - -#define fake_widget_2 { \ - fake_widget_2_setup, \ - fake_widget_2_activate, \ - fake_widget_2_loop, \ - fake_widget_2_resign, \ -} - -#endif // FAKE_WIDGET_2_H_ \ No newline at end of file -- cgit v1.2.3 From 3516f4295eafbb04f7ccd1f4947c4921fae6e9bf Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Wed, 6 Oct 2021 10:25:28 -0400 Subject: launcher: clean up structs --- launcher/launcher.c | 32 ++++++++++++---------- launcher/launcher.h | 15 ++++------ launcher/widgets/clock/simple_clock_widget.c | 8 +++--- .../widgets/complications/pulseometer_widget.c | 2 +- launcher/widgets/settings/preferences_widget.c | 4 +-- launcher/widgets/settings/set_time_widget.c | 4 +-- 6 files changed, 31 insertions(+), 34 deletions(-) diff --git a/launcher/launcher.c b/launcher/launcher.c index 7d87c58a..7adb01a8 100644 --- a/launcher/launcher.c +++ b/launcher/launcher.c @@ -84,8 +84,8 @@ void app_setup() { } widgets[0].activate(&launcher_state.launcher_settings, widget_contexts[0]); - event.value = 0; - event.bit.event_type = EVENT_ACTIVATE; + event.subsecond = 0; + event.event_type = EVENT_ACTIVATE; } } @@ -105,8 +105,8 @@ bool app_loop() { 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.value = 0; - event.bit.event_type = EVENT_ACTIVATE; + event.subsecond = 0; + event.event_type = EVENT_ACTIVATE; launcher_state.widget_changed = false; } @@ -137,17 +137,18 @@ bool app_loop() { 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.value = 0; + 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.bit.event_type = EVENT_SCREENSAVER; + 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.bit.event_type = EVENT_ACTIVATE; + 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(); @@ -155,10 +156,11 @@ bool app_loop() { static bool can_sleep = true; - if (event.bit.event_type) { - event.bit.subsecond = launcher_state.subsecond; + 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.value = 0; + event.event_type = EVENT_NONE; + event.subsecond = 0; } return can_sleep && !launcher_state.led_on; @@ -179,17 +181,17 @@ LauncherEventType _figure_out_button_event(LauncherEventType button_down_event, void cb_light_btn_interrupt() { _launcher_reset_screensaver_countdown(); - event.bit.event_type = _figure_out_button_event(EVENT_LIGHT_BUTTON_DOWN, &launcher_state.light_down_timestamp); + 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.bit.event_type = _figure_out_button_event(EVENT_MODE_BUTTON_DOWN, &launcher_state.mode_down_timestamp); + 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.bit.event_type = _figure_out_button_event(EVENT_ALARM_BUTTON_DOWN, &launcher_state.alarm_down_timestamp); + event.event_type = _figure_out_button_event(EVENT_ALARM_BUTTON_DOWN, &launcher_state.alarm_down_timestamp); } void cb_alarm_btn_extwake() { @@ -198,11 +200,11 @@ void cb_alarm_btn_extwake() { } void cb_alarm_fired() { - event.bit.event_type = EVENT_SCREENSAVER; + event.event_type = EVENT_SCREENSAVER; } void cb_tick() { - event.bit.event_type = EVENT_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--; diff --git a/launcher/launcher.h b/launcher/launcher.h index 97c56545..5c5315a3 100644 --- a/launcher/launcher.h +++ b/launcher/launcher.h @@ -37,29 +37,24 @@ typedef enum { EVENT_ALARM_LONG_PRESS, // The alarm button was held for >2 seconds, and released. } LauncherEventType; -typedef union { - struct { - uint32_t event_type : 8; - uint32_t subsecond : 8; - uint32_t reserved : 16; - } bit; - uint32_t value; +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 WatchWidget { +typedef struct { launcher_widget_setup setup; launcher_widget_activate activate; launcher_widget_loop loop; launcher_widget_resign resign; } WatchWidget; -typedef struct LauncherState { +typedef struct { // properties stored in BACKUP register LauncherSettings launcher_settings; diff --git a/launcher/widgets/clock/simple_clock_widget.c b/launcher/widgets/clock/simple_clock_widget.c index 0c89cbc2..7fe0183d 100644 --- a/launcher/widgets/clock/simple_clock_widget.c +++ b/launcher/widgets/clock/simple_clock_widget.c @@ -25,7 +25,7 @@ bool simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, v watch_date_time date_time; uint32_t previous_date_time; - switch (event.bit.event_type) { + switch (event.event_type) { case EVENT_ACTIVATE: case EVENT_TICK: case EVENT_SCREENSAVER: @@ -33,11 +33,11 @@ bool simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, v previous_date_time = *((uint32_t *)context); *((uint32_t *)context) = date_time.reg; - if (date_time.reg >> 6 == previous_date_time >> 6 && event.bit.event_type != EVENT_SCREENSAVER) { + 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.bit.event_type != EVENT_SCREENSAVER) { + } 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); @@ -54,7 +54,7 @@ bool simple_clock_widget_loop(LauncherEvent event, LauncherSettings *settings, v if (date_time.unit.hour == 0) date_time.unit.hour = 12; } pos = 0; - if (event.bit.event_type == EVENT_SCREENSAVER) { + 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); diff --git a/launcher/widgets/complications/pulseometer_widget.c b/launcher/widgets/complications/pulseometer_widget.c index 3df1cb44..a7272dcb 100644 --- a/launcher/widgets/complications/pulseometer_widget.c +++ b/launcher/widgets/complications/pulseometer_widget.c @@ -20,7 +20,7 @@ bool pulseometer_widget_loop(LauncherEvent event, LauncherSettings *settings, vo char buf[14]; // starts at index 15 const uint8_t pulse_lookup[] = {240, 225, 212, 200, 189, 180, 171, 164, 157, 150, 144, 138, 133, 129, 124, 120, 116, 113, 109, 106, 103, 100, 97, 95, 92, 90, 88, 86, 84, 82, 80, 78, 77, 75, 73, 72, 71, 69, 68, 67, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 55, 54, 53, 52, 51, 51, 50, 49, 49, 48, 47, 47, 46, 46, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, 40, 40}; - switch (event.bit.event_type) { + switch (event.event_type) { case EVENT_TICK: if (pulsometer_state->pulse == 0 && !pulsometer_state->measuring) { switch (pulsometer_state->ticks % 5) { diff --git a/launcher/widgets/settings/preferences_widget.c b/launcher/widgets/settings/preferences_widget.c index 40e0ac50..1618a72c 100644 --- a/launcher/widgets/settings/preferences_widget.c +++ b/launcher/widgets/settings/preferences_widget.c @@ -19,7 +19,7 @@ void preferences_widget_activate(LauncherSettings *settings, void *context) { bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, void *context) { printf("preferences_widget_loop\n"); uint8_t current_page = *((uint8_t *)context); - switch (event.bit.event_type) { + switch (event.event_type) { case EVENT_MODE_BUTTON_UP: watch_set_led_off(); launcher_move_to_next_widget(); @@ -53,7 +53,7 @@ bool preferences_widget_loop(LauncherEvent event, LauncherSettings *settings, vo watch_display_string((char *)preferences_widget_titles[current_page], 0); - if (event.bit.subsecond % 2) return current_page <= 2; + if (event.subsecond % 2) return current_page <= 2; char buf[3]; switch (current_page) { case 0: diff --git a/launcher/widgets/settings/set_time_widget.c b/launcher/widgets/settings/set_time_widget.c index c7949a4f..d8cfb9fc 100644 --- a/launcher/widgets/settings/set_time_widget.c +++ b/launcher/widgets/settings/set_time_widget.c @@ -21,7 +21,7 @@ bool set_time_widget_loop(LauncherEvent event, LauncherSettings *settings, void 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.bit.event_type) { + switch (event.event_type) { case EVENT_MODE_BUTTON_UP: launcher_move_to_next_widget(); return false; @@ -79,7 +79,7 @@ bool set_time_widget_loop(LauncherEvent event, LauncherSettings *settings, void 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.bit.subsecond % 2) { + if (event.subsecond % 2) { switch (current_page) { case 0: case 3: -- cgit v1.2.3 From 9d4367565b20ef9d42f793fc00daa575c46b0e3c Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Wed, 6 Oct 2021 12:03:02 -0400 Subject: pulseometer: support variable update frequency --- launcher/widgets/complications/pulseometer_widget.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/launcher/widgets/complications/pulseometer_widget.c b/launcher/widgets/complications/pulseometer_widget.c index a7272dcb..7b3f3efe 100644 --- a/launcher/widgets/complications/pulseometer_widget.c +++ b/launcher/widgets/complications/pulseometer_widget.c @@ -3,6 +3,9 @@ #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)); @@ -18,8 +21,6 @@ bool pulseometer_widget_loop(LauncherEvent event, LauncherSettings *settings, vo (void) settings; PulsometerState *pulsometer_state = (PulsometerState *)context; char buf[14]; - // starts at index 15 - const uint8_t pulse_lookup[] = {240, 225, 212, 200, 189, 180, 171, 164, 157, 150, 144, 138, 133, 129, 124, 120, 116, 113, 109, 106, 103, 100, 97, 95, 92, 90, 88, 86, 84, 82, 80, 78, 77, 75, 73, 72, 71, 69, 68, 67, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 55, 54, 53, 52, 51, 51, 50, 49, 49, 48, 47, 47, 46, 46, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, 40, 40}; switch (event.event_type) { case EVENT_TICK: if (pulsometer_state->pulse == 0 && !pulsometer_state->measuring) { @@ -40,14 +41,16 @@ bool pulseometer_widget_loop(LauncherEvent event, LauncherSettings *settings, vo watch_clear_display(); break; } - pulsometer_state->ticks++; + pulsometer_state->ticks = (pulsometer_state->ticks + 1) % 5; } else { - if (pulsometer_state->ticks < 15) { - watch_display_string(" Lo", 0); - } else if (pulsometer_state->ticks > 91) { + 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 { - if (pulsometer_state->measuring) pulsometer_state->pulse = pulse_lookup[pulsometer_state->ticks - 15]; sprintf(buf, " %-3dbpn", pulsometer_state->pulse); watch_display_string(buf, 0); } @@ -62,8 +65,9 @@ bool pulseometer_widget_loop(LauncherEvent event, LauncherSettings *settings, vo break; case EVENT_ALARM_BUTTON_DOWN: pulsometer_state->ticks = 0; + pulsometer_state->pulse = 0xFFFF; pulsometer_state->measuring = true; - launcher_request_tick_frequency(2); + launcher_request_tick_frequency(PULSOMETER_WIDGET_FREQUENCY); break; case EVENT_ALARM_BUTTON_UP: case EVENT_ALARM_LONG_PRESS: -- cgit v1.2.3 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 From d5ac4cb71b4e328a27e26843cfdc6719b152ac7d Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 13:14:52 -0400 Subject: widgets are now watch faces --- movement/make/Makefile | 20 ++-- movement/movement.c | 40 +++---- movement/movement.h | 34 +++--- movement/movement_config.h | 18 ++-- movement/watch_faces/clock/simple_clock_face.c | 92 ++++++++++++++++ movement/watch_faces/clock/simple_clock_face.h | 20 ++++ .../watch_faces/complications/pulseometer_face.c | 87 +++++++++++++++ .../watch_faces/complications/pulseometer_face.h | 24 +++++ movement/watch_faces/settings/preferences_face.c | 120 +++++++++++++++++++++ movement/watch_faces/settings/preferences_face.h | 18 ++++ movement/watch_faces/settings/set_time_face.c | 109 +++++++++++++++++++ movement/watch_faces/settings/set_time_face.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 ---- 20 files changed, 544 insertions(+), 544 deletions(-) create mode 100644 movement/watch_faces/clock/simple_clock_face.c create mode 100644 movement/watch_faces/clock/simple_clock_face.h create mode 100644 movement/watch_faces/complications/pulseometer_face.c create mode 100644 movement/watch_faces/complications/pulseometer_face.h create mode 100644 movement/watch_faces/settings/preferences_face.c create mode 100644 movement/watch_faces/settings/preferences_face.h create mode 100644 movement/watch_faces/settings/set_time_face.c create mode 100644 movement/watch_faces/settings/set_time_face.h delete mode 100644 movement/widgets/clock/simple_clock_widget.c delete mode 100644 movement/widgets/clock/simple_clock_widget.h delete mode 100644 movement/widgets/complications/pulseometer_widget.c delete mode 100644 movement/widgets/complications/pulseometer_widget.h delete mode 100644 movement/widgets/settings/preferences_widget.c delete mode 100644 movement/widgets/settings/preferences_widget.h delete mode 100644 movement/widgets/settings/set_time_widget.c delete mode 100644 movement/widgets/settings/set_time_widget.h diff --git a/movement/make/Makefile b/movement/make/Makefile index 35b80079..8b29fba4 100755 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -7,26 +7,26 @@ include $(TOP)/make.mk # INCLUDES += \ # -I../ \ # -I../drivers/ \ -# -I../widgets/fitness/ +# -I../watch_faces/fitness/ INCLUDES += \ -I../ \ - -I../widgets/ \ - -I../widgets/clock/ \ - -I../widgets/settings/ \ - -I../widgets/complications/ \ + -I../watch_faces/ \ + -I../watch_faces/clock/ \ + -I../watch_faces/settings/ \ + -I../watch_faces/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 +# ../watch_faces/fitness/step_count_face.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 \ + ../watch_faces/clock/simple_clock_face.c \ + ../watch_faces/settings/preferences_face.c \ + ../watch_faces/settings/set_time_face.c \ + ../watch_faces/complications/pulseometer_face.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 index 9c15833c..3417345c 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -6,7 +6,7 @@ #include "movement_config.h" LauncherState movement_state; -void * widget_contexts[MOVEMENT_NUM_WIDGETS]; +void * watch_face_contexts[MOVEMENT_NUM_FACES]; const int32_t movement_screensaver_deadlines[8] = {INT_MAX, 3600, 7200, 21600, 43200, 86400, 172800, 604800}; LauncherEvent event; @@ -33,13 +33,13 @@ 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_face(uint8_t watch_face_index) { + movement_state.watch_face_changed = true; + movement_state.next_face = watch_face_index; } -void movement_move_to_next_widget() { - movement_move_to_widget((movement_state.current_widget + 1) % MOVEMENT_NUM_WIDGETS); +void movement_move_to_next_face() { + movement_move_to_face((movement_state.current_watch_face + 1) % MOVEMENT_NUM_FACES); } void app_init() { @@ -59,8 +59,8 @@ 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; + for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) { + watch_face_contexts[i] = NULL; is_first_launch = false; } } @@ -79,11 +79,11 @@ void app_setup() { 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]); + for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) { + watch_faces[i].setup(&movement_state.movement_settings, &watch_face_contexts[i]); } - widgets[0].activate(&movement_state.movement_settings, widget_contexts[0]); + watch_faces[0].activate(&movement_state.movement_settings, watch_face_contexts[0]); event.subsecond = 0; event.event_type = EVENT_ACTIVATE; } @@ -96,18 +96,18 @@ void app_wake_from_sleep() { } bool app_loop() { - if (movement_state.widget_changed) { + if (movement_state.watch_face_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); + // low note for nonzero case, high note for return to watch_face 0 + watch_buzzer_play_note(movement_state.next_face ? 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_faces[movement_state.current_watch_face].resign(&movement_state.movement_settings, watch_face_contexts[movement_state.current_watch_face]); + movement_state.current_watch_face = movement_state.next_face; watch_clear_display(); - widgets[movement_state.current_widget].activate(&movement_state.movement_settings, widget_contexts[movement_state.current_widget]); + watch_faces[movement_state.current_watch_face].activate(&movement_state.movement_settings, watch_face_contexts[movement_state.current_watch_face]); event.subsecond = 0; event.event_type = EVENT_ACTIVATE; - movement_state.widget_changed = false; + movement_state.watch_face_changed = false; } // If the LED is off and should be on, turn it on @@ -144,7 +144,7 @@ bool app_loop() { // 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_faces[movement_state.current_watch_face].loop(event, &movement_state.movement_settings, watch_face_contexts[movement_state.current_watch_face]); 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. @@ -158,7 +158,7 @@ bool app_loop() { 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]); + can_sleep = watch_faces[movement_state.current_watch_face].loop(event, &movement_state.movement_settings, watch_face_contexts[movement_state.current_watch_face]); event.event_type = EVENT_NONE; event.subsecond = 0; } diff --git a/movement/movement.h b/movement/movement.h index 025a5aa6..719a3d95 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -23,9 +23,9 @@ typedef union { 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_ACTIVATE, // Your watch face is entering the foreground. + EVENT_TICK, // Most common event type. Your watch face is being called from the tick callback. + EVENT_SCREENSAVER, // Your watch face 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. @@ -42,26 +42,26 @@ typedef struct { 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 void (*watch_face_setup)(LauncherSettings *settings, void ** context_ptr); +typedef void (*watch_face_activate)(LauncherSettings *settings, void *context); +typedef bool (*watch_face_loop)(LauncherEvent event, LauncherSettings *settings, void *context); +typedef void (*watch_face_resign)(LauncherSettings *settings, void *context); typedef struct { - movement_widget_setup setup; - movement_widget_activate activate; - movement_widget_loop loop; - movement_widget_resign resign; -} WatchWidget; + watch_face_setup setup; + watch_face_activate activate; + watch_face_loop loop; + watch_face_resign resign; +} WatchFace; typedef struct { // properties stored in BACKUP register LauncherSettings movement_settings; // transient properties - int16_t current_widget; - int16_t next_widget; - bool widget_changed; + int16_t current_watch_face; + int16_t next_face; + bool watch_face_changed; // LED stuff uint8_t light_ticks; @@ -81,8 +81,8 @@ typedef struct { uint8_t subsecond; } LauncherState; -void movement_move_to_widget(uint8_t widget_index); -void movement_move_to_next_widget(); +void movement_move_to_face(uint8_t watch_face_index); +void movement_move_to_next_face(); void movement_illuminate_led(); void movement_request_tick_frequency(uint8_t freq); diff --git a/movement/movement_config.h b/movement/movement_config.h index 49f342ca..41066d19 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -1,17 +1,17 @@ #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" +#include "simple_clock_face.h" +#include "preferences_face.h" +#include "set_time_face.h" +#include "pulseometer_face.h" -#define MOVEMENT_NUM_WIDGETS 3 +#define MOVEMENT_NUM_FACES 3 -WatchWidget widgets[MOVEMENT_NUM_WIDGETS] = { - simple_clock_widget, - preferences_widget, - set_time_widget, +WatchFace watch_faces[MOVEMENT_NUM_FACES] = { + simple_clock_face, + preferences_face, + set_time_face, }; diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c new file mode 100644 index 00000000..97067b0f --- /dev/null +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -0,0 +1,92 @@ +#include +#include "simple_clock_face.h" +#include "watch.h" + +void simple_clock_face_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_face_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_face_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + printf("simple_clock_face_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_face_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_face_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_face(); + return false; + case EVENT_LIGHT_BUTTON_UP: + movement_illuminate_led(); + break; + case EVENT_ALARM_BUTTON_UP: + break; + default: + break; + } + + return true; +} + +void simple_clock_face_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} + +uint8_t simple_clock_face_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/watch_faces/clock/simple_clock_face.h b/movement/watch_faces/clock/simple_clock_face.h new file mode 100644 index 00000000..59201a1f --- /dev/null +++ b/movement/watch_faces/clock/simple_clock_face.h @@ -0,0 +1,20 @@ +#ifndef SIMPLE_CLOCK_FACE_H_ +#define SIMPLE_CLOCK_FACE_H_ + +#include "movement.h" + +void simple_clock_face_setup(LauncherSettings *settings, void ** context_ptr); +void simple_clock_face_activate(LauncherSettings *settings, void *context); +bool simple_clock_face_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void simple_clock_face_resign(LauncherSettings *settings, void *context); + +uint8_t simple_clock_face_get_weekday(uint16_t day, uint16_t month, uint16_t year); + +#define simple_clock_face { \ + simple_clock_face_setup, \ + simple_clock_face_activate, \ + simple_clock_face_loop, \ + simple_clock_face_resign, \ +} + +#endif // FAKE_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/complications/pulseometer_face.c b/movement/watch_faces/complications/pulseometer_face.c new file mode 100644 index 00000000..e38153d6 --- /dev/null +++ b/movement/watch_faces/complications/pulseometer_face.c @@ -0,0 +1,87 @@ +#include +#include +#include "pulseometer_face.h" +#include "watch.h" + +#define PULSOMETER_FACE_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.) +#define PULSOMETER_FACE_FREQUENCY (1 << PULSOMETER_FACE_FREQUENCY_FACTOR) + +void pulseometer_face_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(PulsometerState)); +} + +void pulseometer_face_activate(LauncherSettings *settings, void *context) { + (void) settings; + memset(context, 0, sizeof(PulsometerState)); +} + +bool pulseometer_face_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + printf("pulseometer_face_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_FACE_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_face(); + 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_FACE_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_face_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; +} diff --git a/movement/watch_faces/complications/pulseometer_face.h b/movement/watch_faces/complications/pulseometer_face.h new file mode 100644 index 00000000..7b96259c --- /dev/null +++ b/movement/watch_faces/complications/pulseometer_face.h @@ -0,0 +1,24 @@ +#ifndef PULSEOMETER_FACE_H_ +#define PULSEOMETER_FACE_H_ + +#include "movement.h" + +typedef struct { + bool measuring; + int16_t pulse; + int16_t ticks; +} PulsometerState; + +void pulseometer_face_setup(LauncherSettings *settings, void ** context_ptr); +void pulseometer_face_activate(LauncherSettings *settings, void *context); +bool pulseometer_face_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void pulseometer_face_resign(LauncherSettings *settings, void *context); + +#define pulseometer_face { \ + pulseometer_face_setup, \ + pulseometer_face_activate, \ + pulseometer_face_loop, \ + pulseometer_face_resign, \ +} + +#endif // PULSEOMETER_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/settings/preferences_face.c b/movement/watch_faces/settings/preferences_face.c new file mode 100644 index 00000000..f499e36f --- /dev/null +++ b/movement/watch_faces/settings/preferences_face.c @@ -0,0 +1,120 @@ +#include +#include "preferences_face.h" +#include "watch.h" + +#define PREFERENCES_FACE_NUM_PREFEFENCES (5) +const char preferences_face_titles[PREFERENCES_FACE_NUM_PREFEFENCES][11] = {"CL ", "Bt Beep ", "SC ", "Lt grn ", "Lt red "}; + +void preferences_face_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); +} + +void preferences_face_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_face_loop(LauncherEvent event, LauncherSettings *settings, void *context) { + printf("preferences_face_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_face(); + return false; + case EVENT_LIGHT_BUTTON_UP: + current_page = (current_page + 1) % PREFERENCES_FACE_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_face_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_face_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; + watch_set_led_off(); + movement_request_tick_frequency(1); +} diff --git a/movement/watch_faces/settings/preferences_face.h b/movement/watch_faces/settings/preferences_face.h new file mode 100644 index 00000000..218f99c2 --- /dev/null +++ b/movement/watch_faces/settings/preferences_face.h @@ -0,0 +1,18 @@ +#ifndef PREFERENCES_FACE_H_ +#define PREFERENCES_FACE_H_ + +#include "movement.h" + +void preferences_face_setup(LauncherSettings *settings, void ** context_ptr); +void preferences_face_activate(LauncherSettings *settings, void *context); +bool preferences_face_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void preferences_face_resign(LauncherSettings *settings, void *context); + +#define preferences_face { \ + preferences_face_setup, \ + preferences_face_activate, \ + preferences_face_loop, \ + preferences_face_resign, \ +} + +#endif // PREFERENCES_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c new file mode 100644 index 00000000..36498611 --- /dev/null +++ b/movement/watch_faces/settings/set_time_face.c @@ -0,0 +1,109 @@ +#include +#include "set_time_face.h" +#include "watch.h" + +#define SET_TIME_FACE_NUM_SETTINGS (6) +const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "MN", "SE", "YR", "MO", "DA"}; + +void set_time_face_setup(LauncherSettings *settings, void ** context_ptr) { + (void) settings; + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); +} + +void set_time_face_activate(LauncherSettings *settings, void *context) { + (void) settings; + *((uint8_t *)context) = 0; + movement_request_tick_frequency(4); +} + +bool set_time_face_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_face(); + return false; + case EVENT_LIGHT_BUTTON_UP: + current_page = (current_page + 1) % SET_TIME_FACE_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_face_titles[current_page], date_time.unit.hour, date_time.unit.minute, date_time.unit.second); + } else { + sprintf(buf, "%s %2d%02d%02d", set_time_face_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_face_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_face_resign(LauncherSettings *settings, void *context) { + (void) settings; + (void) context; + watch_set_led_off(); + movement_request_tick_frequency(1); +} diff --git a/movement/watch_faces/settings/set_time_face.h b/movement/watch_faces/settings/set_time_face.h new file mode 100644 index 00000000..0c34d6b2 --- /dev/null +++ b/movement/watch_faces/settings/set_time_face.h @@ -0,0 +1,18 @@ +#ifndef SET_TIME_FACE_H_ +#define SET_TIME_FACE_H_ + +#include "movement.h" + +void set_time_face_setup(LauncherSettings *settings, void ** context_ptr); +void set_time_face_activate(LauncherSettings *settings, void *context); +bool set_time_face_loop(LauncherEvent event, LauncherSettings *settings, void *context); +void set_time_face_resign(LauncherSettings *settings, void *context); + +#define set_time_face { \ + set_time_face_setup, \ + set_time_face_activate, \ + set_time_face_loop, \ + set_time_face_resign, \ +} + +#endif // SET_TIME_FACE_H_ diff --git a/movement/widgets/clock/simple_clock_widget.c b/movement/widgets/clock/simple_clock_widget.c deleted file mode 100644 index 22218860..00000000 --- a/movement/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: - 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 deleted file mode 100644 index 3bf4c9a3..00000000 --- a/movement/widgets/clock/simple_clock_widget.h +++ /dev/null @@ -1,20 +0,0 @@ -#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 deleted file mode 100644 index 6384685a..00000000 --- a/movement/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: - 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 deleted file mode 100644 index e5947660..00000000 --- a/movement/widgets/complications/pulseometer_widget.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 deleted file mode 100644 index c4537329..00000000 --- a/movement/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; - 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 deleted file mode 100644 index 3d463027..00000000 --- a/movement/widgets/settings/preferences_widget.h +++ /dev/null @@ -1,18 +0,0 @@ -#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 deleted file mode 100644 index 9464eb5b..00000000 --- a/movement/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; - 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 deleted file mode 100644 index 363fd571..00000000 --- a/movement/widgets/settings/set_time_widget.h +++ /dev/null @@ -1,18 +0,0 @@ -#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 From d36331ce4e79d275e0ef09414a1a1a1a8949b0ea Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 13:23:23 -0400 Subject: rename types to be more c-like --- movement/movement.c | 12 ++++++------ movement/movement.h | 20 ++++++++++---------- movement/movement_config.h | 2 +- movement/watch_faces/clock/simple_clock_face.c | 8 ++++---- movement/watch_faces/clock/simple_clock_face.h | 8 ++++---- .../watch_faces/complications/pulseometer_face.c | 8 ++++---- .../watch_faces/complications/pulseometer_face.h | 8 ++++---- movement/watch_faces/settings/preferences_face.c | 8 ++++---- movement/watch_faces/settings/preferences_face.h | 8 ++++---- movement/watch_faces/settings/set_time_face.c | 8 ++++---- movement/watch_faces/settings/set_time_face.h | 8 ++++---- 11 files changed, 49 insertions(+), 49 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 3417345c..779674c7 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -5,10 +5,10 @@ #include "movement.h" #include "movement_config.h" -LauncherState movement_state; +movement_state_t movement_state; void * watch_face_contexts[MOVEMENT_NUM_FACES]; const int32_t movement_screensaver_deadlines[8] = {INT_MAX, 3600, 7200, 21600, 43200, 86400, 172800, 604800}; -LauncherEvent event; +movement_event_t event; void cb_mode_btn_interrupt(); void cb_light_btn_interrupt(); @@ -166,16 +166,16 @@ bool app_loop() { return can_sleep && !movement_state.led_on; } -LauncherEventType _figure_out_button_event(LauncherEventType button_down_event, uint8_t *down_timestamp) { +movement_event_type_t _figure_out_button_event(movement_event_type_t button_down_event_type, 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; + if (diff > 1) return button_down_event_type + 2; + else return button_down_event_type + 1; } else { *down_timestamp = date_time.unit.second + 1; - return button_down_event; + return button_down_event_type; } } diff --git a/movement/movement.h b/movement/movement.h index 719a3d95..d78e0f44 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -19,7 +19,7 @@ typedef union { uint32_t led_green_color : 4; // for general purpose illumination, the green LED value (0-15) } bit; uint32_t value; -} LauncherSettings; +} movement_settings_t; typedef enum { EVENT_NONE = 0, // There is no event to report. @@ -35,28 +35,28 @@ typedef enum { 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; +} movement_event_type_t; typedef struct { uint8_t event_type; uint8_t subsecond; -} LauncherEvent; +} movement_event_t; -typedef void (*watch_face_setup)(LauncherSettings *settings, void ** context_ptr); -typedef void (*watch_face_activate)(LauncherSettings *settings, void *context); -typedef bool (*watch_face_loop)(LauncherEvent event, LauncherSettings *settings, void *context); -typedef void (*watch_face_resign)(LauncherSettings *settings, void *context); +typedef void (*watch_face_setup)(movement_settings_t *settings, void ** context_ptr); +typedef void (*watch_face_activate)(movement_settings_t *settings, void *context); +typedef bool (*watch_face_loop)(movement_event_t event, movement_settings_t *settings, void *context); +typedef void (*watch_face_resign)(movement_settings_t *settings, void *context); typedef struct { watch_face_setup setup; watch_face_activate activate; watch_face_loop loop; watch_face_resign resign; -} WatchFace; +} watch_face_t; typedef struct { // properties stored in BACKUP register - LauncherSettings movement_settings; + movement_settings_t movement_settings; // transient properties int16_t current_watch_face; @@ -79,7 +79,7 @@ typedef struct { uint8_t tick_frequency; uint8_t last_second; uint8_t subsecond; -} LauncherState; +} movement_state_t; void movement_move_to_face(uint8_t watch_face_index); void movement_move_to_next_face(); diff --git a/movement/movement_config.h b/movement/movement_config.h index 41066d19..172736f1 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -8,7 +8,7 @@ #define MOVEMENT_NUM_FACES 3 -WatchFace watch_faces[MOVEMENT_NUM_FACES] = { +watch_face_t watch_faces[MOVEMENT_NUM_FACES] = { simple_clock_face, preferences_face, set_time_face, diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index 97067b0f..bdee7dec 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -2,13 +2,13 @@ #include "simple_clock_face.h" #include "watch.h" -void simple_clock_face_setup(LauncherSettings *settings, void ** context_ptr) { +void simple_clock_face_setup(movement_settings_t *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_face_activate(LauncherSettings *settings, void *context) { +void simple_clock_face_activate(movement_settings_t *settings, void *context) { if (settings->bit.clock_mode_24h) { watch_set_indicator(WATCH_INDICATOR_24H); } @@ -17,7 +17,7 @@ void simple_clock_face_activate(LauncherSettings *settings, void *context) { *((uint32_t *)context) = 0xFFFFFFFF; } -bool simple_clock_face_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +bool simple_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { printf("simple_clock_face_loop\n"); const char weekdays[7][3] = {"SA", "SU", "MO", "TU", "WE", "TH", "FR"}; char buf[11]; @@ -77,7 +77,7 @@ bool simple_clock_face_loop(LauncherEvent event, LauncherSettings *settings, voi return true; } -void simple_clock_face_resign(LauncherSettings *settings, void *context) { +void simple_clock_face_resign(movement_settings_t *settings, void *context) { (void) settings; (void) context; } diff --git a/movement/watch_faces/clock/simple_clock_face.h b/movement/watch_faces/clock/simple_clock_face.h index 59201a1f..3cdc09e8 100644 --- a/movement/watch_faces/clock/simple_clock_face.h +++ b/movement/watch_faces/clock/simple_clock_face.h @@ -3,10 +3,10 @@ #include "movement.h" -void simple_clock_face_setup(LauncherSettings *settings, void ** context_ptr); -void simple_clock_face_activate(LauncherSettings *settings, void *context); -bool simple_clock_face_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void simple_clock_face_resign(LauncherSettings *settings, void *context); +void simple_clock_face_setup(movement_settings_t *settings, void ** context_ptr); +void simple_clock_face_activate(movement_settings_t *settings, void *context); +bool simple_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void simple_clock_face_resign(movement_settings_t *settings, void *context); uint8_t simple_clock_face_get_weekday(uint16_t day, uint16_t month, uint16_t year); diff --git a/movement/watch_faces/complications/pulseometer_face.c b/movement/watch_faces/complications/pulseometer_face.c index e38153d6..7c524b8d 100644 --- a/movement/watch_faces/complications/pulseometer_face.c +++ b/movement/watch_faces/complications/pulseometer_face.c @@ -6,17 +6,17 @@ #define PULSOMETER_FACE_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.) #define PULSOMETER_FACE_FREQUENCY (1 << PULSOMETER_FACE_FREQUENCY_FACTOR) -void pulseometer_face_setup(LauncherSettings *settings, void ** context_ptr) { +void pulseometer_face_setup(movement_settings_t *settings, void ** context_ptr) { (void) settings; if (*context_ptr == NULL) *context_ptr = malloc(sizeof(PulsometerState)); } -void pulseometer_face_activate(LauncherSettings *settings, void *context) { +void pulseometer_face_activate(movement_settings_t *settings, void *context) { (void) settings; memset(context, 0, sizeof(PulsometerState)); } -bool pulseometer_face_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +bool pulseometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { printf("pulseometer_face_loop\n"); (void) settings; PulsometerState *pulsometer_state = (PulsometerState *)context; @@ -81,7 +81,7 @@ bool pulseometer_face_loop(LauncherEvent event, LauncherSettings *settings, void return true; } -void pulseometer_face_resign(LauncherSettings *settings, void *context) { +void pulseometer_face_resign(movement_settings_t *settings, void *context) { (void) settings; (void) context; } diff --git a/movement/watch_faces/complications/pulseometer_face.h b/movement/watch_faces/complications/pulseometer_face.h index 7b96259c..f8e69d27 100644 --- a/movement/watch_faces/complications/pulseometer_face.h +++ b/movement/watch_faces/complications/pulseometer_face.h @@ -9,10 +9,10 @@ typedef struct { int16_t ticks; } PulsometerState; -void pulseometer_face_setup(LauncherSettings *settings, void ** context_ptr); -void pulseometer_face_activate(LauncherSettings *settings, void *context); -bool pulseometer_face_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void pulseometer_face_resign(LauncherSettings *settings, void *context); +void pulseometer_face_setup(movement_settings_t *settings, void ** context_ptr); +void pulseometer_face_activate(movement_settings_t *settings, void *context); +bool pulseometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void pulseometer_face_resign(movement_settings_t *settings, void *context); #define pulseometer_face { \ pulseometer_face_setup, \ diff --git a/movement/watch_faces/settings/preferences_face.c b/movement/watch_faces/settings/preferences_face.c index f499e36f..18dff6cd 100644 --- a/movement/watch_faces/settings/preferences_face.c +++ b/movement/watch_faces/settings/preferences_face.c @@ -5,18 +5,18 @@ #define PREFERENCES_FACE_NUM_PREFEFENCES (5) const char preferences_face_titles[PREFERENCES_FACE_NUM_PREFEFENCES][11] = {"CL ", "Bt Beep ", "SC ", "Lt grn ", "Lt red "}; -void preferences_face_setup(LauncherSettings *settings, void ** context_ptr) { +void preferences_face_setup(movement_settings_t *settings, void ** context_ptr) { (void) settings; if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); } -void preferences_face_activate(LauncherSettings *settings, void *context) { +void preferences_face_activate(movement_settings_t *settings, void *context) { (void) settings; *((uint8_t *)context) = 0; movement_request_tick_frequency(4); // we need to manually blink some pixels } -bool preferences_face_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +bool preferences_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { printf("preferences_face_loop\n"); uint8_t current_page = *((uint8_t *)context); switch (event.event_type) { @@ -112,7 +112,7 @@ bool preferences_face_loop(LauncherEvent event, LauncherSettings *settings, void return true; } -void preferences_face_resign(LauncherSettings *settings, void *context) { +void preferences_face_resign(movement_settings_t *settings, void *context) { (void) settings; (void) context; watch_set_led_off(); diff --git a/movement/watch_faces/settings/preferences_face.h b/movement/watch_faces/settings/preferences_face.h index 218f99c2..c8f1a14e 100644 --- a/movement/watch_faces/settings/preferences_face.h +++ b/movement/watch_faces/settings/preferences_face.h @@ -3,10 +3,10 @@ #include "movement.h" -void preferences_face_setup(LauncherSettings *settings, void ** context_ptr); -void preferences_face_activate(LauncherSettings *settings, void *context); -bool preferences_face_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void preferences_face_resign(LauncherSettings *settings, void *context); +void preferences_face_setup(movement_settings_t *settings, void ** context_ptr); +void preferences_face_activate(movement_settings_t *settings, void *context); +bool preferences_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void preferences_face_resign(movement_settings_t *settings, void *context); #define preferences_face { \ preferences_face_setup, \ diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index 36498611..7bb63d0b 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -5,18 +5,18 @@ #define SET_TIME_FACE_NUM_SETTINGS (6) const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "MN", "SE", "YR", "MO", "DA"}; -void set_time_face_setup(LauncherSettings *settings, void ** context_ptr) { +void set_time_face_setup(movement_settings_t *settings, void ** context_ptr) { (void) settings; if (*context_ptr == NULL) *context_ptr = malloc(sizeof(uint8_t)); } -void set_time_face_activate(LauncherSettings *settings, void *context) { +void set_time_face_activate(movement_settings_t *settings, void *context) { (void) settings; *((uint8_t *)context) = 0; movement_request_tick_frequency(4); } -bool set_time_face_loop(LauncherEvent event, LauncherSettings *settings, void *context) { +bool set_time_face_loop(movement_event_t event, movement_settings_t *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(); @@ -101,7 +101,7 @@ bool set_time_face_loop(LauncherEvent event, LauncherSettings *settings, void *c return true; } -void set_time_face_resign(LauncherSettings *settings, void *context) { +void set_time_face_resign(movement_settings_t *settings, void *context) { (void) settings; (void) context; watch_set_led_off(); diff --git a/movement/watch_faces/settings/set_time_face.h b/movement/watch_faces/settings/set_time_face.h index 0c34d6b2..b330d852 100644 --- a/movement/watch_faces/settings/set_time_face.h +++ b/movement/watch_faces/settings/set_time_face.h @@ -3,10 +3,10 @@ #include "movement.h" -void set_time_face_setup(LauncherSettings *settings, void ** context_ptr); -void set_time_face_activate(LauncherSettings *settings, void *context); -bool set_time_face_loop(LauncherEvent event, LauncherSettings *settings, void *context); -void set_time_face_resign(LauncherSettings *settings, void *context); +void set_time_face_setup(movement_settings_t *settings, void ** context_ptr); +void set_time_face_activate(movement_settings_t *settings, void *context); +bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void set_time_face_resign(movement_settings_t *settings, void *context); #define set_time_face { \ set_time_face_setup, \ -- cgit v1.2.3 From 8f5de18b94ad4b01b43421e13e7d38e56afd54db Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 13:28:52 -0400 Subject: clarify property names --- movement/movement.c | 34 +++++++++++++++++----------------- movement/movement.h | 4 ++-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 779674c7..1a6ca496 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -19,7 +19,7 @@ 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; + movement_state.screensaver_ticks = movement_screensaver_deadlines[movement_state.settings.bit.screensaver_interval] / 60; } void movement_request_tick_frequency(uint8_t freq) { @@ -35,7 +35,7 @@ void movement_illuminate_led() { void movement_move_to_face(uint8_t watch_face_index) { movement_state.watch_face_changed = true; - movement_state.next_face = watch_face_index; + movement_state.next_watch_face = watch_face_index; } void movement_move_to_next_face() { @@ -45,9 +45,9 @@ void movement_move_to_next_face() { 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_state.settings.bit.led_green_color = 0xF; + movement_state.settings.bit.button_should_sound = true; + movement_state.settings.bit.screensaver_interval = 1; _movement_reset_screensaver_countdown(); } @@ -80,10 +80,10 @@ void app_setup() { movement_request_tick_frequency(1); for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) { - watch_faces[i].setup(&movement_state.movement_settings, &watch_face_contexts[i]); + watch_faces[i].setup(&movement_state.settings, &watch_face_contexts[i]); } - watch_faces[0].activate(&movement_state.movement_settings, watch_face_contexts[0]); + watch_faces[0].activate(&movement_state.settings, watch_face_contexts[0]); event.subsecond = 0; event.event_type = EVENT_ACTIVATE; } @@ -97,14 +97,14 @@ void app_wake_from_sleep() { bool app_loop() { if (movement_state.watch_face_changed) { - if (movement_state.movement_settings.bit.button_should_sound) { + if (movement_state.settings.bit.button_should_sound) { // low note for nonzero case, high note for return to watch_face 0 - watch_buzzer_play_note(movement_state.next_face ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 50); + watch_buzzer_play_note(movement_state.next_watch_face ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 50); } - watch_faces[movement_state.current_watch_face].resign(&movement_state.movement_settings, watch_face_contexts[movement_state.current_watch_face]); - movement_state.current_watch_face = movement_state.next_face; + watch_faces[movement_state.current_watch_face].resign(&movement_state.settings, watch_face_contexts[movement_state.current_watch_face]); + movement_state.current_watch_face = movement_state.next_watch_face; watch_clear_display(); - watch_faces[movement_state.current_watch_face].activate(&movement_state.movement_settings, watch_face_contexts[movement_state.current_watch_face]); + watch_faces[movement_state.current_watch_face].activate(&movement_state.settings, watch_face_contexts[movement_state.current_watch_face]); event.subsecond = 0; event.event_type = EVENT_ACTIVATE; movement_state.watch_face_changed = false; @@ -112,8 +112,8 @@ bool app_loop() { // 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); + watch_set_led_color(movement_state.settings.bit.led_red_color ? (0xF | movement_state.settings.bit.led_red_color << 4) : 0, + movement_state.settings.bit.led_green_color ? (0xF | movement_state.settings.bit.led_green_color << 4) : 0); movement_state.led_on = true; } @@ -144,7 +144,7 @@ bool app_loop() { // 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; - watch_faces[movement_state.current_watch_face].loop(event, &movement_state.movement_settings, watch_face_contexts[movement_state.current_watch_face]); + watch_faces[movement_state.current_watch_face].loop(event, &movement_state.settings, watch_face_contexts[movement_state.current_watch_face]); 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. @@ -158,7 +158,7 @@ bool app_loop() { if (event.event_type) { event.subsecond = movement_state.subsecond; - can_sleep = watch_faces[movement_state.current_watch_face].loop(event, &movement_state.movement_settings, watch_face_contexts[movement_state.current_watch_face]); + can_sleep = watch_faces[movement_state.current_watch_face].loop(event, &movement_state.settings, watch_face_contexts[movement_state.current_watch_face]); event.event_type = EVENT_NONE; event.subsecond = 0; } @@ -208,7 +208,7 @@ void cb_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--; + if (movement_state.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; diff --git a/movement/movement.h b/movement/movement.h index d78e0f44..65b66ab5 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -56,11 +56,11 @@ typedef struct { typedef struct { // properties stored in BACKUP register - movement_settings_t movement_settings; + movement_settings_t settings; // transient properties int16_t current_watch_face; - int16_t next_face; + int16_t next_watch_face; bool watch_face_changed; // LED stuff -- cgit v1.2.3 From 3e539a9e6362841b9cd5924f0d309ec15b2698b1 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 13:40:17 -0400 Subject: screensaver mode is now low energy mode --- movement/movement.c | 38 ++++++++++++------------ movement/movement.h | 8 ++--- movement/watch_faces/clock/simple_clock_face.c | 8 ++--- movement/watch_faces/settings/preferences_face.c | 4 +-- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 1a6ca496..9b7e90a0 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -7,7 +7,7 @@ movement_state_t movement_state; void * watch_face_contexts[MOVEMENT_NUM_FACES]; -const int32_t movement_screensaver_deadlines[8] = {INT_MAX, 3600, 7200, 21600, 43200, 86400, 172800, 604800}; +const int32_t movement_inactivity_deadlines[8] = {INT_MAX, 3600, 7200, 21600, 43200, 86400, 172800, 604800}; movement_event_t event; void cb_mode_btn_interrupt(); @@ -17,9 +17,9 @@ void cb_alarm_btn_extwake(); void cb_alarm_fired(); void cb_tick(); -static inline void _movement_reset_screensaver_countdown() { +static inline void _movement_reset_inactivity_countdown() { // for testing, make the timeout happen 60x faster. - movement_state.screensaver_ticks = movement_screensaver_deadlines[movement_state.settings.bit.screensaver_interval] / 60; + movement_state.le_mode_ticks = movement_inactivity_deadlines[movement_state.settings.bit.le_inactivity_interval] / 60; } void movement_request_tick_frequency(uint8_t freq) { @@ -47,8 +47,8 @@ void app_init() { movement_state.settings.bit.led_green_color = 0xF; movement_state.settings.bit.button_should_sound = true; - movement_state.settings.bit.screensaver_interval = 1; - _movement_reset_screensaver_countdown(); + movement_state.settings.bit.le_inactivity_interval = 1; + _movement_reset_inactivity_countdown(); } void app_wake_from_deep_sleep() { @@ -64,7 +64,7 @@ void app_setup() { is_first_launch = false; } } - if (movement_state.screensaver_ticks != -1) { + if (movement_state.le_mode_ticks != -1) { watch_disable_extwake_interrupt(BTN_ALARM); watch_rtc_disable_alarm_callback(); @@ -129,9 +129,9 @@ bool app_loop() { } } - // if we have timed out of our screensaver countdown, enter screensaver mode. - if (movement_state.screensaver_ticks == 0) { - movement_state.screensaver_ticks = -1; + // if we have timed out of our low energy mode countdown, enter low energy mode. + if (movement_state.le_mode_ticks == 0) { + movement_state.le_mode_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 @@ -141,13 +141,13 @@ bool app_loop() { 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; + // as long as le_mode_ticks is -1 (i.e. we are in low energy mode), we wake up here, update the screen, and go right back to sleep. + while (movement_state.le_mode_ticks == -1) { + event.event_type = EVENT_LOW_POWER_TICK; watch_faces[movement_state.current_watch_face].loop(event, &movement_state.settings, watch_face_contexts[movement_state.current_watch_face]); 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. + // as soon as le_mode_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. @@ -180,27 +180,27 @@ movement_event_type_t _figure_out_button_event(movement_event_type_t button_down } void cb_light_btn_interrupt() { - _movement_reset_screensaver_countdown(); + _movement_reset_inactivity_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(); + _movement_reset_inactivity_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(); + _movement_reset_inactivity_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(); + _movement_reset_inactivity_countdown(); } void cb_alarm_fired() { - event.event_type = EVENT_SCREENSAVER; + event.event_type = EVENT_LOW_POWER_TICK; } void cb_tick() { @@ -208,7 +208,7 @@ void cb_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.settings.bit.screensaver_interval && movement_state.screensaver_ticks > 0) movement_state.screensaver_ticks--; + if (movement_state.settings.bit.le_inactivity_interval && movement_state.le_mode_ticks > 0) movement_state.le_mode_ticks--; movement_state.last_second = date_time.unit.second; movement_state.subsecond = 0; diff --git a/movement/movement.h b/movement/movement.h index 65b66ab5..934254bc 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -13,7 +13,7 @@ typedef union { 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 le_inactivity_interval : 3;// 0 to disable low energy mode, or an inactivity interval for going into low energy mode. 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) @@ -25,7 +25,7 @@ typedef enum { EVENT_NONE = 0, // There is no event to report. EVENT_ACTIVATE, // Your watch face is entering the foreground. EVENT_TICK, // Most common event type. Your watch face is being called from the tick callback. - EVENT_SCREENSAVER, // Your watch face is being asked to display its output for screensaver mode. + EVENT_LOW_POWER_TICK, // The watch is in low energy mode, and you are getting the once-per-minute tick callback. 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. @@ -72,8 +72,8 @@ typedef struct { uint8_t mode_down_timestamp; uint8_t alarm_down_timestamp; - // screensaver countdown - int32_t screensaver_ticks; + // low energy mode countdown + int32_t le_mode_ticks; // stuff for subsecond tracking uint8_t tick_frequency; diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index bdee7dec..6e58313a 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -28,16 +28,16 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting switch (event.event_type) { case EVENT_ACTIVATE: case EVENT_TICK: - case EVENT_SCREENSAVER: + case EVENT_LOW_POWER_TICK: 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) { + if (date_time.reg >> 6 == previous_date_time >> 6 && event.event_type != EVENT_LOW_POWER_TICK) { // 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) { + } else if (date_time.reg >> 12 == previous_date_time >> 12 && event.event_type != EVENT_LOW_POWER_TICK) { // everything before minutes is the same. pos = 6; sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second); @@ -54,7 +54,7 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting if (date_time.unit.hour == 0) date_time.unit.hour = 12; } pos = 0; - if (event.event_type == EVENT_SCREENSAVER) { + if (event.event_type == EVENT_LOW_POWER_TICK) { sprintf(buf, "%s%2d%2d%02d ", weekdays[simple_clock_face_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_face_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); diff --git a/movement/watch_faces/settings/preferences_face.c b/movement/watch_faces/settings/preferences_face.c index 18dff6cd..c3b7663c 100644 --- a/movement/watch_faces/settings/preferences_face.c +++ b/movement/watch_faces/settings/preferences_face.c @@ -37,7 +37,7 @@ bool preferences_face_loop(movement_event_t event, movement_settings_t *settings settings->bit.button_should_sound = !(settings->bit.button_should_sound); break; case 2: - settings->bit.screensaver_interval = settings->bit.screensaver_interval + 1; + settings->bit.le_inactivity_interval = settings->bit.le_inactivity_interval + 1; break; case 3: settings->bit.led_green_color = settings->bit.led_green_color + 1; @@ -65,7 +65,7 @@ bool preferences_face_loop(movement_event_t event, movement_settings_t *settings else watch_display_string("n", 9); break; case 2: - switch (settings->bit.screensaver_interval) { + switch (settings->bit.le_inactivity_interval) { case 0: watch_display_string(" never", 4); break; -- cgit v1.2.3 From 69397e9b0f011cd9108547f05ab21073c4a9ddb6 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 13:47:42 -0400 Subject: implement led duration setting --- movement/movement.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/movement/movement.c b/movement/movement.c index 9b7e90a0..03ddb244 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -30,7 +30,7 @@ void movement_request_tick_frequency(uint8_t freq) { } void movement_illuminate_led() { - movement_state.light_ticks = 3; + movement_state.light_ticks = movement_state.settings.bit.led_duration; } void movement_move_to_face(uint8_t watch_face_index) { @@ -48,6 +48,7 @@ void app_init() { movement_state.settings.bit.led_green_color = 0xF; movement_state.settings.bit.button_should_sound = true; movement_state.settings.bit.le_inactivity_interval = 1; + movement_state.settings.bit.led_duration = 3; _movement_reset_inactivity_countdown(); } -- cgit v1.2.3 From 458ebf6987d6de143c53493782d438e55253d6e9 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 13:48:16 -0400 Subject: remove unimplemented settings for now --- movement/movement.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/movement/movement.h b/movement/movement.h index 934254bc..0ae0cc13 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -6,13 +6,9 @@ // TODO: none of this is implemented typedef union { struct { - uint32_t reserved : 3; + uint32_t reserved : 16; 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 le_inactivity_interval : 3;// 0 to disable low energy mode, or an inactivity interval for going into low energy mode. 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) -- cgit v1.2.3 From 0cfb37c6716326dea78a8dca512d286e6484e509 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 16:03:27 -0400 Subject: early work on background tasks, documentation --- movement/movement.c | 4 +- movement/movement.h | 93 +++++++++++++++++++++- movement/watch_faces/clock/simple_clock_face.c | 8 +- movement/watch_faces/clock/simple_clock_face.h | 1 + .../watch_faces/complications/pulseometer_face.h | 1 + movement/watch_faces/settings/preferences_face.h | 1 + movement/watch_faces/settings/set_time_face.h | 1 + 7 files changed, 102 insertions(+), 7 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 03ddb244..d43285e1 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -144,7 +144,7 @@ bool app_loop() { // this is a little mini-runloop. // as long as le_mode_ticks is -1 (i.e. we are in low energy mode), we wake up here, update the screen, and go right back to sleep. while (movement_state.le_mode_ticks == -1) { - event.event_type = EVENT_LOW_POWER_TICK; + event.event_type = EVENT_LOW_ENERGY_UPDATE; watch_faces[movement_state.current_watch_face].loop(event, &movement_state.settings, watch_face_contexts[movement_state.current_watch_face]); watch_enter_shallow_sleep(true); } @@ -201,7 +201,7 @@ void cb_alarm_btn_extwake() { } void cb_alarm_fired() { - event.event_type = EVENT_LOW_POWER_TICK; + event.event_type = EVENT_LOW_ENERGY_UPDATE; } void cb_tick() { diff --git a/movement/movement.h b/movement/movement.h index 0ae0cc13..3f3f0365 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -21,7 +21,8 @@ typedef enum { EVENT_NONE = 0, // There is no event to report. EVENT_ACTIVATE, // Your watch face is entering the foreground. EVENT_TICK, // Most common event type. Your watch face is being called from the tick callback. - EVENT_LOW_POWER_TICK, // The watch is in low energy mode, and you are getting the once-per-minute tick callback. + EVENT_LOW_ENERGY_UPDATE, // If the watch is in low energy mode and you are in the foreground, you will get a chance to update the display once per minute. + EVENT_BACKGROUND_TASK, // Your watch face is being invoked to perform a background task. Don't update the display here; you may not be in the foreground. 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. @@ -38,16 +39,106 @@ typedef struct { uint8_t subsecond; } movement_event_t; +/** @brief Perform setup for your watch face. + * @details It's tempting to say this is 'one-time' setup, but technically this function is called more than + * once. When the watch first boots, this function is called with a NULL context_ptr, indicating + * that it is the first run. At this time you should set context_ptr to something non-NULL if you + * need to keep track of any state in your watch face. If your watch face requires any other setup, + * like configuring a pin mode or a peripheral, you may want to do that here too. + * This function will be called again after waking from sleep mode, since sleep mode disables all + * of the device's pins and peripherals. + * @param settings A pointer to the global Movement settings. You can use this to inform how you present your + * display to the user (i.e. taking into account whether they have silenced the buttons, or if + * they prefer 12 or 24-hour mode). You can also change these settings if you like. + * @param context_ptr A pointer to a pointer; at first invocation, this value will be NULL, and you can set it + * to any value you like. Subsequent invocations will pass in whatever value you previously + * set. You may want to check if this is NULL and if so, allocate some space to store any + * data required for your watch face. + * + */ typedef void (*watch_face_setup)(movement_settings_t *settings, void ** context_ptr); + +/** @brief Prepare to go on-screen. + * @details This function is called just before your watch enters the foreground. If your watch face has any + * segments or text that is always displayed, you may want to set that here. In addition, if your + * watch face depends on data from a peripheral (like an I2C sensor), you will likely want to enable + * that peripheral here. In addition, if your watch face requires an update frequncy other than 1 Hz, + * you may want to request that here using the movement_request_tick_frequency function. + * @param settings A pointer to the global Movement settings. @see watch_face_setup. + * @param context A pointer to your watch face's context. @see watch_face_setup. + * + */ typedef void (*watch_face_activate)(movement_settings_t *settings, void *context); + +/** @brief Handle events and update the display. + * @details This function is called in response to an event. You should set up a switch statement that handles, + * at the very least, the EVENT_TICK and EVENT_MODE_BUTTON_UP event types. The tick event happens once + * per second (or more frequently if you asked for a faster tick with movement_request_tick_frequency). + * The mode button up event occurs when the user presses the MODE button. **Your loop function SHOULD + * call the movement_move_to_next_face function in response to this event.** If you have a good reason + * to override this behavior (e.g. your user interface requires all three buttons), your watch face MUST + * call the movement_move_to_next_face function in response to the EVENT_MODE_LONG_PRESS event. If you + * fail to do this, the user will become stuck on your watch face. + * @param event A struct containing information about the event, including its type. @see movement_event_type_t + * for a list of all possible event types. + * @param settings A pointer to the global Movement settings. @see watch_face_setup. + * @param context A pointer to your application's context. @see watch_face_setup. + * @return true if Movement can enter STANDBY mode; false to keep it awake. You should almost always return true. + * @note There are two event types that require some extra thought: + The EVENT_LOW_ENERGY_UPDATE event type is a special case. If you are in the foreground when the watch + goes into low energy mode, you will receive this tick once a minute (at the top of the minute) so that + you can update the screen. Great! But! When you receive this event, all pins and peripherals other than + the RTC will have been disabled to save energy. If your display is clock or calendar oriented, this is + fine. But if your display requires polling an I2C sensor or reading a value with the ADC, you won't be + able to do this. You should either display the name of the watch face in response to the low power tick, + or ensure that you resign before low power mode triggers, (e.g. by calling movement_move_to_face(0)). + **Your watch face MUST NOT wake up peripherals in response to a low power tick.** The purpose of this + mode is to consume as little energy as possible during the (potentially long) intervals when it's + unlikely the user is wearing or looking at the watch. + EVENT_BACKGROUND_TASK is also a special case. @see watch_face_wants_background_task for details. + */ typedef bool (*watch_face_loop)(movement_event_t event, movement_settings_t *settings, void *context); + +/** @brief Prepare to go off-screen. + * @details This function is called before your watch face enters the background. If you requested a tick + * frequency other than the standard 1 Hz, **you must call movement_request_tick_frequency(1) here** + * to reset to 1 Hz. You should also disable any peripherals you enabled when you entered the foreground. + * @param settings A pointer to the global Movement settings. @see watch_face_setup. + * @param context A pointer to your application's context. @see watch_face_setup. + */ typedef void (*watch_face_resign)(movement_settings_t *settings, void *context); +/** @brief OPTIONAL. Request an opportunity to run a background task. + * @warning NOT YET IMPLEMENTED. + * @details Most apps will not need this function, but if you provide it, Movement will call it once per minute in + * both active and low power modes, regardless of whether your app is in the foreground. You can check the + * current time to determine whether you require a background task. If you return true here, Movement will + * immediately call your loop function with an EVENT_BACKGROUND_TASK event. Note that it will not call your + * activate or deactivate functions, since you are not going on screen. + * + * Examples of background tasks: + * - Wake and play a sound when an alarm or timer has been triggered. + * - Check the state of an RTC interrupt pin or the timestamp of an RTC interrupt event. + * - Log a data point from a sensor, and then return to sleep mode. + * + * Guidelines for background tasks: + * - Assume all peripherals and pins other than the RTC will be disabled when you get an EVENT_BACKGROUND_TASK. + * - Even if your background task involves only the RTC peripheral, try to request background tasks sparingly. + * - If your background task involves an external pin or peripheral, request background tasks no more than once per hour. + * - If you need to enable a pin or a peripheral to perform your task, return it to its original state afterwards. + * + * @param settings A pointer to the global Movement settings. @see watch_face_setup. + * @param context A pointer to your application's context. @see watch_face_setup. + * @return true to request a background task; false otherwise. + */ +typedef bool (*watch_face_wants_background_task)(movement_settings_t *settings, void *context); + typedef struct { watch_face_setup setup; watch_face_activate activate; watch_face_loop loop; watch_face_resign resign; + watch_face_wants_background_task wants_background_task; } watch_face_t; typedef struct { diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index 6e58313a..351c7ebc 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -28,16 +28,16 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting switch (event.event_type) { case EVENT_ACTIVATE: case EVENT_TICK: - case EVENT_LOW_POWER_TICK: + case EVENT_LOW_ENERGY_UPDATE: 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_LOW_POWER_TICK) { + if (date_time.reg >> 6 == previous_date_time >> 6 && event.event_type != EVENT_LOW_ENERGY_UPDATE) { // 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_LOW_POWER_TICK) { + } else if (date_time.reg >> 12 == previous_date_time >> 12 && event.event_type != EVENT_LOW_ENERGY_UPDATE) { // everything before minutes is the same. pos = 6; sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second); @@ -54,7 +54,7 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting if (date_time.unit.hour == 0) date_time.unit.hour = 12; } pos = 0; - if (event.event_type == EVENT_LOW_POWER_TICK) { + if (event.event_type == EVENT_LOW_ENERGY_UPDATE) { sprintf(buf, "%s%2d%2d%02d ", weekdays[simple_clock_face_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_face_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); diff --git a/movement/watch_faces/clock/simple_clock_face.h b/movement/watch_faces/clock/simple_clock_face.h index 3cdc09e8..7daaba3e 100644 --- a/movement/watch_faces/clock/simple_clock_face.h +++ b/movement/watch_faces/clock/simple_clock_face.h @@ -15,6 +15,7 @@ uint8_t simple_clock_face_get_weekday(uint16_t day, uint16_t month, uint16_t yea simple_clock_face_activate, \ simple_clock_face_loop, \ simple_clock_face_resign, \ + NULL, \ } #endif // FAKE_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/complications/pulseometer_face.h b/movement/watch_faces/complications/pulseometer_face.h index f8e69d27..5a9c66b0 100644 --- a/movement/watch_faces/complications/pulseometer_face.h +++ b/movement/watch_faces/complications/pulseometer_face.h @@ -19,6 +19,7 @@ void pulseometer_face_resign(movement_settings_t *settings, void *context); pulseometer_face_activate, \ pulseometer_face_loop, \ pulseometer_face_resign, \ + NULL, \ } #endif // PULSEOMETER_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/settings/preferences_face.h b/movement/watch_faces/settings/preferences_face.h index c8f1a14e..5b735db9 100644 --- a/movement/watch_faces/settings/preferences_face.h +++ b/movement/watch_faces/settings/preferences_face.h @@ -13,6 +13,7 @@ void preferences_face_resign(movement_settings_t *settings, void *context); preferences_face_activate, \ preferences_face_loop, \ preferences_face_resign, \ + NULL, \ } #endif // PREFERENCES_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/settings/set_time_face.h b/movement/watch_faces/settings/set_time_face.h index b330d852..06dbe2ce 100644 --- a/movement/watch_faces/settings/set_time_face.h +++ b/movement/watch_faces/settings/set_time_face.h @@ -13,6 +13,7 @@ void set_time_face_resign(movement_settings_t *settings, void *context); set_time_face_activate, \ set_time_face_loop, \ set_time_face_resign, \ + NULL, \ } #endif // SET_TIME_FACE_H_ -- cgit v1.2.3 From 05fe055f99b9b950086b1e38a9c795d7e076c5b2 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 16:25:30 -0400 Subject: automatically determine number of watch faces --- movement/movement_config.h | 5 ++--- movement/watch_faces/clock/simple_clock_face.h | 16 ++++++++-------- movement/watch_faces/complications/pulseometer_face.h | 14 +++++++------- movement/watch_faces/settings/preferences_face.h | 14 +++++++------- movement/watch_faces/settings/set_time_face.h | 14 +++++++------- 5 files changed, 31 insertions(+), 32 deletions(-) diff --git a/movement/movement_config.h b/movement/movement_config.h index 172736f1..e95e46c0 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -6,13 +6,12 @@ #include "set_time_face.h" #include "pulseometer_face.h" -#define MOVEMENT_NUM_FACES 3 - -watch_face_t watch_faces[MOVEMENT_NUM_FACES] = { +const watch_face_t watch_faces[] = { simple_clock_face, preferences_face, set_time_face, }; +#define MOVEMENT_NUM_FACES (sizeof(watch_faces) / sizeof(watch_face_t)) #endif // MOVEMENT_CONFIG_H_ diff --git a/movement/watch_faces/clock/simple_clock_face.h b/movement/watch_faces/clock/simple_clock_face.h index 7daaba3e..3db894d2 100644 --- a/movement/watch_faces/clock/simple_clock_face.h +++ b/movement/watch_faces/clock/simple_clock_face.h @@ -10,12 +10,12 @@ void simple_clock_face_resign(movement_settings_t *settings, void *context); uint8_t simple_clock_face_get_weekday(uint16_t day, uint16_t month, uint16_t year); -#define simple_clock_face { \ - simple_clock_face_setup, \ - simple_clock_face_activate, \ - simple_clock_face_loop, \ - simple_clock_face_resign, \ - NULL, \ -} +static const watch_face_t simple_clock_face = { + simple_clock_face_setup, + simple_clock_face_activate, + simple_clock_face_loop, + simple_clock_face_resign, + NULL +}; -#endif // FAKE_FACE_H_ \ No newline at end of file +#endif // SIMPLE_CLOCK_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/complications/pulseometer_face.h b/movement/watch_faces/complications/pulseometer_face.h index 5a9c66b0..52bccf24 100644 --- a/movement/watch_faces/complications/pulseometer_face.h +++ b/movement/watch_faces/complications/pulseometer_face.h @@ -14,12 +14,12 @@ void pulseometer_face_activate(movement_settings_t *settings, void *context); bool pulseometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context); void pulseometer_face_resign(movement_settings_t *settings, void *context); -#define pulseometer_face { \ - pulseometer_face_setup, \ - pulseometer_face_activate, \ - pulseometer_face_loop, \ - pulseometer_face_resign, \ - NULL, \ -} +static const watch_face_t pulseometer_face = { + pulseometer_face_setup, + pulseometer_face_activate, + pulseometer_face_loop, + pulseometer_face_resign, + NULL +}; #endif // PULSEOMETER_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/settings/preferences_face.h b/movement/watch_faces/settings/preferences_face.h index 5b735db9..af628ba3 100644 --- a/movement/watch_faces/settings/preferences_face.h +++ b/movement/watch_faces/settings/preferences_face.h @@ -8,12 +8,12 @@ void preferences_face_activate(movement_settings_t *settings, void *context); bool preferences_face_loop(movement_event_t event, movement_settings_t *settings, void *context); void preferences_face_resign(movement_settings_t *settings, void *context); -#define preferences_face { \ - preferences_face_setup, \ - preferences_face_activate, \ - preferences_face_loop, \ - preferences_face_resign, \ - NULL, \ -} +static const watch_face_t preferences_face = { + preferences_face_setup, + preferences_face_activate, + preferences_face_loop, + preferences_face_resign, + NULL +}; #endif // PREFERENCES_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/settings/set_time_face.h b/movement/watch_faces/settings/set_time_face.h index 06dbe2ce..21fb1e44 100644 --- a/movement/watch_faces/settings/set_time_face.h +++ b/movement/watch_faces/settings/set_time_face.h @@ -8,12 +8,12 @@ void set_time_face_activate(movement_settings_t *settings, void *context); bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, void *context); void set_time_face_resign(movement_settings_t *settings, void *context); -#define set_time_face { \ - set_time_face_setup, \ - set_time_face_activate, \ - set_time_face_loop, \ - set_time_face_resign, \ - NULL, \ -} +static const watch_face_t set_time_face = { + set_time_face_setup, + set_time_face_activate, + set_time_face_loop, + set_time_face_resign, + NULL +}; #endif // SET_TIME_FACE_H_ -- cgit v1.2.3 From c27dbc779b252c32370e2cd34a22f51d120d0f38 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Sat, 16 Oct 2021 16:51:36 -0400 Subject: fix misspelling of pulsometer --- movement/make/Makefile | 2 +- movement/movement_config.h | 2 +- .../watch_faces/complications/pulseometer_face.c | 87 ---------------------- .../watch_faces/complications/pulseometer_face.h | 25 ------- .../watch_faces/complications/pulsometer_face.c | 87 ++++++++++++++++++++++ .../watch_faces/complications/pulsometer_face.h | 25 +++++++ 6 files changed, 114 insertions(+), 114 deletions(-) delete mode 100644 movement/watch_faces/complications/pulseometer_face.c delete mode 100644 movement/watch_faces/complications/pulseometer_face.h create mode 100644 movement/watch_faces/complications/pulsometer_face.c create mode 100644 movement/watch_faces/complications/pulsometer_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index 8b29fba4..f17fc7e5 100755 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -26,7 +26,7 @@ SRCS += \ ../watch_faces/clock/simple_clock_face.c \ ../watch_faces/settings/preferences_face.c \ ../watch_faces/settings/set_time_face.c \ - ../watch_faces/complications/pulseometer_face.c \ + ../watch_faces/complications/pulsometer_face.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_config.h b/movement/movement_config.h index e95e46c0..6a7aec5a 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -4,7 +4,7 @@ #include "simple_clock_face.h" #include "preferences_face.h" #include "set_time_face.h" -#include "pulseometer_face.h" +#include "pulsometer_face.h" const watch_face_t watch_faces[] = { simple_clock_face, diff --git a/movement/watch_faces/complications/pulseometer_face.c b/movement/watch_faces/complications/pulseometer_face.c deleted file mode 100644 index 7c524b8d..00000000 --- a/movement/watch_faces/complications/pulseometer_face.c +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include "pulseometer_face.h" -#include "watch.h" - -#define PULSOMETER_FACE_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.) -#define PULSOMETER_FACE_FREQUENCY (1 << PULSOMETER_FACE_FREQUENCY_FACTOR) - -void pulseometer_face_setup(movement_settings_t *settings, void ** context_ptr) { - (void) settings; - if (*context_ptr == NULL) *context_ptr = malloc(sizeof(PulsometerState)); -} - -void pulseometer_face_activate(movement_settings_t *settings, void *context) { - (void) settings; - memset(context, 0, sizeof(PulsometerState)); -} - -bool pulseometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { - printf("pulseometer_face_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_FACE_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_face(); - 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_FACE_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_face_resign(movement_settings_t *settings, void *context) { - (void) settings; - (void) context; -} diff --git a/movement/watch_faces/complications/pulseometer_face.h b/movement/watch_faces/complications/pulseometer_face.h deleted file mode 100644 index 52bccf24..00000000 --- a/movement/watch_faces/complications/pulseometer_face.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef PULSEOMETER_FACE_H_ -#define PULSEOMETER_FACE_H_ - -#include "movement.h" - -typedef struct { - bool measuring; - int16_t pulse; - int16_t ticks; -} PulsometerState; - -void pulseometer_face_setup(movement_settings_t *settings, void ** context_ptr); -void pulseometer_face_activate(movement_settings_t *settings, void *context); -bool pulseometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context); -void pulseometer_face_resign(movement_settings_t *settings, void *context); - -static const watch_face_t pulseometer_face = { - pulseometer_face_setup, - pulseometer_face_activate, - pulseometer_face_loop, - pulseometer_face_resign, - NULL -}; - -#endif // PULSEOMETER_FACE_H_ \ No newline at end of file diff --git a/movement/watch_faces/complications/pulsometer_face.c b/movement/watch_faces/complications/pulsometer_face.c new file mode 100644 index 00000000..dffdcd35 --- /dev/null +++ b/movement/watch_faces/complications/pulsometer_face.c @@ -0,0 +1,87 @@ +#include +#include +#include "pulsometer_face.h" +#include "watch.h" + +#define PULSOMETER_FACE_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.) +#define PULSOMETER_FACE_FREQUENCY (1 << PULSOMETER_FACE_FREQUENCY_FACTOR) + +void pulsometer_face_setup(movement_settings_t *settings, void ** context_ptr) { + (void) settings; + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(PulsometerState)); +} + +void pulsometer_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + memset(context, 0, sizeof(PulsometerState)); +} + +bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + printf("pulsometer_face_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_FACE_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_face(); + 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_FACE_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 pulsometer_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} diff --git a/movement/watch_faces/complications/pulsometer_face.h b/movement/watch_faces/complications/pulsometer_face.h new file mode 100644 index 00000000..76af8257 --- /dev/null +++ b/movement/watch_faces/complications/pulsometer_face.h @@ -0,0 +1,25 @@ +#ifndef PULSOMETER_FACE_H_ +#define PULSOMETER_FACE_H_ + +#include "movement.h" + +typedef struct { + bool measuring; + int16_t pulse; + int16_t ticks; +} PulsometerState; + +void pulsometer_face_setup(movement_settings_t *settings, void ** context_ptr); +void pulsometer_face_activate(movement_settings_t *settings, void *context); +bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void pulsometer_face_resign(movement_settings_t *settings, void *context); + +static const watch_face_t pulsometer_face = { + pulsometer_face_setup, + pulsometer_face_activate, + pulsometer_face_loop, + pulsometer_face_resign, + NULL +}; + +#endif // PULSOMETER_FACE_H_ \ No newline at end of file -- cgit v1.2.3 From b4d4f62ba8ec4f5983a639ddd0e0089cebe380e4 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 18 Oct 2021 11:15:45 -0400 Subject: pulsometer: reset tick in resign function --- movement/watch_faces/complications/pulsometer_face.c | 1 + 1 file changed, 1 insertion(+) diff --git a/movement/watch_faces/complications/pulsometer_face.c b/movement/watch_faces/complications/pulsometer_face.c index dffdcd35..396ac0dd 100644 --- a/movement/watch_faces/complications/pulsometer_face.c +++ b/movement/watch_faces/complications/pulsometer_face.c @@ -84,4 +84,5 @@ bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, void pulsometer_face_resign(movement_settings_t *settings, void *context) { (void) settings; (void) context; + movement_request_tick_frequency(1); } -- cgit v1.2.3 From 8dbbe92a9bad950219c4bc1a46871f44dda5fef3 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 18 Oct 2021 11:27:36 -0400 Subject: pulsometer: make type more c-like --- movement/watch_faces/complications/pulsometer_face.c | 6 +++--- movement/watch_faces/complications/pulsometer_face.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/movement/watch_faces/complications/pulsometer_face.c b/movement/watch_faces/complications/pulsometer_face.c index 396ac0dd..db5c5d0e 100644 --- a/movement/watch_faces/complications/pulsometer_face.c +++ b/movement/watch_faces/complications/pulsometer_face.c @@ -8,18 +8,18 @@ void pulsometer_face_setup(movement_settings_t *settings, void ** context_ptr) { (void) settings; - if (*context_ptr == NULL) *context_ptr = malloc(sizeof(PulsometerState)); + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(pulsometer_state_t)); } void pulsometer_face_activate(movement_settings_t *settings, void *context) { (void) settings; - memset(context, 0, sizeof(PulsometerState)); + memset(context, 0, sizeof(pulsometer_state_t)); } bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { printf("pulsometer_face_loop\n"); (void) settings; - PulsometerState *pulsometer_state = (PulsometerState *)context; + pulsometer_state_t *pulsometer_state = (pulsometer_state_t *)context; char buf[14]; switch (event.event_type) { case EVENT_TICK: diff --git a/movement/watch_faces/complications/pulsometer_face.h b/movement/watch_faces/complications/pulsometer_face.h index 76af8257..cdb5b977 100644 --- a/movement/watch_faces/complications/pulsometer_face.h +++ b/movement/watch_faces/complications/pulsometer_face.h @@ -7,7 +7,7 @@ typedef struct { bool measuring; int16_t pulse; int16_t ticks; -} PulsometerState; +} pulsometer_state_t; void pulsometer_face_setup(movement_settings_t *settings, void ** context_ptr); void pulsometer_face_activate(movement_settings_t *settings, void *context); -- cgit v1.2.3 From 93624f0b692648eac415a3df5689de6dee06c2db Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 18 Oct 2021 12:15:57 -0400 Subject: add timeout event to give faces a chance to resign --- movement/movement.c | 15 ++++++++++++--- movement/movement.h | 7 ++++++- movement/watch_faces/clock/simple_clock_face.c | 2 +- movement/watch_faces/complications/pulsometer_face.c | 4 +++- movement/watch_faces/settings/preferences_face.c | 4 +++- movement/watch_faces/settings/set_time_face.c | 3 +++ 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index d43285e1..6576bd75 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -7,7 +7,8 @@ movement_state_t movement_state; void * watch_face_contexts[MOVEMENT_NUM_FACES]; -const int32_t movement_inactivity_deadlines[8] = {INT_MAX, 3600, 7200, 21600, 43200, 86400, 172800, 604800}; +const int32_t movement_le_inactivity_deadlines[8] = {INT_MAX, 3600, 7200, 21600, 43200, 86400, 172800, 604800}; +const int32_t movement_timeout_inactivity_deadlines[4] = {60, 120, 300, 1800}; movement_event_t event; void cb_mode_btn_interrupt(); @@ -18,8 +19,10 @@ void cb_alarm_fired(); void cb_tick(); static inline void _movement_reset_inactivity_countdown() { - // for testing, make the timeout happen 60x faster. - movement_state.le_mode_ticks = movement_inactivity_deadlines[movement_state.settings.bit.le_inactivity_interval] / 60; + // for testing, make the low energy timeout happen 60x faster. + movement_state.le_mode_ticks = movement_le_inactivity_deadlines[movement_state.settings.bit.le_inactivity_interval] / 60; + // for testing, make the inactivity timeout happen 4x faster. + movement_state.timeout_ticks = movement_timeout_inactivity_deadlines[movement_state.settings.bit.to_inactivity_interval] / 4; } void movement_request_tick_frequency(uint8_t freq) { @@ -130,6 +133,11 @@ bool app_loop() { } } + // if we have timed out of our timeout countdown, give the app a hint that they can resign. + if (movement_state.timeout_ticks == 0) { + event.event_type = EVENT_TIMEOUT; + } + // if we have timed out of our low energy mode countdown, enter low energy mode. if (movement_state.le_mode_ticks == 0) { movement_state.le_mode_ticks = -1; @@ -210,6 +218,7 @@ void cb_tick() { if (date_time.unit.second != movement_state.last_second) { if (movement_state.light_ticks) movement_state.light_ticks--; if (movement_state.settings.bit.le_inactivity_interval && movement_state.le_mode_ticks > 0) movement_state.le_mode_ticks--; + if (movement_state.timeout_ticks > 0) movement_state.timeout_ticks--; movement_state.last_second = date_time.unit.second; movement_state.subsecond = 0; diff --git a/movement/movement.h b/movement/movement.h index 3f3f0365..d116a6e0 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -6,9 +6,10 @@ // TODO: none of this is implemented typedef union { struct { - uint32_t reserved : 16; + uint32_t reserved : 14; 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 to_inactivity_interval : 2;// an inactivity interval for asking the active face to resign. uint32_t le_inactivity_interval : 3;// 0 to disable low energy mode, or an inactivity interval for going into low energy mode. 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) @@ -23,6 +24,7 @@ typedef enum { EVENT_TICK, // Most common event type. Your watch face is being called from the tick callback. EVENT_LOW_ENERGY_UPDATE, // If the watch is in low energy mode and you are in the foreground, you will get a chance to update the display once per minute. EVENT_BACKGROUND_TASK, // Your watch face is being invoked to perform a background task. Don't update the display here; you may not be in the foreground. + EVENT_TIMEOUT, // Your watch face has been inactive for a while. You may want to resign, depending on your watch face's intended use case. 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. @@ -162,6 +164,9 @@ typedef struct { // low energy mode countdown int32_t le_mode_ticks; + // app resignation countdown (TODO: consolidate with LE countdown?) + int16_t timeout_ticks; + // stuff for subsecond tracking uint8_t tick_frequency; uint8_t last_second; diff --git a/movement/watch_faces/clock/simple_clock_face.c b/movement/watch_faces/clock/simple_clock_face.c index 351c7ebc..95db3901 100644 --- a/movement/watch_faces/clock/simple_clock_face.c +++ b/movement/watch_faces/clock/simple_clock_face.c @@ -18,7 +18,6 @@ void simple_clock_face_activate(movement_settings_t *settings, void *context) { } bool simple_clock_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { - printf("simple_clock_face_loop\n"); const char weekdays[7][3] = {"SA", "SU", "MO", "TU", "WE", "TH", "FR"}; char buf[11]; uint8_t pos; @@ -28,6 +27,7 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting switch (event.event_type) { case EVENT_ACTIVATE: case EVENT_TICK: + case EVENT_TIMEOUT: case EVENT_LOW_ENERGY_UPDATE: date_time = watch_rtc_get_date_time(); previous_date_time = *((uint32_t *)context); diff --git a/movement/watch_faces/complications/pulsometer_face.c b/movement/watch_faces/complications/pulsometer_face.c index db5c5d0e..e54b4551 100644 --- a/movement/watch_faces/complications/pulsometer_face.c +++ b/movement/watch_faces/complications/pulsometer_face.c @@ -17,7 +17,6 @@ void pulsometer_face_activate(movement_settings_t *settings, void *context) { } bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { - printf("pulsometer_face_loop\n"); (void) settings; pulsometer_state_t *pulsometer_state = (pulsometer_state_t *)context; char buf[14]; @@ -74,6 +73,9 @@ bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, pulsometer_state->measuring = false; movement_request_tick_frequency(1); break; + case EVENT_TIMEOUT: + movement_move_to_face(0); + break; default: break; } diff --git a/movement/watch_faces/settings/preferences_face.c b/movement/watch_faces/settings/preferences_face.c index c3b7663c..98a2372d 100644 --- a/movement/watch_faces/settings/preferences_face.c +++ b/movement/watch_faces/settings/preferences_face.c @@ -17,7 +17,6 @@ void preferences_face_activate(movement_settings_t *settings, void *context) { } bool preferences_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { - printf("preferences_face_loop\n"); uint8_t current_page = *((uint8_t *)context); switch (event.event_type) { case EVENT_MODE_BUTTON_UP: @@ -47,6 +46,9 @@ bool preferences_face_loop(movement_event_t event, movement_settings_t *settings break; } break; + case EVENT_TIMEOUT: + movement_move_to_face(0); + break; default: break; } diff --git a/movement/watch_faces/settings/set_time_face.c b/movement/watch_faces/settings/set_time_face.c index 7bb63d0b..6b82c68b 100644 --- a/movement/watch_faces/settings/set_time_face.c +++ b/movement/watch_faces/settings/set_time_face.c @@ -58,6 +58,9 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v } watch_rtc_set_date_time(date_time); break; + case EVENT_TIMEOUT: + movement_move_to_face(0); + break; default: break; } -- cgit v1.2.3 From 1db9e18b927a45b6f93279ee6b9929679f0a9cc5 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 18 Oct 2021 12:31:44 -0400 Subject: pulsometer: rearrange switch for clarity --- .../watch_faces/complications/pulsometer_face.c | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/movement/watch_faces/complications/pulsometer_face.c b/movement/watch_faces/complications/pulsometer_face.c index e54b4551..abe002fb 100644 --- a/movement/watch_faces/complications/pulsometer_face.c +++ b/movement/watch_faces/complications/pulsometer_face.c @@ -21,6 +21,23 @@ bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, pulsometer_state_t *pulsometer_state = (pulsometer_state_t *)context; char buf[14]; switch (event.event_type) { + case EVENT_MODE_BUTTON_UP: + movement_move_to_next_face(); + break; + case EVENT_LIGHT_BUTTON_UP: + movement_illuminate_led(); + break; + case EVENT_ALARM_BUTTON_DOWN: + pulsometer_state->measuring = true; + pulsometer_state->pulse = 0xFFFF; + pulsometer_state->ticks = 0; + movement_request_tick_frequency(PULSOMETER_FACE_FREQUENCY); + break; + case EVENT_ALARM_BUTTON_UP: + case EVENT_ALARM_LONG_PRESS: + pulsometer_state->measuring = false; + movement_request_tick_frequency(1); + break; case EVENT_TICK: if (pulsometer_state->pulse == 0 && !pulsometer_state->measuring) { switch (pulsometer_state->ticks % 5) { @@ -55,23 +72,6 @@ bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, } if (pulsometer_state->measuring) pulsometer_state->ticks++; } - return false; - case EVENT_MODE_BUTTON_UP: - movement_move_to_next_face(); - 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_FACE_FREQUENCY); - break; - case EVENT_ALARM_BUTTON_UP: - case EVENT_ALARM_LONG_PRESS: - pulsometer_state->measuring = false; - movement_request_tick_frequency(1); break; case EVENT_TIMEOUT: movement_move_to_face(0); -- cgit v1.2.3 From 86d316008c7e6667c83760a8eaefadf91b1456bb Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 18 Oct 2021 12:33:07 -0400 Subject: movement: remove faster sleep / timeout intervals --- movement/movement.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/movement/movement.c b/movement/movement.c index 6576bd75..8d28cc55 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -19,10 +19,8 @@ void cb_alarm_fired(); void cb_tick(); static inline void _movement_reset_inactivity_countdown() { - // for testing, make the low energy timeout happen 60x faster. - movement_state.le_mode_ticks = movement_le_inactivity_deadlines[movement_state.settings.bit.le_inactivity_interval] / 60; - // for testing, make the inactivity timeout happen 4x faster. - movement_state.timeout_ticks = movement_timeout_inactivity_deadlines[movement_state.settings.bit.to_inactivity_interval] / 4; + movement_state.le_mode_ticks = movement_le_inactivity_deadlines[movement_state.settings.bit.le_inactivity_interval]; + movement_state.timeout_ticks = movement_timeout_inactivity_deadlines[movement_state.settings.bit.to_inactivity_interval]; } void movement_request_tick_frequency(uint8_t freq) { -- cgit v1.2.3 From 77951ccda1d11caa9a53214cefcceb9a9c6e7a76 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 18 Oct 2021 15:11:01 -0400 Subject: add readme for movement app --- movement/README.md | 297 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 movement/README.md diff --git a/movement/README.md b/movement/README.md new file mode 100644 index 00000000..8b3d478a --- /dev/null +++ b/movement/README.md @@ -0,0 +1,297 @@ +Movement: the community watch face app +====================================== + +The Sensor Watch Library allows you to write your own bare-metal applications for the Sensor Watch. This is great if you want full control over the code running on the device, but it also means that you may have to implement your own UI for many common tasks like setting the time or illuminating the screen. + +**Movement** is an application that manages the display of different screens of content on the watch. These screens are called **watch faces**. Watch faces can be passive displays of information like a clock or a calendar, or they can be fully interactive user interfaces like the Preferences face, which allows the user to customize Movement's behavior. Movement handles the instantiation of your watch face and manages transitions between screens. It also provides a low-power sleep mode, triggered after a period of inactivity, to preserve the watch battery. + +Several faces are provided that offer baseline functionality like a clock, a settings screen and an interface for setting the time. You can change and reorder the watch faces that Movement displays by editing `movement_config.h`, and you can write your own watch face using the guidance in this document. + +Watch Face API +-------------- + +You can implement a watch face using just four functions: + +* `watch_face_setup` +* `watch_face_activate` +* `watch_face_loop` +* `watch_face_resign` + +A fifth optional function, `watch_face_wants_background_task`, has not yet had its implementation ironed out, but it will be added to the guide at a later date. + +To create a new watch face, you should create a new C header and source file in the watch-faces folder (i.e. for a watch face that displays moon phases: `moon_phase_face.h`, `moon_phase_face.c`), and implement these functions with your own unique prefix (i.e. `moon_phase_face_setup`). Then declare your watch face in your header file as follows: + +```c +static const watch_face_t moon_phase_face = { + moon_phase_face_setup, + moon_phase_face_activate, + moon_phase_face_loop, + moon_phase_face_resign, + NULL // or moon_phase_face_wants_background_task, if you implemented this function +}; +``` + +This section will go over how each function works. The section headings use the watch_face prefix, but know that you should implement each function with your own prefix as described above. + +### watch_face_setup + +If you have worked with Arduino, this function is similar to setup() in that it is called at first boot. In our case, it is also called when waking from sleep mode. You will be passed three parameters: + +* `settings` - a pointer to the global Movement settings. You can use this to inform how you present your display to the user (i.e. taking into account whether they have silenced the buttons, or if they prefer 12 or 24-hour mode). You can also change these settings if you like. +* `position` - The 0-indexed position of your watch face in the list of faces. +* `context_ptr` - A pointer to a pointer. On first run, the pointee will be NULL. If you need to keep track of any state within your watch face, you should check if it is NULL, and if so, set its value to a pointer to some value or struct that will keep track of that state. For example, the Preferences face needs to keep track of which page the user is viewing (just an integer), whereas the Pulsometer face needs to track several different properties in a struct. + +Beyond setting up the context pointer, you may want to configure any peripherals that your watch face requires; for example, a temperature watch face that reads a thermistor output may want to configure the ADC here. Still, to save power, you should avoid leaving the peripheral enabled, and wait to set pin function in the activate function. + +It was mentioned above but it's worth mentioning again: this function will be called again after waking from sleep mode, since sleep mode disables all of the device's pins and peripherals. This would give the temperature watch face a chance to re-configure the ADC. + +### watch_face_activate + +This function is called just before your watch enters the foreground. If your watch face has any segments or text that is always displayed, you may want to set that here. In addition, if your watch face depends on data from a peripheral (like that temperature watch face), you will likely want to enable that peripheral and set any required pin modes here. This function is also passed a pointer to the settings and your application context. + +### watch_face_loop + +This is a lot like your loop() function in Arduinoland in that it is called repeatedly whenever your watch face is on screen. There is one crucial difference though: it is called less often. By default, this function is called once per second, and in response to events like button presses. You can request a more frequent tick interval by calling `movement_request_tick_frequency` with any power of 2 from 1 to 128. + +In addition to the settings and context, this function receives another parameter: an `event`. This is a struct containing information about the event that triggered the update. You mostly need to check the `event_type` to determine what kind of event triggered the loop. A detailed list of all events is provided at the bottom of this document. + +There is also a `subsecond` property on the event that contains the fractional second of the event. If you are using 1 Hz updates, subsecond will always be 0. + +You should set up a switch statement that handles, at the very least, the `EVENT_TICK` and `EVENT_MODE_BUTTON_UP` event types. The mode button up event occurs when the user presses the MODE button. **Your loop function SHOULD call the movement_move_to_next_face function in response to this event.** If you have a good reason to override this behavior (e.g. your user interface requires all three buttons), your watch face MUST call the movement_move_to_next_face function in response to the EVENT_MODE_LONG_PRESS event. If you fail to do this, the user will become stuck on your watch face. + +### watch_face_resign + +This function is called just before your watch face goes off screen. You should disable any peripherals you enabled in `watch_face_activate`. If you requested an tick frequency other than 1 Hz at any point in your code, **you must reset it to 1 Hz when you resign**. It is passed the same settings and context as the other functions. + +Putting it into practice: the Pulsometer watch face +--------------------------------------------------- + +Let's take a look at a watch face to see how these pieces fit together. A *pulsometer* is [a mechanical watch complication designed to determine someone's pulse](https://www.ablogtowatch.com/longines-pulsometer-chronograph-watch/) by counting their heartbeats: you start the pulsometer, count heartbeats, and stop it when you reach the specified number. The needle will point to the pulse rate. + +Let's implement a pulsometer for the Sensor Watch. These files are in the repository as `pulsometer_face.h` and `pulsometer_face.c`, but we'll walk through them inline here. + +### pulsometer_face.h + +First, let's take a look at the header file. First we include the Movement header file, which defines the various types we need to build a watch face: + +```c +#include "movement.h" +``` + +The pulsometer needs to track certain state to do its job, so we define a struct to contain our watch face's context: + +```c +typedef struct { + bool measuring; + int16_t pulse; + int16_t ticks; +} pulsometer_state_t; +``` + +Finally, we define the four required functions, and define the watch face struct that users will use to add the face to their watch: + +```c +void pulsometer_face_setup(movement_settings_t *settings, void ** context_ptr); +void pulsometer_face_activate(movement_settings_t *settings, void *context); +bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void pulsometer_face_resign(movement_settings_t *settings, void *context); + +static const watch_face_t pulsometer_face = { + pulsometer_face_setup, + pulsometer_face_activate, + pulsometer_face_loop, + pulsometer_face_resign, + NULL +}; +``` + +### pulsometer_face.c + +Now let's look at the implementation of the Pulsometer face. First up, we have a couple of definitions that we'll reference in the code: + +```c +#define PULSOMETER_FACE_FREQUENCY_FACTOR (4ul) // refresh rate will be 2 to this power Hz (0 for 1 Hz, 2 for 4 Hz, etc.) +#define PULSOMETER_FACE_FREQUENCY (1 << PULSOMETER_FACE_FREQUENCY_FACTOR) +``` + +These define the tick frequency: when the pulsometer widget is updating the screen, it will request 16 Hz updates (2^4). + +#### Watch Face Setup + +```c +void pulsometer_face_setup(movement_settings_t *settings, void ** context_ptr) { + (void) settings; + if (*context_ptr == NULL) *context_ptr = malloc(sizeof(pulsometer_state_t)); +} +``` + +The `(void) settings;` line just silences a compiler warning about the unused parameter. The next line checks if the context pointer is NULL, and if so, allocates a `pulsometer_state_t`-sized chunk of memory to hold our state. + +#### Watch Face Activation + +```c +void pulsometer_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + memset(context, 0, sizeof(pulsometer_state_t)); +} +``` + +The pulsometer face doesn't need to keep track of context in between appearances; there's no need to keep displaying an old pulse reading hours or days after it was taken. So this line just sets the context to all zeroes before the watch face goes on screen. + +#### Watch Face Loop + +Next we have the loop function. First things first: it fetches our application context, and casts it to a `pulsometer_state_t` type so we can make use of it. It also creates a buffer for any text we plan to put on screen, and declares a switch statement for handling events: + +```c +bool pulsometer_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + (void) settings; + pulsometer_state_t *pulsometer_state = (pulsometer_state_t *)context; + char buf[14]; + switch (event.event_type) { +``` + +Let's go through each case one by one. In response to the user releasing the MODE button, we tell Movement to move to the next watch face. + +```c +case EVENT_MODE_BUTTON_UP: + movement_move_to_next_face(); + break; +``` + +Similarly in response to the user releasing the LIGHT button, we tell Movement to illuminate the LED. Movement does not do this automatically, in case your watch face UI has another use for the LIGHT button. + +```c +case EVENT_LIGHT_BUTTON_UP: + movement_illuminate_led(); + break; +``` + +The ALARM button is the main button the user will use to interact with the pulsometer. In response to the user pressing the ALARM button, we begin a measurement. We also request a faster tick frequency, so that we can update the display at 16 Hz. + +```c +case EVENT_ALARM_BUTTON_DOWN: + pulsometer_state->measuring = true; + pulsometer_state->pulse = 0xFFFF; + pulsometer_state->ticks = 0; + movement_request_tick_frequency(PULSOMETER_FACE_FREQUENCY); + break; +``` + +When the user releases the ALARM button, we finish the measurement. We also scale the update frequency back down to 1 Hz. + +```c +case EVENT_ALARM_BUTTON_UP: +case EVENT_ALARM_LONG_PRESS: + pulsometer_state->measuring = false; + movement_request_tick_frequency(1); + break; +``` + +The tick event handler is long, but handles all display updates. The first half of this conditional handles the case where we haven't yet measured anything: it just loops through five screens with instructions, and increments the tick count. + +```c +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; +``` + +The second half of the conditional handles the case where we are measuring or have a measurement to display. It does the math, updates the screen, and increments the tick count if needed. + +```c + } else { + if (pulsometer_state->measuring && pulsometer_state->ticks) { + pulsometer_state->pulse = (int16_t)((30.0 * ((float)(60 << PULSOMETER_FACE_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++; + } + break; +``` + +Finally, the timeout event. After a period of inactivity (configurable from one to thirty minutes), Movement will send this event to indicate that the user has not interacted with your watch face in some time. Watch faces do not need to resign when they receive the timeout event, but depending on what kind of information your watch face displays, you may want to resign by asking Movement to return to the first watch face (usually a clock). The pulsometer widget has no need to remain on screen, so it opts to return to the clock when it receives the timeout event. + +```c +case EVENT_TIMEOUT: + movement_move_to_face(0); + break; +``` + +#### Watch Face Resignation + +The resign function doesn't have to do much here; it just resets the tick frequency to 1 Hz. + +```c +void pulsometer_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; + movement_request_tick_frequency(1); +} +``` + +And that's that! + +Low Energy Mode +--------------- + +To save energy, the watch enters a low energy mode after a timeout period (confugurable from 1 hour to 7 days). In this mode, the watch will turn off all pins and peripherals except for the screen and real-time clock, and will wake up once a minute to allow the current watch face to update its display. + +Movement Event Types +-------------------- + +### EVENT_ACTIVATE + +You will receive this event when your watch face is entering the foreground. You can treat it like a tick event and just update the display. + +### EVENT_TICK + +This is the most common event type. Your watch face is being called as a result of the real-time clock ticking. By default this tick occurs once per second, but you can request more frequent updates. + +### EVENT_LIGHT_BUTTON_DOWN, EVENT_MODE_BUTTON_DOWN, EVENT_ALARM_BUTTON_DOWN + +Your watch face receives these events when one of the buttons is initially depressed, but before it is released. + +### EVENT_LIGHT_BUTTON_UP, EVENT_MODE_BUTTON_UP, EVENT_ALARM_BUTTON_UP + +Your watch face receives these events when one of these buttons is released quickly after being depressed (i.e. held for less than one second). + +### EVENT_LIGHT_LONG_PRESS, EVENT_MODE_LONG_PRESS, EVENT_ALARM_LONG_PRESS + +Your watch face receives these events when one of these buttons is released after having been held down for more than two seconds. + +### EVENT_TIMEOUT + +Your watch face receives this event after it has has been inactive for a while. You may want to resign here, depending on your watch face's intended use case. + +### EVENT_LOW_ENERGY_UPDATE + +If your watch face is in the foreground when the watch goes into low energy mode, you will receive an `EVENT_LOW_ENERGY_UPDATE` event once a minute (at the top of the minute) so that you can update the screen. Note however that when you receive this event, all pins and peripherals other than the RTC will have been disabled to save energy. If your display is clock or calendar oriented, this is fine. But if your display requires polling an I2C sensor or reading a value with the ADC, you won't be able to do this. You should either display the name of the watch face in response to the low power tick, or ensure that you resign before low power mode triggers (you can do this by calling `movement_move_to_face(0)` in your `EVENT_TIMEOUT` handler). + +**Your watch face MUST NOT wake up peripherals in response to a low energy update event.** The purpose of this mode is to consume as little energy as possible during the (potentially long) intervals when it's unlikely the user is wearing or looking at the watch. + +### EVENT_BACKGROUND_TASK + +The `EVENT_BACKGROUND_TASK` event is not yet implemented, but the plan is for this event type to allow waking peripherals even in low power mode. More information will be added in a future version of this guide. -- cgit v1.2.3 From 32710098577c982123d9391132165c0b02a57482 Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 18 Oct 2021 15:36:39 -0400 Subject: fix typos --- movement/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/movement/README.md b/movement/README.md index 8b3d478a..e4eeb1c4 100644 --- a/movement/README.md +++ b/movement/README.md @@ -61,7 +61,7 @@ You should set up a switch statement that handles, at the very least, the `EVENT ### watch_face_resign -This function is called just before your watch face goes off screen. You should disable any peripherals you enabled in `watch_face_activate`. If you requested an tick frequency other than 1 Hz at any point in your code, **you must reset it to 1 Hz when you resign**. It is passed the same settings and context as the other functions. +This function is called just before your watch face goes off screen. You should disable any peripherals you enabled in `watch_face_activate`. If you requested a tick frequency other than 1 Hz at any point in your code, **you must reset it to 1 Hz when you resign**. The watch_face_resign function is passed the same settings and context as the other functions. Putting it into practice: the Pulsometer watch face --------------------------------------------------- -- cgit v1.2.3