]> git.earman.xyz Git - sensor-watch.git/commitdiff
new settings watch face
authorJoey Castillo <joeycastillo@utexas.edu>
Sun, 11 May 2025 20:07:12 +0000 (16:07 -0400)
committerJoey Castillo <joeycastillo@utexas.edu>
Sun, 11 May 2025 20:53:03 +0000 (16:53 -0400)
movement_config.h
movement_faces.h
watch-faces.mk
watch-faces/settings/preferences_face.h
watch-faces/settings/settings_face.c [new file with mode: 0644]
watch-faces/settings/settings_face.h [new file with mode: 0644]

index 2645af5e39aff4665c877788ff4fba88e59cc5c2..6a63fc6fb5857e53c80274f7e7036a6be01a9b51 100644 (file)
@@ -38,7 +38,7 @@ const watch_face_t watch_faces[] = {
     voltage_face,
     days_since_face,
     accel_interrupt_count_face,
-    preferences_face,
+    settings_face,
     set_time_face,
 };
 
index d7aa6bba177d12d67c56b67e8c717d3ffbc7ddca..fb5d9b342343c09b3a2285e48fae9080ba58a9f0 100644 (file)
@@ -44,6 +44,7 @@
 #include "voltage_face.h"
 #include "set_time_face.h"
 #include "preferences_face.h"
+#include "settings_face.h"
 #include "light_sensor_face.h"
 #include "irda_demo_face.h"
 #include "file_demo_face.h"
index d6dd8f44dc0a36709b78da74328b938616e40fb8..567618fe5a76f3502484c90dd346bdfce7bd9587 100644 (file)
@@ -19,6 +19,7 @@ SRCS += \
   ./watch-faces/sensor/voltage_face.c \
   ./watch-faces/settings/set_time_face.c \
   ./watch-faces/settings/preferences_face.c \
+  ./watch-faces/settings/settings_face.c \
   ./watch-faces/demo/light_sensor_face.c \
   ./watch-faces/demo/irda_demo_face.c \
   ./watch-faces/demo/file_demo_face.c \
index 7b085d862ea59d7b8d5c40ca7dd30b51c7809924..77f6409ccecec158a623bc90e2bcb0c9ea0cc6dc 100644 (file)
 #pragma once
 
 /*
- * PREFERENCES face
+ * Legacy PREFERENCES face
+ *
+ * This face is deprecated and will be removed in a future release.
+ * Please use the Settings watch face instead.
  *
  * The Preferences watch face allows you to configure various options on your
  * Sensor Watch. Like all other screens, you advance the field you’re setting
diff --git a/watch-faces/settings/settings_face.c b/watch-faces/settings/settings_face.c
new file mode 100644 (file)
index 0000000..113ccb8
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2025 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include "settings_face.h"
+#include "watch.h"
+
+static void clock_setting_display(uint8_t subsecond) {
+    watch_display_text_with_fallback(WATCH_POSITION_TOP, "CLOCK", "CL");
+    if (subsecond % 2) {
+        if (movement_clock_mode_24h()) watch_display_text(WATCH_POSITION_BOTTOM, "24h");
+        else watch_display_text(WATCH_POSITION_BOTTOM, "12h");
+    }
+}
+
+static void clock_setting_advance(void) {
+    movement_set_clock_mode_24h(((movement_clock_mode_24h() + 1) % MOVEMENT_NUM_CLOCK_MODES));
+}
+
+static void beep_setting_display(uint8_t subsecond) {
+    watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "BTN", "BT");
+    watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "beep  ", " beep ");
+    if (subsecond % 2) {
+            if (movement_button_should_sound()) {
+            watch_display_text(WATCH_POSITION_TOP_RIGHT, " Y");
+        } else {
+            watch_display_text(WATCH_POSITION_TOP_RIGHT, " N");
+        }
+    }
+}
+
+static void beep_setting_advance(void) {
+    movement_set_button_should_sound(!movement_button_should_sound());
+}
+
+static void timeout_setting_display(uint8_t subsecond) {
+    watch_display_text_with_fallback(WATCH_POSITION_TOP, "TMOUt", "TO");
+    if (subsecond % 2) {
+        switch (movement_get_fast_tick_timeout()) {
+            case 0:
+                watch_display_text(WATCH_POSITION_BOTTOM, "60 SeC");
+                break;
+            case 1:
+                watch_display_text(WATCH_POSITION_BOTTOM, "2 n&in");
+                break;
+            case 2:
+                watch_display_text(WATCH_POSITION_BOTTOM, "5 n&in");
+                break;
+            case 3:
+                watch_display_text(WATCH_POSITION_BOTTOM, "30n&in");
+                break;
+        }
+    }
+}
+
+static void timeout_setting_advance(void) {
+    movement_set_fast_tick_timeout((movement_get_fast_tick_timeout() + 1));
+}
+
+static void low_energy_setting_display(uint8_t subsecond) {
+    watch_display_text_with_fallback(WATCH_POSITION_TOP, "LoEne", "LE");
+    if (subsecond % 2) {
+        switch (movement_get_low_energy_timeout()) {
+            case 0:
+                watch_display_text(WATCH_POSITION_BOTTOM, " Never");
+                break;
+            case 1:
+                watch_display_text(WATCH_POSITION_BOTTOM, "10n&in");
+                break;
+            case 2:
+                watch_display_text(WATCH_POSITION_BOTTOM, "1 hour");
+                break;
+            case 3:
+                watch_display_text(WATCH_POSITION_BOTTOM, "2 hour");
+                break;
+            case 4:
+                watch_display_text(WATCH_POSITION_BOTTOM, "6 hour");
+                break;
+            case 5:
+                watch_display_text(WATCH_POSITION_BOTTOM, "12 hr");
+                break;
+            case 6:
+                watch_display_text(WATCH_POSITION_BOTTOM, " 1 day");
+                break;
+            case 7:
+                watch_display_text(WATCH_POSITION_BOTTOM, " 7 day");
+                break;
+        }
+    }
+}
+
+static void low_energy_setting_advance(void) {
+    movement_set_low_energy_timeout((movement_get_low_energy_timeout() + 1));
+}
+
+static void led_duration_setting_display(uint8_t subsecond) {
+    char buf[8];
+
+    watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "LED", "LT");
+    if (subsecond % 2) {
+        if (movement_get_backlight_dwell() == 0) {
+            watch_display_text(WATCH_POSITION_BOTTOM, "instnt");
+        } else if (movement_get_backlight_dwell() == 0b111) {
+            watch_display_text(WATCH_POSITION_BOTTOM, "no LEd");
+        } else {
+            sprintf(buf, " %1d SeC", (movement_get_backlight_dwell() * 2 - 1) % 10);
+            watch_display_text(WATCH_POSITION_BOTTOM, buf);
+        }
+    }
+}
+
+static void led_duration_setting_advance(void) {
+    movement_set_backlight_dwell(movement_get_backlight_dwell() + 1);
+    if (movement_get_backlight_dwell() > 3) {
+        // set all bits to disable the LED
+        movement_set_backlight_dwell(0b111);
+    }
+}
+
+static void red_led_setting_display(uint8_t subsecond) {
+    char buf[8];
+    movement_color_t color = movement_backlight_color();
+
+    watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "LED", "LT");
+    watch_display_text(WATCH_POSITION_BOTTOM, " red  ");
+    if (subsecond % 2) {
+        sprintf(buf, "%2d", color.red);
+        watch_display_text(WATCH_POSITION_TOP_RIGHT, buf);
+    }
+}
+
+static void red_led_setting_advance(void) {
+    movement_color_t color = movement_backlight_color();
+    color.red++;
+    movement_set_backlight_color(color);
+}
+
+static void green_led_setting_display(uint8_t subsecond) {
+    char buf[8];
+    movement_color_t color = movement_backlight_color();
+
+    watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "LED", "LT");
+    watch_display_text(WATCH_POSITION_BOTTOM, " green");
+    if (subsecond % 2) {
+        sprintf(buf, "%2d", color.green);
+        watch_display_text(WATCH_POSITION_TOP_RIGHT, buf);
+    }
+}
+
+static void green_led_setting_advance(void) {
+    movement_color_t color = movement_backlight_color();
+    color.green++;
+    movement_set_backlight_color(color);
+}
+
+static void blue_led_setting_display(uint8_t subsecond) {
+    char buf[8];
+    movement_color_t color = movement_backlight_color();
+
+    watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "LED", "LT");
+    watch_display_text_with_fallback(WATCH_POSITION_BOTTOM, "blue  ", " blue ");
+    if (subsecond % 2) {
+        sprintf(buf, "%2d", color.blue);
+        watch_display_text(WATCH_POSITION_TOP_RIGHT, buf);
+    }
+}
+
+static void blue_led_setting_advance(void) {
+    movement_color_t color = movement_backlight_color();
+    color.blue++;
+    movement_set_backlight_color(color);
+}
+
+void settings_face_setup(uint8_t watch_face_index, void ** context_ptr) {
+    (void) watch_face_index;
+    if (*context_ptr == NULL) {
+        *context_ptr = malloc(sizeof(settings_state_t));
+        settings_state_t *state = (settings_state_t *)*context_ptr;
+        int8_t current_setting = 0;
+
+        state->num_settings = 5; // baseline, without LED settings
+#ifdef WATCH_RED_TCC_CHANNEL
+        state->num_settings++;
+#endif
+#ifdef WATCH_GREEN_TCC_CHANNEL
+        state->num_settings++;
+#endif
+#ifdef WATCH_BLUE_TCC_CHANNEL
+        state->num_settings++;
+#endif
+
+        state->settings_screens = malloc(state->num_settings * sizeof(settings_screen_t));
+        state->settings_screens[current_setting].display = clock_setting_display;
+        state->settings_screens[current_setting].advance = clock_setting_advance;
+        current_setting++;
+        state->settings_screens[current_setting].display = beep_setting_display;
+        state->settings_screens[current_setting].advance = beep_setting_advance;
+        current_setting++;
+        state->settings_screens[current_setting].display = timeout_setting_display;
+        state->settings_screens[current_setting].advance = timeout_setting_advance;
+        current_setting++;
+        state->settings_screens[current_setting].display = low_energy_setting_display;
+        state->settings_screens[current_setting].advance = low_energy_setting_advance;
+        current_setting++;
+        state->settings_screens[current_setting].display = led_duration_setting_display;
+        state->settings_screens[current_setting].advance = led_duration_setting_advance;
+        current_setting++;
+#ifdef WATCH_RED_TCC_CHANNEL
+        state->settings_screens[current_setting].display = red_led_setting_display;
+        state->settings_screens[current_setting].advance = red_led_setting_advance;
+        current_setting++;
+#endif
+#ifdef WATCH_GREEN_TCC_CHANNEL
+        state->settings_screens[current_setting].display = green_led_setting_display;
+        state->settings_screens[current_setting].advance = green_led_setting_advance;
+        current_setting++;
+#endif
+#ifdef WATCH_BLUE_TCC_CHANNEL
+        state->settings_screens[current_setting].display = blue_led_setting_display;
+        state->settings_screens[current_setting].advance = blue_led_setting_advance;
+        current_setting++;
+#endif
+    }
+}
+
+void settings_face_activate(void *context) {
+    settings_state_t *state = (settings_state_t *)context;
+    state->current_page = 0;
+    movement_request_tick_frequency(4); // we need to manually blink some pixels
+}
+
+bool settings_face_loop(movement_event_t event, void *context) {
+    settings_state_t *state = (settings_state_t *)context;
+
+    switch (event.event_type) {
+        case EVENT_LIGHT_BUTTON_DOWN:
+            state->current_page = (state->current_page + 1) % state->num_settings;
+            // fall through
+        case EVENT_TICK:
+        case EVENT_ACTIVATE:
+            watch_clear_display();
+            state->settings_screens[state->current_page].display(event.subsecond);
+            break;
+        case EVENT_MODE_BUTTON_UP:
+            movement_force_led_off();
+            movement_move_to_next_face();
+            return false;
+        case EVENT_ALARM_BUTTON_UP:
+            state->settings_screens[state->current_page].advance();
+            break;
+        case EVENT_TIMEOUT:
+            movement_move_to_face(0);
+            break;
+        default:
+            return movement_default_loop_handler(event);
+    }
+
+    if (state->current_page > 4) {
+        movement_color_t color = movement_backlight_color();
+        // this bitwise math turns #000 into #000000, #111 into #111111, etc.
+        movement_force_led_on(color.red | color.red << 4,
+                              color.green | color.green << 4,
+                              color.blue | color.blue << 4);
+        return false;
+    } else {
+        movement_force_led_off();
+        return true;
+    }
+}
+
+void settings_face_resign(void *context) {
+    (void) context;
+    movement_force_led_off();
+    movement_store_settings();
+}
diff --git a/watch-faces/settings/settings_face.h b/watch-faces/settings/settings_face.h
new file mode 100644 (file)
index 0000000..7f47ff7
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022-2024 Joey Castillo
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#pragma once
+
+/*
+ * SETTINGS face (replaces old Preferences watch face)
+ *
+ * The Settings watch face allows you to configure various options on your
+ * Sensor Watch. Like all other screens, you advance the field you’re setting
+ * with the Light button, and advance its value with the Alarm button. The
+ * Settings watch face labels each setting with a two-letter code up top
+ * when using the classic LCD, or something more readable on the custom LCD.
+ * The following list describes each setting and their options:
+ *
+ *  CL / Clock - Clock mode.
+ *      This setting allows you to select a 12-or 24-hour clock display. All
+ *      watch faces that support displaying the time will respect this setting;
+ *      for example, both Simple Clock, World Clock and Sunrise/Sunset will
+ *      display the time in 24 hour format if the 24 hour clock is selected here.
+ *
+ *  BT / BTN - Button beep.
+ *      This setting allows you to choose whether the Mode button should emit
+ *      a beep when pressed, and if so, how loud it should be. Options are
+ *      "Y" for yes and "N" for no.
+ *
+ *  TO / Tmout - Timeout.
+ *      Sets the time until screens that time out (like Settings and Time Set)
+ *      snap back to the first screen. 60 seconds is a good default for the
+ *      stock firmware, but if you choose a custom firmware with faces that
+ *      you’d like to keep on screen for longer, you can set that here.
+ *
+ *  LE / LoEne - Low Energy mode.
+ *      Sets the time until the watch enters its low energy sleep mode.
+ *      Options range from 1 hour to 7 days, or Never. The more often Sensor
+ *      Watch goes to sleep, the longer its battery will last — but you will
+ *      lose the seconds indicator while it is asleep. This setting allows
+ *      you to make a tradeoff between the device’s responsiveness and its
+ *      longevity.
+ *
+ *  LT / LED - Light Duration and Color
+ *      The first LED screen lets you choose how long the LED should stay lit
+ *       when the LIGHT button is pressed. Options are 1 second, 3 seconds
+ *       and 5 seconds, or “No LED” to disable the LED entirely.
+ *      The remaining screens set the intensity of the red, green or blue LEDs
+ *       depending on the target Sensor Board hardware to allow a custom color
+ *       blend. Values range from 0 (off) to 15 (full intensity).
+ *      On the LED color screens, the LED remains on so that you can see the
+ *      effect of mixing the LED colors.
+ */
+
+#include "movement.h"
+
+typedef struct {
+    void (*display)(uint8_t subsecond);
+    void (*advance)();
+} settings_screen_t;
+
+typedef struct {
+    int8_t current_page;
+    int8_t num_settings;
+    settings_screen_t *settings_screens;
+} settings_state_t;
+
+void settings_face_setup(uint8_t watch_face_index, void ** context_ptr);
+void settings_face_activate(void *context);
+bool settings_face_loop(movement_event_t event, void *context);
+void settings_face_resign(void *context);
+
+#define settings_face ((const watch_face_t){ \
+    settings_face_setup, \
+    settings_face_activate, \
+    settings_face_loop, \
+    settings_face_resign, \
+    NULL, \
+})