From 0f9bb0bd37c8183cf8eb0f3385c831503d1f76bf Mon Sep 17 00:00:00 2001 From: Konrad Rieck Date: Fri, 11 Aug 2023 22:26:34 +0200 Subject: [PATCH 01/13] Fixed incorrect conversion from UNIX timestamp to watch_date_time. --- watch-library/shared/watch/watch_utility.c | 80 ++++++++++++++++++++-- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/watch-library/shared/watch/watch_utility.c b/watch-library/shared/watch/watch_utility.c index 9e524762..64b3bb79 100644 --- a/watch-library/shared/watch/watch_utility.c +++ b/watch-library/shared/watch/watch_utility.c @@ -102,13 +102,81 @@ uint16_t watch_utility_days_since_new_year(uint16_t year, uint8_t month, uint8_t return (is_leap(year) && (month > 2) ? 1 : 0) + DAYS_SO_FAR[month - 1] + day; } -uint32_t watch_utility_convert_to_unix_time(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, uint32_t utc_offset) { - uint32_t year_adj = year + 4800; - uint32_t leap_days = 1 + (year_adj / 4) - (year_adj / 100) + (year_adj / 400); - uint32_t days = 365 * year_adj + leap_days + watch_utility_days_since_new_year(year, month, day) - 1; - days -= 2472692; /* Adjust to Unix epoch. */ +// Function taken from `src/time/__year_to_secs.c` of musl libc +// https://musl.libc.org +static uint32_t __year_to_secs(uint32_t year, int *is_leap) +{ + if (year-2ULL <= 136) { + int y = year; + int leaps = (y-68)>>2; + if (!((y-68)&3)) { + leaps--; + if (is_leap) *is_leap = 1; + } else if (is_leap) *is_leap = 0; + return 31536000*(y-70) + 86400*leaps; + } - uint32_t timestamp = days * 86400; + int cycles, centuries, leaps, rem; + + if (!is_leap) is_leap = &(int){0}; + cycles = (year-100) / 400; + rem = (year-100) % 400; + if (rem < 0) { + cycles--; + rem += 400; + } + if (!rem) { + *is_leap = 1; + centuries = 0; + leaps = 0; + } else { + if (rem >= 200) { + if (rem >= 300) centuries = 3, rem -= 300; + else centuries = 2, rem -= 200; + } else { + if (rem >= 100) centuries = 1, rem -= 100; + else centuries = 0; + } + if (!rem) { + *is_leap = 0; + leaps = 0; + } else { + leaps = rem / 4U; + rem %= 4U; + *is_leap = !rem; + } + } + + leaps += 97*cycles + 24*centuries - *is_leap; + + return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400; +} + +// Function taken from `src/time/__month_to_secs.c` of musl libc +// https://musl.libc.org +static int __month_to_secs(int month, int is_leap) +{ + static const int secs_through_month[] = { + 0, 31*86400, 59*86400, 90*86400, + 120*86400, 151*86400, 181*86400, 212*86400, + 243*86400, 273*86400, 304*86400, 334*86400 }; + int t = secs_through_month[month]; + if (is_leap && month >= 2) t+=86400; + return t; +} + +// Function adapted from `src/time/__tm_to_secs.c` of musl libc +// https://musl.libc.org +uint32_t watch_utility_convert_to_unix_time(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, uint32_t utc_offset) { + int is_leap; + + // POSIX tm struct starts year at 1900 and month at 0 + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/time.h.html + uint32_t timestamp = __year_to_secs(year - 1900, &is_leap); + timestamp += __month_to_secs(month - 1, is_leap); + + // Regular conversion from musl libc + timestamp += (day - 1) * 86400; timestamp += hour * 3600; timestamp += minute * 60; timestamp += second; From a5abf7ff7a105ebfe31b804d1e951b91a6c0a050 Mon Sep 17 00:00:00 2001 From: Navaneeth Bhardwaj Date: Sun, 3 Sep 2023 00:17:45 +0530 Subject: [PATCH 02/13] Print memory percentages this gives better idea of memories used. --- make.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/make.mk b/make.mk index bf61708c..2870c4f3 100644 --- a/make.mk +++ b/make.mk @@ -62,6 +62,7 @@ CFLAGS += -MD -MP -MT $(BUILD)/$(*F).o -MF $(BUILD)/$(@F).d LDFLAGS += -mcpu=cortex-m0plus -mthumb LDFLAGS += -Wl,--gc-sections LDFLAGS += -Wl,--script=$(TOP)/watch-library/hardware/linker/saml22j18.ld +LDFLAGS += -Wl,--print-memory-usage LIBS += -lm From 9e88f37ced6a21740803f804c6528fc472ca3dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Waag=C3=B8?= Date: Sun, 19 Mar 2023 21:46:21 +0100 Subject: [PATCH 03/13] new face: Tuning tones Add a new face that plays out tones that can be used as a reference when tuning musical instruments. --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../complication/tuning_tones_face.c | 140 ++++++++++++++++++ .../complication/tuning_tones_face.h | 57 +++++++ 4 files changed, 199 insertions(+) create mode 100644 movement/watch_faces/complication/tuning_tones_face.c create mode 100644 movement/watch_faces/complication/tuning_tones_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index 625c7729..06e1725d 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -118,6 +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/tuning_tones_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. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index ff34c063..23c2613f 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -95,6 +95,7 @@ #include "flashlight_face.h" #include "decimal_time_face.h" #include "wyoscan_face.h" +#include "tuning_tones_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/complication/tuning_tones_face.c b/movement/watch_faces/complication/tuning_tones_face.c new file mode 100644 index 00000000..a139427a --- /dev/null +++ b/movement/watch_faces/complication/tuning_tones_face.c @@ -0,0 +1,140 @@ +/* + * MIT License + * + * Copyright (c) 2023 Per Waagø + * + * 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 +#include +#include "tuning_tones_face.h" + +/* + + This face plays a tone that can be used as a reference when tuning + musical instrument. + + - The alarm button (short press) starts and stops the tone + - The light button (short press) changes which note is played. The name + of the note is shown in the display. + +*/ + +typedef struct Note { + BuzzerNote note; + char * name; +} Note; + +static Note notes[] = { + { .note = BUZZER_NOTE_C5, .name = "C " }, + { .note = BUZZER_NOTE_C5SHARP_D5FLAT, .name = "Db" }, + { .note = BUZZER_NOTE_D5, .name = "D " }, + { .note = BUZZER_NOTE_D5SHARP_E5FLAT, .name = "Eb" }, + { .note = BUZZER_NOTE_E5, .name = "E " }, + { .note = BUZZER_NOTE_F5, .name = "F " }, + { .note = BUZZER_NOTE_F5SHARP_G5FLAT, .name = "Gb" }, + { .note = BUZZER_NOTE_G5, .name = "G " }, + { .note = BUZZER_NOTE_G5SHARP_A5FLAT, .name = "Ab" }, + { .note = BUZZER_NOTE_A5, .name = "A " }, + { .note = BUZZER_NOTE_A5SHARP_B5FLAT, .name = "Bb" }, + { .note = BUZZER_NOTE_B5, .name = "B " }, +}; + +static size_t note_count = sizeof notes / sizeof *notes; + +static void draw(tuning_tones_state_t *state) +{ + watch_display_string(notes[state->note_ind].name, 8); +} + +void tuning_tones_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { + (void) settings; + (void) watch_face_index; + if (*context_ptr == NULL) { + tuning_tones_state_t *state = malloc(sizeof *state); + memset(state, 0, sizeof *state); + state->note_ind = 9; + *context_ptr = state; + } +} + +void tuning_tones_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} + +static void update_buzzer(const tuning_tones_state_t *state) +{ + if (state->playing) { + watch_set_buzzer_off(); + watch_set_buzzer_period(NotePeriods[notes[state->note_ind].note]); + watch_set_buzzer_on(); + } +} + +bool tuning_tones_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + tuning_tones_state_t *state = (tuning_tones_state_t *)context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + draw(state); + break; + case EVENT_TICK: + break; + case EVENT_LIGHT_BUTTON_DOWN: + state->note_ind++; + if (state->note_ind == note_count) { + state->note_ind = 0; + } + update_buzzer(state); + draw(state); + break; + case EVENT_LIGHT_BUTTON_UP: + break; + case EVENT_ALARM_BUTTON_DOWN: + state->playing = !state->playing; + if (!state->playing) { + watch_set_buzzer_off(); + } else { + update_buzzer(state); + } + case EVENT_ALARM_BUTTON_UP: + break; + case EVENT_TIMEOUT: + movement_move_to_face(0); + break; + case EVENT_LOW_ENERGY_UPDATE: + break; + default: + return movement_default_loop_handler(event, settings); + } + + return !state->playing; +} + +void tuning_tones_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + tuning_tones_state_t *state = (tuning_tones_state_t *)context; + + if (state->playing) { + state->playing = false; + watch_set_buzzer_off(); + } +} diff --git a/movement/watch_faces/complication/tuning_tones_face.h b/movement/watch_faces/complication/tuning_tones_face.h new file mode 100644 index 00000000..d6e3495e --- /dev/null +++ b/movement/watch_faces/complication/tuning_tones_face.h @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2023 Per Waagø + * + * 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 TUNING_TONES_FACE_H_ +#define TUNING_TONES_FACE_H_ + +#include "movement.h" + +/* + * A DESCRIPTION OF YOUR WATCH FACE + * + * and a description of how use it + * + */ + +typedef struct { + // Anything you need to keep track of, put it here! + bool playing; + size_t note_ind; +} tuning_tones_state_t; + +void tuning_tones_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void tuning_tones_face_activate(movement_settings_t *settings, void *context); +bool tuning_tones_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void tuning_tones_face_resign(movement_settings_t *settings, void *context); + +#define tuning_tones_face ((const watch_face_t){ \ + tuning_tones_face_setup, \ + tuning_tones_face_activate, \ + tuning_tones_face_loop, \ + tuning_tones_face_resign, \ + NULL, \ +}) + +#endif // TUNING_TONES_FACE_H_ + From ebfeb1f21a290f91dc21b28a9eb707ebbaf5615c Mon Sep 17 00:00:00 2001 From: Hugo Chargois Date: Sat, 9 Sep 2023 02:22:08 +0200 Subject: [PATCH 04/13] Turn on the funky segment of pos 0 for char '@' --- watch-library/shared/watch/watch_private_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/watch-library/shared/watch/watch_private_display.c b/watch-library/shared/watch/watch_private_display.c index 245b20ed..c12957d9 100644 --- a/watch-library/shared/watch/watch_private_display.c +++ b/watch-library/shared/watch/watch_private_display.c @@ -93,7 +93,7 @@ void watch_display_character(uint8_t character, uint8_t position) { } if (character == 'T' && position == 1) watch_set_pixel(1, 12); // add descender - else if (position == 0 && (character == 'B' || character == 'D')) watch_set_pixel(0, 15); // add funky ninth segment + else if (position == 0 && (character == 'B' || character == 'D' || character == '@')) watch_set_pixel(0, 15); // add funky ninth segment else if (position == 1 && (character == 'B' || character == 'D' || character == '@')) watch_set_pixel(0, 12); // add funky ninth segment } From 2e364f4ef9c27424d909480a2150c24f6e649a02 Mon Sep 17 00:00:00 2001 From: Hugo Chargois Date: Sat, 16 Sep 2023 01:39:52 +0200 Subject: [PATCH 05/13] Add a volume slider in the simulator --- watch-library/simulator/shell.html | 48 ++++++++++++++++++-- watch-library/simulator/watch/watch_buzzer.c | 2 +- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/watch-library/simulator/shell.html b/watch-library/simulator/shell.html index 335b9534..c1162f7d 100644 --- a/watch-library/simulator/shell.html +++ b/watch-library/simulator/shell.html @@ -882,10 +882,22 @@ -
- - - Original F-91W SVG is © 2020 Alexis Philip,
used here under the terms of the MIT license.
+ + + + + + +
+ + + + Original F-91W SVG is © 2020 Alexis Philip,
used here under the terms of the MIT license. +
+ +
@@ -981,6 +993,34 @@ ); } toggleSkin(); + + // emulator runs on localhost:8000 which could very well be used by other + // things, so we'll scope our localStorage keys with a prefix + const localStoragePrefix = "sensorwatch_"; + function setLocalPref(key, val) { + localStorage.setItem(localStoragePrefix+key, val); + } + function getLocalPref(key, dfault) { + let pref = localStorage.getItem(localStoragePrefix+key); + if (pref === null) return dfault; + return pref; + } + + volumeGain = 0.1; + function setVolume(vol) { + setLocalPref("volume", vol); + volumeGain = Math.pow(100, (vol / 100) - 1) - 0.01; + } + + function loadPrefs() { + let vol = +getLocalPref("volume", "50"); + if (isNaN(vol) || vol < 0 || vol > 100) { + vol = 50; + } + document.getElementById("volume").value = vol; + setVolume(vol); + } + loadPrefs(); {{{ SCRIPT }}} diff --git a/watch-library/simulator/watch/watch_buzzer.c b/watch-library/simulator/watch/watch_buzzer.c index 68d9a139..211235df 100644 --- a/watch-library/simulator/watch/watch_buzzer.c +++ b/watch-library/simulator/watch/watch_buzzer.c @@ -152,7 +152,7 @@ void watch_set_buzzer_on(void) { } audioContext._oscillator.frequency.value = 1e6/$0; - audioContext._gain.gain.value = 1; + audioContext._gain.gain.value = volumeGain; }, buzzer_period); } From baadb0b43f0fcd5df09c511142f8a320df34f643 Mon Sep 17 00:00:00 2001 From: Hugo Chargois Date: Sat, 16 Sep 2023 02:39:00 +0200 Subject: [PATCH 06/13] Save the selected skin of the simulator in local storage --- watch-library/simulator/shell.html | 32 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/watch-library/simulator/shell.html b/watch-library/simulator/shell.html index c1162f7d..583829cc 100644 --- a/watch-library/simulator/shell.html +++ b/watch-library/simulator/shell.html @@ -885,8 +885,8 @@
- - + + Original F-91W SVG is © 2020 Alexis Philip,
used here under the terms of the MIT license. @@ -979,20 +979,17 @@ } } - function toggleSkin() { - var isBlack = document.getElementById('f91w').checked; - Array.from(document.getElementsByClassName("f91w")).forEach( - function(element, index, array) { - element.setAttribute('style', 'display:' + (isBlack ? 'inline':'none') + ';'); + const validSkins = ["f91w", "a158wea9"]; + function setSkin(chosenSkin) { + setLocalPref("skin", chosenSkin); + validSkins.forEach(function(skin) { + Array.from(document.getElementsByClassName(skin)).forEach( + function(element) { + element.setAttribute('style', 'display:' + (skin == chosenSkin ? 'inline':'none') + ';'); } - ); - Array.from(document.getElementsByClassName("a158wea9")).forEach( - function(element, index, array) { - element.setAttribute('style', 'display:' + (isBlack ? 'none':'inline') + ';'); - } - ); + ); + }); } - toggleSkin(); // emulator runs on localhost:8000 which could very well be used by other // things, so we'll scope our localStorage keys with a prefix @@ -1019,6 +1016,13 @@ } document.getElementById("volume").value = vol; setVolume(vol); + + let skin = getLocalPref("skin", "f91w"); + if (!validSkins.includes(skin)) { + skin = "f91w"; + } + document.getElementById(skin).checked = true; + setSkin(skin); } loadPrefs(); From c28ba6ef0bfc8f76c156b92b5c0acfdf1df13fcf Mon Sep 17 00:00:00 2001 From: Wesley Aptekar-Cassels Date: Thu, 28 Sep 2023 18:29:32 -0400 Subject: [PATCH 07/13] Don't allow building without setting board color. Fixes: #288 --- make.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/make.mk b/make.mk index a83108bd..31189c8f 100644 --- a/make.mk +++ b/make.mk @@ -207,6 +207,10 @@ ifeq ($(LED), BLUE) CFLAGS += -DWATCH_IS_BLUE_BOARD endif +ifndef COLOR +$(error Set the COLOR variable to RED, BLUE, or GREEN depending on what board you have.) +endif + ifeq ($(COLOR), BLUE) CFLAGS += -DWATCH_IS_BLUE_BOARD endif From 7d353bba1c171e7885e69e88f314ca0dbe521863 Mon Sep 17 00:00:00 2001 From: Wesley Aptekar-Cassels Date: Thu, 5 Oct 2023 12:42:43 -0400 Subject: [PATCH 08/13] Set default board color for GH workflow. I've chosen blue arbitrarily. --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b150afb1..6b4fc793 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,9 @@ on: branches-ignore: - gh-pages +env: + COLOR: BLUE + jobs: build: container: From ad846f50602d3824097e9fbbbac4dcd88d9a8a3f Mon Sep 17 00:00:00 2001 From: LtKeks Date: Sun, 15 Oct 2023 17:35:36 +0200 Subject: [PATCH 09/13] Update timer_face.c Corrects the data type of the standard values in order to be able to configure seconds as well. --- movement/watch_faces/complication/timer_face.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/movement/watch_faces/complication/timer_face.c b/movement/watch_faces/complication/timer_face.c index 70f250a5..8bf7cf99 100644 --- a/movement/watch_faces/complication/timer_face.c +++ b/movement/watch_faces/complication/timer_face.c @@ -30,7 +30,7 @@ #include "watch.h" #include "watch_utility.h" -static const uint16_t _default_timer_values[] = {0x200, 0x500, 0xA00, 0x1400, 0x2D02}; // default timers: 2 min, 5 min, 10 min, 20 min, 2 h 45 min +static const uint32_t _default_timer_values[] = {0x000200, 0x000500, 0x000A00, 0x001400, 0x002D02}; // default timers: 2 min, 5 min, 10 min, 20 min, 2 h 45 min // sound sequence for a single beeping sequence static const int8_t _sound_seq_beep[] = {BUZZER_NOTE_C8, 3, BUZZER_NOTE_REST, 3, -2, 2, BUZZER_NOTE_C8, 5, BUZZER_NOTE_REST, 25, 0}; @@ -199,7 +199,7 @@ void timer_face_setup(movement_settings_t *settings, uint8_t watch_face_index, v timer_state_t *state = (timer_state_t *)*context_ptr; memset(*context_ptr, 0, sizeof(timer_state_t)); state->watch_face_index = watch_face_index; - for (uint8_t i = 0; i < sizeof(_default_timer_values) / sizeof(uint16_t); i++) { + for (uint8_t i = 0; i < sizeof(_default_timer_values) / sizeof(uint32_t); i++) { state->timers[i].value = _default_timer_values[i]; } } From 67be6affd3367c4f6c302a57fdb35fc2bda50ce6 Mon Sep 17 00:00:00 2001 From: joeycastillo Date: Thu, 19 Oct 2023 15:49:27 -0400 Subject: [PATCH 10/13] new standard firmware for october boards --- .../movement_bulk_installer/standard-red.uf2 | Bin 217088 -> 217088 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/utils/movement_bulk_installer/standard-red.uf2 b/utils/movement_bulk_installer/standard-red.uf2 index 385c611a3569c8bd5a12d5b25c84de2e3ce565c9..b74e539cf33b46fdb4403ab355ad14396fc3619d 100644 GIT binary patch delta 2120 zcmZuyeQXJA#NI}nW4?cy4p?CmTB}urA z>DcD0dc%akzttJ><3HH6Yo;Kf_G`nYEnnHS?Y3NS&?wM;c_n;Up7-nQRu+81UI=iB z3_FqKqY`A;m8>h?3Ly1&11Jv_qBOB+*!OPngHHlcd)N~D}av^Q^)V-`zN zOar?-zo23_&xdvf~pV#{7a7}nWitHJ)@7zABvJz+LSM6K7# zNdb~}SRkW6>}!k!V;Q!|kRQbMFske6DUKqn=;R39x0+$s4c=cRNzn{@kEBY;OVSOJ zIB7FUZy6b;n;AAn>j48Li(OZC72zbVC@V!+=WfdDzML)e&m5nM9`BukxjWV!9efS| z5a2Xzv$W@aBg3CPEWrY}R<6cd#c&~s@BK*D|lW%+S=0)nVotkDdQ zalzKa5S@c+hTW;`FToL9P+l&<&AB*Aus!cmF&tR$KG@$n2CbV^83OH|vEf*u{O)l@ za^jPNJE@~cJ$l)IhOLc#O(7Wk2HygSPU_bUlW*bG5FC;+)CuhPa6xGKC{dmdjZa{B z=y8jghRzxYqe6uS!`}@2EO%biUNTSl18^^J7uWM{nx#Z3-zRAMU*2|YOP0G-Gj^$m zQ@L5tlnnS4$%TQckdYq*a`3uw>phDV7;;Kyzf}fY81B$4ln-|;FSz2a#X=~9YT%}I zR+HYS)!F_H=L~yY`JD@^8Z{jP-lTKxPLKdET5R7c1S*qdYH-XE!62_XB3uwQPDMFZ zY%ZbsId7gAE~fn{(={tiJOwl-LG{-65`^rFyEtc&o z>km;2E%xoS;NA3QDZ#vIu4yXMP=BGvnOr-4Eq!;--Q5;TSO_+<+O5o$V-SC%tdwK3 z{iRF};~lFxSJSt1Z*t2|*J^rZLrn#*sJ4;aXEMD#hr6#WNDzzjgjK9GxUmz@C<}G=)I?soshfm6J^GY4Zaal;hXfV;$hed%~U6g{fTs+B-i8#<$X8G3in@H z`1GZ<5Wc%11P+{ixof-r57Y}AppGaVmFPS4h!NW_K_^kbk?dEZ0_~$TP{lNYqIMvg zqY#T-&a(Ik%HlOcr^0_JDry6!%^S+w?8pjUObkVCrH1w++yz6C9Yh^}Rpu(OD$Vp%EbPQ2KhRND{TM@vD>%?j`to;}wEGK=4fzfH=_IwPhBw->N`B{oQXP+mK-SmG6#rRMnE!nh;mG+0AfSf+P znk%=cy=w}9Pw&_#L5NPwfPhDpAWXMzlU%Oiv>!U7|L};~2_r|3drh3iwi-_-5hxy* z6t!1O!TU}km&Z;s=pkje6Ff0dd(jlVTO#MwPx-dfeeYlOTJDdhD214rEiWMwQ+ysiP&DXX2wy1V4143Wh*$I z6qBWYUXUN5=BqiLf6>y}^Yqr~i!d0A>+BcS2Sfr(%1SjhIN#6(u>Zx?$`aUkZam)} z#i+shO>OYiu5+VbAj~H%2^{lUJk7clUoFLBm?BWJ;F0*dX9GA9KAUX zK~5FO<*+hPi&fE&Gi_~!zU2j-iB^$M0zpgYZK2l|>^_}kvsiyMB^OLPg-L&LxOl3k zHo>=6t24Its4z9{w9ks#o>iOAKC81@4p4z&VOSShnHrHPx!l*>+3mpZ;H2^e5)ssW^)$tOX8C@t1ZpWQ(8LPZVA&I;ADB! zHVsP7DZdK7B`VPE?C)v*bk<}&YZIoQb>^(>WW}9Zv%97Llcw}R{rjoqdz3-|`2_^4 zG;6^;Lg_p%{m6@P9-sQoF+VOH^K*};?e|mfPtr;(tyJ?C@AxKh+cUI&PV4H@-@SZI F=syN0JAD8E delta 2176 zcmYjS4{RG}6@TB|*%zm=@-m$~$vwowv^dtwD!~TXAcTj_d10ZjZ&b$)uURI&PAxac@tk z;i3hR|0z@ae=oVUS1nE;+V4%b_Um(7d+vy?e$%C$KNsIE&iEiik~%$K^k=&O1o>zg z5kjw-p8hpq2noPzU$^-_agd066XK(;?)DxrZPOd_g)&mNe{zZR1qS`uh0h$X1fek3 z*^fsK81(p(G$4(D-N1GYm&kV@Jhl5jlKmXy*uZ8pU(lkT{}2nLmwbb8rj z!wrIv(y3r+S6(b9 z&9$***+{W0Jhgu^wSUVboV`yLDoE`;bXut z@t8OU5>ad#wc;a;)TIs~FDW|hk%ubC%nb!~kgi^1Tx*iKM;0YkFbPF9J*E z{aOE985nfW3SOs|?7O?sxw?LJMu6IFF* zAen_mV0_q?U<{R)LV7tJ+>Q5W{lX^alklSa+oojVM-0A{3zIRKk}labtxC5)W*) zscI8&qt<8T3)Ms-f0q}lNqg|3?qFQA+kp%6N)+h_^(~3rn_r(5Af3I}Sb<-lx0vN; zak-mi7QyP|-dfVTb;5K|@-{q&O>q^3V%E2#ayt<_pcfp>Nxa+t$&n09jwj%q8mweV z{uh+zb0zv^dA^pkhMqtXxq`PZ8;5n!jv2P;b(DROE79MPty&`1FyQNRwsw4^?l}97 zlg!AOlC$4k;p|M_7 zR40s-B?VCSsdz60(!91{@xe9831w|)J#IdMU@rBMW%>}(L}^Q&$j7j(y!Hn2-eri0 zd^3ZX|Uw^Wj5KeYRBD?|)QTxx10<+c#tjgC~Z!4yUu} zITuvm3_6O#j^IE$Ubs23*UemJaR4gz)F=@^s!|O42U{A=@wv*kX2ru;Z3S?D!8YhC zc<1qH7))mk`nr7!C&mSNv5~Zf|7LLD`G>!Ys-$gVGzwqaG%@@I1bW^Uz&oR53v{_2 zve`sh#`^51n4Is*touhDqi@fyJ-|U^@P`SnA`R=b3Eu_Yx8Ni3XMJs#8w8Py5P40&rSUk4DQv3$q5TvP+Vj5sm$B))O*l znN3<=%Zsjzj~fqfSQTG2nYbU1H!4%^^(k&LLOP+pu pUzp7A3o}Owo&ju?$MDO}zum~%toIO(&Tr$F!SBrcjUd|;`#;2mQG@^h From 9c895817a07cfaae6e1d27fe8e84fd4a1926e960 Mon Sep 17 00:00:00 2001 From: Brian Blakley Date: Tue, 24 Oct 2023 18:20:54 -0400 Subject: [PATCH 11/13] Add minute repeater decimal face --- movement/make/Makefile | 1 + movement/movement_faces.h | 1 + .../clock/minute_repeater_decimal_face.c | 238 ++++++++++++++++++ .../clock/minute_repeater_decimal_face.h | 84 +++++++ 4 files changed, 324 insertions(+) create mode 100644 movement/watch_faces/clock/minute_repeater_decimal_face.c create mode 100644 movement/watch_faces/clock/minute_repeater_decimal_face.h diff --git a/movement/make/Makefile b/movement/make/Makefile index 625c7729..64c0364f 100644 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -118,6 +118,7 @@ SRCS += \ ../watch_faces/complication/flashlight_face.c \ ../watch_faces/clock/decimal_time_face.c \ ../watch_faces/clock/wyoscan_face.c \ + ../watch_faces/clock/minute_repeater_decimal_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. diff --git a/movement/movement_faces.h b/movement/movement_faces.h index ff34c063..8ece8b8d 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -95,6 +95,7 @@ #include "flashlight_face.h" #include "decimal_time_face.h" #include "wyoscan_face.h" +#include "minute_repeater_decimal_face.h" // New includes go above this line. #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/clock/minute_repeater_decimal_face.c b/movement/watch_faces/clock/minute_repeater_decimal_face.c new file mode 100644 index 00000000..2cedc307 --- /dev/null +++ b/movement/watch_faces/clock/minute_repeater_decimal_face.c @@ -0,0 +1,238 @@ +/* + * MIT License + * + * Copyright (c) 2023 Jonas Termeau - original repetition_minute_face + * Copyright (c) 2023 Brian Blakley - modified minute_repeater_decimal_face + * + * 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. + */ + +/* + * This face, minute_repeater_decimal_face, is a modification of the original + * repetition_minute_face by Jonas Termeau. + * + * This version was created by BrianBinFL to use a decimal minute repeater pattern + * (hours, tens, and minutes) instead of the traditional pattern (hours, quarters, + * minutes). + * + * Also 500ms delays were added after the hours segment and after the tens segment + * to make it easier for the user to realize that the counting for the current + * segment has ended. + * + */ + +#include +#include "minute_repeater_decimal_face.h" +#include "watch.h" +#include "watch_utility.h" +#include "watch_private_display.h" + +void mrd_play_hour_chime(void) { + watch_buzzer_play_note(BUZZER_NOTE_C6, 75); + watch_buzzer_play_note(BUZZER_NOTE_REST, 500); +} + +void mrd_play_tens_chime(void) { + watch_buzzer_play_note(BUZZER_NOTE_E6, 75); + watch_buzzer_play_note(BUZZER_NOTE_REST, 150); + watch_buzzer_play_note(BUZZER_NOTE_C6, 75); + watch_buzzer_play_note(BUZZER_NOTE_REST, 750); +} + +void mrd_play_minute_chime(void) { + watch_buzzer_play_note(BUZZER_NOTE_E6, 75); + watch_buzzer_play_note(BUZZER_NOTE_REST, 500); +} + +static void _update_alarm_indicator(bool settings_alarm_enabled, minute_repeater_decimal_state_t *state) { + state->alarm_enabled = settings_alarm_enabled; + if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL); + else watch_clear_indicator(WATCH_INDICATOR_SIGNAL); +} + +void minute_repeater_decimal_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(minute_repeater_decimal_state_t)); + minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)*context_ptr; + state->signal_enabled = false; + state->watch_face_index = watch_face_index; + } +} + +void minute_repeater_decimal_face_activate(movement_settings_t *settings, void *context) { + minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context; + + if (watch_tick_animation_is_running()) watch_stop_tick_animation(); + + if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H); + + // handle chime indicator + if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL); + else watch_clear_indicator(WATCH_INDICATOR_BELL); + + // show alarm indicator if there is an active alarm + _update_alarm_indicator(settings->bit.alarm_enabled, state); + + watch_set_colon(); + + // this ensures that none of the timestamp fields will match, so we can re-render them all. + state->previous_date_time = 0xFFFFFFFF; +} + +bool minute_repeater_decimal_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context; + char buf[11]; + uint8_t pos; + + watch_date_time date_time; + uint32_t previous_date_time; + switch (event.event_type) { + case EVENT_ACTIVATE: + case EVENT_TICK: + case EVENT_LOW_ENERGY_UPDATE: + date_time = watch_rtc_get_date_time(); + previous_date_time = state->previous_date_time; + state->previous_date_time = date_time.reg; + + // check the battery voltage once a day... + if (date_time.unit.day != state->last_battery_check) { + state->last_battery_check = date_time.unit.day; + watch_enable_adc(); + uint16_t voltage = watch_get_vcc_voltage(); + watch_disable_adc(); + // 2.2 volts will happen when the battery has maybe 5-10% remaining? + // we can refine this later. + state->battery_low = (voltage < 2200); + } + + // ...and set the LAP indicator if low. + if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP); + + if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) { + // everything before seconds is the same, don't waste cycles setting those segments. + watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8); + watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9); + break; + } else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) { + // everything before minutes is the same. + pos = 6; + sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second); + } else { + // other stuff changed; let's do it all. + if (!settings->bit.clock_mode_24h) { + // if we are in 12 hour mode, do some cleanup. + if (date_time.unit.hour < 12) { + watch_clear_indicator(WATCH_INDICATOR_PM); + } else { + watch_set_indicator(WATCH_INDICATOR_PM); + } + date_time.unit.hour %= 12; + if (date_time.unit.hour == 0) date_time.unit.hour = 12; + } + pos = 0; + if (event.event_type == EVENT_LOW_ENERGY_UPDATE) { + if (!watch_tick_animation_is_running()) watch_start_tick_animation(500); + sprintf(buf, "%s%2d%2d%02d ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute); + } else { + sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); + } + } + watch_display_string(buf, pos); + // handle alarm indicator + if (state->alarm_enabled != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.alarm_enabled, state); + break; + case EVENT_ALARM_LONG_PRESS: + state->signal_enabled = !state->signal_enabled; + if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL); + else watch_clear_indicator(WATCH_INDICATOR_BELL); + break; + case EVENT_BACKGROUND_TASK: + movement_play_signal(); + break; + case EVENT_LIGHT_LONG_UP: + /* + * Howdy neighbors, this is the actual complication. Like an actual + * (very expensive) watch with a repetition minute complication it's + * boring at 00:00 or 1:00 and very quite musical at 23:59 or 12:59. + */ + + date_time = watch_rtc_get_date_time(); + + + int hours = date_time.unit.hour; + int tens = date_time.unit.minute / 10; + int minutes = date_time.unit.minute % 10; + + // chiming hours + if (!settings->bit.clock_mode_24h) { + hours = date_time.unit.hour % 12; + if (hours == 0) hours = 12; + } + if (hours > 0) { + int count = 0; + for(count = hours; count > 0; --count) { + mrd_play_hour_chime(); + } + // do a little pause before proceeding to tens + watch_buzzer_play_note(BUZZER_NOTE_REST, 500); + } + + // chiming tens (if needed) + if (tens > 0) { + int count = 0; + for(count = tens; count > 0; --count) { + mrd_play_tens_chime(); + } + // do a little pause before proceeding to minutes + watch_buzzer_play_note(BUZZER_NOTE_REST, 500); + } + + // chiming minutes (if needed) + if (minutes > 0) { + int count = 0; + for(count = minutes; count > 0; --count) { + mrd_play_minute_chime(); + } + } + + break; + default: + return movement_default_loop_handler(event, settings); + } + + return true; +} + +void minute_repeater_decimal_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} + +bool minute_repeater_decimal_face_wants_background_task(movement_settings_t *settings, void *context) { + (void) settings; + minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context; + if (!state->signal_enabled) return false; + + watch_date_time date_time = watch_rtc_get_date_time(); + + return date_time.unit.minute == 0; +} diff --git a/movement/watch_faces/clock/minute_repeater_decimal_face.h b/movement/watch_faces/clock/minute_repeater_decimal_face.h new file mode 100644 index 00000000..4bc9a8b6 --- /dev/null +++ b/movement/watch_faces/clock/minute_repeater_decimal_face.h @@ -0,0 +1,84 @@ +/* + * MIT License + * + * Copyright (c) 2023 Jonas Termeau - original repetition_minute_face + * Copyright (c) 2023 Brian Blakley - modified minute_repeater_decimal_face + * + * 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 MINUTE_REPEATER_DECIMAL_FACE_H_ +#define MINUTE_REPEATER_DECIMAL_FACE_H_ + +#include "movement.h" + +/* + * A hopefully useful complication for friendly neighbors in the dark + * + * Originating from 1676 from reverend and mechanician Edward Barlow, and + * perfected in 1820 by neighbor Abraham Breguet, a minute repeater or + * "repetition minute" is a complication in a mechanical watch or clock that + * chimes the hours and often minutes at the press of a button. There are many + * types of repeater, from the simple repeater which merely strikes the number + * of hours, to the minute repeater which chimes the time down to the minute, + * using separate tones for hours, decimal hours, and minutes. They originated + * before widespread artificial illumination, to allow the time to be determined + * in the dark, and were also used by the visually impaired. + * + * + * How to use it : + * + * Long press the light button to get an auditive reading of the time like so : + * 0..23 (1..12 if 24-hours format isn't enabled) low beep(s) for the hours + * 0..9 low-high couple pitched beeps for the tens of minutes + * 0..9 high pitched beep(s) for the remaining minutes (ones of minutes) + * + * Prerequisite : a watch with a working buzzer + * + * ~ Only in the darkness can you see the stars. - Martin Luther King ~ + * + */ + +typedef struct { + uint32_t previous_date_time; + uint8_t last_battery_check; + uint8_t watch_face_index; + bool signal_enabled; + bool battery_low; + bool alarm_enabled; +} minute_repeater_decimal_state_t; + +void mrd_play_hour_chime(void); +void mrd_play_tens_chime(void); +void mrd_play_minute_chime(void); +void minute_repeater_decimal_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void minute_repeater_decimal_face_activate(movement_settings_t *settings, void *context); +bool minute_repeater_decimal_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void minute_repeater_decimal_face_resign(movement_settings_t *settings, void *context); +bool minute_repeater_decimal_face_wants_background_task(movement_settings_t *settings, void *context); + +#define minute_repeater_decimal_face ((const watch_face_t){ \ + minute_repeater_decimal_face_setup, \ + minute_repeater_decimal_face_activate, \ + minute_repeater_decimal_face_loop, \ + minute_repeater_decimal_face_resign, \ + minute_repeater_decimal_face_wants_background_task, \ +}) + +#endif // MINUTE_REPEATER_DECIMAL_FACE_H_ From 2e9ea8c36f1af70dfaa8a428a31afe961c48bbef Mon Sep 17 00:00:00 2001 From: Hugo Chargois Date: Sat, 18 Nov 2023 00:28:54 +0100 Subject: [PATCH 12/13] Improve simulator page design --- watch-library/simulator/shell.html | 66 ++++++++++++++++++------------ 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/watch-library/simulator/shell.html b/watch-library/simulator/shell.html index 583829cc..c8da063e 100644 --- a/watch-library/simulator/shell.html +++ b/watch-library/simulator/shell.html @@ -37,12 +37,13 @@ .highlight { fill: #fff !important; } #skinselect label { display: inline-block; - padding: 8px; + padding: 4px; background-color: black; color: white; border-radius: 8px; border: 2px solid #0e57a9; outline: 4px solid black; + margin: 4px; cursor: pointer; } #skinselect #a158wea-label { @@ -50,13 +51,16 @@ color: black; border-color: black; outline-color: #b68855ff; - + } + h2 { + margin: 8px 0; + font-size: 1em; } -
+

Sensor Watch Emulator

@@ -882,30 +886,40 @@ - - - - - - -
- - - - Original F-91W SVG is © 2020 Alexis Philip,
used here under the terms of the MIT license. -
- -
-
- -
- - -
- +
+

Skin

+
+ + +
+ +

Volume

+
+ +
+ +

Location

+
+ +
+
+ +
+ +
+ + +
+
+ +

+ Original F-91W SVG is © 2020 Alexis Philip, used here + under the terms of the MIT license. +

+