mirror of
https://github.com/firewalkwithm3/Sensor-Watch.git
synced 2024-11-22 19:20:30 +08:00
Merge branch 'main' into repetition_minute
This commit is contained in:
commit
266831cef0
|
@ -96,6 +96,7 @@ SRCS += \
|
|||
../watch_faces/complication/morsecalc_face.c \
|
||||
../watch_faces/complication/rpn_calculator_face.c \
|
||||
../watch_faces/complication/ships_bell_face.c \
|
||||
../watch_faces/complication/habit_face.c \
|
||||
../watch_faces/clock/repetition_minute_face.c \
|
||||
# New watch faces go above this line.
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
#include "morsecalc_face.h"
|
||||
#include "rpn_calculator_face.h"
|
||||
#include "ships_bell_face.h"
|
||||
#include "habit_face.h"
|
||||
#include "repetition_minute_face.h"
|
||||
// New includes go above this line.
|
||||
|
||||
|
|
|
@ -225,7 +225,7 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
|
|||
abort_quick_ticks(state);
|
||||
movement_move_to_next_face();
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
switch(state->mode) {
|
||||
case cd_running:
|
||||
movement_illuminate_led();
|
||||
|
@ -278,6 +278,21 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
|
|||
movement_request_tick_frequency(8);
|
||||
}
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
if (state->mode == cd_setting) {
|
||||
switch (state->selection) {
|
||||
case 0:
|
||||
state->hours = 0;
|
||||
// intentional fallthrough
|
||||
case 1:
|
||||
state->minutes = 0;
|
||||
// intentional fallthrough
|
||||
case 2:
|
||||
state->seconds = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVENT_ALARM_LONG_UP:
|
||||
abort_quick_ticks(state);
|
||||
break;
|
||||
|
@ -289,6 +304,8 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
|
|||
movement_move_to_face(0);
|
||||
break;
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
// intentionally squelch the light default event; we only show the light when cd is running
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
break;
|
||||
default:
|
||||
movement_default_loop_handler(event, settings);
|
||||
|
|
|
@ -33,11 +33,15 @@ static uint32_t _day_one_face_juliandaynum(uint16_t year, uint16_t month, uint16
|
|||
}
|
||||
|
||||
static void _day_one_face_update(day_one_state_t state) {
|
||||
char buf[14];
|
||||
char buf[15];
|
||||
watch_date_time date_time = watch_rtc_get_date_time();
|
||||
uint32_t julian_date = _day_one_face_juliandaynum(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day);
|
||||
uint32_t julian_birthdate = _day_one_face_juliandaynum(state.birth_year, state.birth_month, state.birth_day);
|
||||
sprintf(buf, "DA %6lu", julian_date - julian_birthdate);
|
||||
if (julian_date < julian_birthdate) {
|
||||
sprintf(buf, "DA %6lu", julian_birthdate - julian_date);
|
||||
} else {
|
||||
sprintf(buf, "DA %6lu", julian_date - julian_birthdate);
|
||||
}
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
|
||||
|
|
157
movement/watch_faces/complication/habit_face.c
Normal file
157
movement/watch_faces/complication/habit_face.c
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 tslil clingman
|
||||
*
|
||||
* 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 "habit_face.h"
|
||||
#include "watch_private_display.h"
|
||||
#include "watch_rtc.h"
|
||||
#include "watch_slcd.h"
|
||||
#include "watch_utility.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static inline uint32_t today_unix(const uint32_t utc_offset) {
|
||||
const watch_date_time dt = watch_rtc_get_date_time();
|
||||
return watch_utility_convert_to_unix_time(dt.unit.year + 2020, dt.unit.month,
|
||||
dt.unit.day, 0, 0, 0, utc_offset);
|
||||
}
|
||||
|
||||
static inline uint32_t days_since_unix(const uint32_t since,
|
||||
const uint32_t until) {
|
||||
return (until - since) / (60 * 60 * 24);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint16_t total_count;
|
||||
uint8_t lookback;
|
||||
uint32_t last_update;
|
||||
bool display_total;
|
||||
} habit_state_t;
|
||||
|
||||
void habit_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(habit_state_t));
|
||||
memset(*context_ptr, 0, sizeof(habit_state_t));
|
||||
habit_state_t *state = (habit_state_t *)*context_ptr;
|
||||
state->lookback = 0;
|
||||
state->last_update = watch_utility_offset_timestamp(
|
||||
today_unix(settings->bit.time_zone), -24, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void display_state(habit_state_t *state) {
|
||||
const bool can_do = (state->lookback & 1) == 0;
|
||||
char buf[16];
|
||||
|
||||
if (can_do) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_LAP);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
}
|
||||
|
||||
if (state->display_total) {
|
||||
sprintf(buf, "HA %03dtot", state->total_count);
|
||||
watch_display_string(buf, 0);
|
||||
} else {
|
||||
sprintf(buf, "HA d%c", can_do ? 'o' : 'n');
|
||||
uint8_t copy = state->lookback;
|
||||
for (uint8_t c = 0; copy; copy >>= 2, c++) {
|
||||
switch (copy & 3) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
buf[4 + c] = 'I';
|
||||
break;
|
||||
case 2:
|
||||
buf[4 + c] = '1';
|
||||
break;
|
||||
case 3:
|
||||
buf[4 + c] = '|';
|
||||
break;
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, 0);
|
||||
if (can_do) {
|
||||
const uint64_t segmap = Segment_Map[4] >> 48;
|
||||
watch_set_pixel((segmap & 0xFF) >> 6, segmap & 0x3F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void habit_face_activate(movement_settings_t *settings, void *context) {
|
||||
(void)settings;
|
||||
habit_state_t *state = (habit_state_t *)context;
|
||||
display_state(state);
|
||||
}
|
||||
|
||||
bool habit_face_loop(movement_event_t event, movement_settings_t *settings,
|
||||
void *context) {
|
||||
habit_state_t *state = (habit_state_t *)context;
|
||||
|
||||
const uint32_t today_now_unix = today_unix(settings->bit.time_zone);
|
||||
const bool can_do = (state->lookback & 1) == 0;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK: {
|
||||
display_state(state);
|
||||
if (today_now_unix > state->last_update) {
|
||||
uint8_t num_shifts = days_since_unix(state->last_update, today_now_unix);
|
||||
if (num_shifts > 7)
|
||||
num_shifts = 7;
|
||||
state->lookback <<= num_shifts;
|
||||
state->last_update = today_now_unix;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVENT_LIGHT_BUTTON_UP: {
|
||||
state->display_total = !state->display_total;
|
||||
display_state(state);
|
||||
break;
|
||||
}
|
||||
case EVENT_ALARM_BUTTON_UP: {
|
||||
if (can_do) {
|
||||
state->lookback |= 1;
|
||||
state->total_count++;
|
||||
state->last_update = today_now_unix;
|
||||
display_state(state);
|
||||
};
|
||||
break;
|
||||
}
|
||||
case EVENT_TIMEOUT: {
|
||||
movement_move_to_face(0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return movement_default_loop_handler(event, settings);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void habit_face_resign(movement_settings_t *settings, void *context) {
|
||||
(void)settings;
|
||||
(void)context;
|
||||
}
|
53
movement/watch_faces/complication/habit_face.h
Normal file
53
movement/watch_faces/complication/habit_face.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 tslil clingman
|
||||
*
|
||||
* 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 HABIT_FACE_H_
|
||||
#define HABIT_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
* Habit tracking face
|
||||
*
|
||||
* Allows the user to record a single succesful instance of a particular habit
|
||||
* occuring per day, and displays history for eight days prior as well as a
|
||||
* total counter.
|
||||
*
|
||||
*/
|
||||
|
||||
void habit_face_setup(movement_settings_t *settings, uint8_t watch_face_index,
|
||||
void **context_ptr);
|
||||
void habit_face_activate(movement_settings_t *settings, void *context);
|
||||
bool habit_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
|
||||
void habit_face_resign(movement_settings_t *settings, void *context);
|
||||
|
||||
#define habit_face ((const watch_face_t){ \
|
||||
habit_face_setup, \
|
||||
habit_face_activate, \
|
||||
habit_face_loop, \
|
||||
habit_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // HABIT_FACE_H_
|
|
@ -129,15 +129,15 @@ static const int8_t _sound_seq_break[] = {BUZZER_NOTE_B6, 15, BUZZER_NOTE_REST,
|
|||
static const int8_t _sound_seq_cooldown[] = {BUZZER_NOTE_C7, 15, BUZZER_NOTE_REST, 1, -2, 1, BUZZER_NOTE_C7, 24, 0};
|
||||
static const int8_t _sound_seq_finish[] = {BUZZER_NOTE_C7, 6, BUZZER_NOTE_E7, 6, BUZZER_NOTE_G7, 6, BUZZER_NOTE_C8, 18, 0};
|
||||
|
||||
interval_setting_idx_t _setting_idx;
|
||||
int8_t _ticks;
|
||||
bool _erase_timer_flag;
|
||||
uint32_t _target_ts;
|
||||
uint32_t _now_ts;
|
||||
uint32_t _paused_ts;
|
||||
uint8_t _timer_work_round;
|
||||
uint8_t _timer_full_round;
|
||||
uint8_t _timer_run_state;
|
||||
static interval_setting_idx_t _setting_idx;
|
||||
static int8_t _ticks;
|
||||
static bool _erase_timer_flag;
|
||||
static uint32_t _target_ts;
|
||||
static uint32_t _now_ts;
|
||||
static uint32_t _paused_ts;
|
||||
static uint8_t _timer_work_round;
|
||||
static uint8_t _timer_full_round;
|
||||
static uint8_t _timer_run_state;
|
||||
|
||||
static inline void _inc_uint8(uint8_t *value, uint8_t step, uint8_t max) {
|
||||
*value += step;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Jan H. Voigt
|
||||
* Copyright (c) 2022 Wesley Ellis
|
||||
* Copyright (c) 2022 Niclas Hoyer
|
||||
*
|
||||
|
@ -31,6 +32,37 @@
|
|||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
/*
|
||||
|
||||
Implements a sailing timer.
|
||||
|
||||
Usage:
|
||||
|
||||
Waiting mode: Light button enters settings, alarm button starts the timer (sailing mode).
|
||||
|
||||
Sailing mode:
|
||||
Alarm button switches to next programmed start signal, long press on light button
|
||||
resets timer and enters waiting mode. Countdown to zero, then switch to counting mode.
|
||||
|
||||
Counting mode:
|
||||
After the start signal (0s), the duration of the race is counted (like a stopwatch timer).
|
||||
Alarm button increases the lap counter, alarm long press resets lap counter.
|
||||
Long press on light button resets timer and enters waiting mode.
|
||||
|
||||
Setting mode:
|
||||
Alarm button increases active (blinking) signal. Goes to 0 if upper boundary
|
||||
(11 or whatever the signal left to the active one is set to) is met.
|
||||
10 is printed vertically (letter o plus top segment).
|
||||
Alarm button long press resets to default minutes (5-4-1-0).
|
||||
Light button cycles through the signals.
|
||||
Long press on light button cycles through sound modes:
|
||||
- Bell indicator: Sound at start (0s) only.
|
||||
- Signal indicator: Sound at each programmed signal and at start.
|
||||
- Bell+Signal: Sound at each minute, at 30s and at 10s countdown.
|
||||
- No indicator: No sound.
|
||||
|
||||
*/
|
||||
|
||||
#define sl_SELECTIONS 6
|
||||
#define DEFAULT_MINUTES { 5,4,1,0,0,0 }
|
||||
|
||||
|
@ -38,30 +70,29 @@ static inline int32_t get_tz_offset(movement_settings_t *settings) {
|
|||
return movement_timezone_offsets[settings->bit.time_zone] * 60;
|
||||
}
|
||||
|
||||
static int lap = 0;
|
||||
bool ringflag = false;
|
||||
int8_t double_beep[] = {BUZZER_NOTE_C8, 4, BUZZER_NOTE_REST, 5, BUZZER_NOTE_C8, 5, 0};
|
||||
int8_t single_beep[] = {BUZZER_NOTE_C8, 4, 0};
|
||||
int8_t long_beep[] = {BUZZER_NOTE_C8, 40, 0};
|
||||
int beepseconds[] = {600, 540, 480, 420, 360, 300, 240, 180, 120, 60, 30, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; //seconds before start that can trigger the buzzer. Every whole minute, 30s before start & 10s countdown.
|
||||
int beepseconds_size = sizeof(beepseconds) / sizeof(int);
|
||||
int beepflag = 0;
|
||||
int alarmflag = 3;
|
||||
|
||||
static void reset(sailing_state_t *state) {
|
||||
state->index = 0;
|
||||
state->mode = sl_waiting;
|
||||
movement_cancel_background_task();
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_clear_indicator(WATCH_INDICATOR_LAP);
|
||||
beepflag = 0;
|
||||
ringflag = false;
|
||||
}
|
||||
|
||||
static void start(sailing_state_t *state, movement_settings_t *settings) {
|
||||
uint8_t minutes = state->minutes[state->index];
|
||||
if (minutes == 0) {
|
||||
reset(state);
|
||||
return;
|
||||
}
|
||||
if (state->index < 5) {
|
||||
minutes -= state->minutes[state->index+1];
|
||||
}
|
||||
|
||||
state->mode = sl_running;
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
|
||||
state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, minutes, 0);
|
||||
watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings));
|
||||
movement_schedule_background_task_for_face(state->watch_face_index, target_dt);
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
static void counting(sailing_state_t *state) {
|
||||
state->mode = sl_counting;
|
||||
movement_cancel_background_task();
|
||||
watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
}
|
||||
|
||||
static void draw(sailing_state_t *state, uint8_t subsecond, movement_settings_t *settings) {
|
||||
|
@ -72,36 +103,54 @@ static void draw(sailing_state_t *state, uint8_t subsecond, movement_settings_t
|
|||
|
||||
uint32_t delta;
|
||||
div_t result;
|
||||
uint8_t min, sec;
|
||||
uint8_t add = 0;
|
||||
uint8_t hrs, min, sec;
|
||||
|
||||
switch (state->mode) {
|
||||
case sl_running:
|
||||
if (state->index < 5) {
|
||||
add = state->minutes[state->index+1];
|
||||
}
|
||||
if (state->now_ts >= state->target_ts) {
|
||||
if (state->now_ts > state->target_ts) {
|
||||
delta = 0;
|
||||
counting(state); //in case buttons are pressed while sound is played (esp. in the last 10s), the timing of ring might be thrown off. Beep stops and the switch to counting doesn't occur. temporary fix/safety net.
|
||||
} else {
|
||||
delta = state->target_ts - state->now_ts;
|
||||
}
|
||||
result = div(delta, 60);
|
||||
min = result.quot + add;
|
||||
min = result.quot;
|
||||
sec = result.rem;
|
||||
|
||||
if (min > 0) {
|
||||
sprintf(buf, "SL %2d%02d", min, sec);
|
||||
sprintf(buf, "SA1L %2d%02d", min, sec);
|
||||
} else {
|
||||
sprintf(buf, "SL %2d ", sec);
|
||||
sprintf(buf, "SA1L %2d ", sec);
|
||||
}
|
||||
break;
|
||||
case sl_waiting:
|
||||
sprintf(buf, "SL %2d%02d", state->minutes[0], 0);
|
||||
sprintf(buf, "SA1L %2d%02d", state->minutes[0], 0);
|
||||
break;
|
||||
case sl_setting:
|
||||
// this sprintf to a larger tmp is to guarantee that no buffer overflows
|
||||
// occur here (and to squelch the corresponding compiler warning)
|
||||
sprintf(tmp, "SL %1d%1d%1d%1d%1d%1d",
|
||||
if (state->minutes[0] == 10) { //print 10 vertically.
|
||||
sprintf(tmp, "SA1L %1d%1d%1d%1d%1d",
|
||||
state->minutes[1],
|
||||
state->minutes[2],
|
||||
state->minutes[3],
|
||||
state->minutes[4],
|
||||
state->minutes[5]
|
||||
);
|
||||
memcpy(buf, tmp, sizeof(buf));
|
||||
if (subsecond % 2) {
|
||||
buf[4 + state->selection] = ' ';
|
||||
}
|
||||
watch_display_string(buf, 0);
|
||||
if (!(subsecond % 2) || state->selection != 0) {
|
||||
watch_set_pixel(0, 18);
|
||||
watch_set_pixel(0, 19);
|
||||
watch_set_pixel(1, 18);
|
||||
watch_set_pixel(1, 19);
|
||||
}
|
||||
return;
|
||||
}
|
||||
sprintf(tmp, "SA1L%1d%1d%1d%1d%1d%1d",
|
||||
state->minutes[0],
|
||||
state->minutes[1],
|
||||
state->minutes[2],
|
||||
|
@ -114,29 +163,89 @@ static void draw(sailing_state_t *state, uint8_t subsecond, movement_settings_t
|
|||
buf[4 + state->selection] = ' ';
|
||||
}
|
||||
break;
|
||||
case sl_counting:
|
||||
delta = state->now_ts - state->target_ts;
|
||||
if (state->now_ts <= state->target_ts) {
|
||||
sprintf(buf, "SA1L %2d ", 0);
|
||||
}
|
||||
else {
|
||||
result = div(delta, 3600);
|
||||
hrs = result.quot;
|
||||
delta -= 60*hrs;
|
||||
result = div(delta, 60);
|
||||
min = result.quot;
|
||||
sec = result.rem;
|
||||
sprintf(buf, "SL%2d%2d%02d%02d", lap, hrs, min, sec);//implement counting
|
||||
if (hrs > 23) {
|
||||
reset(state);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
|
||||
static void ring(sailing_state_t *state, movement_settings_t *settings) {
|
||||
movement_play_signal();
|
||||
state->index += 1;
|
||||
if (state->index > 5) {
|
||||
reset(state);
|
||||
return;
|
||||
}
|
||||
uint8_t next_min = state->minutes[state->index];
|
||||
if (next_min == 0) {
|
||||
reset(state);
|
||||
return;
|
||||
}
|
||||
static void ring(sailing_state_t *state, movement_settings_t *settings) {
|
||||
// if ring is called in background (while on another face), a button press can interrupt and cancel the execution.
|
||||
// To reduce the probability of cancelling all future alarms, the new alarm is set as soon as possible after calling ring.
|
||||
movement_cancel_background_task();
|
||||
start(state, settings);
|
||||
if (beepflag + 1 == beepseconds_size) { //equivalent to (beepflag + 1 == sizeof(beepseconds) / sizeof(int)) but without needing to divide here => quicker
|
||||
if (alarmflag != 0){
|
||||
watch_buzzer_play_sequence(long_beep, NULL);
|
||||
}
|
||||
movement_cancel_background_task();
|
||||
counting(state);
|
||||
return;
|
||||
}
|
||||
state->nextbeep_ts = state->target_ts - beepseconds[beepflag+1];
|
||||
watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->nextbeep_ts, get_tz_offset(settings));
|
||||
movement_schedule_background_task_for_face(state->watch_face_index, target_dt);
|
||||
//background task is set, now we have time to play the tune. If this is cancelled accidentally, the next alarm will still ring. Sound is implemented non-blocking, so that neither buttons nor display output are compromised.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (beepseconds[beepflag] == 60 * state->minutes[i]) {
|
||||
if (alarmflag > 1) {
|
||||
watch_buzzer_play_sequence((int8_t *)double_beep, NULL);
|
||||
}
|
||||
ringflag = true;
|
||||
}
|
||||
}
|
||||
if (!ringflag) {
|
||||
if (alarmflag == 3) {
|
||||
watch_buzzer_play_sequence((int8_t *)single_beep, NULL);
|
||||
}
|
||||
}
|
||||
ringflag = false;
|
||||
beepflag++;
|
||||
}
|
||||
|
||||
static void start(sailing_state_t *state, movement_settings_t *settings) {//gets called by starting / switching to next signal
|
||||
while (beepseconds[beepflag] < state->minutes[state->index]*60) {
|
||||
state->index++;
|
||||
}
|
||||
while (beepseconds[beepflag] > state->minutes[state->index]*60) {
|
||||
beepflag++;
|
||||
}
|
||||
if (state->index > 5 || state->minutes[state->index] == 0) {
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
|
||||
state->target_ts = state->now_ts;
|
||||
if (alarmflag != 0){
|
||||
watch_buzzer_play_sequence(long_beep, NULL);
|
||||
}
|
||||
counting(state);
|
||||
return;
|
||||
}
|
||||
movement_request_tick_frequency(1); //synchronises tick with the moment the button was pressed. Solves 1s offset between sound and display, solves up to +-0.5s offset between button action and display.
|
||||
state->mode = sl_running;
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
|
||||
state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, state->minutes[state->index], 0);
|
||||
ring(state, settings);
|
||||
}
|
||||
|
||||
static void settings_increment(sailing_state_t *state) {
|
||||
state->minutes[state->selection] += 1;
|
||||
uint8_t max = 10;
|
||||
uint8_t max = 11;
|
||||
if (state->selection > 0) {
|
||||
max = state->minutes[state->selection-1];
|
||||
}
|
||||
|
@ -179,19 +288,41 @@ void sailing_face_activate(movement_settings_t *settings, void *context) {
|
|||
watch_date_time now = watch_rtc_get_date_time();
|
||||
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
|
||||
}
|
||||
if(state->mode == sl_counting) {
|
||||
watch_date_time now = watch_rtc_get_date_time();
|
||||
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings));
|
||||
watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
}
|
||||
switch (alarmflag) {
|
||||
case 0: //no sound
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
case 1: //sound at start only
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
case 2: //sound at set minutes
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
case 3: //sound at every minute, 30s, 10-0s
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool sailing_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
sailing_state_t *state = (sailing_state_t *)context;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
draw(state, event.subsecond, settings);
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
if (state->mode == sl_running) {
|
||||
if (state->mode == sl_running || state->mode == sl_counting) {
|
||||
state->now_ts++;
|
||||
}
|
||||
draw(state, event.subsecond, settings);
|
||||
|
@ -200,6 +331,35 @@ bool sailing_face_loop(movement_event_t event, movement_settings_t *settings, vo
|
|||
if (state->mode == sl_running) {
|
||||
reset(state);
|
||||
}
|
||||
if (state->mode == sl_counting) {
|
||||
reset(state);
|
||||
}
|
||||
if (state->mode == sl_setting) {
|
||||
if (alarmflag == 3) {
|
||||
alarmflag = 0;
|
||||
}
|
||||
else {
|
||||
alarmflag++;
|
||||
}
|
||||
switch (alarmflag) {
|
||||
case 0: //no sound
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
case 1: //sound at start only
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
case 2: //sound at set minutes
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
case 3: //sound at every minute, 30s, 10-0s
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
switch(state->mode) {
|
||||
|
@ -218,21 +378,29 @@ bool sailing_face_loop(movement_event_t event, movement_settings_t *settings, vo
|
|||
movement_request_tick_frequency(1);
|
||||
}
|
||||
break;
|
||||
case sl_counting:
|
||||
movement_illuminate_led();
|
||||
break;
|
||||
}
|
||||
draw(state, event.subsecond, settings);
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
switch(state->mode) {
|
||||
case sl_running:
|
||||
ring(state, settings);
|
||||
start(state, settings);
|
||||
break;
|
||||
case sl_waiting:
|
||||
movement_play_signal();
|
||||
start(state, settings);
|
||||
break;
|
||||
case sl_setting:
|
||||
settings_increment(state);
|
||||
break;
|
||||
case sl_counting:
|
||||
//implement lap counting up to 39
|
||||
if (lap <39){
|
||||
lap++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
draw(state, event.subsecond, settings);
|
||||
break;
|
||||
|
@ -247,9 +415,12 @@ bool sailing_face_loop(movement_event_t event, movement_settings_t *settings, vo
|
|||
draw(state, event.subsecond, settings);
|
||||
break;
|
||||
}
|
||||
if (state->mode == sl_counting) {
|
||||
lap = 0;
|
||||
}
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
if (state->mode != sl_running) {
|
||||
if (state->mode != sl_running && state->mode != sl_counting) {
|
||||
movement_move_to_face(0);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Jan H. Voigt
|
||||
* Copyright (c) 2022 Wesley Ellis
|
||||
* Copyright (c) 2022 Niclas Hoyer
|
||||
*
|
||||
|
@ -38,13 +39,15 @@ A sailing sailing/timer face
|
|||
typedef enum {
|
||||
sl_waiting,
|
||||
sl_running,
|
||||
sl_setting
|
||||
sl_setting,
|
||||
sl_counting
|
||||
} sailing_mode_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t watch_face_index;
|
||||
uint32_t target_ts;
|
||||
uint32_t now_ts;
|
||||
uint32_t nextbeep_ts;
|
||||
uint8_t index;
|
||||
uint8_t minutes[6];
|
||||
uint8_t selection;
|
||||
|
|
|
@ -40,6 +40,22 @@ static const hmac_alg algorithms[] = {
|
|||
// END OF KEY DATA.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void _update_display(totp_state_t *totp_state) {
|
||||
char buf[14];
|
||||
div_t result;
|
||||
uint8_t valid_for;
|
||||
|
||||
result = div(totp_state->timestamp, timesteps[totp_state->current_index]);
|
||||
if (result.quot != totp_state->steps) {
|
||||
totp_state->current_code = getCodeFromTimestamp(totp_state->timestamp);
|
||||
totp_state->steps = result.quot;
|
||||
}
|
||||
valid_for = timesteps[totp_state->current_index] - result.rem;
|
||||
sprintf(buf, "%c%c%2d%06lu", labels[totp_state->current_index][0], labels[totp_state->current_index][1], valid_for, totp_state->current_code);
|
||||
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
|
||||
void totp_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) settings;
|
||||
(void) watch_face_index;
|
||||
|
@ -57,34 +73,21 @@ void totp_face_activate(movement_settings_t *settings, void *context) {
|
|||
|
||||
bool totp_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
|
||||
(void) settings;
|
||||
|
||||
totp_state_t *totp_state = (totp_state_t *)context;
|
||||
char buf[14];
|
||||
uint8_t valid_for;
|
||||
div_t result;
|
||||
uint8_t index = totp_state->current_index;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_TICK:
|
||||
totp_state->timestamp++;
|
||||
// fall through
|
||||
case EVENT_ACTIVATE:
|
||||
result = div(totp_state->timestamp, timesteps[index]);
|
||||
if (result.quot != totp_state->steps) {
|
||||
totp_state->current_code = getCodeFromTimestamp(totp_state->timestamp);
|
||||
totp_state->steps = result.quot;
|
||||
}
|
||||
valid_for = timesteps[index] - result.rem;
|
||||
sprintf(buf, "%c%c%2d%06lu", labels[index][0], labels[index][1], valid_for, totp_state->current_code);
|
||||
|
||||
watch_display_string(buf, 0);
|
||||
_update_display(totp_state);
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
movement_move_to_face(0);
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
if (index + 1 < num_keys) {
|
||||
totp_state->current_key_offset += key_sizes[index];
|
||||
if (totp_state->current_index + 1 < num_keys) {
|
||||
totp_state->current_key_offset += key_sizes[totp_state->current_index];
|
||||
totp_state->current_index++;
|
||||
} else {
|
||||
// wrap around to first key
|
||||
|
@ -92,6 +95,7 @@ bool totp_face_loop(movement_event_t event, movement_settings_t *settings, void
|
|||
totp_state->current_index = 0;
|
||||
}
|
||||
TOTP(keys + totp_state->current_key_offset, key_sizes[totp_state->current_index], timesteps[totp_state->current_index], algorithms[totp_state->current_index]);
|
||||
_update_display(totp_state);
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_DOWN:
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
|
|
|
@ -46,7 +46,11 @@ uint8_t watch_utility_get_weeknumber(uint16_t year, uint8_t month, uint8_t day)
|
|||
uint8_t weekday;
|
||||
uint16_t days;
|
||||
|
||||
weekday = watch_utility_get_iso8601_weekday_number(year, month, day) % 7;
|
||||
if (use_iso_8601_weeknumber == 1) {
|
||||
weekday = ((watch_utility_get_iso8601_weekday_number(year, month, day) + 5) % 7) + 1;
|
||||
} else {
|
||||
weekday = watch_utility_get_iso8601_weekday_number(year, month, day) % 7;
|
||||
}
|
||||
days = watch_utility_days_since_new_year(year, month, day);
|
||||
|
||||
int val = (days + 7U - (weekday+6U)%7) / 7;
|
||||
|
|
|
@ -26,6 +26,11 @@
|
|||
#define _WATCH_UTILITY_H_INCLUDED
|
||||
////< @file watch_utility.h
|
||||
|
||||
/*
|
||||
* Define use_iso_8601_weeknumber as 1 to let weeknumbers start on Monday, 0 to start on Sunday.
|
||||
*/
|
||||
#define use_iso_8601_weeknumber 0
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
/** @addtogroup utility Utility Functions
|
||||
|
|
Loading…
Reference in a new issue