diff --git a/movement/movement.c b/movement/movement.c index 09ebf0c..c3b096e 100644 --- a/movement/movement.c +++ b/movement/movement.c @@ -22,6 +22,8 @@ * SOFTWARE. */ +#define MOVEMENT_LONG_PRESS_TICKS 64 + #include #include #include @@ -250,11 +252,16 @@ void movement_play_signal(void) { } void movement_play_alarm(void) { + movement_play_alarm_beeps(5, BUZZER_NOTE_C8); +} + +void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note) { + if (rounds == 0) rounds = 1; + if (rounds > 20) rounds = 20; 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_state.alarm_note = alarm_note; + // our tone is 0.375 seconds of beep and 0.625 of silence, repeated as given. + movement_state.alarm_ticks = 128 * rounds - 75; _movement_enable_fast_tick_if_needed(); } @@ -453,10 +460,13 @@ bool app_loop(void) { if (movement_state.alarm_ticks >= 0) { uint8_t buzzer_phase = (movement_state.alarm_ticks + 80) % 128; if(buzzer_phase == 127) { + // failsafe: buzzer could have been disabled in the meantime + if (!watch_is_buzzer_or_led_enabled()) watch_enable_buzzer(); + // play 4 beeps plus pause for(uint8_t i = 0; i < 4; i++) { // TODO: This method of playing the buzzer blocks the UI while it's beeping. // It might be better to time it with the fast tick. - watch_buzzer_play_note(BUZZER_NOTE_C8, (i != 3) ? 50 : 75); + watch_buzzer_play_note(movement_state.alarm_note, (i != 3) ? 50 : 75); if (i != 3) watch_buzzer_play_note(BUZZER_NOTE_REST, 50); } } @@ -503,7 +513,7 @@ bool app_loop(void) { return can_sleep; } -static movement_event_type_t _figure_out_button_event(bool pin_level, movement_event_type_t button_down_event_type, uint8_t *down_timestamp) { +static movement_event_type_t _figure_out_button_event(bool pin_level, movement_event_type_t button_down_event_type, uint16_t *down_timestamp) { // force alarm off if the user pressed a button. if (movement_state.alarm_ticks) movement_state.alarm_ticks = 0; @@ -513,15 +523,15 @@ static movement_event_type_t _figure_out_button_event(bool pin_level, movement_e *down_timestamp = movement_state.fast_ticks + 1; return button_down_event_type; } else { - // this line is hack but it handles the situation where the light button was held for more than 10 seconds. + // this line is hack but it handles the situation where the light button was held for more than 20 seconds. // fast tick is disabled by then, and the LED would get stuck on since there's no one left decrementing light_ticks. if (movement_state.light_ticks == 1) movement_state.light_ticks = 0; // 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. - if (diff > 64) return button_down_event_type + 2; + // 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; } } @@ -557,9 +567,21 @@ void cb_fast_tick(void) { movement_state.fast_ticks++; if (movement_state.light_ticks > 0) movement_state.light_ticks--; if (movement_state.alarm_ticks > 0) movement_state.alarm_ticks--; + // check timestamps and auto-fire the long-press events + // Notice: is it possible that two or more buttons have an identical timestamp? In this case + // only one of these buttons would receive the long press event. Don't bother for now... + if (movement_state.light_down_timestamp > 0) + if (movement_state.fast_ticks - movement_state.light_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) + event.event_type = EVENT_LIGHT_LONG_PRESS; + if (movement_state.mode_down_timestamp > 0) + if (movement_state.fast_ticks - movement_state.mode_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) + event.event_type = EVENT_MODE_LONG_PRESS; + if (movement_state.alarm_down_timestamp > 0) + if (movement_state.fast_ticks - movement_state.alarm_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) + event.event_type = EVENT_ALARM_LONG_PRESS; // this is just a fail-safe; fast tick should be disabled as soon as the button is up, the LED times out, and/or the alarm finishes. - // but if for whatever reason it isn't, this forces the fast tick off after 10 seconds. - if (movement_state.fast_ticks >= 1280) watch_rtc_disable_periodic_callback(128); + // but if for whatever reason it isn't, this forces the fast tick off after 20 seconds. + if (movement_state.fast_ticks >= 128 * 20) watch_rtc_disable_periodic_callback(128); } void cb_tick(void) { diff --git a/movement/movement.h b/movement/movement.h index 79222e8..6cf8cf0 100644 --- a/movement/movement.h +++ b/movement/movement.h @@ -61,7 +61,8 @@ typedef union { // altimeter to display feet or meters as easily as it tells a thermometer to display degrees in F or C. bool clock_mode_24h : 1; // indicates whether clock should use 12 or 24 hour mode. bool use_imperial_units : 1; // indicates whether to use metric units (the default) or imperial. - uint8_t reserved : 7; // room for more preferences if needed. + bool alarm_enabled : 1; // indicates whether there is at least one alarm enabled. + uint8_t reserved : 6; // room for more preferences if needed. } bit; uint32_t reg; } movement_settings_t; @@ -109,13 +110,16 @@ typedef enum { EVENT_TIMEOUT, // Your watch face has been inactive for a while. You may want to resign, depending on your watch face's intended use case. EVENT_LIGHT_BUTTON_DOWN, // The light button has been pressed, but not yet released. EVENT_LIGHT_BUTTON_UP, // The light button was pressed and released. - EVENT_LIGHT_LONG_PRESS, // The light button was held for >2 seconds, and released. + EVENT_LIGHT_LONG_PRESS, // The light button was held for >2 seconds, but not yet released. + EVENT_LIGHT_LONG_UP, // The light button was held for >2 seconds, and released. EVENT_MODE_BUTTON_DOWN, // The mode button has been pressed, but not yet released. EVENT_MODE_BUTTON_UP, // The mode button was pressed and released. - EVENT_MODE_LONG_PRESS, // The mode button was held for >2 seconds, and released. NOTE: your watch face will resign immediately after receiving this event. + EVENT_MODE_LONG_PRESS, // The mode button was held for >2 seconds, but not yet released. + EVENT_MODE_LONG_UP, // The mode button was held for >2 seconds, and released. NOTE: your watch face will resign immediately after receiving this event. EVENT_ALARM_BUTTON_DOWN, // The alarm button has been pressed, but not yet released. EVENT_ALARM_BUTTON_UP, // The alarm button was pressed and released. - EVENT_ALARM_LONG_PRESS, // The alarm button was held for >2 seconds, and released. + EVENT_ALARM_LONG_PRESS, // The alarm button was held for >2 seconds, but not yet released. + EVENT_ALARM_LONG_UP, // The alarm button was held for >2 seconds, and released. } movement_event_type_t; typedef struct { @@ -252,11 +256,12 @@ typedef struct { // alarm stuff int16_t alarm_ticks; bool is_buzzing; + BuzzerNote alarm_note; // button tracking for long press - uint8_t light_down_timestamp; - uint8_t mode_down_timestamp; - uint8_t alarm_down_timestamp; + uint16_t light_down_timestamp; + uint16_t mode_down_timestamp; + uint16_t alarm_down_timestamp; // background task handling bool needs_background_tasks_handled; @@ -300,6 +305,7 @@ void movement_request_wake(void); void movement_play_signal(void); void movement_play_alarm(void); +void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note); uint8_t movement_claim_backup_register(void);