From b0703ec7778f8a7e4c9d6dd964b1773f3d59ce38 Mon Sep 17 00:00:00 2001 From: Wesley Ellis Date: Tue, 14 Sep 2021 18:52:15 -0400 Subject: Beats time app + watch Use BMP280 code as a base, but stripped down to bare bones. Then added a new 'mode' for the current Swatch Internet Time (aka beats) --- apps/beats-time/app.c | 269 ++++++++++++++++++++++++++++++++++++++++ apps/beats-time/make/.gitignore | 1 + apps/beats-time/make/Makefile | 24 ++++ 3 files changed, 294 insertions(+) create mode 100644 apps/beats-time/app.c create mode 100755 apps/beats-time/make/.gitignore create mode 100755 apps/beats-time/make/Makefile (limited to 'apps') diff --git a/apps/beats-time/app.c b/apps/beats-time/app.c new file mode 100644 index 00000000..c694bc5e --- /dev/null +++ b/apps/beats-time/app.c @@ -0,0 +1,269 @@ +#include +#include +#include +#include "watch.h" + +const uint8_t UTC_OFFSET = 4; // set to your current UTC offset to see correct beats time + +typedef enum ApplicationMode { + MODE_CLOCK = 0, // Displays month, day and current time. + MODE_BEATS, + MODE_SET, // (ST) Set time and date + NUM_MODES // Last item in the enum, it's the number of cases. +} ApplicationMode; + + +typedef struct ApplicationState { + // Internal application state + ApplicationMode mode; // Current mode + bool mode_changed; // Lets us perform one-time setup for a given mode + uint16_t mode_ticks; // Timeout for the mode (returns to clock after timeout expires) + uint8_t light_ticks; // Timeout for the light + bool led_on; // Indicates that the LED is on + uint8_t page; // Tracks the current page in log, prefs or settings. +} ApplicationState; + +void do_clock_mode(); +void do_beats_mode(); +void do_set_time_mode(); +void set_time_mode_handle_primary_button(); +void set_time_mode_handle_secondary_button(); + +uint16_t clock2beats(uint16_t, uint16_t, uint16_t, int16_t); + +void cb_light_pressed(); +void cb_mode_pressed(); +void cb_alarm_pressed(); +void cb_tick(); + +ApplicationState application_state; +char buf[16] = {0}; + +/** + * @brief Zeroes out the application state struct. + */ +void app_init() { + memset(&application_state, 0, sizeof(application_state)); +} + +void app_wake_from_deep_sleep() { + // This app does not support deep sleep mode. +} + +void app_setup() { + struct calendar_date_time date_time; + watch_get_date_time(&date_time); + if (date_time.date.year < 2020) { + date_time.date.year = 2020; + watch_set_date_time(date_time); + } + + watch_enable_external_interrupts(); + watch_register_interrupt_callback(BTN_MODE, cb_mode_pressed, INTERRUPT_TRIGGER_RISING); + watch_register_interrupt_callback(BTN_LIGHT, cb_light_pressed, INTERRUPT_TRIGGER_RISING); + watch_register_extwake_callback(BTN_ALARM, cb_alarm_pressed, true); + + watch_enable_buzzer(); + watch_enable_leds(); + watch_enable_display(); + + watch_register_tick_callback(cb_tick); +} + +void app_prepare_for_sleep() { +} + +void app_wake_from_sleep() { +} + +bool app_loop() { + // play a beep if the mode has changed in response to a user's press of the MODE button + if (application_state.mode_changed) { + // low note for nonzero case, high note for return to clock + watch_buzzer_play_note(application_state.mode ? BUZZER_NOTE_C7 : BUZZER_NOTE_C8, 100); + application_state.mode_changed = false; + } + + // If the user is not in clock mode and the mode timeout has expired, return them to clock mode + if (application_state.mode != MODE_CLOCK && application_state.mode_ticks == 0) { + application_state.mode = MODE_CLOCK; + application_state.mode_changed = true; + } + + // If the LED is off and should be on, turn it on + if (application_state.light_ticks > 0 && !application_state.led_on) { + watch_set_led_green(); + application_state.led_on = true; + } + + // if the LED is on and should be off, turn it off + if (application_state.led_on && application_state.light_ticks == 0) { + // unless the user is holding down the LIGHT button, in which case, give them more time. + if (watch_get_pin_level(BTN_LIGHT)) { + application_state.light_ticks = 3; + } else { + watch_set_led_off(); + application_state.led_on = false; + } + } + + switch (application_state.mode) { + case MODE_CLOCK: + do_clock_mode(); + break; + case MODE_BEATS: + do_beats_mode(); + break; + case MODE_SET: + do_set_time_mode(); + break; + case NUM_MODES: + // dummy case, just silences a warning + break; + } + + application_state.mode_changed = false; + + return true; +} + +void do_clock_mode() { + struct calendar_date_time date_time; + const char months[12][3] = {"JA", "FE", "MR", "AR", "MA", "JN", "JL", "AU", "SE", "OC", "NO", "dE"}; + + watch_get_date_time(&date_time); + watch_display_string((char *)months[date_time.date.month - 1], 0); + sprintf(buf, "%2d%2d%02d%02d", date_time.date.day, date_time.time.hour, date_time.time.min, date_time.time.sec); + watch_display_string(buf, 2); + watch_set_colon(); +} + +void do_beats_mode() { + watch_clear_colon(); + + struct calendar_date_time date_time; + + watch_get_date_time(&date_time); + + uint16_t beats = clock2beats(date_time.time.hour, date_time.time.min, date_time.time.sec, UTC_OFFSET); + sprintf(buf, "bt %04d ", beats); + + watch_display_string(buf, 0); +} + +uint16_t clock2beats(uint16_t hours, uint16_t minutes, uint16_t seconds, int16_t utc_offset) { + uint32_t beats = seconds; + beats += 60 * minutes; + beats += (uint32_t)hours * 60 * 60; + beats += (utc_offset + 1) * 60 * 60; // offset from utc + 1 since beats in in UTC+1 + + beats /= 86.4; // convert to beats + beats %= 1000; // truncate to 3 digits for overflow + + return (uint16_t) beats; +} + +void do_set_time_mode() { + struct calendar_date_time date_time; + + watch_get_date_time(&date_time); + watch_display_string(" ", 0); + switch (application_state.page) { + case 0: // hour + sprintf(buf, "ST t%2d", date_time.time.hour); + break; + case 1: // minute + sprintf(buf, "ST t %02d", date_time.time.min); + break; + case 2: // second + sprintf(buf, "ST t %02d", date_time.time.sec); + break; + case 3: // year + sprintf(buf, "ST d%2d", date_time.date.year - 2000); + break; + case 4: // month + sprintf(buf, "ST d %02d", date_time.date.month); + break; + case 5: // day + sprintf(buf, "ST d %02d", date_time.date.day); + break; + } + watch_display_string(buf, 0); + watch_set_pixel(1, 12); // required for T in position 1 +} + +void set_time_mode_handle_primary_button() { + application_state.page++; + if (application_state.page == 6) application_state.page = 0; +} + +void set_time_mode_handle_secondary_button() { + struct calendar_date_time date_time; + watch_get_date_time(&date_time); + const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31}; + + switch (application_state.page) { + case 0: // hour + date_time.time.hour = (date_time.time.hour + 1) % 24; + break; + case 1: // minute + date_time.time.min = (date_time.time.min + 1) % 60; + break; + case 2: // second + date_time.time.sec = 0; + break; + case 3: // year + // only allow 2021-2030. fix this sometime next decade + date_time.date.year = ((date_time.date.year % 10) + 1) + 2020; + break; + case 4: // month + date_time.date.month = ((date_time.date.month + 1) % 12); + break; + case 5: // day + date_time.date.day = date_time.date.day + 1; + // can't set to the 29th on a leap year. if it's february 29, set to 11:59 on the 28th. + // and it should roll over. + if (date_time.date.day > days_in_month[date_time.date.month - 1]) { + date_time.date.day = 1; + } + break; + } + watch_set_date_time(date_time); +} + +void cb_mode_pressed() { + application_state.mode = (application_state.mode + 1) % NUM_MODES; + application_state.mode_changed = true; + application_state.mode_ticks = 300; + application_state.page = 0; +} + +void cb_light_pressed() { + switch (application_state.mode) { + case MODE_SET: + set_time_mode_handle_secondary_button(); + break; + default: + application_state.light_ticks = 3; + break; + } +} + +void cb_alarm_pressed() { + switch (application_state.mode) { + case MODE_SET: + set_time_mode_handle_primary_button(); + break; + default: + break; + } +} + +void cb_tick() { + if (application_state.light_ticks > 0) { + application_state.light_ticks--; + } + if (application_state.mode_ticks > 0) { + application_state.mode_ticks--; + } +} diff --git a/apps/beats-time/make/.gitignore b/apps/beats-time/make/.gitignore new file mode 100755 index 00000000..3722ac63 --- /dev/null +++ b/apps/beats-time/make/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/apps/beats-time/make/Makefile b/apps/beats-time/make/Makefile new file mode 100755 index 00000000..112d8e50 --- /dev/null +++ b/apps/beats-time/make/Makefile @@ -0,0 +1,24 @@ +# Leave this line at the top of the file; it has all the watch library sources and includes. +TOP = ../../.. +include $(TOP)/make.mk + +# If you add any other subdirectories with header files you wish to include, add them after ../ +# Note that you will need to add a backslash at the end of any line you wish to continue, i.e. +# INCLUDES += \ +# -I../ \ +# -I../drivers/ \ +# -I../utils/ +INCLUDES += \ + -I../ \ + +# If you add any other source files you wish to compile, add them after ../app.c +# Note that you will need to add a backslash at the end of any line you wish to continue, i.e. +# SRCS += \ +# ../app.c \ +# ../drivers/bmp280.c \ +# ../utils/temperature.c +SRCS += \ + ../app.c \ + +# Leave this line at the bottom of the file; it has all the targets for making your project. +include $(TOP)/rules.mk -- cgit v1.2.3