From b8de35658ffd78ad8b22f91ccbbd3d63663afda9 Mon Sep 17 00:00:00 2001 From: Alexsander Akers Date: Tue, 25 Jan 2022 15:03:22 -0500 Subject: Sensor Watch Simulator (#35) * Put something on screen * Use the 32bit watch_date_time repr to pass from JS * Implement periodic callbacks * Clear display on enabling * Hook up watch_set_led_color() to SVG (green-only) * Make debug output full-width * Remove default Emscripten canvas * Implement sleep and button clicks * Fix time zone conversion bug in beats-time app * Clean up warnings * Fix pin levels * Set time zone to browser value (if available) * Add basic backup data saving * Silence format specifier warnings in both targets * Remove unnecessary, copied files * Use RTC pointer to clear callbacks (if available) * Use preprocessor define to avoid hardcoding MOVEMENT_NUM_FACES * Change each face to const preprocessor definition * Remove Intl.DateTimeFormat usage * Update shell.html title, header * Add touch start/end event handlers on SVG buttons * Update shell.html * Update folder structure (shared, simulator, hardware under watch-library) * Tease out shared components from watch_slcd * Clean up simulator watch_slcd.c inline JS calls * Fix missing newlines at end of file * Add simulator warnings (except format, unused-paremter) * Implement remaining watch_rtc functions * Fix button bug on mouse down then drag out * Implement remaining watch_slcd functions * Link keyboard events to buttons (for keys A, L, M) * Rewrite event handling (mouse, touch, keyboard) in C * Set explicit text UTF-8 charset in shell.html * Address PR comments * Remove unused directories from include paths --- watch-library/simulator/watch/watch_rtc.c | 223 ++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 watch-library/simulator/watch/watch_rtc.c (limited to 'watch-library/simulator/watch/watch_rtc.c') diff --git a/watch-library/simulator/watch/watch_rtc.c b/watch-library/simulator/watch/watch_rtc.c new file mode 100644 index 00000000..573c0ff2 --- /dev/null +++ b/watch-library/simulator/watch/watch_rtc.c @@ -0,0 +1,223 @@ +/* + * MIT License + * + * Copyright (c) 2020 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 "watch_rtc.h" + +#include +#include + +static double time_offset = 0; +static long tick_callbacks[8]; + +static long alarm_interval_id; +static long alarm_timeout_id; +static double alarm_interval; +ext_irq_cb_t alarm_callback; +ext_irq_cb_t btn_alarm_callback; +ext_irq_cb_t a2_callback; +ext_irq_cb_t a4_callback; + +bool _watch_rtc_is_enabled(void) { + return true; +} + +void _watch_rtc_init(void) { +} + +void watch_rtc_set_date_time(watch_date_time date_time) { + time_offset = EM_ASM_DOUBLE({ + const year = 2020 + (($0 >> 26) & 0x3f); + const month = ($0 >> 22) & 0xf; + const day = ($0 >> 17) & 0x1f; + const hour = ($0 >> 12) & 0x1f; + const minute = ($0 >> 6) & 0x3f; + const second = $0 & 0x3f; + const date = new Date(year, month - 1, day, hour, minute, second); + return date - Date.now(); + }, date_time.reg); +} + +watch_date_time watch_rtc_get_date_time(void) { + watch_date_time retval; + retval.reg = EM_ASM_INT({ + const date = new Date(Date.now() + $0); + return date.getSeconds() | + (date.getMinutes() << 6) | + (date.getHours() << 12) | + (date.getDate() << 17) | + ((date.getMonth() + 1) << 22) | + ((date.getFullYear() - 2020) << 26); + }, time_offset); + return retval; +} + +void watch_rtc_register_tick_callback(ext_irq_cb_t callback) { + watch_rtc_register_periodic_callback(callback, 1); +} + +void watch_rtc_disable_tick_callback(void) { + watch_rtc_disable_periodic_callback(1); +} + +static void watch_invoke_periodic_callback(void *userData) { + ext_irq_cb_t callback = userData; + callback(); + + void resume_main_loop(void); + resume_main_loop(); +} + +void watch_rtc_register_periodic_callback(ext_irq_cb_t callback, uint8_t frequency) { + // we told them, it has to be a power of 2. + if (__builtin_popcount(frequency) != 1) return; + + // this left-justifies the period in a 32-bit integer. + uint32_t tmp = frequency << 24; + // now we can count the leading zeroes to get the value we need. + // 0x01 (1 Hz) will have 7 leading zeros for PER7. 0xF0 (128 Hz) will have no leading zeroes for PER0. + uint8_t per_n = __builtin_clz(tmp); + + // this also maps nicely to an index for our list of tick callbacks. + double interval = 1000 / frequency; // in msec + tick_callbacks[per_n] = emscripten_set_interval(watch_invoke_periodic_callback, interval, (void *)callback); +} + +void watch_rtc_disable_periodic_callback(uint8_t frequency) { + if (__builtin_popcount(frequency) != 1) return; + uint8_t per_n = __builtin_clz(frequency << 24); + emscripten_clear_interval(tick_callbacks[per_n]); + tick_callbacks[per_n] = 0; +} + +void watch_rtc_disable_all_periodic_callbacks(void) { + for (int i = 0; i < 8; i++) { + if (tick_callbacks[i] != 0) { + emscripten_clear_interval(tick_callbacks[i]); + tick_callbacks[i] = 0; + } + } +} + +static void watch_invoke_alarm_interval_callback(void *userData) { + (void)userData; + if (alarm_callback) alarm_callback(); +} + +static void watch_invoke_alarm_callback(void *userData) { + (void)userData; + if (alarm_callback) alarm_callback(); + alarm_interval_id = emscripten_set_interval(watch_invoke_alarm_interval_callback, alarm_interval, NULL); +} + +void watch_rtc_register_alarm_callback(ext_irq_cb_t callback, watch_date_time alarm_time, watch_rtc_alarm_match mask) { + watch_rtc_disable_alarm_callback(); + + switch (mask) { + case ALARM_MATCH_DISABLED: + return; + case ALARM_MATCH_SS: + alarm_interval = 60 * 1000; + break; + case ALARM_MATCH_MMSS: + alarm_interval = 60 * 60 * 1000; + break; + case ALARM_MATCH_HHMMSS: + alarm_interval = 60 * 60 * 60 * 1000; + break; + } + + double timeout = EM_ASM_DOUBLE({ + const now = Date.now(); + const date = new Date(now + $0); + + const hour = ($1 >> 12) & 0x1f; + const minute = ($1 >> 6) & 0x3f; + const second = $1 & 0x3f; + + if ($2 == 1) { // SS + if (second < date.getSeconds()) date.setMinutes(date.getMinutes() + 1); + date.setSeconds(second); + } else if ($2 == 2) { // MMSS + if (second < date.getSeconds()) date.setMinutes(date.getMinutes() + 1); + if (minute < date.getMinutes()) date.setHours(date.getHours() + 1); + date.setMinutes(minute, second); + } else if ($2 == 3) { // HHMMSS + if (second < date.getSeconds()) date.setMinutes(date.getMinutes() + 1); + if (minute < date.getMinutes()) date.setHours(date.getHours() + 1); + if (hour < date.getHours()) date.setDate(date.getDate() + 1); + date.setHours(hour, minute, second); + } else { + throw 'Invalid alarm match mask'; + } + + return date - now; + }, time_offset, alarm_time.reg, mask); + + alarm_callback = callback; + alarm_timeout_id = emscripten_set_timeout(watch_invoke_alarm_callback, timeout, NULL); +} + +void watch_rtc_disable_alarm_callback(void) { + alarm_callback = NULL; + alarm_interval = 0; + + if (alarm_timeout_id) { + emscripten_clear_timeout(alarm_timeout_id); + alarm_timeout_id = 0; + } + + if (alarm_interval_id) { + emscripten_clear_interval(alarm_interval_id); + alarm_interval_id = 0; + } +} + +/////////////////////// +// Deprecated functions + +void watch_set_date_time(struct calendar_date_time date_time) { + watch_date_time val; + val.unit.second = date_time.time.sec; + val.unit.minute = date_time.time.min; + val.unit.hour = date_time.time.hour; + val.unit.day = date_time.date.day; + val.unit.month = date_time.date.month; + val.unit.year = date_time.date.year - WATCH_RTC_REFERENCE_YEAR; + watch_rtc_set_date_time(val); +} + +void watch_get_date_time(struct calendar_date_time *date_time) { + if (date_time == NULL) return; + watch_date_time val = watch_rtc_get_date_time(); + date_time->time.sec = val.unit.second; + date_time->time.min = val.unit.minute; + date_time->time.hour = val.unit.hour; + date_time->date.day = val.unit.day; + date_time->date.month = val.unit.month; + date_time->date.year = val.unit.year + WATCH_RTC_REFERENCE_YEAR; +} + +void watch_register_tick_callback(ext_irq_cb_t callback) { + watch_rtc_register_tick_callback(callback); +} -- cgit v1.2.3