../watch_faces/complication/flashlight_face.c \
../watch_faces/clock/decimal_time_face.c \
../watch_faces/clock/wyoscan_face.c \
- ../watch_faces/complication/couchTo5k_face.c \
+ ../watch_faces/complication/couch_to_5k_face.c \
# New watch faces go above this line.
# Leave this line at the bottom of the file; it has all the targets for making your project.
#include "flashlight_face.h"
#include "decimal_time_face.h"
#include "wyoscan_face.h"
-#include "couchTo5k_face.h"
+#include "couch_to_5k_face.h"
// New includes go above this line.
#endif // MOVEMENT_FACES_H_
+++ /dev/null
-/*
- * MIT License
- *
- * Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include "couchTo5k_face.h"
-
-// They go: Warmup, Run, Walk, Run, Walk, Run, Walk ... , End (0)
-// Time is defined in seconds
-// Maybe do /10 to reduce memory usage?
-// (i don't want to use floats)
-
-// uint16_t C25K_WEEK_TEST[] = {10, 10, 10, 0};
-uint16_t C25K_WEEK_1[] = {300, 60, 90, 60, 90, 60, 90, 60, 90, 60, 90, 60,
- 90, 60, 90, 60, 90, 0};
-uint16_t C25K_WEEK_2[] = {300, 90, 120, 90, 120, 90, 120, 90, 120, 90, 120,
- 90, 120, 0};
-uint16_t C25K_WEEK_3[] = {300, 90, 90, 180, 180, 90, 90, 180, 180, 0};
-uint16_t C25K_WEEK_4[] = {300, 180, 90, 300, 150, 180, 90, 300, 0};
-uint16_t C25K_WEEK_5_1[] = {300, 300, 180, 300, 180, 300, 0 };
-uint16_t C25K_WEEK_5_2[] = {300, 480, 300, 480 , 0};
-uint16_t C25K_WEEK_5_3[] = {300, 1200, 0};
-uint16_t C25K_WEEK_6_1[] = {300, 300, 180, 480, 180, 300, 0 };
-uint16_t C25K_WEEK_6_2[] = {300, 600, 180, 600 , 0};
-uint16_t C25K_WEEK_6_3[] = {300, 1500, 0};
-uint16_t C25K_WEEK_7[] = {300, 1500, 0};
-uint16_t C25K_WEEK_8[] = {300, 1680, 0};
-uint16_t C25K_WEEK_9[] = {300, 1800, 0};
-
-
-#define C25K_SESSIONS_LENGTH 3*9
-uint16_t *C25K_SESSIONS[C25K_SESSIONS_LENGTH];
-
-static inline bool _finished(couchTo5k_state_t *state){
- return state->exercise_type == C25K_FINISHED;
-}
-static inline bool _cleared(couchTo5k_state_t *state){
- return state->timer == C25K_SESSIONS[state->session][0]
- && state->exercise == 0;
-}
-static inline void _next_session(couchTo5k_state_t *state){
- if (++state->session >= C25K_SESSIONS_LENGTH){
- state->session = 0;
- }
-}
-
-static inline void _assign_exercise_type(couchTo5k_state_t *state){
- if (state->exercise == 0){
- state->exercise_type = C25K_WARMUP;
- } else if (state->exercise % 2 == 1){
- state->exercise_type = C25K_RUN;
- } else {
- state->exercise_type = C25K_WALK;
- }
-}
-
-static void _next_exercise(couchTo5k_state_t *state){
- state->exercise++;
- state->timer = C25K_SESSIONS[state->session][state->exercise];
- // If the new timer starts in zero, it's finished
- if (state->timer == 0){
- movement_play_alarm_beeps(7, BUZZER_NOTE_C8);
- state->exercise_type = C25K_FINISHED;
- return;
- }
- movement_play_alarm_beeps(4, BUZZER_NOTE_A7);
- _assign_exercise_type(state);
-}
-
-static void _init_session(couchTo5k_state_t *state){
- state->exercise = 0; // Restart exercise counter
- state->timer = C25K_SESSIONS[state->session][state->exercise];
- _assign_exercise_type(state);
-}
-
-static char *_exercise_type_to_str(exercise_type_t t){
- switch (t){
- case C25K_WARMUP:
- return "WU";
- case C25K_RUN:
- return "RU";
- case C25K_WALK:
- return "WA";
- case C25K_FINISHED:
- return "--";
- default:
- return " ";
- }
-}
-static void _display(couchTo5k_state_t *state, char *buf){
- // TODO only repaint needed parts
- uint8_t seconds = state->timer % 60;
- sprintf(buf, "%s%2d%2d%02d%02d",
- _exercise_type_to_str(state->exercise_type),
- (state->session + 1) % 100,
- ((state->timer - seconds) / 60) % 100,
- seconds,
- (state->exercise + 1) % 100);
- watch_display_string(buf, 0);
-}
-
-
-void couchTo5k_face_setup(movement_settings_t *settings, uint8_t
- watch_face_index, void ** context_ptr) {
- (void) settings;
- (void) watch_face_index;
- if (*context_ptr == NULL) {
- *context_ptr = malloc(sizeof(couchTo5k_state_t));
- memset(*context_ptr, 0, sizeof(couchTo5k_state_t));
- // Do any one-time tasks in here; the inside of this conditional
- // happens only at boot.
- // C25K_SESSIONS[0] = C25K_WEEK_TEST;
- C25K_SESSIONS[0] = C25K_WEEK_1;
- C25K_SESSIONS[1] = C25K_WEEK_1;
- C25K_SESSIONS[2] = C25K_WEEK_1;
- C25K_SESSIONS[3] = C25K_WEEK_2;
- C25K_SESSIONS[4] = C25K_WEEK_2;
- C25K_SESSIONS[5] = C25K_WEEK_2;
- C25K_SESSIONS[6] = C25K_WEEK_3;
- C25K_SESSIONS[7] = C25K_WEEK_3;
- C25K_SESSIONS[8] = C25K_WEEK_3;
- C25K_SESSIONS[9] = C25K_WEEK_4;
- C25K_SESSIONS[10] = C25K_WEEK_4;
- C25K_SESSIONS[11] = C25K_WEEK_4;
- C25K_SESSIONS[12] = C25K_WEEK_5_1;
- C25K_SESSIONS[13] = C25K_WEEK_5_2;
- C25K_SESSIONS[14] = C25K_WEEK_5_3;
- C25K_SESSIONS[15] = C25K_WEEK_6_1;
- C25K_SESSIONS[16] = C25K_WEEK_6_2;
- C25K_SESSIONS[17] = C25K_WEEK_6_3;
- C25K_SESSIONS[18] = C25K_WEEK_7;
- C25K_SESSIONS[19] = C25K_WEEK_7;
- C25K_SESSIONS[20] = C25K_WEEK_7;
- C25K_SESSIONS[21] = C25K_WEEK_8;
- C25K_SESSIONS[22] = C25K_WEEK_8;
- C25K_SESSIONS[23] = C25K_WEEK_8;
- C25K_SESSIONS[24] = C25K_WEEK_9;
- C25K_SESSIONS[25] = C25K_WEEK_9;
- C25K_SESSIONS[26] = C25K_WEEK_9;
- }
- // Do any pin or peripheral setup here; this will be called whenever the
- // watch wakes from deep sleep.
-}
-
-void couchTo5k_face_activate(movement_settings_t *settings, void *context) {
- (void) settings;
- (void) context;
- // Handle any tasks related to your watch face coming on screen.
- watch_set_colon();
-}
-
-
-bool couchTo5k_face_loop(movement_event_t event, movement_settings_t *settings,
- void *context) {
- couchTo5k_state_t *state = (couchTo5k_state_t *)context;
- static char buf[11];
- static bool paused = true;
-
- switch (event.event_type) {
- case EVENT_ACTIVATE:
- // Show your initial UI here.
- movement_request_tick_frequency(1);
- _init_session(state);
- paused = true;
- _display(state, buf);
- break;
- case EVENT_TICK:
- if ( !paused && !_finished(state) ) {
- if (state->timer == 0){
- _next_exercise(state);
- } else {
- state->timer--;
- }
- }
- _display(state, buf);
- break;
- case EVENT_LIGHT_BUTTON_UP:
- // This is the next-exercise / reset button.
-
- // When finished move to the next session and leave it paused
- if ( _finished(state) ){
- _next_session(state);
- _init_session(state);
- paused = true;
- break;
- }
- // When paused and cleared move to next, when only paused, clear
- if ( paused ) {
- if ( _cleared(state) ){
- _next_session(state);
- }
- _init_session(state);
- }
- break;
- case EVENT_ALARM_BUTTON_UP:
- if (settings->bit.button_should_sound) {
- watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
- }
- paused = !paused;
- break;
- case EVENT_TIMEOUT:
- // Your watch face will receive this event after a period of
- // inactivity. If it makes sense to resign,
- // movement_move_to_face(0);
- break;
- case EVENT_LOW_ENERGY_UPDATE:
- // If you did not resign in EVENT_TIMEOUT, you can use this event
- // to update the display once a minute. Avoid displaying
- // fast-updating values like seconds, since the display won't
- // update again for 60 seconds. You should also consider starting
- // the tick animation, to show the wearer that this is sleep mode:
- // watch_start_tick_animation(500);
- break;
- default:
- // Movement's default loop handler will step in for any cases you
- // don't handle above:
- // * EVENT_LIGHT_BUTTON_DOWN lights the LED
- // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
- // * EVENT_MODE_LONG_PRESS returns to the first watch face (or
- // skips to the secondary watch face, if configured)
- // You can override any of these behaviors by adding a case for
- // these events to this switch statement.
- return movement_default_loop_handler(event, settings);
- }
-
- // return true if the watch can enter standby mode. Generally speaking, you
- // should always return true.
- // Exceptions:
- // * If you are displaying a color using the low-level watch_set_led_color
- // function, you should return false.
- // * If you are sounding the buzzer using the low-level
- // watch_set_buzzer_on function, you should return false.
- // Note that if you are driving the LED or buzzer using Movement functions
- // like movement_illuminate_led or movement_play_alarm, you can still
- // return true. This guidance only applies to the low-level watch_
- // functions.
- return true;
-}
-
-void couchTo5k_face_resign(movement_settings_t *settings, void *context) {
- (void) settings;
- (void) context;
-
- // handle any cleanup before your watch face goes off-screen.
-}
-
+++ /dev/null
-/*
- * MIT License
- *
- * Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-#ifndef COUCHTO5K_FACE_H_
-#define COUCHTO5K_FACE_H_
-
-#include "movement.h"
-
-/*
- * Couch To 5k;
- *
- *
- * The program is designed to train 3 times a week. Each training is a
- * *session*. Each of the rounds you have in the training is an *exercise*.
- *
- * The training goes like this:
- * 5min warm-up walk -> Run X minutes -> Walk Y minutes -> ... -> Stop
- *
- * The watch face shows it like this: The weekday indicator shows if you need
- * to Warm Up (`WU`), run (`rU`), walk (`WA`) or stop (`--`).
- *
- * The month-day indicator shows the session you are in (from 1 to 27).
- *
- * The timer shows the time you have left in the exercise and the exercise you
- * are doing (MM:SS:ee). When an exercise finishes you are notified with an
- * alarm. When the whole session finishes, a different tone is played for a
- * longer period.
- *
- * Pressing the ALARM button pauses/resumes the clock.
- *
- * Pressing the LIGHT button does nothing if the timer is not paused. When it
- * is paused it clears the current session (it restarts it to the beginning)
- * and if it was already cleared or the current session was finished moves to
- * the next session.
- */
-
-typedef enum {
- C25K_WARMUP,
- C25K_RUN,
- C25K_WALK,
- C25K_FINISHED
-} exercise_type_t;
-
-typedef struct {
- // Anything you need to keep track of, put it here!
- uint8_t session;
- uint8_t exercise;
- exercise_type_t exercise_type;
- uint16_t timer;
-} couchTo5k_state_t;
-
-void couchTo5k_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
-void couchTo5k_face_activate(movement_settings_t *settings, void *context);
-bool couchTo5k_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
-void couchTo5k_face_resign(movement_settings_t *settings, void *context);
-
-#define couchTo5k_face ((const watch_face_t){ \
- couchTo5k_face_setup, \
- couchTo5k_face_activate, \
- couchTo5k_face_loop, \
- couchTo5k_face_resign, \
- NULL, \
-})
-
-#endif // COUCHTO5K_FACE_H_
-
--- /dev/null
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "couch_to_5k_face.h"
+
+// They go: Warmup, Run, Walk, Run, Walk, Run, Walk ... , End (0)
+// Time is defined in seconds
+// Maybe do /10 to reduce memory usage?
+// (i don't want to use floats)
+
+// uint16_t C25K_WEEK_TEST[] = {10, 10, 10, 0};
+uint16_t C25K_WEEK_1[] = {300, 60, 90, 60, 90, 60, 90, 60, 90, 60, 90, 60,
+ 90, 60, 90, 60, 90, 0};
+uint16_t C25K_WEEK_2[] = {300, 90, 120, 90, 120, 90, 120, 90, 120, 90, 120,
+ 90, 120, 0};
+uint16_t C25K_WEEK_3[] = {300, 90, 90, 180, 180, 90, 90, 180, 180, 0};
+uint16_t C25K_WEEK_4[] = {300, 180, 90, 300, 150, 180, 90, 300, 0};
+uint16_t C25K_WEEK_5_1[] = {300, 300, 180, 300, 180, 300, 0 };
+uint16_t C25K_WEEK_5_2[] = {300, 480, 300, 480 , 0};
+uint16_t C25K_WEEK_5_3[] = {300, 1200, 0};
+uint16_t C25K_WEEK_6_1[] = {300, 300, 180, 480, 180, 300, 0 };
+uint16_t C25K_WEEK_6_2[] = {300, 600, 180, 600 , 0};
+uint16_t C25K_WEEK_6_3[] = {300, 1500, 0};
+uint16_t C25K_WEEK_7[] = {300, 1500, 0};
+uint16_t C25K_WEEK_8[] = {300, 1680, 0};
+uint16_t C25K_WEEK_9[] = {300, 1800, 0};
+
+
+#define C25K_SESSIONS_LENGTH 3*9
+uint16_t *C25K_SESSIONS[C25K_SESSIONS_LENGTH];
+
+static inline bool _finished(couch_to_5k_state_t *state){
+ return state->exercise_type == C25K_FINISHED;
+}
+static inline bool _cleared(couch_to_5k_state_t *state){
+ return state->timer == C25K_SESSIONS[state->session][0]
+ && state->exercise == 0;
+}
+static inline void _next_session(couch_to_5k_state_t *state){
+ if (++state->session >= C25K_SESSIONS_LENGTH){
+ state->session = 0;
+ }
+}
+
+static inline void _assign_exercise_type(couch_to_5k_state_t *state){
+ if (state->exercise == 0){
+ state->exercise_type = C25K_WARMUP;
+ } else if (state->exercise % 2 == 1){
+ state->exercise_type = C25K_RUN;
+ } else {
+ state->exercise_type = C25K_WALK;
+ }
+}
+
+static void _next_exercise(couch_to_5k_state_t *state){
+ state->exercise++;
+ state->timer = C25K_SESSIONS[state->session][state->exercise];
+ // If the new timer starts in zero, it's finished
+ if (state->timer == 0){
+ movement_play_alarm_beeps(7, BUZZER_NOTE_C8);
+ state->exercise_type = C25K_FINISHED;
+ return;
+ }
+ movement_play_alarm_beeps(4, BUZZER_NOTE_A7);
+ _assign_exercise_type(state);
+}
+
+static void _init_session(couch_to_5k_state_t *state){
+ state->exercise = 0; // Restart exercise counter
+ state->timer = C25K_SESSIONS[state->session][state->exercise];
+ _assign_exercise_type(state);
+}
+
+static char *_exercise_type_to_str(exercise_type_t t){
+ switch (t){
+ case C25K_WARMUP:
+ return "WU";
+ case C25K_RUN:
+ return "RU";
+ case C25K_WALK:
+ return "WA";
+ case C25K_FINISHED:
+ return "--";
+ default:
+ return " ";
+ }
+}
+static void _display(couch_to_5k_state_t *state, char *buf){
+ // TODO only repaint needed parts
+ uint8_t seconds = state->timer % 60;
+ sprintf(buf, "%s%2d%2d%02d%02d",
+ _exercise_type_to_str(state->exercise_type),
+ (state->session + 1) % 100,
+ ((state->timer - seconds) / 60) % 100,
+ seconds,
+ (state->exercise + 1) % 100);
+ watch_display_string(buf, 0);
+}
+
+
+void couch_to_5k_face_setup(movement_settings_t *settings, uint8_t
+ watch_face_index, void ** context_ptr) {
+ (void) settings;
+ (void) watch_face_index;
+ if (*context_ptr == NULL) {
+ *context_ptr = malloc(sizeof(couch_to_5k_state_t));
+ memset(*context_ptr, 0, sizeof(couch_to_5k_state_t));
+ // Do any one-time tasks in here; the inside of this conditional
+ // happens only at boot.
+ // C25K_SESSIONS[0] = C25K_WEEK_TEST;
+ C25K_SESSIONS[0] = C25K_WEEK_1;
+ C25K_SESSIONS[1] = C25K_WEEK_1;
+ C25K_SESSIONS[2] = C25K_WEEK_1;
+ C25K_SESSIONS[3] = C25K_WEEK_2;
+ C25K_SESSIONS[4] = C25K_WEEK_2;
+ C25K_SESSIONS[5] = C25K_WEEK_2;
+ C25K_SESSIONS[6] = C25K_WEEK_3;
+ C25K_SESSIONS[7] = C25K_WEEK_3;
+ C25K_SESSIONS[8] = C25K_WEEK_3;
+ C25K_SESSIONS[9] = C25K_WEEK_4;
+ C25K_SESSIONS[10] = C25K_WEEK_4;
+ C25K_SESSIONS[11] = C25K_WEEK_4;
+ C25K_SESSIONS[12] = C25K_WEEK_5_1;
+ C25K_SESSIONS[13] = C25K_WEEK_5_2;
+ C25K_SESSIONS[14] = C25K_WEEK_5_3;
+ C25K_SESSIONS[15] = C25K_WEEK_6_1;
+ C25K_SESSIONS[16] = C25K_WEEK_6_2;
+ C25K_SESSIONS[17] = C25K_WEEK_6_3;
+ C25K_SESSIONS[18] = C25K_WEEK_7;
+ C25K_SESSIONS[19] = C25K_WEEK_7;
+ C25K_SESSIONS[20] = C25K_WEEK_7;
+ C25K_SESSIONS[21] = C25K_WEEK_8;
+ C25K_SESSIONS[22] = C25K_WEEK_8;
+ C25K_SESSIONS[23] = C25K_WEEK_8;
+ C25K_SESSIONS[24] = C25K_WEEK_9;
+ C25K_SESSIONS[25] = C25K_WEEK_9;
+ C25K_SESSIONS[26] = C25K_WEEK_9;
+ }
+ // Do any pin or peripheral setup here; this will be called whenever the
+ // watch wakes from deep sleep.
+}
+
+void couch_to_5k_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+ // Handle any tasks related to your watch face coming on screen.
+ watch_set_colon();
+}
+
+
+bool couch_to_5k_face_loop(movement_event_t event, movement_settings_t *settings,
+ void *context) {
+ couch_to_5k_state_t *state = (couch_to_5k_state_t *)context;
+ static char buf[11];
+ static bool paused = true;
+
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ // Show your initial UI here.
+ movement_request_tick_frequency(1);
+ _init_session(state);
+ paused = true;
+ _display(state, buf);
+ break;
+ case EVENT_TICK:
+ if ( !paused && !_finished(state) ) {
+ if (state->timer == 0){
+ _next_exercise(state);
+ } else {
+ state->timer--;
+ }
+ }
+ _display(state, buf);
+ break;
+ case EVENT_LIGHT_BUTTON_UP:
+ // This is the next-exercise / reset button.
+
+ // When finished move to the next session and leave it paused
+ if ( _finished(state) ){
+ _next_session(state);
+ _init_session(state);
+ paused = true;
+ break;
+ }
+ // When paused and cleared move to next, when only paused, clear
+ if ( paused ) {
+ if ( _cleared(state) ){
+ _next_session(state);
+ }
+ _init_session(state);
+ }
+ break;
+ case EVENT_ALARM_BUTTON_UP:
+ if (settings->bit.button_should_sound) {
+ watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+ }
+ paused = !paused;
+ break;
+ case EVENT_TIMEOUT:
+ // Your watch face will receive this event after a period of
+ // inactivity. If it makes sense to resign,
+ // movement_move_to_face(0);
+ break;
+ case EVENT_LOW_ENERGY_UPDATE:
+ // If you did not resign in EVENT_TIMEOUT, you can use this event
+ // to update the display once a minute. Avoid displaying
+ // fast-updating values like seconds, since the display won't
+ // update again for 60 seconds. You should also consider starting
+ // the tick animation, to show the wearer that this is sleep mode:
+ // watch_start_tick_animation(500);
+ break;
+ default:
+ // Movement's default loop handler will step in for any cases you
+ // don't handle above:
+ // * EVENT_LIGHT_BUTTON_DOWN lights the LED
+ // * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
+ // * EVENT_MODE_LONG_PRESS returns to the first watch face (or
+ // skips to the secondary watch face, if configured)
+ // You can override any of these behaviors by adding a case for
+ // these events to this switch statement.
+ return movement_default_loop_handler(event, settings);
+ }
+
+ // return true if the watch can enter standby mode. Generally speaking, you
+ // should always return true.
+ // Exceptions:
+ // * If you are displaying a color using the low-level watch_set_led_color
+ // function, you should return false.
+ // * If you are sounding the buzzer using the low-level
+ // watch_set_buzzer_on function, you should return false.
+ // Note that if you are driving the LED or buzzer using Movement functions
+ // like movement_illuminate_led or movement_play_alarm, you can still
+ // return true. This guidance only applies to the low-level watch_
+ // functions.
+ return true;
+}
+
+void couch_to_5k_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+
+ // handle any cleanup before your watch face goes off-screen.
+}
+
--- /dev/null
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef COUCHTO5K_FACE_H_
+#define COUCHTO5K_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Couch To 5k;
+ *
+ *
+ * The program is designed to train 3 times a week. Each training is a
+ * *session*. Each of the rounds you have in the training is an *exercise*.
+ *
+ * The training goes like this:
+ * 5min warm-up walk -> Run X minutes -> Walk Y minutes -> ... -> Stop
+ *
+ * The watch face shows it like this: The weekday indicator shows if you need
+ * to Warm Up (`WU`), run (`rU`), walk (`WA`) or stop (`--`).
+ *
+ * The month-day indicator shows the session you are in (from 1 to 27).
+ *
+ * The timer shows the time you have left in the exercise and the exercise you
+ * are doing (MM:SS:ee). When an exercise finishes you are notified with an
+ * alarm. When the whole session finishes, a different tone is played for a
+ * longer period.
+ *
+ * Pressing the ALARM button pauses/resumes the clock.
+ *
+ * Pressing the LIGHT button does nothing if the timer is not paused. When it
+ * is paused it clears the current session (it restarts it to the beginning)
+ * and if it was already cleared or the current session was finished moves to
+ * the next session.
+ */
+
+typedef enum {
+ C25K_WARMUP,
+ C25K_RUN,
+ C25K_WALK,
+ C25K_FINISHED
+} exercise_type_t;
+
+typedef struct {
+ // Anything you need to keep track of, put it here!
+ uint8_t session;
+ uint8_t exercise;
+ exercise_type_t exercise_type;
+ uint16_t timer;
+} couch_to_5k_state_t;
+
+void couch_to_5k_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void couch_to_5k_face_activate(movement_settings_t *settings, void *context);
+bool couch_to_5k_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void couch_to_5k_face_resign(movement_settings_t *settings, void *context);
+
+#define couch_to_5k_face ((const watch_face_t){ \
+ couch_to_5k_face_setup, \
+ couch_to_5k_face_activate, \
+ couch_to_5k_face_loop, \
+ couch_to_5k_face_resign, \
+ NULL, \
+})
+
+#endif // COUCHTO5K_FACE_H_
+