Merge branch 'next'
This merge adds numerous individually small fixes and improvements which amount to a sizeable set of features. - New hourly chime tunes added - Old hourly chime tunes improved - Display of characters on the stock LCD improved - Documentation improved - Simulator improved - Leap year handling improved - Months and their days sanity checked and fixed - More compile time configurable defaults added - Excessively exact time checks relaxed - Clock face indicators matched to original watch - Hardware interface issue fixed The most significant new feature however is software debouncing. The sensor watch now properly handles hardware switch bouncing, making the button inputs much more precise at the cost of a small amount of latency, greatly improving usability. Any watch faces which require holding down buttons as part of their user interface, the pulsometer for example, should see huge improvements in their usability. * 9c093f9 Merge PR #387 - configurable default birthdate/location * 879c48c Merge PR #417 - improve 24h only mode * db4097b Merge PR #426 - add temperature input to simulator * dea0566 Merge PR #428 - fix issues in sunrise/sunset * c8ca0d3 Merge PR #431 - fix wrong number of days in month * 95ca374 Merge PR #433 - fix clock face indicators * 663cd72 Merge PR #434 - fix leap years * a715265 Merge PR #437 - debouncing logic * c741332 Merge PR #439 - fix scheduled task misses * 657ff72 Merge PR #440 - fix countdown face issues * c8a87d3 Merge PR #441 - update documentation * dd04443 Merge PR #443 - improved t and y character display * 42dc151 Merge PR #447 - improve kim possible chime * fa0cdef Merge PR #450 - sync after enabling RTC * a67076f Merge PR #458 - add layla tune * 23c422b Merge PR #459 - add power rangers tune * a2e5417 Merge PR #461 - improve t/y special case docs Tested-on-hardware-by: Alex Maestas <git@se30.xyz> Tested-on-hardware-by: Matheus Afonso Martins Moreira <matheus@matheusmoreira.com> Tested-on-hardware-by: Wesley Ellis <tahnok@gmail.com> GitHub-Pull-Request: https://github.com/joeycastillo/Sensor-Watch/pull/460
This commit is contained in:
commit
d4bd10ba5e
@ -2,6 +2,7 @@
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
const int8_t UTC_OFFSET = 4; // set to your current UTC offset to see correct beats time
|
||||
const uint8_t BEAT_REFRESH_FREQUENCY = 8;
|
||||
@ -203,7 +204,6 @@ void set_time_mode_handle_primary_button(void) {
|
||||
|
||||
void set_time_mode_handle_secondary_button(void) {
|
||||
watch_date_time date_time = watch_rtc_get_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
|
||||
@ -224,13 +224,10 @@ void set_time_mode_handle_secondary_button(void) {
|
||||
break;
|
||||
case 5: // day
|
||||
date_time.unit.day = date_time.unit.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.unit.day > days_in_month[date_time.unit.month - 1]) {
|
||||
date_time.unit.day = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (date_time.unit.day > days_in_month(date_time.unit.month, date_time.unit.year + WATCH_RTC_REFERENCE_YEAR))
|
||||
date_time.unit.day = 1;
|
||||
watch_rtc_set_date_time(date_time);
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,16 @@
|
||||
*/
|
||||
|
||||
#define MOVEMENT_LONG_PRESS_TICKS 64
|
||||
#define DEBOUNCE_TICKS_DOWN 0
|
||||
#define DEBOUNCE_TICKS_UP 0
|
||||
/*
|
||||
DEBOUNCE_TICKS_DOWN and DEBOUNCE_TICKS_UP are in terms of fast_cb ticks after a button is pressed.
|
||||
The logic is that pressed of a button are ignored until the cb_fast_tick function runs this variable amount of times.
|
||||
Without modifying the code, the cb_fast_tick frequency is 128Hz, or 7.8125ms.
|
||||
It is not suggested to set this value to one for debouncing, as the callback occurs asynchronously of the button's press,
|
||||
meaning that if a button was pressed and 7ms passed since th elast time cb_fast_tick was called, then there will be only 812.5us
|
||||
of debounce time.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -95,6 +105,31 @@
|
||||
#define MOVEMENT_DEFAULT_LED_DURATION 1
|
||||
#endif
|
||||
|
||||
// Default to no set location latitude
|
||||
#ifndef MOVEMENT_DEFAULT_LATITUDE
|
||||
#define MOVEMENT_DEFAULT_LATITUDE 0
|
||||
#endif
|
||||
|
||||
// Default to no set location longitude
|
||||
#ifndef MOVEMENT_DEFAULT_LONGITUDE
|
||||
#define MOVEMENT_DEFAULT_LONGITUDE 0
|
||||
#endif
|
||||
|
||||
// Default to no set birthdate year
|
||||
#ifndef MOVEMENT_DEFAULT_BIRTHDATE_YEAR
|
||||
#define MOVEMENT_DEFAULT_BIRTHDATE_YEAR 0
|
||||
#endif
|
||||
|
||||
// Default to no set birthdate month
|
||||
#ifndef MOVEMENT_DEFAULT_BIRTHDATE_MONTH
|
||||
#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0
|
||||
#endif
|
||||
|
||||
// Default to no set birthdate day
|
||||
#ifndef MOVEMENT_DEFAULT_BIRTHDATE_DAY
|
||||
#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
|
||||
#endif
|
||||
|
||||
#if __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
@ -169,6 +204,9 @@ static inline void _movement_reset_inactivity_countdown(void) {
|
||||
static inline void _movement_enable_fast_tick_if_needed(void) {
|
||||
if (!movement_state.fast_tick_enabled) {
|
||||
movement_state.fast_ticks = 0;
|
||||
movement_state.debounce_ticks_light = 0;
|
||||
movement_state.debounce_ticks_alarm = 0;
|
||||
movement_state.debounce_ticks_mode = 0;
|
||||
watch_rtc_register_periodic_callback(cb_fast_tick, 128);
|
||||
movement_state.fast_tick_enabled = true;
|
||||
}
|
||||
@ -177,6 +215,7 @@ static inline void _movement_enable_fast_tick_if_needed(void) {
|
||||
static inline void _movement_disable_fast_tick_if_possible(void) {
|
||||
if ((movement_state.light_ticks == -1) &&
|
||||
(movement_state.alarm_ticks == -1) &&
|
||||
((movement_state.debounce_ticks_light + movement_state.debounce_ticks_mode + movement_state.debounce_ticks_alarm) == 0) &&
|
||||
((movement_state.light_down_timestamp + movement_state.mode_down_timestamp + movement_state.alarm_down_timestamp) == 0)) {
|
||||
movement_state.fast_tick_enabled = false;
|
||||
watch_rtc_disable_periodic_callback(128);
|
||||
@ -201,7 +240,7 @@ static void _movement_handle_scheduled_tasks(void) {
|
||||
|
||||
for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) {
|
||||
if (scheduled_tasks[i].reg) {
|
||||
if (scheduled_tasks[i].reg == date_time.reg) {
|
||||
if (scheduled_tasks[i].reg <= date_time.reg) {
|
||||
scheduled_tasks[i].reg = 0;
|
||||
movement_event_t background_event = { EVENT_BACKGROUND_TASK, 0 };
|
||||
watch_faces[i].loop(background_event, &movement_state.settings, watch_face_contexts[i]);
|
||||
@ -328,6 +367,14 @@ static void end_buzzing_and_disable_buzzer(void) {
|
||||
watch_disable_buzzer();
|
||||
}
|
||||
|
||||
static void set_initial_clock_mode(void) {
|
||||
#ifdef CLOCK_FACE_24H_ONLY
|
||||
movement_state.settings.bit.clock_mode_24h = true;
|
||||
#else
|
||||
movement_state.settings.bit.clock_mode_24h = MOVEMENT_DEFAULT_24H_MODE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void movement_play_signal(void) {
|
||||
void *maybe_disable_buzzer = end_buzzing_and_disable_buzzer;
|
||||
if (watch_is_buzzer_or_led_enabled()) {
|
||||
@ -376,14 +423,18 @@ void app_init(void) {
|
||||
#endif
|
||||
|
||||
memset(&movement_state, 0, sizeof(movement_state));
|
||||
|
||||
movement_state.settings.bit.clock_mode_24h = MOVEMENT_DEFAULT_24H_MODE;
|
||||
set_initial_clock_mode();
|
||||
movement_state.settings.bit.led_red_color = MOVEMENT_DEFAULT_RED_COLOR;
|
||||
movement_state.settings.bit.led_green_color = MOVEMENT_DEFAULT_GREEN_COLOR;
|
||||
movement_state.settings.bit.button_should_sound = MOVEMENT_DEFAULT_BUTTON_SOUND;
|
||||
movement_state.settings.bit.to_interval = MOVEMENT_DEFAULT_TIMEOUT_INTERVAL;
|
||||
movement_state.settings.bit.le_interval = MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL;
|
||||
movement_state.settings.bit.led_duration = MOVEMENT_DEFAULT_LED_DURATION;
|
||||
movement_state.location.bit.latitude = MOVEMENT_DEFAULT_LATITUDE;
|
||||
movement_state.location.bit.longitude = MOVEMENT_DEFAULT_LONGITUDE;
|
||||
movement_state.birthdate.bit.year = MOVEMENT_DEFAULT_BIRTHDATE_YEAR;
|
||||
movement_state.birthdate.bit.month = MOVEMENT_DEFAULT_BIRTHDATE_MONTH;
|
||||
movement_state.birthdate.bit.day = MOVEMENT_DEFAULT_BIRTHDATE_DAY;
|
||||
movement_state.light_ticks = -1;
|
||||
movement_state.alarm_ticks = -1;
|
||||
movement_state.next_available_backup_register = 4;
|
||||
@ -406,10 +457,14 @@ void app_init(void) {
|
||||
|
||||
void app_wake_from_backup(void) {
|
||||
movement_state.settings.reg = watch_get_backup_data(0);
|
||||
movement_state.location.reg = watch_get_backup_data(1);
|
||||
movement_state.birthdate.reg = watch_get_backup_data(2);
|
||||
}
|
||||
|
||||
void app_setup(void) {
|
||||
watch_store_backup_data(movement_state.settings.reg, 0);
|
||||
watch_store_backup_data(movement_state.location.reg, 1);
|
||||
watch_store_backup_data(movement_state.birthdate.reg, 2);
|
||||
|
||||
static bool is_first_launch = true;
|
||||
|
||||
@ -462,6 +517,7 @@ void app_wake_from_standby(void) {
|
||||
|
||||
static void _sleep_mode_app_loop(void) {
|
||||
movement_state.needs_wake = false;
|
||||
movement_state.ignore_alarm_btn_after_sleep = true;
|
||||
// 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
|
||||
@ -627,29 +683,66 @@ static movement_event_type_t _figure_out_button_event(bool pin_level, movement_e
|
||||
// now that that's out of the way, handle falling edge
|
||||
uint16_t diff = movement_state.fast_ticks - *down_timestamp;
|
||||
*down_timestamp = 0;
|
||||
_movement_disable_fast_tick_if_possible();
|
||||
// any press over a half second is considered a long press. Fire the long-up event
|
||||
if (diff > MOVEMENT_LONG_PRESS_TICKS) return button_down_event_type + 3;
|
||||
else return button_down_event_type + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void cb_light_btn_interrupt(void) {
|
||||
bool pin_level = watch_get_pin_level(BTN_LIGHT);
|
||||
static movement_event_type_t btn_action(bool pin_level, int code, uint16_t *timestamp) {
|
||||
_movement_reset_inactivity_countdown();
|
||||
event.event_type = _figure_out_button_event(pin_level, EVENT_LIGHT_BUTTON_DOWN, &movement_state.light_down_timestamp);
|
||||
return _figure_out_button_event(pin_level, code, timestamp);
|
||||
}
|
||||
|
||||
static void light_btn_action(bool pin_level) {
|
||||
event.event_type = btn_action(pin_level, EVENT_LIGHT_BUTTON_DOWN, &movement_state.light_down_timestamp);
|
||||
}
|
||||
|
||||
static void mode_btn_action(bool pin_level) {
|
||||
event.event_type = btn_action(pin_level, EVENT_MODE_BUTTON_DOWN, &movement_state.mode_down_timestamp);
|
||||
}
|
||||
|
||||
static void alarm_btn_action(bool pin_level) {
|
||||
uint8_t event_type = btn_action(pin_level, EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp);
|
||||
if (movement_state.ignore_alarm_btn_after_sleep){
|
||||
if (event_type == EVENT_ALARM_BUTTON_UP || event_type == EVENT_ALARM_LONG_UP) movement_state.ignore_alarm_btn_after_sleep = false;
|
||||
return;
|
||||
}
|
||||
event.event_type = event_type;
|
||||
}
|
||||
|
||||
static void debounce_btn_press(uint8_t pin, uint8_t *debounce_ticks, uint16_t *down_timestamp, void (*function)(bool)) {
|
||||
if (*debounce_ticks == 0) {
|
||||
bool pin_level = watch_get_pin_level(pin);
|
||||
function(pin_level);
|
||||
*debounce_ticks = pin_level ? DEBOUNCE_TICKS_DOWN : DEBOUNCE_TICKS_UP;
|
||||
if (*debounce_ticks != 0) _movement_enable_fast_tick_if_needed();
|
||||
}
|
||||
else
|
||||
*down_timestamp = 0;
|
||||
}
|
||||
|
||||
static void disable_if_needed(uint8_t *ticks) {
|
||||
if (*ticks > 0 && --*ticks == 0)
|
||||
_movement_disable_fast_tick_if_possible();
|
||||
}
|
||||
|
||||
static void movement_disable_if_debounce_complete(void) {
|
||||
disable_if_needed(&movement_state.debounce_ticks_light);
|
||||
disable_if_needed(&movement_state.debounce_ticks_alarm);
|
||||
disable_if_needed(&movement_state.debounce_ticks_mode);
|
||||
}
|
||||
|
||||
void cb_light_btn_interrupt(void) {
|
||||
debounce_btn_press(BTN_LIGHT, &movement_state.debounce_ticks_light, &movement_state.light_down_timestamp, light_btn_action);
|
||||
}
|
||||
|
||||
void cb_mode_btn_interrupt(void) {
|
||||
bool pin_level = watch_get_pin_level(BTN_MODE);
|
||||
_movement_reset_inactivity_countdown();
|
||||
event.event_type = _figure_out_button_event(pin_level, EVENT_MODE_BUTTON_DOWN, &movement_state.mode_down_timestamp);
|
||||
debounce_btn_press(BTN_MODE, &movement_state.debounce_ticks_mode, &movement_state.mode_down_timestamp, mode_btn_action);
|
||||
}
|
||||
|
||||
void cb_alarm_btn_interrupt(void) {
|
||||
bool pin_level = watch_get_pin_level(BTN_ALARM);
|
||||
_movement_reset_inactivity_countdown();
|
||||
event.event_type = _figure_out_button_event(pin_level, EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp);
|
||||
debounce_btn_press(BTN_ALARM, &movement_state.debounce_ticks_alarm, &movement_state.alarm_down_timestamp, alarm_btn_action);
|
||||
}
|
||||
|
||||
void cb_alarm_btn_extwake(void) {
|
||||
@ -662,6 +755,8 @@ void cb_alarm_fired(void) {
|
||||
}
|
||||
|
||||
void cb_fast_tick(void) {
|
||||
movement_disable_if_debounce_complete();
|
||||
if (movement_state.debounce_ticks_light + movement_state.debounce_ticks_mode + movement_state.debounce_ticks_alarm == 0)
|
||||
movement_state.fast_ticks++;
|
||||
if (movement_state.light_ticks > 0) movement_state.light_ticks--;
|
||||
if (movement_state.alarm_ticks > 0) movement_state.alarm_ticks--;
|
||||
|
@ -242,6 +242,8 @@ typedef struct {
|
||||
typedef struct {
|
||||
// properties stored in BACKUP register
|
||||
movement_settings_t settings;
|
||||
movement_location_t location;
|
||||
movement_birthdate_t birthdate;
|
||||
|
||||
// transient properties
|
||||
int16_t current_face_idx;
|
||||
@ -270,6 +272,10 @@ typedef struct {
|
||||
|
||||
// low energy mode countdown
|
||||
int32_t le_mode_ticks;
|
||||
uint8_t debounce_ticks_light;
|
||||
uint8_t debounce_ticks_alarm;
|
||||
uint8_t debounce_ticks_mode;
|
||||
bool ignore_alarm_btn_after_sleep;
|
||||
|
||||
// app resignation countdown (TODO: consolidate with LE countdown?)
|
||||
int16_t timeout_ticks;
|
||||
|
@ -76,15 +76,15 @@ const watch_face_t watch_faces[] = {
|
||||
/* Set the timeout before switching to low energy mode
|
||||
* Valid values are:
|
||||
* 0: Never
|
||||
* 1: 1 hour
|
||||
* 2: 2 hours
|
||||
* 3: 6 hours
|
||||
* 4: 12 hours
|
||||
* 5: 1 day
|
||||
* 6: 2 days
|
||||
* 1: 10 mins
|
||||
* 2: 1 hour
|
||||
* 3: 2 hours
|
||||
* 4: 6 hours
|
||||
* 5: 12 hours
|
||||
* 6: 1 day
|
||||
* 7: 7 days
|
||||
*/
|
||||
#define MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL 1
|
||||
#define MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL 2
|
||||
|
||||
/* Set the led duration
|
||||
* Valid values are:
|
||||
@ -95,4 +95,20 @@ const watch_face_t watch_faces[] = {
|
||||
*/
|
||||
#define MOVEMENT_DEFAULT_LED_DURATION 1
|
||||
|
||||
/* The latitude and longitude used for the wearers location
|
||||
* Set signed values in 1/100ths of a degree
|
||||
*/
|
||||
#define MOVEMENT_DEFAULT_LATITUDE 0
|
||||
#define MOVEMENT_DEFAULT_LONGITUDE 0
|
||||
|
||||
/* The wearers birthdate
|
||||
* Valid values:
|
||||
* Year: 1 - 4095
|
||||
* Month: 1 - 12
|
||||
* Day: 1 - 31
|
||||
*/
|
||||
#define MOVEMENT_DEFAULT_BIRTHDATE_YEAR 0
|
||||
#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0
|
||||
#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
|
||||
|
||||
#endif // MOVEMENT_CONFIG_H_
|
||||
|
@ -70,18 +70,53 @@ int8_t signal_tune[] = {
|
||||
#ifdef SIGNAL_TUNE_KIM_POSSIBLE
|
||||
int8_t signal_tune[] = {
|
||||
BUZZER_NOTE_G7, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_G4, 3,
|
||||
BUZZER_NOTE_G4, 2,
|
||||
BUZZER_NOTE_REST, 5,
|
||||
BUZZER_NOTE_G7, 6,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_G4, 3,
|
||||
BUZZER_NOTE_G4, 2,
|
||||
BUZZER_NOTE_REST, 5,
|
||||
BUZZER_NOTE_A7SHARP_B7FLAT, 6,
|
||||
BUZZER_NOTE_REST, 2,
|
||||
BUZZER_NOTE_G7, 6,
|
||||
BUZZER_NOTE_G4, 2,
|
||||
0
|
||||
};
|
||||
#endif // SIGNAL_TUNE_KIM_POSSIBLE
|
||||
|
||||
#ifdef SIGNAL_TUNE_POWER_RANGERS
|
||||
int8_t signal_tune[] = {
|
||||
BUZZER_NOTE_D8, 6,
|
||||
BUZZER_NOTE_REST, 8,
|
||||
BUZZER_NOTE_D8, 6,
|
||||
BUZZER_NOTE_REST, 8,
|
||||
BUZZER_NOTE_C8, 6,
|
||||
BUZZER_NOTE_REST, 2,
|
||||
BUZZER_NOTE_D8, 6,
|
||||
BUZZER_NOTE_REST, 8,
|
||||
BUZZER_NOTE_F8, 6,
|
||||
BUZZER_NOTE_REST, 8,
|
||||
BUZZER_NOTE_D8, 6,
|
||||
0
|
||||
};
|
||||
#endif // SIGNAL_TUNE_POWER_RANGERS
|
||||
|
||||
#ifdef SIGNAL_TUNE_LAYLA
|
||||
int8_t signal_tune[] = {
|
||||
BUZZER_NOTE_A6, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_C7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_D7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_F7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_D7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_C7, 5,
|
||||
BUZZER_NOTE_REST, 1,
|
||||
BUZZER_NOTE_D7, 20,
|
||||
0
|
||||
};
|
||||
#endif // SIGNAL_TUNE_LAYLA
|
||||
|
||||
#endif // MOVEMENT_CUSTOM_SIGNAL_TUNES_H_
|
||||
|
@ -42,10 +42,6 @@
|
||||
#define CLOCK_FACE_LOW_BATTERY_VOLTAGE_THRESHOLD 2200
|
||||
#endif
|
||||
|
||||
#ifndef CLOCK_FACE_24H_ONLY
|
||||
#define CLOCK_FACE_24H_ONLY 0
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
watch_date_time previous;
|
||||
@ -57,8 +53,11 @@ typedef struct {
|
||||
} clock_state_t;
|
||||
|
||||
static bool clock_is_in_24h_mode(movement_settings_t *settings) {
|
||||
if (CLOCK_FACE_24H_ONLY) { return true; }
|
||||
#ifdef CLOCK_FACE_24H_ONLY
|
||||
return true;
|
||||
#else
|
||||
return settings->bit.clock_mode_24h;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void clock_indicate(WatchIndicatorSegment indicator, bool on) {
|
||||
@ -70,11 +69,11 @@ static void clock_indicate(WatchIndicatorSegment indicator, bool on) {
|
||||
}
|
||||
|
||||
static void clock_indicate_alarm(movement_settings_t *settings) {
|
||||
clock_indicate(WATCH_INDICATOR_BELL, settings->bit.alarm_enabled);
|
||||
clock_indicate(WATCH_INDICATOR_SIGNAL, settings->bit.alarm_enabled);
|
||||
}
|
||||
|
||||
static void clock_indicate_time_signal(clock_state_t *clock) {
|
||||
clock_indicate(WATCH_INDICATOR_SIGNAL, clock->time_signal_enabled);
|
||||
clock_indicate(WATCH_INDICATOR_BELL, clock->time_signal_enabled);
|
||||
}
|
||||
|
||||
static void clock_indicate_24h(movement_settings_t *settings) {
|
||||
|
@ -51,7 +51,11 @@ void simple_clock_face_activate(movement_settings_t *settings, void *context) {
|
||||
|
||||
if (watch_tick_animation_is_running()) watch_stop_tick_animation();
|
||||
|
||||
#ifdef CLOCK_FACE_24H_ONLY
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
#else
|
||||
if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
#endif
|
||||
|
||||
// handle chime indicator
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
@ -106,6 +110,7 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting
|
||||
sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
|
||||
} else {
|
||||
// other stuff changed; let's do it all.
|
||||
#ifndef CLOCK_FACE_24H_ONLY
|
||||
if (!settings->bit.clock_mode_24h) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
if (date_time.unit.hour < 12) {
|
||||
@ -116,6 +121,7 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
}
|
||||
#endif
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
if (!watch_tick_animation_is_running()) watch_start_tick_animation(500);
|
||||
|
@ -87,6 +87,9 @@ static void draw(countdown_state_t *state, uint8_t subsecond) {
|
||||
|
||||
switch (state->mode) {
|
||||
case cd_running:
|
||||
if (state->target_ts <= state->now_ts)
|
||||
delta = 0;
|
||||
else
|
||||
delta = state->target_ts - state->now_ts;
|
||||
result = div(delta, 60);
|
||||
state->seconds = result.rem;
|
||||
@ -97,6 +100,7 @@ static void draw(countdown_state_t *state, uint8_t subsecond) {
|
||||
break;
|
||||
case cd_reset:
|
||||
case cd_paused:
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
sprintf(buf, "CD %2d%02d%02d", state->hours, state->minutes, state->seconds);
|
||||
break;
|
||||
case cd_setting:
|
||||
@ -130,7 +134,6 @@ static void pause(countdown_state_t *state) {
|
||||
static void reset(countdown_state_t *state) {
|
||||
state->mode = cd_reset;
|
||||
movement_cancel_background_task();
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
load_countdown(state);
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,7 @@
|
||||
#include <string.h>
|
||||
#include "day_one_face.h"
|
||||
#include "watch.h"
|
||||
|
||||
static const uint8_t days_in_month[12] = {31, 29, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31};
|
||||
#include "watch_utility.h"
|
||||
|
||||
static uint32_t _day_one_face_juliandaynum(uint16_t year, uint16_t month, uint16_t day) {
|
||||
// from here: https://en.wikipedia.org/wiki/Julian_day#Julian_day_number_calculation
|
||||
@ -66,13 +65,12 @@ static void _day_one_face_increment(day_one_state_t *state) {
|
||||
break;
|
||||
case PAGE_DAY:
|
||||
state->birth_day = state->birth_day + 1;
|
||||
if (state->birth_day == 0 || state->birth_day > days_in_month[state->birth_month - 1]) {
|
||||
state->birth_day = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (state->birth_day == 0 || state->birth_day > days_in_month(state->birth_month, state->birth_year))
|
||||
state->birth_day = 1;
|
||||
}
|
||||
|
||||
void day_one_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
|
||||
|
@ -49,7 +49,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s
|
||||
double rise, set, minutes, seconds;
|
||||
bool show_next_match = false;
|
||||
movement_location_t movement_location;
|
||||
if (state->longLatToUse == 0)
|
||||
if (state->longLatToUse == 0 || _location_count <= 1)
|
||||
movement_location = (movement_location_t) watch_get_backup_data(1);
|
||||
else{
|
||||
movement_location.bit.latitude = longLatPresets[state->longLatToUse].latitude;
|
||||
@ -359,7 +359,7 @@ bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *setti
|
||||
_sunrise_sunset_face_update_location_register(state);
|
||||
}
|
||||
_sunrise_sunset_face_update_settings_display(event, context);
|
||||
} else if (_location_count == 1) {
|
||||
} else if (_location_count <= 1) {
|
||||
movement_illuminate_led();
|
||||
}
|
||||
if (state->page == 0) {
|
||||
@ -368,7 +368,7 @@ bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *setti
|
||||
}
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
if (_location_count == 1) break;
|
||||
if (_location_count <= 1) break;
|
||||
else if (!state->page) movement_illuminate_led();
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "time_left_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_private_display.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
const char _state_titles[][3] = {{'D', 'L', ' '}, {'D', 'L', ' '}, {'D', 'A', ' '}, {'D', 'A', ' '}, {'Y', 'R', 'b'}, {'M', 'O', 'b'}, {'D', 'A', 'b'},
|
||||
{'Y', 'R', 'd'}, {'M', 'O', 'd'}, {'D', 'A', 'd'}};
|
||||
@ -158,8 +159,6 @@ static void _draw(time_left_state_t *state, uint8_t subsecond) {
|
||||
/// @brief handle short or long pressing the alarm button
|
||||
static void _handle_alarm_button(time_left_state_t *state) {
|
||||
|
||||
const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31};
|
||||
uint32_t tmp_day;
|
||||
switch (state->current_page) {
|
||||
case TIME_LEFT_FACE_SETTINGS_STATE: // birth year
|
||||
state->birth_date.bit.year++;
|
||||
@ -169,14 +168,7 @@ static void _handle_alarm_button(time_left_state_t *state) {
|
||||
state->birth_date.bit.month = (state->birth_date.bit.month % 12) + 1;
|
||||
break;
|
||||
case TIME_LEFT_FACE_SETTINGS_STATE + 2: // birth day
|
||||
tmp_day = state->birth_date.bit.day; // use a temporary variable to avoid messing up the months
|
||||
tmp_day++;
|
||||
// handle February 29th on a leap year
|
||||
if (((tmp_day > days_in_month[state->birth_date.bit.month - 1]) && (state->birth_date.bit.month != 2 || (state->birth_date.bit.year % 4) != 0))
|
||||
|| (state->birth_date.bit.month == 2 && (state->birth_date.bit.year % 4) == 0 && tmp_day > 29)) {
|
||||
tmp_day = 1;
|
||||
}
|
||||
state->birth_date.bit.day = tmp_day;
|
||||
state->birth_date.bit.day++;
|
||||
break;
|
||||
case TIME_LEFT_FACE_SETTINGS_STATE + 3: // target year
|
||||
state->target_date.bit.year++;
|
||||
@ -186,16 +178,13 @@ static void _handle_alarm_button(time_left_state_t *state) {
|
||||
state->target_date.bit.month = (state->target_date.bit.month % 12) + 1;
|
||||
break;
|
||||
case TIME_LEFT_FACE_SETTINGS_STATE + 5: // target day
|
||||
tmp_day = state->target_date.bit.day;
|
||||
tmp_day++;
|
||||
// handle February 29th on a leap year
|
||||
if (((tmp_day > days_in_month[state->target_date.bit.month - 1]) && (state->target_date.bit.month != 2 || (state->target_date.bit.year % 4) != 0))
|
||||
|| (state->target_date.bit.month == 2 && (state->target_date.bit.year % 4) == 0 && tmp_day > 29)) {
|
||||
tmp_day = 1;
|
||||
}
|
||||
state->target_date.bit.day = tmp_day;
|
||||
state->target_date.bit.day++;
|
||||
break;
|
||||
}
|
||||
if (state->birth_date.bit.day > days_in_month(state->birth_date.bit.month, state->birth_date.bit.year))
|
||||
state->birth_date.bit.day = 1;
|
||||
if (state->target_date.bit.day > days_in_month(state->target_date.bit.month, state->birth_date.bit.year))
|
||||
state->target_date.bit.day = 1;
|
||||
}
|
||||
|
||||
static void _initiate_setting(time_left_state_t *state) {
|
||||
|
@ -48,15 +48,19 @@
|
||||
* o SHA512
|
||||
*
|
||||
* Instructions:
|
||||
* o Find your secret key(s) and convert them to the required format.
|
||||
* o Use https://cryptii.com/pipes/base32-to-hex to convert base32 to hex
|
||||
* o Use https://github.com/susam/mintotp to generate test codes for verification
|
||||
* o Edit global variables in "totp_face.c" to configure your stored keys:
|
||||
* o "keys", "key_sizes", "timesteps", and "algorithms" set the
|
||||
* cryptographic parameters for each secret key.
|
||||
* o "labels" sets the two-letter label for each key
|
||||
* (This replaces the day-of-week indicator)
|
||||
* o Once finished, remove the two provided examples.
|
||||
* o Find your secret key(s).
|
||||
* o Use https://github.com/susam/mintotp to generate test codes for
|
||||
* verification
|
||||
* o Edit global `credentials` variable in "totp_face.c" to configure your
|
||||
* TOTP credentials. The file includes two examples that you can use as a
|
||||
* reference. Credentials are added with the `CREDENTIAL` macro in the form
|
||||
* `CREDENTIAL(label, key, algorithm, timestep)` where:
|
||||
* o `label` is a 2 character label that is displayed in the weekday digits
|
||||
* to identify the TOTP credential.
|
||||
* o `key` is a string with the base32 encoded secret.
|
||||
* o `algorithm` is one of the supported hashing algorithms listed above.
|
||||
* o `timestep` is how often the TOTP refreshes in seconds. This is usually
|
||||
* 30 seconds.
|
||||
*
|
||||
* If you have more than one secret key, press ALARM to cycle through them.
|
||||
* Press LIGHT to cycle in the other direction or keep it pressed longer to
|
||||
|
@ -26,8 +26,8 @@
|
||||
#include "preferences_face.h"
|
||||
#include "watch.h"
|
||||
|
||||
#define PREFERENCES_FACE_NUM_PREFEFENCES (7)
|
||||
const char preferences_face_titles[PREFERENCES_FACE_NUM_PREFEFENCES][11] = {
|
||||
#define PREFERENCES_FACE_NUM_PREFERENCES (7)
|
||||
const char preferences_face_titles[PREFERENCES_FACE_NUM_PREFERENCES][11] = {
|
||||
"CL ", // Clock: 12 or 24 hour
|
||||
"BT Beep ", // Buttons: should they beep?
|
||||
"TO ", // Timeout: how long before we snap back to the clock face?
|
||||
@ -65,7 +65,7 @@ bool preferences_face_loop(movement_event_t event, movement_settings_t *settings
|
||||
movement_move_to_next_face();
|
||||
return false;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
current_page = (current_page + 1) % PREFERENCES_FACE_NUM_PREFEFENCES;
|
||||
current_page = (current_page + 1) % PREFERENCES_FACE_NUM_PREFERENCES;
|
||||
*((uint8_t *)context) = current_page;
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
@ -99,7 +99,9 @@ bool preferences_face_loop(movement_event_t event, movement_settings_t *settings
|
||||
default:
|
||||
return movement_default_loop_handler(event, settings);
|
||||
}
|
||||
|
||||
#ifdef CLOCK_FACE_24H_ONLY
|
||||
if (current_page == 0) current_page++; // Skips past 12/24HR mode
|
||||
#endif
|
||||
watch_display_string((char *)preferences_face_titles[current_page], 0);
|
||||
|
||||
// blink active setting on even-numbered quarter-seconds
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <stdlib.h>
|
||||
#include "set_time_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
#define SET_TIME_FACE_NUM_SETTINGS (7)
|
||||
const char set_time_face_titles[SET_TIME_FACE_NUM_SETTINGS][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO"};
|
||||
@ -33,7 +34,6 @@ static bool _quick_ticks_running;
|
||||
|
||||
static void _handle_alarm_button(movement_settings_t *settings, watch_date_time date_time, uint8_t current_page) {
|
||||
// handles short or long pressing of the alarm button
|
||||
const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
switch (current_page) {
|
||||
case 0: // hour
|
||||
@ -52,14 +52,7 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time
|
||||
date_time.unit.month = (date_time.unit.month % 12) + 1;
|
||||
break;
|
||||
case 5: { // day
|
||||
uint32_t tmp_day = date_time.unit.day; // use a temporary variable to avoid messing up the months
|
||||
tmp_day = tmp_day + 1;
|
||||
// handle February 29th on a leap year
|
||||
if (((tmp_day > days_in_month[date_time.unit.month - 1]) && (date_time.unit.month != 2 || (date_time.unit.year % 4) != 0))
|
||||
|| (date_time.unit.month == 2 && (date_time.unit.year % 4) == 0 && tmp_day > 29)) {
|
||||
tmp_day = 1;
|
||||
}
|
||||
date_time.unit.day = tmp_day;
|
||||
date_time.unit.day = date_time.unit.day + 1;
|
||||
break;
|
||||
}
|
||||
case 6: // time zone
|
||||
@ -67,6 +60,8 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time
|
||||
if (settings->bit.time_zone > 40) settings->bit.time_zone = 0;
|
||||
break;
|
||||
}
|
||||
if (date_time.unit.day > days_in_month(date_time.unit.month, date_time.unit.year + WATCH_RTC_REFERENCE_YEAR))
|
||||
date_time.unit.day = 1;
|
||||
watch_rtc_set_date_time(date_time);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <stdlib.h>
|
||||
#include "set_time_hackwatch_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
char set_time_hackwatch_face_titles[][3] = {"HR", "M1", "SE", "YR", "MO", "DA", "ZO"};
|
||||
#define set_time_hackwatch_face_NUM_SETTINGS (sizeof(set_time_hackwatch_face_titles) / sizeof(*set_time_hackwatch_face_titles))
|
||||
@ -47,7 +48,6 @@ void set_time_hackwatch_face_activate(movement_settings_t *settings, void *conte
|
||||
|
||||
bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
|
||||
uint8_t current_page = *((uint8_t *)context);
|
||||
const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31};
|
||||
|
||||
if (event.subsecond == 15) // Delay displayed time update by ~0.5 seconds, to align phase exactly to main clock at 1Hz
|
||||
date_time_settings = watch_rtc_get_date_time();
|
||||
@ -119,10 +119,8 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s
|
||||
break;
|
||||
case 5: // day
|
||||
date_time_settings.unit.day = date_time_settings.unit.day - 2;
|
||||
// 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_settings.unit.day == 0) {
|
||||
date_time_settings.unit.day = days_in_month[date_time_settings.unit.month - 1];
|
||||
date_time_settings.unit.day = days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR);
|
||||
} else
|
||||
date_time_settings.unit.day++;
|
||||
break;
|
||||
@ -167,17 +165,14 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s
|
||||
break;
|
||||
case 5: // day
|
||||
date_time_settings.unit.day = date_time_settings.unit.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_settings.unit.day > days_in_month[date_time_settings.unit.month - 1]) {
|
||||
date_time_settings.unit.day = 1;
|
||||
}
|
||||
break;
|
||||
case 6: // time zone
|
||||
settings->bit.time_zone++;
|
||||
if (settings->bit.time_zone > 40) settings->bit.time_zone = 0;
|
||||
break;
|
||||
}
|
||||
if (date_time_settings.unit.day > days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR))
|
||||
date_time_settings.unit.day = 1;
|
||||
if (current_page != 2) // Do not set time when we are at seconds, it was already set previously
|
||||
watch_rtc_set_date_time(date_time_settings);
|
||||
//TODO: Do not update whole RTC, just what we are changing
|
||||
|
@ -77,6 +77,7 @@ void watch_register_extwake_callback(uint8_t pin, ext_irq_cb_t callback, bool le
|
||||
RTC->MODE2.TAMPCTRL.reg = config;
|
||||
// re-enable the RTC
|
||||
RTC->MODE2.CTRLA.bit.ENABLE = 1;
|
||||
while (RTC->MODE2.SYNCBUSY.bit.ENABLE); // wait for RTC to be enabled
|
||||
|
||||
NVIC_ClearPendingIRQ(RTC_IRQn);
|
||||
NVIC_EnableIRQ(RTC_IRQn);
|
||||
|
@ -45,7 +45,15 @@ void thermistor_driver_disable(void) {
|
||||
// Disable the enable pin's output circuitry.
|
||||
watch_disable_digital_output(THERMISTOR_ENABLE_PIN);
|
||||
}
|
||||
|
||||
#if __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
float thermistor_driver_get_temperature(void)
|
||||
{
|
||||
return EM_ASM_DOUBLE({
|
||||
return temp_c || 25.0;
|
||||
});
|
||||
}
|
||||
#else
|
||||
float thermistor_driver_get_temperature(void) {
|
||||
// set the enable pin to the level that powers the thermistor circuit.
|
||||
watch_set_pin_level(THERMISTOR_ENABLE_PIN, THERMISTOR_ENABLE_VALUE);
|
||||
@ -56,3 +64,4 @@ float thermistor_driver_get_temperature(void) {
|
||||
|
||||
return watch_utility_thermistor_temperature(value, THERMISTOR_HIGH_SIDE, THERMISTOR_B_COEFFICIENT, THERMISTOR_NOMINAL_TEMPERATURE, THERMISTOR_NOMINAL_RESISTANCE, THERMISTOR_SERIES_RESISTANCE);
|
||||
}
|
||||
#endif
|
||||
|
@ -43,6 +43,8 @@ void watch_display_character(uint8_t character, uint8_t position) {
|
||||
else if (character == 'M' || character == 'm' || character == 'N') character = 'n'; // M and uppercase N need to be lowercase n
|
||||
else if (character == 'c') character = 'C'; // C needs to be uppercase
|
||||
else if (character == 'J') character = 'j'; // same
|
||||
else if (character == 't' || character == 'T') character = '+'; // t in those locations looks like E otherwise
|
||||
else if (character == 'y' || character == 'Y') character = '4'; // y in those locations looks like g otherwise
|
||||
else if (character == 'v' || character == 'V' || character == 'U' || character == 'W' || character == 'w') character = 'u'; // bottom segment duplicated, so show in top half
|
||||
} else {
|
||||
if (character == 'u') character = 'v'; // we can use the bottom segment; move to lower half
|
||||
|
@ -315,3 +315,11 @@ uint32_t watch_utility_offset_timestamp(uint32_t now, int8_t hours, int8_t minut
|
||||
new += seconds;
|
||||
return new;
|
||||
}
|
||||
|
||||
uint8_t days_in_month(uint8_t month, uint16_t year) {
|
||||
static const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
uint8_t days = days_in_month[month - 1];
|
||||
if (month == 2 && is_leap(year))
|
||||
days += 1;
|
||||
return days;
|
||||
}
|
||||
|
@ -164,4 +164,10 @@ float watch_utility_thermistor_temperature(uint16_t value, bool highside, float
|
||||
*/
|
||||
uint32_t watch_utility_offset_timestamp(uint32_t now, int8_t hours, int8_t minutes, int8_t seconds);
|
||||
|
||||
/** @brief Returns the number of days in a month. It also handles Leap Years for February.
|
||||
* @param month The month of the date (1-12)
|
||||
* @param year The year of the date (ex. 2022)
|
||||
*/
|
||||
uint8_t days_in_month(uint8_t month, uint16_t year);
|
||||
|
||||
#endif
|
||||
|
@ -905,6 +905,11 @@
|
||||
<div>
|
||||
<button onclick="getLocation()">Set register (will prompt for access)</button>
|
||||
</div>
|
||||
<h2>Temp.</h2>
|
||||
<div>
|
||||
<input type="number" min="-100" max="120" id="temp-c" />C
|
||||
<button onclick="setTemp()">Set</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit="sendText(); return false" style="display: flex; flex-direction: column; width: 100%">
|
||||
@ -962,6 +967,7 @@
|
||||
lat = 0;
|
||||
lon = 0;
|
||||
tx = "";
|
||||
temp_c = 25.0;
|
||||
function updateLocation(location) {
|
||||
lat = Math.round(location.coords.latitude * 100);
|
||||
lon = Math.round(location.coords.longitude * 100);
|
||||
@ -1038,10 +1044,25 @@
|
||||
document.getElementById(skin).checked = true;
|
||||
setSkin(skin);
|
||||
}
|
||||
|
||||
function setTemp() {
|
||||
let tempInput = document.getElementById("temp-c");
|
||||
if (!tempInput) {
|
||||
return console.warn("no input found");
|
||||
}
|
||||
if (tempInput.value == "") {
|
||||
return console.warn("no value in input");
|
||||
}
|
||||
|
||||
try {
|
||||
temp_c = Number.parseFloat(tempInput.value);
|
||||
} catch (e) {
|
||||
return console.warn("input value is not a valid float:", tempInput.value, e);
|
||||
}
|
||||
}
|
||||
loadPrefs();
|
||||
</script>
|
||||
{{{ SCRIPT }}}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user