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
This commit is contained in:
Alexsander Akers
2022-01-25 15:03:22 -05:00
committed by GitHub
parent 9e24f6c336
commit b8de35658f
327 changed files with 2303 additions and 570 deletions

View File

@@ -0,0 +1,9 @@
#include "watch.h"
bool watch_is_battery_low(void) {
return false;
}
bool watch_is_buzzer_or_led_enabled(void) {
return false;
}

View File

@@ -0,0 +1,48 @@
/*
* 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_adc.h"
void watch_enable_adc(void) {}
void watch_enable_analog_input(const uint8_t pin) {}
uint16_t watch_get_analog_pin_level(const uint8_t pin) {
return 0;
}
void watch_set_analog_num_samples(uint16_t samples) {}
void watch_set_analog_sampling_length(uint8_t cycles) {}
void watch_set_analog_reference_voltage(watch_adc_reference_voltage reference) {}
uint16_t watch_get_vcc_voltage(void) {
// TODO: (a2) hook to UI
return 3000;
}
inline void watch_disable_analog_input(const uint8_t pin) {}
inline void watch_disable_adc(void) {}

View File

@@ -0,0 +1,60 @@
/*
* 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_buzzer.h"
inline void watch_enable_buzzer(void) {
// TODO: (a2) hook to UI
}
inline void watch_set_buzzer_period(uint32_t period) {
// TODO: (a2) hook to UI
}
void watch_disable_buzzer(void) {
_watch_disable_tcc();
}
inline void watch_set_buzzer_on(void) {
// TODO: (a2) hook to UI
}
inline void watch_set_buzzer_off(void) {
// TODO: (a2) hook to UI
}
// note: the buzzer uses a 1 MHz clock. these values were determined by dividing 1,000,000 by the target frequency.
// i.e. for a 440 Hz tone (A4 on the piano), 1MHz/440Hz = 2273
const uint16_t NotePeriods[108] = {0};
void watch_buzzer_play_note(BuzzerNote note, uint16_t duration_ms) {
if (note == BUZZER_NOTE_REST) {
watch_set_buzzer_off();
} // else {
// hri_tcc_write_PERBUF_reg(TCC0, NotePeriods[note]);
// hri_tcc_write_CCBUF_reg(TCC0, WATCH_BUZZER_TCC_CHANNEL, NotePeriods[note] / 2);
// watch_set_buzzer_on();
// }
// delay_ms(duration_ms);
// watch_set_buzzer_off();
}

View File

@@ -0,0 +1,99 @@
/*
* 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_extint.h"
// this warning only appears when you `make BOARD=OSO-SWAT-A1-02`. it's annoying,
// but i'd rather have it warn us at build-time than fail silently at run-time.
// besides, no one but me really has any of these boards anyway.
#if BTN_ALARM != GPIO(GPIO_PORTA, 2)
#warning This board revision does not support external wake on BTN_ALARM, so watch_register_extwake_callback will not work with it. Use watch_register_interrupt_callback instead.
#endif
static uint32_t watch_backup_data[8];
void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool level) {
if (pin == BTN_ALARM) {
watch_register_interrupt_callback(pin, callback, level ? INTERRUPT_TRIGGER_RISING : INTERRUPT_TRIGGER_FALLING);
}
}
void watch_disable_extwake_interrupt(uint8_t pin) {
if (pin == BTN_ALARM) {
watch_register_interrupt_callback(pin, NULL, INTERRUPT_TRIGGER_NONE);
}
}
void watch_store_backup_data(uint32_t data, uint8_t reg) {
if (reg < 8) {
watch_backup_data[reg] = data;
}
}
uint32_t watch_get_backup_data(uint8_t reg) {
if (reg < 8) {
return watch_backup_data[reg];
}
return 0;
}
void watch_enter_sleep_mode(void) {
// TODO: (a2) hook to UI
// enter standby (4); we basically hang out here until an interrupt wakes us.
// sleep(4);
// call app_setup so the app can re-enable everything we disabled.
app_setup();
// and call app_wake_from_standby (since main won't have a chance to do it)
app_wake_from_standby();
}
void watch_enter_deep_sleep_mode(void) {
// identical to sleep mode except we disable the LCD first.
// TODO: (a2) hook to UI
watch_enter_sleep_mode();
}
void watch_enter_backup_mode(void) {
// TODO: (a2) hook to UI
// go into backup sleep mode (5). when we exit, the reset controller will take over.
// sleep(5);
}
// deprecated
void watch_enter_shallow_sleep(bool display_on) {
if (display_on) watch_enter_sleep_mode();
else watch_enter_deep_sleep_mode();
}
// deprecated
void watch_enter_deep_sleep(void) {
watch_register_extwake_callback(BTN_ALARM, NULL, true);
watch_enter_backup_mode();
}

View File

@@ -0,0 +1,191 @@
/*
* 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_extint.h"
#include <emscripten.h>
#include <emscripten/html5.h>
static bool output_focused = false;
static bool external_interrupt_enabled = false;
static bool button_callbacks_installed = false;
static ext_irq_cb_t external_interrupt_mode_callback = NULL;
static watch_interrupt_trigger external_interrupt_mode_trigger = INTERRUPT_TRIGGER_NONE;
static ext_irq_cb_t external_interrupt_light_callback = NULL;
static watch_interrupt_trigger external_interrupt_light_trigger = INTERRUPT_TRIGGER_NONE;
static ext_irq_cb_t external_interrupt_alarm_callback = NULL;
static watch_interrupt_trigger external_interrupt_alarm_trigger = INTERRUPT_TRIGGER_NONE;
#define BTN_ID_ALARM 3
#define BTN_ID_LIGHT 1
#define BTN_ID_MODE 2
static const uint8_t BTN_IDS[] = { BTN_ID_ALARM, BTN_ID_LIGHT, BTN_ID_MODE };
static void watch_invoke_interrupt_callback(const uint8_t button_id, watch_interrupt_trigger trigger);
static EM_BOOL watch_invoke_key_callback(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData) {
if (output_focused || keyEvent->repeat) return EM_FALSE;
const char *key = keyEvent->key;
if (key[1] != 0) return EM_FALSE;
uint8_t button_id;
switch (key[0]) {
case 'A':
case 'a':
button_id = BTN_ID_ALARM;
break;
case 'L':
case 'l':
button_id = BTN_ID_LIGHT;
break;
case 'M':
case 'm':
button_id = BTN_ID_MODE;
break;
default:
return EM_FALSE;
}
watch_interrupt_trigger trigger = eventType == EMSCRIPTEN_EVENT_KEYDOWN ? INTERRUPT_TRIGGER_RISING : INTERRUPT_TRIGGER_FALLING;
watch_invoke_interrupt_callback(button_id, trigger);
return EM_TRUE;
}
static EM_BOOL watch_invoke_mouse_callback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) {
if (eventType == EMSCRIPTEN_EVENT_MOUSEOUT && mouseEvent->buttons == 0) return EM_FALSE;
uint8_t button_id = *(const char *)userData;
watch_interrupt_trigger trigger = eventType == EMSCRIPTEN_EVENT_MOUSEDOWN ? INTERRUPT_TRIGGER_RISING : INTERRUPT_TRIGGER_FALLING;
watch_invoke_interrupt_callback(button_id, trigger);
return EM_TRUE;
}
static EM_BOOL watch_invoke_touch_callback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) {
uint8_t button_id = *(const char *)userData;
watch_interrupt_trigger trigger = eventType == EMSCRIPTEN_EVENT_TOUCHSTART ? INTERRUPT_TRIGGER_RISING : INTERRUPT_TRIGGER_FALLING;
watch_invoke_interrupt_callback(button_id, trigger);
return EM_TRUE;
}
static EM_BOOL watch_invoke_focus_callback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData) {
output_focused = eventType == EMSCRIPTEN_EVENT_FOCUS;
return EM_TRUE;
}
static void watch_install_button_callbacks(void) {
emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, watch_invoke_key_callback);
emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, watch_invoke_key_callback);
const char *target_output = "#output";
emscripten_set_focus_callback(target_output, NULL, EM_FALSE, watch_invoke_focus_callback);
emscripten_set_blur_callback(target_output, NULL, EM_FALSE, watch_invoke_focus_callback);
for (int i = 0, count = sizeof(BTN_IDS) / sizeof(BTN_IDS[0]); i < count; i++) {
char target[] = "#btn_";
target[4] = BTN_IDS[i] + '0';
emscripten_set_mousedown_callback(target, (void *)&BTN_IDS[i], EM_FALSE, watch_invoke_mouse_callback);
emscripten_set_mouseup_callback(target, (void *)&BTN_IDS[i], EM_FALSE, watch_invoke_mouse_callback);
emscripten_set_mouseout_callback(target, (void *)&BTN_IDS[i], EM_FALSE, watch_invoke_mouse_callback);
emscripten_set_touchstart_callback(target, (void *)&BTN_IDS[i], EM_FALSE, watch_invoke_touch_callback);
emscripten_set_touchend_callback(target, (void *)&BTN_IDS[i], EM_FALSE, watch_invoke_touch_callback);
}
}
void watch_enable_external_interrupts(void) {
external_interrupt_enabled = true;
if (!button_callbacks_installed) {
watch_install_button_callbacks();
button_callbacks_installed = true;
}
}
void watch_disable_external_interrupts(void) {
external_interrupt_enabled = false;
}
static void watch_invoke_interrupt_callback(const uint8_t button_id, watch_interrupt_trigger event) {
if (!external_interrupt_enabled) return;
ext_irq_cb_t callback;
watch_interrupt_trigger trigger;
uint8_t pin;
switch (button_id) {
case BTN_ID_MODE:
pin = BTN_MODE;
callback = external_interrupt_mode_callback;
trigger = external_interrupt_mode_trigger;
break;
case BTN_ID_LIGHT:
pin = BTN_LIGHT;
callback = external_interrupt_light_callback;
trigger = external_interrupt_light_trigger;
break;
case BTN_ID_ALARM:
pin = BTN_ALARM;
callback = external_interrupt_alarm_callback;
trigger = external_interrupt_alarm_trigger;
break;
default:
return;
}
const bool level = (event & INTERRUPT_TRIGGER_RISING) != 0;
EM_ASM({
const classList = document.querySelector('#btn' + $0).classList;
const highlight = 'highlight';
$1 ? classList.add(highlight) : classList.remove(highlight);
}, button_id, level);
watch_set_pin_level(pin, level);
if (callback && (event & trigger) != 0) {
callback();
void resume_main_loop(void);
resume_main_loop();
}
}
void watch_register_interrupt_callback(const uint8_t pin, ext_irq_cb_t callback, watch_interrupt_trigger trigger) {
if (pin == BTN_MODE) {
external_interrupt_mode_callback = callback;
external_interrupt_mode_trigger = trigger;
} else if (pin == BTN_LIGHT) {
external_interrupt_light_callback = callback;
external_interrupt_light_trigger = trigger;
} else if (pin == BTN_ALARM) {
external_interrupt_alarm_callback = callback;
external_interrupt_alarm_trigger = trigger;
}
}
void watch_register_button_callback(const uint8_t pin, ext_irq_cb_t callback) {
watch_register_interrupt_callback(pin, callback, INTERRUPT_TRIGGER_RISING);
}
void watch_enable_buttons(void) {
watch_enable_external_interrupts();
}

View File

@@ -0,0 +1,47 @@
/*
* 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_gpio.h"
static bool pin_levels[UINT8_MAX];
void watch_enable_digital_input(const uint8_t pin) {}
void watch_disable_digital_input(const uint8_t pin) {}
void watch_enable_pull_up(const uint8_t pin) {}
void watch_enable_pull_down(const uint8_t pin) {}
bool watch_get_pin_level(const uint8_t pin) {
return pin_levels[pin];
}
void watch_enable_digital_output(const uint8_t pin) {}
void watch_disable_digital_output(const uint8_t pin) {}
void watch_set_pin_level(const uint8_t pin, const bool level) {
pin_levels[pin] = level;
}

View File

@@ -0,0 +1,51 @@
/*
* 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_i2c.h"
void watch_enable_i2c(void) {}
void watch_disable_i2c(void) {}
void watch_i2c_send(int16_t addr, uint8_t *buf, uint16_t length) {}
void watch_i2c_receive(int16_t addr, uint8_t *buf, uint16_t length) {}
void watch_i2c_write8(int16_t addr, uint8_t reg, uint8_t data) {}
uint8_t watch_i2c_read8(int16_t addr, uint8_t reg) {
return 0;
}
uint16_t watch_i2c_read16(int16_t addr, uint8_t reg) {
return 0;
}
uint32_t watch_i2c_read24(int16_t addr, uint8_t reg) {
return 0;
}
uint32_t watch_i2c_read32(int16_t addr, uint8_t reg) {
return 0;
}

View File

@@ -0,0 +1,63 @@
/*
* 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_led.h"
#include <emscripten.h>
void watch_enable_leds(void) {}
void watch_disable_leds(void) {}
void watch_enable_led(bool unused) {
(void)unused;
watch_enable_leds();
}
void watch_disable_led(bool unused) {
(void)unused;
watch_disable_leds();
}
void watch_set_led_color(uint8_t red, uint8_t green) {
EM_ASM({
document.getElementById('light').style.opacity = $1 / 255;
}, red, green);
}
void watch_set_led_red(void) {
watch_set_led_color(255, 0);
}
void watch_set_led_green(void) {
watch_set_led_color(0, 255);
}
void watch_set_led_yellow(void) {
watch_set_led_color(255, 255);
}
void watch_set_led_off(void) {
watch_set_led_color(0, 0);
}

View File

@@ -0,0 +1,78 @@
/*
* 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_private.h"
#include "watch_utility.h"
#include <sys/time.h>
void _watch_init(void) {
// External wake depends on RTC; calendar is a required module.
_watch_rtc_init();
}
// this function is called by arc4random to get entropy for random number generation.
// let's use the SAM L22's true random number generator to seed the PRNG!
int getentropy(void *buf, size_t buflen);
int getentropy(void *buf, size_t buflen) {
// TODO: (a2) hook to RNG
return 0;
}
int _gettimeofday(struct timeval *tv, void *tzvp);
int _gettimeofday(struct timeval *tv, void *tzvp) {
(void)tzvp;
watch_date_time date_time = watch_rtc_get_date_time();
// FIXME: this assumes the system time is UTC! Will break for any other time zone.
tv->tv_sec = watch_utility_date_time_to_unix_time(date_time, 0);
tv->tv_usec = 0;
return 0;
}
void _watch_enable_tcc(void) {}
void _watch_disable_tcc(void) {}
void _watch_enable_usb(void) {}
// this function ends up getting called by printf to log stuff to the USB console.
int _write(int file, char *ptr, int len) {
// TODO: (a2) hook to UI
return 0;
}
// this method could be overridden to read stuff from the USB console? but no need rn.
int _read(void) {
return 0;
}
// Alternate function that outputs to the debug UART. useful for debugging USB issues.
// int _write(int file, char *ptr, int len) {
// (void)file;
// int pos = 0;
// while(pos < len) watch_debug_putc(ptr[pos++]);
// return 0;
// }

View File

@@ -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 <emscripten.h>
#include <emscripten/html5.h>
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);
}

View File

@@ -0,0 +1,115 @@
/*
* 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_slcd.h"
#include "watch_private_display.h"
#include "hpl_slcd_config.h"
#include <emscripten.h>
#include <emscripten/html5.h>
//////////////////////////////////////////////////////////////////////////////////////////
// Segmented Display
static char blink_character;
static bool blink_state;
static long blink_interval_id;
static bool tick_state;
static long tick_interval_id;
void watch_enable_display(void) {
watch_clear_display();
}
void watch_set_pixel(uint8_t com, uint8_t seg) {
EM_ASM({
document.querySelectorAll("[data-com='" + $0 + "'][data-seg='" + $1 + "']")
.forEach((e) => e.style.opacity = 1);
}, com, seg);
}
void watch_clear_pixel(uint8_t com, uint8_t seg) {
EM_ASM({
document.querySelectorAll("[data-com='" + $0 + "'][data-seg='" + $1 + "']")
.forEach((e) => e.style.opacity = 0);
}, com, seg);
}
void watch_clear_display(void) {
EM_ASM({
document.querySelectorAll("[data-com][data-seg]")
.forEach((e) => e.style.opacity = 0);
});
}
static void watch_invoke_blink_callback(void *userData) {
blink_state = !blink_state;
watch_display_character(blink_state ? blink_character : ' ', 7);
watch_clear_pixel(2, 10); // clear segment B of position 7 since it can't blink
}
void watch_start_character_blink(char character, uint32_t duration) {
watch_display_character(character, 7);
watch_clear_pixel(2, 10); // clear segment B of position 7 since it can't blink
blink_state = true;
blink_character = character;
blink_interval_id = emscripten_set_interval(watch_invoke_blink_callback, (double)duration, NULL);
}
void watch_stop_blink(void) {
emscripten_clear_timeout(blink_interval_id);
blink_interval_id = 0;
blink_state = false;
}
static void watch_invoke_tick_callback(void *userData) {
tick_state = !tick_state;
if (tick_state) {
watch_clear_pixel(0, 2);
watch_set_pixel(0, 3);
} else {
watch_clear_pixel(0, 3);
watch_set_pixel(0, 2);
}
}
void watch_start_tick_animation(uint32_t duration) {
watch_display_character(' ', 8);
tick_state = true;
tick_interval_id = emscripten_set_interval(watch_invoke_tick_callback, (double)duration, NULL);
}
bool watch_tick_animation_is_running(void) {
return tick_interval_id != 0;
}
void watch_stop_tick_animation(void) {
emscripten_clear_timeout(tick_interval_id);
tick_interval_id = 0;
tick_state = false;
watch_display_character(' ', 8);
}

View File

@@ -0,0 +1,65 @@
/*
* 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.
*/
/*
* UART methods are Copyright (c) 2014-2017, Alex Taradov <alex@taradov.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "watch_uart.h"
#include "peripheral_clk_config.h"
void watch_enable_debug_uart(uint32_t baud) {}
void watch_debug_putc(char c) {}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
void watch_debug_puts(char *s) {
while (*s) watch_debug_putc(*s++);
}
#pragma GCC diagnostic pop