diff options
| author | Joey Castillo <joeycastillo@utexas.edu> | 2022-08-02 08:17:26 -0600 | 
|---|---|---|
| committer | Joey Castillo <joeycastillo@utexas.edu> | 2022-08-02 08:17:26 -0600 | 
| commit | bcd3b666848214a735f37a5a4f08b157ba7bb3a1 (patch) | |
| tree | 149bd53bbd46ebddd31957cc827a7c40ccf6f1a0 | |
| parent | ed526355f69a931d51a6ebff1b45c70988e7b255 (diff) | |
| parent | 6d87f5a6268a9a516d8c577dfd71b39a5bfc384a (diff) | |
| download | Sensor-Watch-bcd3b666848214a735f37a5a4f08b157ba7bb3a1.tar.gz Sensor-Watch-bcd3b666848214a735f37a5a4f08b157ba7bb3a1.tar.bz2 Sensor-Watch-bcd3b666848214a735f37a5a4f08b157ba7bb3a1.zip  | |
Merge branch 'main' of github.com:joeycastillo/Sensor-Watch into lfs
| -rw-r--r-- | .devcontainer/Dockerfile | 50 | ||||
| -rw-r--r-- | .devcontainer/devcontainer.json | 17 | ||||
| -rw-r--r-- | apps/functional-test/app.c | 104 | ||||
| -rw-r--r-- | apps/sensor-board-test/app.c | 75 | ||||
| -rwxr-xr-x | apps/sensor-board-test/make/Makefile | 10 | ||||
| -rw-r--r-- | make.mk | 28 | ||||
| -rw-r--r-- | movement/alt_fw/focus.h | 1 | ||||
| -rw-r--r-- | movement/alt_fw/timers.h | 43 | ||||
| -rwxr-xr-x | movement/make/Makefile | 3 | ||||
| -rw-r--r-- | movement/movement.c | 43 | ||||
| -rw-r--r-- | movement/movement.h | 3 | ||||
| -rw-r--r-- | movement/movement_faces.h | 3 | ||||
| -rw-r--r-- | movement/watch_faces/complication/probability_face.c | 182 | ||||
| -rw-r--r-- | movement/watch_faces/complication/probability_face.h | 51 | ||||
| -rw-r--r-- | movement/watch_faces/complication/wake_face.c | 159 | ||||
| -rw-r--r-- | movement/watch_faces/complication/wake_face.h | 53 | ||||
| -rw-r--r-- | rules.mk | 11 | ||||
| -rw-r--r-- | watch-library/hardware/watch/watch_private.c | 2 | 
18 files changed, 752 insertions, 86 deletions
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..55565a07 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,50 @@ +FROM ubuntu:22.10 + +# TODO: install emscripten (https://emscripten.org/docs/getting_started/downloads.html) + +# TODO: Clean this up once buildkit is supported gracefully in devcontainers +# https://github.com/microsoft/vscode-remote-release/issues/1409 + +ARG X86_64_TOOLCHAIN_FILENAME="gcc-arm-none-eabi-10.3-2021.07-x86_64-linux.tar.bz2" +ARG X86_64_TOOLCHAIN="https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.07/gcc-arm-none-eabi-10.3-2021.07-x86_64-linux.tar.bz2" +ARG X86_64_TOOLCHAIN_CHECKSUM="b56ae639d9183c340f065ae114a30202" + +ARG AARCH64_TOOLCHAIN_FILENAME="gcc-arm-none-eabi-10.3-2021.07-aarch64-linux.tar.bz2" +ARG AARCH64_TOOLCHAIN="https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.07/gcc-arm-none-eabi-10.3-2021.07-aarch64-linux.tar.bz2" +ARG AARCH64_TOOLCHAIN_CHECKSUM="c20b0535d01f8d4418341d893c62a782" + +WORKDIR /setup + +# Install required packages +RUN apt-get update \ +    && apt-get install -y --no-install-recommends \ +    # make is used as the build system +    make \ +    # git is used for fetching submodules & interactive development +    git \ +    # bzip2 is required to extract the Arm toolchain +    bzip2 \ +    # ca certs need to be available for fetching git submodules +    ca-certificates \ +    # python is used to convert binaries to uf2 files +    python3 python-is-python3 + +# Download and verify both x86-64 and aarch64 toolchains. This is unfortunate and +# slows down the build, but it's a clean-ish option until buildkit can be used. +ADD $X86_64_TOOLCHAIN $X86_64_TOOLCHAIN_FILENAME +ADD $AARCH64_TOOLCHAIN $AARCH64_TOOLCHAIN_FILENAME + +RUN echo "${X86_64_TOOLCHAIN_CHECKSUM} ${X86_64_TOOLCHAIN_FILENAME}" | md5sum --check +RUN echo "${AARCH64_TOOLCHAIN_CHECKSUM} ${AARCH64_TOOLCHAIN_FILENAME}" | md5sum --check + +# Extract toolchain directly into /usr +RUN /bin/sh -c 'set -ex && \ +    ARCH=`uname -m` && \ +    if [ "$ARCH" = "x86_64" ]; then \ +       tar --strip-components=1 -C /usr -xjf $X86_64_TOOLCHAIN_FILENAME ; \ +    else \ +       tar --strip-components=1 -C /usr -xjf $AARCH64_TOOLCHAIN_FILENAME ; \ +    fi' + +RUN rm $X86_64_TOOLCHAIN_FILENAME +RUN rm $AARCH64_TOOLCHAIN_FILENAME
\ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..a71a0f22 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.231.6/containers/docker-existing-dockerfile +{ +    "name": "GNU Arm Embedded Environment", +    // Sets the run context to one level up instead of the .devcontainer folder. +    "context": "..", +    // Set the location of the dockerfile to use +    "dockerFile": "Dockerfile", +    // Set *default* container specific settings.json values on container create. +    "settings": {}, +    // Add the IDs of extensions you want installed when the container is created. +    "extensions": [ +        "ms-vscode.cpptools" +    ] +    // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. +    // "remoteUser": "vscode" +}
\ No newline at end of file diff --git a/apps/functional-test/app.c b/apps/functional-test/app.c index b4ee0412..179bea8b 100644 --- a/apps/functional-test/app.c +++ b/apps/functional-test/app.c @@ -2,15 +2,6 @@  #include <string.h>  #include "watch.h" -bool even = false; -bool beep = false; -uint32_t i = 0; - -static void cb_tick(void) { -    beep = true; -    even = !even; -} -  void app_init(void) {  } @@ -22,30 +13,14 @@ void app_setup(void) {      watch_enable_buzzer(); -    watch_enable_digital_output(A0); -    watch_enable_digital_output(SCL); -    watch_enable_digital_output(SDA); -    watch_enable_digital_output(A1); -    watch_enable_digital_output(A2); -    watch_enable_digital_output(A3); -    watch_enable_digital_output(A4); - +    watch_enable_digital_output(RED); +    watch_enable_digital_output(GREEN);      watch_enable_digital_input(BTN_ALARM);      watch_enable_digital_input(BTN_LIGHT);      watch_enable_digital_input(BTN_MODE);      watch_enable_pull_down(BTN_ALARM);      watch_enable_pull_down(BTN_LIGHT);      watch_enable_pull_down(BTN_MODE); - -    watch_set_pin_level(A0, false); -    watch_set_pin_level(SCL, false); -    watch_set_pin_level(SDA, false); -    watch_set_pin_level(A1, false); -    watch_set_pin_level(A2, false); -    watch_set_pin_level(A3, false); -    watch_set_pin_level(A4, false); - -    watch_rtc_register_periodic_callback(cb_tick, 2);  }  void app_prepare_for_standby(void) { @@ -55,51 +30,44 @@ void app_wake_from_standby(void) {  }  bool app_loop(void) { -    char buf[14]; +    static int last_button = 0; +    static int button = 0; +    static int8_t loop = 0; -    if (beep) watch_buzzer_play_note(BUZZER_NOTE_E5, 100); - -    if (even) { -        printf("Flashing even lights\n"); -        #ifdef WATCH_SWAP_LED_PINS -        sprintf(buf, "WT%2d'blu_E", (uint8_t)(i++ % 40)); -        #else -        sprintf(buf, "WT%2d'Grn_E", (uint8_t)(i++ % 40)); -        #endif -        watch_set_led_green(); -        watch_set_pin_level(A0, true); -        watch_set_pin_level(SCL, false); -        watch_set_pin_level(SDA, true); -        watch_set_pin_level(A1, false); -        watch_set_pin_level(A2, true); -        watch_set_pin_level(A3, false); -        watch_set_pin_level(A4, true); -    } else { -        printf("Flashing odd lights\n"); -        sprintf(buf, "WT%2d-red~O", (uint8_t)(i++ % 40)); -        watch_display_string(buf, 0); -        watch_set_led_red(); -        watch_set_pin_level(A0, false); -        watch_set_pin_level(SCL, true); -        watch_set_pin_level(SDA, false); -        watch_set_pin_level(A1, true); -        watch_set_pin_level(A2, false); -        watch_set_pin_level(A3, true); -        watch_set_pin_level(A4, false); -    } +    watch_set_pin_level(GREEN, false); +    watch_set_pin_level(RED, false);      if (watch_get_pin_level(BTN_ALARM)) { -        buf[2] = 'a'; -        buf[3] = 'L'; +        watch_set_pin_level(GREEN, true); +        button = 1; +    } else if (watch_get_pin_level(BTN_LIGHT)) { +        watch_set_pin_level(RED, true); +        button = 2; +    } else if (watch_get_pin_level(BTN_MODE)) { +        watch_set_pin_level(GREEN, true); +        watch_set_pin_level(RED, true); +        button = 3;      } -    if (watch_get_pin_level(BTN_LIGHT)) { -        buf[2] = '1'; -        buf[3] = 'i'; + +    if (button != last_button) { +        last_button = button; +        watch_buzzer_play_note(BUZZER_NOTE_C8, 100);      } -    if (watch_get_pin_level(BTN_MODE)) { -        buf[2] = '-'; -        buf[3] = 'O'; + +    static const bool segmap[3][24] = { +       //0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 +        {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0}, +        {0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1}, +        {1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1} +    }; + +    for(int com = 0; com < 3; com++) { +        for(int seg = 0; seg < 24; seg++) { +            if (segmap[com][seg]) (loop >= 0) ? watch_set_pixel(com, seg) : watch_clear_pixel(com, seg); +            else (loop < 0) ? watch_set_pixel(com, seg) : watch_clear_pixel(com, seg); +        }      } -    watch_display_string(buf, 0); -    return true; +    loop++; + +    return false;  } diff --git a/apps/sensor-board-test/app.c b/apps/sensor-board-test/app.c new file mode 100644 index 00000000..d7da190a --- /dev/null +++ b/apps/sensor-board-test/app.c @@ -0,0 +1,75 @@ +#include <stdio.h> +#include <string.h> +#include "watch.h" + +bool even = false; + +static void cb_tick(void) { +    even = !even; +} + +void app_init(void) { +} + +void app_wake_from_backup(void) { +} + +void app_setup(void) { +    watch_enable_digital_output(RED); +    watch_enable_digital_output(GREEN); +    watch_enable_digital_output(A0); +    watch_enable_digital_output(SCL); +    watch_enable_digital_output(SDA); +    watch_enable_digital_output(A1); +    watch_enable_digital_output(A2); +    watch_enable_digital_output(A3); +    watch_enable_digital_output(A4); + +    watch_set_pin_level(A0, false); +    watch_set_pin_level(SCL, false); +    watch_set_pin_level(SDA, false); +    watch_set_pin_level(A1, false); +    watch_set_pin_level(A2, false); +    watch_set_pin_level(A3, false); +    watch_set_pin_level(A4, false); + +    watch_rtc_register_periodic_callback(cb_tick, 2); +} + +void app_prepare_for_standby(void) { +} + +void app_wake_from_standby(void) { +} + +bool app_loop(void) { +    watch_date_time date_time = watch_rtc_get_date_time(); +    char buf[16]; +    sprintf(buf, "%2d:%02d:%02d: ", date_time.unit.hour, date_time.unit.minute, date_time.unit.second); +    printf(buf); +    if (even) { +        printf("Even\n"); +        watch_set_pin_level(RED, false); +        watch_set_pin_level(GREEN, true); +        watch_set_pin_level(A0, true); +        watch_set_pin_level(SCL, false); +        watch_set_pin_level(SDA, true); +        watch_set_pin_level(A1, false); +        watch_set_pin_level(A2, true); +        watch_set_pin_level(A3, false); +        watch_set_pin_level(A4, true); +    } else { +        printf("Odd\n"); +        watch_set_pin_level(RED, true); +        watch_set_pin_level(GREEN, false); +        watch_set_pin_level(A0, false); +        watch_set_pin_level(SCL, true); +        watch_set_pin_level(SDA, false); +        watch_set_pin_level(A1, true); +        watch_set_pin_level(A2, false); +        watch_set_pin_level(A3, true); +        watch_set_pin_level(A4, false); +    } + +    return true; +} diff --git a/apps/sensor-board-test/make/Makefile b/apps/sensor-board-test/make/Makefile new file mode 100755 index 00000000..c66ad20c --- /dev/null +++ b/apps/sensor-board-test/make/Makefile @@ -0,0 +1,10 @@ +TOP = ../../.. +include $(TOP)/make.mk + +INCLUDES += \ +  -I../ + +SRCS += \ +  ../app.c + +include $(TOP)/rules.mk @@ -9,17 +9,43 @@ endif  ##############################################################################  .PHONY: all directory clean size +# OS detection, adapted from https://gist.github.com/sighingnow/deee806603ec9274fd47 +DETECTED_OS := +ifeq ($(OS),Windows_NT) +  DETECTED_OS = WINDOWS +else +  UNAME_S := $(shell uname -s) +  ifeq ($(UNAME_S),Linux) +    DETECTED_OS = LINUX +  endif +  ifeq ($(UNAME_S),Darwin) +    DETECTED_OS = OSX +  endif +endif +$(if ${VERBOSE},$(info OS detected: $(DETECTED_OS))) +  ifeq ($(OS), Windows_NT)    MKDIR = gmkdir  else    MKDIR = mkdir  endif +ifeq ($(DETECTED_OS), LINUX) +  MAKEFLAGS += -j `nproc` +endif +ifeq ($(DETECTED_OS), OSX) +  NPROCS = $(shell sysctl hw.ncpu  | grep -o '[0-9]\+') +  MAKEFLAGS += -j $(NPROCS) +endif +ifeq ($(DETECTED_OS), WINDOWS) +  MAKEFLAGS += -j $(NUMBER_OF_PROCESSORS) +endif +  ifndef EMSCRIPTEN  CC = arm-none-eabi-gcc  OBJCOPY = arm-none-eabi-objcopy  SIZE = arm-none-eabi-size -UF2 = python $(TOP)/utils/uf2conv.py +UF2 = python3 $(TOP)/utils/uf2conv.py  CFLAGS += -W -Wall -Wextra -Wmissing-prototypes -Wmissing-declarations  CFLAGS += --std=gnu99 -Os diff --git a/movement/alt_fw/focus.h b/movement/alt_fw/focus.h index 865459e8..ab5525a5 100644 --- a/movement/alt_fw/focus.h +++ b/movement/alt_fw/focus.h @@ -32,6 +32,7 @@ const watch_face_t watch_faces[] = {      tomato_face,      stopwatch_face,      countdown_face, +    wake_face, // added by @joshber 2022-07-23, per @joeycastillo      preferences_face,      set_time_face, diff --git a/movement/alt_fw/timers.h b/movement/alt_fw/timers.h new file mode 100644 index 00000000..c4d27f50 --- /dev/null +++ b/movement/alt_fw/timers.h @@ -0,0 +1,43 @@ +/* + * MIT License + * + * Copyright (c) 2022 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. + */ + +#ifndef MOVEMENT_CONFIG_H_ +#define MOVEMENT_CONFIG_H_ + +#include "movement_faces.h" + +const watch_face_t watch_faces[] = { +    simple_clock_face, +    wake_face, +    interval_face, +    stopwatch_face, +    sunrise_sunset_face, + +    preferences_face, +    set_time_face, +}; + +#define MOVEMENT_NUM_FACES (sizeof(watch_faces) / sizeof(watch_face_t)) + +#endif // MOVEMENT_CONFIG_H_ diff --git a/movement/make/Makefile b/movement/make/Makefile index 1c880fc2..05f3636c 100755 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -65,6 +65,9 @@ SRCS += \    ../watch_faces/complication/orrery_face.c \    ../watch_faces/complication/astronomy_face.c \    ../watch_faces/complication/tomato_face.c \ +  ../watch_faces/complication/probability_face.c \ +  ../watch_faces/complication/wake_face.c \ +# wake_face.c: Josh Berson, 2022-07-04  # 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.c b/movement/movement.c index 3444a378..d79142ec 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -226,6 +226,11 @@ void movement_cancel_background_task(void) {      movement_state.has_scheduled_background_task = other_tasks_scheduled;  } +void movement_request_wake() { +    movement_state.needs_wake = true; +    _movement_reset_inactivity_countdown(); +} +  void movement_play_signal(void) {      watch_buzzer_play_note(BUZZER_NOTE_C8, 75);      watch_buzzer_play_note(BUZZER_NOTE_REST, 100); @@ -233,7 +238,11 @@ void movement_play_signal(void) {  }  void movement_play_alarm(void) { -    movement_state.alarm_ticks = 128 * 5 - 80; // 80 ticks short of 5 seconds, or 4.375 seconds (our beep is 0.375 seconds) +    movement_request_wake(); +    // alarm length: 75 ticks short of 5 seconds, or 4.414 seconds: +    // our tone is 0.375 seconds of beep and 0.625 of silence, repeated five times. +    // so 4.375 + a few ticks to wake up from sleep mode. +    movement_state.alarm_ticks = 128 * 5 - 75;      _movement_enable_fast_tick_if_needed();  } @@ -325,6 +334,23 @@ void app_prepare_for_standby(void) {  void app_wake_from_standby(void) {  } +static void _sleep_mode_app_loop(void) { +    movement_state.needs_wake = false; +    // as long as le_mode_ticks is -1 (i.e. we are in low energy mode), we wake up here, update the screen, and go right back to sleep. +    while (movement_state.le_mode_ticks == -1) { +        // we also have to handle background tasks here in the mini-runloop +        if (movement_state.needs_background_tasks_handled) _movement_handle_background_tasks(); + +        event.event_type = EVENT_LOW_ENERGY_UPDATE; +        watch_faces[movement_state.current_watch_face].loop(event, &movement_state.settings, watch_face_contexts[movement_state.current_watch_face]); + +        // if we need to wake immediately, do it! +        if (movement_state.needs_wake) return; +        // otherwise enter sleep mode, and when the extwake handler is called, it will reset le_mode_ticks and force us out at the next loop. +        else watch_enter_sleep_mode(); +    } +} +  bool app_loop(void) {      if (movement_state.watch_face_changed) {          if (movement_state.settings.bit.button_should_sound) { @@ -366,17 +392,10 @@ bool app_loop(void) {          event.event_type = EVENT_NONE;          event.subsecond = 0; -        // this is a little mini-runloop. -        // as long as le_mode_ticks is -1 (i.e. we are in low energy mode), we wake up here, update the screen, and go right back to sleep. -        while (movement_state.le_mode_ticks == -1) { -            // we also have to handle background tasks here in the mini-runloop -            if (movement_state.needs_background_tasks_handled) _movement_handle_background_tasks(); - -            event.event_type = EVENT_LOW_ENERGY_UPDATE; -            watch_faces[movement_state.current_watch_face].loop(event, &movement_state.settings, watch_face_contexts[movement_state.current_watch_face]); -            watch_enter_sleep_mode(); -        } -        // as soon as le_mode_ticks is reset by the extwake handler, we bail out of the loop and reactivate ourselves. +        // _sleep_mode_app_loop takes over at this point and loops until le_mode_ticks is reset by the extwake handler, +        // or wake is requested using the movement_request_wake function. +        _sleep_mode_app_loop(); +        // as soon as _sleep_mode_app_loop returns, we reactivate ourselves.          event.event_type = EVENT_ACTIVATE;          // this is a hack tho: waking from sleep mode, app_setup does get called, but it happens before we have reset our ticks.          // need to figure out if there's a better heuristic for determining how we woke up. diff --git a/movement/movement.h b/movement/movement.h index a29c4469..0442f607 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -257,6 +257,7 @@ typedef struct {      // background task handling      bool needs_background_tasks_handled;      bool has_scheduled_background_task; +    bool needs_wake;      // low energy mode countdown      int32_t le_mode_ticks; @@ -287,6 +288,8 @@ void movement_schedule_background_task(watch_date_time date_time);  // movement will associate the scheduled task with the currently active face.  void movement_cancel_background_task(void); +void movement_request_wake(void); +  void movement_play_signal(void);  void movement_play_alarm(void); diff --git a/movement/movement_faces.h b/movement/movement_faces.h index e7e78ff4..e18f3683 100644 --- a/movement/movement_faces.h +++ b/movement/movement_faces.h @@ -52,6 +52,9 @@  #include "orrery_face.h"  #include "astronomy_face.h"  #include "tomato_face.h" +#include "probability_face.h" +#include "wake_face.h" +// #include "interval_face.h"  // New includes go above this line.  #endif // MOVEMENT_FACES_H_ diff --git a/movement/watch_faces/complication/probability_face.c b/movement/watch_faces/complication/probability_face.c new file mode 100644 index 00000000..7b056b33 --- /dev/null +++ b/movement/watch_faces/complication/probability_face.c @@ -0,0 +1,182 @@ +/* + * MIT License + * + * Copyright (c) 2022 Spencer Bywater + * + * 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. + */ + +// Emulator only: need time() to seed the random number generator. +#if __EMSCRIPTEN__ +#include <time.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include "probability_face.h" + +#define DEFAULT_DICE_SIDES 2 +#define PROBABILITY_ANIMATION_TICK_FREQUENCY 8 +const uint16_t NUM_DICE_TYPES = 8; // Keep this consistent with # of dice types below +const uint16_t DICE_TYPES[] = {2, 4, 6, 8, 10, 12, 20, 100}; + + +// -------------- +// Custom methods +// -------------- + +static void display_dice_roll(probability_state_t *state) { +    char buf[8]; +    if (state->rolled_value == 0) { +        if (state->dice_sides == 100) { +            sprintf(buf, " C    "); +        } else { +            sprintf(buf, "%2d    ", state->dice_sides); +        } +    } else if (state->dice_sides == 2) { +        if (state->rolled_value == 1) { +            sprintf(buf, "%2d   H", state->dice_sides); +        } else { +            sprintf(buf, "%2d   T", state->dice_sides); +        } +    } else if (state->dice_sides == 100) { +        sprintf(buf, " C %3d", state->rolled_value); +    } else { +        sprintf(buf, "%2d %3d", state->dice_sides, state->rolled_value); +    } +    watch_display_string(buf, 4); +} + +static void generate_random_number(probability_state_t *state) { +    // Emulator: use rand. Hardware: use arc4random. +    #if __EMSCRIPTEN__ +    state->rolled_value = rand() % state->dice_sides + 1; +    #else +    state->rolled_value = arc4random_uniform(state->dice_sides) + 1; +    #endif +} + +static void display_dice_roll_animation(probability_state_t *state) { +    if (state->is_rolling) { +        if (state->animation_frame == 0) { +            watch_display_string("   ", 7); +            watch_set_pixel(1, 4); +            watch_set_pixel(1, 6); +            state->animation_frame = 1; +        } else if (state->animation_frame == 1) { +            watch_clear_pixel(1, 4); +            watch_clear_pixel(1, 6); +            watch_set_pixel(2, 4); +            watch_set_pixel(0, 6); +            state->animation_frame = 2; +        } else if (state->animation_frame == 2) { +            watch_clear_pixel(2, 4); +            watch_clear_pixel(0, 6); +            watch_set_pixel(2, 5); +            watch_set_pixel(0, 5); +            state->animation_frame = 3; +        } else if (state->animation_frame == 3) { +            state->animation_frame = 0; +            state->is_rolling = false; +            movement_request_tick_frequency(1); +            display_dice_roll(state); +        } +    } +} + + +// --------------------------- +// Standard watch face methods +// --------------------------- +void probability_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(probability_state_t)); +        memset(*context_ptr, 0, sizeof(probability_state_t)); +    } +    // Emulator only: Seed random number generator +    #if __EMSCRIPTEN__ +    srand(time(NULL)); +    #endif +} + +void probability_face_activate(movement_settings_t *settings, void *context) { +    (void) settings; +    probability_state_t *state = (probability_state_t *)context; + +    state->dice_sides = DEFAULT_DICE_SIDES; +    state->rolled_value = 0; +    watch_display_string("PR", 0); +} + +bool probability_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { +    (void) settings; +    probability_state_t *state = (probability_state_t *)context; + +    if (state->is_rolling && event.event_type != EVENT_TICK) { +        return true; +    } + +    switch (event.event_type) { +        case EVENT_ACTIVATE: +            display_dice_roll(state); +            break; +        case EVENT_TICK: +            display_dice_roll_animation(state); +            break; +        case EVENT_MODE_BUTTON_UP: +            movement_move_to_next_face(); +            break; +        case EVENT_LIGHT_BUTTON_UP: +            // Change how many sides the die has +            for (int i = 0; i < NUM_DICE_TYPES; i++) { +                if (DICE_TYPES[i] == state->dice_sides) { +                    if (i == NUM_DICE_TYPES - 1) { +                        state->dice_sides = DICE_TYPES[0]; +                    } else { +                        state->dice_sides = DICE_TYPES[i + 1]; +                    } +                    break; +                } +            } +            state->rolled_value = 0; +            display_dice_roll(state); +            break; +        case EVENT_ALARM_BUTTON_UP: +            // Roll the die +            generate_random_number(state); +            state->is_rolling = true; +            // Dice rolling animation begins on next tick and new roll will be displayed on completion +            movement_request_tick_frequency(PROBABILITY_ANIMATION_TICK_FREQUENCY); +            break; +        case EVENT_LOW_ENERGY_UPDATE: +            watch_display_string("SLEEP ", 4); +            break; +        default: +            break; +    } + +    return true; +} + +void probability_face_resign(movement_settings_t *settings, void *context) { +    (void) settings; +    (void) context; +} diff --git a/movement/watch_faces/complication/probability_face.h b/movement/watch_faces/complication/probability_face.h new file mode 100644 index 00000000..c6d3638f --- /dev/null +++ b/movement/watch_faces/complication/probability_face.h @@ -0,0 +1,51 @@ +/* + * MIT License + * + * Copyright (c) 2022 Spencer Bywater + * + * 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 PROBABILITY_FACE_H_ +#define PROBABILITY_FACE_H_ + +#include "movement.h" + +typedef struct { +    uint8_t dice_sides; +    uint8_t rolled_value; +    uint8_t animation_frame; +    bool is_rolling; +} probability_state_t; + +void probability_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); +void probability_face_activate(movement_settings_t *settings, void *context); +bool probability_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void probability_face_resign(movement_settings_t *settings, void *context); + +#define probability_face ((const watch_face_t){ \ +    probability_face_setup, \ +    probability_face_activate, \ +    probability_face_loop, \ +    probability_face_resign, \ +    NULL, \ +}) + +#endif // PROBABILITY_FACE_H_ + diff --git a/movement/watch_faces/complication/wake_face.c b/movement/watch_faces/complication/wake_face.c new file mode 100644 index 00000000..4c265c75 --- /dev/null +++ b/movement/watch_faces/complication/wake_face.c @@ -0,0 +1,159 @@ +/* + * MIT License + * + * Copyright (c) 2022 Josh Berson, building on Wesley Ellis’ countdown_face.c + * + * 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 <threads.h> + +#include "wake_face.h" +#include "watch.h" +#include "watch_utility.h" + +/* +    UI Notes +    º Light advances hour by 1 +    º Light long press advances hour by 6 +    º Alarm advances minute by 10 +    º Alarm long press cycles through signal modes (just one at the moment) +*/ + +// +// Private +// + +static +void _wake_face_update_display(movement_settings_t *settings, wake_face_state_t *state) { +    (void) settings; +    uint8_t hour = state->hour; + +    watch_clear_display(); +    if ( settings->bit.clock_mode_24h ) +        watch_set_indicator(WATCH_INDICATOR_24H); +    else { +        if ( hour >= 12 ) +            watch_set_indicator(WATCH_INDICATOR_PM); +        hour = hour % 12 ? hour % 12 : 12; +    } + +    if ( state->mode ) +        watch_set_indicator(WATCH_INDICATOR_BELL); + +    static char lcdbuf[11]; +    sprintf(lcdbuf, "WA  %2d%02d  ", hour, state->minute); + +    watch_set_colon(); +    watch_display_string(lcdbuf, 0); +} + +// +// Exported +// + +void wake_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(wake_face_state_t)); +        wake_face_state_t *state = (wake_face_state_t *)*context_ptr; +        memset(*context_ptr, 0, sizeof(wake_face_state_t)); + +        state->hour = 5; +        state->minute = 0; +        state->mode = 0; +    } +} + +void wake_face_activate(movement_settings_t *settings, void *context) { +    (void) settings; +    (void) context; +} +void wake_face_resign(movement_settings_t *settings, void *context) { +    (void) settings; +    (void) context; +} + +bool wake_face_wants_background_task(movement_settings_t *settings, void *context) { +    (void) settings; +    wake_face_state_t *state = (wake_face_state_t *)context; + +    bool rc = false; +    if ( state->mode ) { +        watch_date_time now = watch_rtc_get_date_time(); +        rc = state->hour==now.unit.hour && state->minute==now.unit.minute; +        // We’re at the mercy of the wants_background_task handler +        // In Safari, the emulator triggers at the ›end‹ of the minute +        // Converting to Unix timestamps and taking a difference between now and wake +        // is not an easy win — because the timestamp for wake has to rely on now +        // for its date. So first we’d have to see if the TOD of wake is after that +        // of now. If it is, take tomorrow’s date, calculating month and year rollover +        // if need be. +    } +    return rc; +} + +bool wake_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { +    (void) settings; +    wake_face_state_t *state = (wake_face_state_t *)context; + +    switch (event.event_type) { +    case EVENT_ACTIVATE: +    case EVENT_TICK: +        _wake_face_update_display(settings, state); +        break; +    case EVENT_LIGHT_BUTTON_UP: +        state->hour = (state->hour + 1) % 24; +        _wake_face_update_display(settings, state); +        break; +    case EVENT_LIGHT_LONG_PRESS: +        state->hour = (state->hour + 6) % 24; +        _wake_face_update_display(settings, state); +        break; +    case EVENT_ALARM_BUTTON_UP: +        state->minute = (state->minute + 10) % 60; +        _wake_face_update_display(settings, state); +        break; +    case EVENT_ALARM_LONG_PRESS: +        state->mode ^= 1; +        _wake_face_update_display(settings, state); +        break; +    case EVENT_BACKGROUND_TASK: +        movement_play_alarm(); +            // 2022-07-23: Thx @joeycastillo for the dedicated “alarm” signal +        break; +    case EVENT_MODE_BUTTON_UP: +        movement_move_to_next_face(); +        break; +    case EVENT_TIMEOUT: +        movement_move_to_face(0); +        break; +    case EVENT_LOW_ENERGY_UPDATE: +    default: +      break; +    } + +    return true; +}
\ No newline at end of file diff --git a/movement/watch_faces/complication/wake_face.h b/movement/watch_faces/complication/wake_face.h new file mode 100644 index 00000000..c091c8f3 --- /dev/null +++ b/movement/watch_faces/complication/wake_face.h @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (c) 2022 Josh Berson + * + * 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 WAKE_FACE_H_ +#define WAKE_FACE_H_ + +#include "movement.h" + +typedef struct { +    uint32_t hour : 5; +    uint32_t minute : 6; +    uint32_t mode : 1; +} wake_face_state_t; + +void wake_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr); +void wake_face_activate(movement_settings_t *settings, void *context); +bool wake_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void wake_face_resign(movement_settings_t *settings, void *context); +bool wake_face_wants_background_task(movement_settings_t *settings, void *context); + +#define wake_face ((const watch_face_t){ \ +    wake_face_setup, \ +    wake_face_activate, \ +    wake_face_loop, \ +    wake_face_resign, \ +    wake_face_wants_background_task \ +}) + +#endif // WAKE_FACE_H_ + @@ -7,9 +7,9 @@ SUBMODULES = tinyusb  COBRA = cobra -f  ifndef EMSCRIPTEN -all: directory $(SUBMODULES) $(BUILD)/$(BIN).elf $(BUILD)/$(BIN).hex $(BUILD)/$(BIN).bin $(BUILD)/$(BIN).uf2 size +all: $(BUILD)/$(BIN).elf $(BUILD)/$(BIN).hex $(BUILD)/$(BIN).bin $(BUILD)/$(BIN).uf2 size  else -all: directory $(SUBMODULES) $(BUILD)/$(BIN).html +all: $(BUILD)/$(BIN).html  endif  $(BUILD)/$(BIN).html: $(OBJS) @@ -35,13 +35,14 @@ $(BUILD)/$(BIN).uf2: $(BUILD)/$(BIN).bin  	@echo UF2CONV $@  	@$(UF2) $^ -co $@ +.phony: $(SUBMODULES)  $(SUBMODULES):  	git submodule update --init  install:  	@$(UF2) -D $(BUILD)/$(BIN).uf2 -%.o: +$(BUILD)/%.o: | $(SUBMODULES) directory  	@echo CC $@  	@$(CC) $(CFLAGS) $(filter %/$(subst .o,.c,$(notdir $@)), $(SRCS)) -c -o $@ @@ -59,4 +60,6 @@ clean:  analyze:  	@$(COBRA) basic $(INCLUDES) $(DEFINES) $(SRCS) --include $(wildcard $(BUILD)/*.d) +DEPFILES := $(SRCS:%.c=$(BUILD)/%.d) + +-include $(wildcard $(DEPFILES)) diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c index a197e522..cd607b8e 100644 --- a/watch-library/hardware/watch/watch_private.c +++ b/watch-library/hardware/watch/watch_private.c @@ -142,7 +142,7 @@ void _watch_enable_tcc(void) {      #endif      // The buzzer will set the period depending on the tone it wants to play, but we have to set some period here to      // get the LED working. Almost any period will do, tho it should be below 20000 (i.e. 50 Hz) to avoid flickering. -    hri_tcc_write_PER_reg(TCC0, 4096); +    hri_tcc_write_PER_reg(TCC0, 1024);      // Set the duty cycle of all pins to 0: LED's off, buzzer not buzzing.      hri_tcc_write_CC_reg(TCC0, WATCH_BUZZER_TCC_CHANNEL, 0);      hri_tcc_write_CC_reg(TCC0, WATCH_RED_TCC_CHANNEL, 0);  | 
