Periodic Table Face

This commit is contained in:
PrimmR 2023-11-24 18:58:07 +00:00
parent 3487d742f1
commit 7eb725a5f6
4 changed files with 420 additions and 0 deletions

View 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.

View 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_

View file

@ -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.
}

View file

@ -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_