]> git.earman.xyz Git - sensor-watch.git/commitdiff
Fit naming conventions
authorEkaitz Zarraga <ekaitz@elenq.tech>
Sun, 19 Nov 2023 14:28:45 +0000 (15:28 +0100)
committerEkaitz Zarraga <ekaitz@elenq.tech>
Sun, 19 Nov 2023 14:28:45 +0000 (15:28 +0100)
movement/make/Makefile
movement/movement_faces.h
movement/watch_faces/complication/couchTo5k_face.c [deleted file]
movement/watch_faces/complication/couchTo5k_face.h [deleted file]
movement/watch_faces/complication/couch_to_5k_face.c [new file with mode: 0644]
movement/watch_faces/complication/couch_to_5k_face.h [new file with mode: 0644]

index 646e3cb8aff329e04730c5e7f7a514de0eaa9cfd..d27b796a1222d470aaad7e5c9c23bde50a7bec61 100644 (file)
@@ -118,7 +118,7 @@ SRCS += \
   ../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.
index 4a98fcf2015005270136bb27be5fb029119a519f..a88b322236be6ef3f5ccdc8627e612c9299ec02f 100644 (file)
@@ -95,7 +95,7 @@
 #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_
diff --git a/movement/watch_faces/complication/couchTo5k_face.c b/movement/watch_faces/complication/couchTo5k_face.c
deleted file mode 100644 (file)
index 79c22a5..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * 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.
-}
-
diff --git a/movement/watch_faces/complication/couchTo5k_face.h b/movement/watch_faces/complication/couchTo5k_face.h
deleted file mode 100644 (file)
index 149db59..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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_
-
diff --git a/movement/watch_faces/complication/couch_to_5k_face.c b/movement/watch_faces/complication/couch_to_5k_face.c
new file mode 100644 (file)
index 0000000..32c2b32
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * 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.
+}
+
diff --git a/movement/watch_faces/complication/couch_to_5k_face.h b/movement/watch_faces/complication/couch_to_5k_face.h
new file mode 100644 (file)
index 0000000..3c36ce6
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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_
+