]> git.earman.xyz Git - sensor-watch.git/commitdiff
Periodic Table Face
authorPrimmR <primmr@icloud.com>
Fri, 24 Nov 2023 18:58:07 +0000 (18:58 +0000)
committerPrimmR <primmr@icloud.com>
Fri, 24 Nov 2023 18:58:07 +0000 (18:58 +0000)
movement/make/Makefile
movement/movement_faces.h
movement/watch_faces/complication/periodic_face.c [new file with mode: 0644]
movement/watch_faces/complication/periodic_face.h [new file with mode: 0644]

index c294b06892392124960e8f8cb4b0428083803e08..2e623e6b07f156c379471a9f7ad710006f8a51d7 100644 (file)
@@ -121,6 +121,7 @@ SRCS += \
   ../watch_faces/complication/couch_to_5k_face.c \
   ../watch_faces/clock/minute_repeater_decimal_face.c \
   ../watch_faces/complication/tuning_tones_face.c \
+  ../watch_faces/complication/periodic_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 cb58612fa00f520cd99af5af0e026de8d1b2fb25..23c5c354a85530976ad9dd565936287751470887 100644 (file)
@@ -98,6 +98,7 @@
 #include "couch_to_5k_face.h"
 #include "minute_repeater_decimal_face.h"
 #include "tuning_tones_face.h"
+#include "periodic_face.h"
 // New includes go above this line.
 
 #endif // MOVEMENT_FACES_H_
diff --git a/movement/watch_faces/complication/periodic_face.c b/movement/watch_faces/complication/periodic_face.c
new file mode 100644 (file)
index 0000000..6746c26
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 PrimmR
+ *
+ * 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 "periodic_face.h"
+
+void periodic_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(periodic_state_t));
+        memset(*context_ptr, 0, sizeof(periodic_state_t));
+    }
+}
+
+void periodic_face_activate(movement_settings_t *settings, void *context)
+{
+    (void)settings;
+    periodic_state_t *state = (periodic_state_t *)context;
+
+    state->atomic_num = 1;
+    state->mode = 0;
+    state->selection_index = 0;
+
+    movement_request_tick_frequency(2);
+}
+
+typedef struct
+{
+    char name[3];
+    uint16_t atomic_mass;
+    char group[3];
+} element;
+
+// Comments on the table denote symbols that cannot be displayed
+#define MAX_ELEMENT 118
+const element table[MAX_ELEMENT] = {
+    {"H ", 1, "  "},
+    {"He", 4, " 0"},
+    {"Li", 7, " 1"},
+    {"Be", 9, " 2"},
+    {"B ", 11, " 3"},
+    {"C ", 12, " 4"},
+    {"N ", 14, " 5"},
+    {"O ", 16, " 6"},
+    {"F ", 19, " 7"},
+    {"Ne", 20, " 0"},
+    {"Na", 23, " 1"},
+    {"Mg", 24, " 2"}, //
+    {"Al", 27, " 3"},
+    {"Si", 28, " 4"},
+    {"P ", 31, " 5"},
+    {"S ", 32, " 6"},
+    {"Cl", 355, " 7"},
+    {"Ar", 40, " 0"},
+    {"K ", 39, " 1"},
+    {"Ca", 40, " 2"},
+    {"Sc", 45, " T"},
+    {"Ti", 48, " T"},
+    {" W", 51, " T"}, // "V"
+    {"Cr", 52, " T"},
+    {"Mn", 55, " T"},
+    {"Fe", 56, " T"},
+    {"Co", 59, " T"},
+    {"Ni", 59, " T"},
+    {"Cu", 635, " T"},
+    {"Zn", 65, " T"},
+    {"Ga", 70, " 3"},
+    {"Ge", 73, " 4"},
+    {"As", 75, " 5"}, //
+    {"Se", 79, " 6"},
+    {"Br", 80, " 7"},
+    {"Kr", 84, " 0"},
+    {"Rb", 85, " 1"},
+    {"Sr", 88, " 2"},
+    {"Y ", 89, " T"},
+    {"Zr", 91, " T"},
+    {"Nb", 93, " T"},
+    {"Mo", 96, " T"},
+    {"Tc", 97, " T"},
+    {"Ru", 101, " T"},
+    {"Rh", 103, " T"},
+    {"Pd", 106, " T"},
+    {"Ag", 108, " T"}, //
+    {"Cd", 112, " T"},
+    {"In", 115, " 3"},
+    {"Sn", 119, " 4"},
+    {"Sb", 122, " 5"},
+    {"Te", 128, " 6"},
+    {"I ", 127, " 7"},
+    {"Xe", 131, " 0"},
+    {"CS", 133, " 1"},
+    {"Ba", 137, " 2"},
+    {"La", 139, "1a"}, // La
+    {"Ce", 140, "1a"},
+    {"Pr", 141, "1a"},
+    {"Nd", 144, "1a"},
+    {"Pm", 145, "1a"},
+    {"Sm", 150, "1a"},
+    {"Eu", 152, "1a"},
+    {"Gd", 157, "1a"},
+    {"Tb", 159, "1a"},
+    {"Dy", 163, "1a"}, // .5 Rounded up due to space constraints
+    {"Ho", 165, "1a"},
+    {"Er", 167, "1a"},
+    {"Tm", 169, "1a"},
+    {"Yb", 173, "1a"},
+    {"Lu", 175, "1a"},
+    {"Hf", 179, " T"},
+    {"Ta", 181, " T"},
+    {"W ", 184, " T"},
+    {"Re", 186, " T"},
+    {"OS", 190, " T"},
+    {"Ir", 192, " T"},
+    {"Pt", 195, " T"},
+    {"Au", 197, " T"},
+    {"Hg", 201, " T"}, //
+    {"Tl", 204, " 3"},
+    {"Pb", 207, " 4"},
+    {"Bi", 209, " 5"},
+    {"Po", 209, " 6"},
+    {"At", 210, " 7"},
+    {"Rn", 222, " 0"},
+    {"Fr", 223, " 1"},
+    {"Ra", 226, " 2"},
+    {"Ac", 227, "Ac"},
+    {"Th", 232, "Ac"},
+    {"Pa", 231, "Ac"},
+    {"U ", 238, "Ac"},
+    {"Np", 237, "Ac"}, //
+    {"Pu", 244, "Ac"},
+    {"Am", 243, "Ac"},
+    {"Cm", 247, "Ac"},
+    {"Bk", 247, "Ac"}, //
+    {"Cf", 251, "Ac"},
+    {"Es", 252, "Ac"}, //
+    {"Fm", 257, "Ac"},
+    {"Md", 258, "Ac"},
+    {"No", 259, "Ac"},
+    {"Lr", 262, "Ac"},
+    {"Rf", 267, " T"},
+    {"Db", 262, " T"},
+    {"Sg", 269, " T"}, //
+    {"Bh", 264, " T"},
+    {"Hs", 269, " T"},
+    {"Mt", 278, " T"},
+    {"Ds", 281, " T"}, //
+    {"Rg", 282, " T"}, //
+    {"Cn", 285, " T"},
+    {"Nh", 286, " 3"},
+    {"Fl", 289, " 4"},
+    {"Mc", 289, " 5"},
+    {"LW", 293, " 6"}, // Lv
+    {"Ts", 294, " 7"}, //
+    {"Og", 294, " 0"}, //
+};
+
+// Warning light for symbols that can't be displayed
+static void _warning(periodic_state_t *state)
+{
+    char second_char = table[state->atomic_num - 1].name[1];
+    if (second_char == 'p' || second_char == 'g' || second_char == 'y' || second_char == 's')
+    {
+        watch_set_indicator(WATCH_INDICATOR_BELL);
+    }
+    else
+    {
+        watch_clear_indicator(WATCH_INDICATOR_BELL);
+    }
+}
+
+// Regular mode display
+static void _periodic_face_update_lcd(periodic_state_t *state)
+{
+    // Colon as a decimal for Cl & Cu
+    if (state->atomic_num == 17 || state->atomic_num == 29)
+    {
+        watch_set_colon();
+    }
+    else
+    {
+        watch_clear_colon();
+    }
+
+    _warning(state);
+
+    char buf[11];
+    sprintf(buf, "%s%s%-3d%3d", table[state->atomic_num - 1].name, table[state->atomic_num - 1].group, table[state->atomic_num - 1].atomic_mass, state->atomic_num);
+    watch_display_string(buf, 0);
+}
+
+// Selection mode logic
+static void _periodic_face_selection_increment(periodic_state_t *state)
+{
+    uint8_t digit0 = (state->atomic_num / 100) % 10;
+    uint8_t digit1 = (state->atomic_num / 10) % 10;
+    uint8_t digit2 = (state->atomic_num) % 10;
+
+    // Increment the selected digit by 1
+    switch (state->selection_index)
+    {
+    case 0:
+        digit0 ^= 1;
+        break;
+    case 1:
+        if (digit0 == MAX_ELEMENT / 100 && digit1 == (MAX_ELEMENT / 10) % 10)
+            digit1 = 0;
+        else
+            digit1 = (digit1 + 1) % 10;
+        break;
+    case 2:
+        if (digit0 == MAX_ELEMENT / 100 && digit1 == (MAX_ELEMENT / 10) % 10 && digit2 == MAX_ELEMENT % 10)
+            digit2 = 0;
+        else
+            digit2 = (digit2 + 1) % 10;
+        break;
+    }
+
+    // Prevent 000
+    if (digit0 == 0 && digit1 == 0 && digit2 == 0) {
+        digit2 = 1;
+    }
+
+    // Prevent Overflow
+    if (digit0 == (MAX_ELEMENT / 100) % 10 && digit1 > (MAX_ELEMENT / 10) % 10)
+    {
+        digit2 = MAX_ELEMENT % 10;
+        digit1 = (MAX_ELEMENT / 10) % 10;
+    }
+
+    state->atomic_num = digit0 * 100 + digit1 * 10 + digit2;
+}
+
+// Selection mode display
+static void _periodic_face_selection(periodic_state_t *state, uint8_t subsec)
+{
+    uint8_t digit0 = (state->atomic_num / 100) % 10;
+    uint8_t digit1 = (state->atomic_num / 10) % 10;
+    uint8_t digit2 = (state->atomic_num) % 10;
+
+    watch_display_string("          ", 0);
+
+    char buf[2] = {'\0'};
+
+    buf[0] = (state->selection_index == 0 && subsec == 0) ? ' ' : digit0 + '0';
+    watch_display_string(buf, 5);
+
+    buf[0] = (state->selection_index == 1 && subsec == 0) ? ' ' : digit1 + '0';
+    watch_display_string(buf, 6);
+
+    buf[0] = (state->selection_index == 2 && subsec == 0) ? ' ' : digit2 + '0';
+    watch_display_string(buf, 7);
+
+    char buf2[3];
+    sprintf(buf2, "%s", table[state->atomic_num - 1].name);
+    watch_display_string(buf2, 0);
+
+    _warning(state);
+}
+
+bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, void *context)
+{
+    periodic_state_t *state = (periodic_state_t *)context;
+
+    switch (event.event_type)
+    {
+    case EVENT_ACTIVATE:
+        _periodic_face_update_lcd(state);
+        break;
+    case EVENT_TICK:
+        if (state->mode != 0)
+        {
+            _periodic_face_selection(state, event.subsecond % 2);
+        }
+        break;
+    case EVENT_LIGHT_BUTTON_UP:
+        // Only light LED when in regular mode
+        if (state->mode != MODE_VIEW)
+        {
+            state->selection_index = (state->selection_index + 1) % 3;
+            _periodic_face_selection(state, event.subsecond % 2);
+        }
+        break;
+    case EVENT_LIGHT_BUTTON_DOWN:
+        if (state->mode != MODE_SELECT)
+            movement_illuminate_led();
+        break;
+    case EVENT_ALARM_BUTTON_UP:
+        if (state->mode == MODE_VIEW)
+        {
+            state->atomic_num = (state->atomic_num % MAX_ELEMENT) + 1; // Wraps back to 1
+            _periodic_face_update_lcd(state);
+            if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+        }
+        else
+        {
+            _periodic_face_selection_increment(state);
+            _periodic_face_selection(state, event.subsecond % 2);
+        }
+        break;
+    case EVENT_ALARM_LONG_PRESS:
+        // Toggle between selection mode and regular
+        if (state->mode == MODE_VIEW)
+        {
+            state->mode = MODE_SELECT;
+            _periodic_face_selection(state, event.subsecond % 2);
+        }
+        else
+        {
+            state->mode = MODE_VIEW;
+            _periodic_face_update_lcd(state);
+        }
+            if (settings->bit.button_should_sound) watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
+
+        break;
+    case EVENT_TIMEOUT:
+        break;
+    case EVENT_LOW_ENERGY_UPDATE:
+        break;
+    default:
+        return movement_default_loop_handler(event, settings);
+    }
+
+    return true;
+}
+
+void periodic_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/periodic_face.h b/movement/watch_faces/complication/periodic_face.h
new file mode 100644 (file)
index 0000000..56476ca
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 PrimmR
+ *
+ * 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 PERIODIC_FACE_H_
+#define PERIODIC_FACE_H_
+
+#include "movement.h"
+
+/*
+ * Periodic Table Face
+ *
+ * Elements can be viewed sequentially with a short press of the alarm button,
+ * or the atomic number can be input directly after holding down the alarm button.
+ *
+ */
+
+#define MODE_VIEW 0
+#define MODE_SELECT 1
+
+typedef struct {
+    uint8_t atomic_num;
+    uint8_t mode;
+    uint8_t selection_index;
+} periodic_state_t;
+
+void periodic_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void periodic_face_activate(movement_settings_t *settings, void *context);
+bool periodic_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void periodic_face_resign(movement_settings_t *settings, void *context);
+
+#define periodic_face ((const watch_face_t){ \
+    periodic_face_setup, \
+    periodic_face_activate, \
+    periodic_face_loop, \
+    periodic_face_resign, \
+    NULL, \
+})
+
+#endif // PERIODIC_FACE_H_
+