'movement' -> 'legacy' to signal things we still need to bring in
This commit is contained in:
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Ruben Nic
|
||||
*
|
||||
* 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 <math.h>
|
||||
#include "close_enough_clock_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
const char *words[12] = {
|
||||
" ",
|
||||
" 5",
|
||||
"10",
|
||||
"15",
|
||||
"20",
|
||||
"25",
|
||||
"30",
|
||||
"35",
|
||||
"40",
|
||||
"45",
|
||||
"50",
|
||||
"55",
|
||||
};
|
||||
|
||||
static const char *past_word = " P";
|
||||
static const char *to_word = " 2";
|
||||
static const char *oclock_word = "OC";
|
||||
|
||||
// sets when in the five minute period we switch
|
||||
// from "X past HH" to "X to HH+1"
|
||||
static const int hour_switch_index = 8;
|
||||
|
||||
static void _update_alarm_indicator(bool settings_alarm_enabled, close_enough_clock_state_t *state) {
|
||||
state->alarm_enabled = settings_alarm_enabled;
|
||||
if (state->alarm_enabled) {
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
} else {
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
};
|
||||
}
|
||||
|
||||
void close_enough_clock_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(close_enough_clock_state_t));
|
||||
}
|
||||
}
|
||||
|
||||
void close_enough_clock_face_activate(void *context) {
|
||||
close_enough_clock_state_t *state = (close_enough_clock_state_t *)context;
|
||||
|
||||
if (watch_sleep_animation_is_running()) {
|
||||
watch_stop_sleep_animation();
|
||||
}
|
||||
|
||||
if (movement_clock_mode_24h()) {
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
}
|
||||
|
||||
// show alarm indicator if there is an active alarm
|
||||
_update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
|
||||
// this ensures that none of the five_minute_periods will match, so we always rerender when the face activates
|
||||
state->prev_five_minute_period = -1;
|
||||
state->prev_min_checked = -1;
|
||||
}
|
||||
|
||||
bool close_enough_clock_face_loop(movement_event_t event, void *context) {
|
||||
close_enough_clock_state_t *state = (close_enough_clock_state_t *)context;
|
||||
|
||||
char buf[11];
|
||||
watch_date_time_t date_time;
|
||||
bool show_next_hour = false;
|
||||
int prev_five_minute_period;
|
||||
int prev_min_checked;
|
||||
int close_enough_hour;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
date_time = watch_rtc_get_date_time();
|
||||
prev_five_minute_period = state->prev_five_minute_period;
|
||||
prev_min_checked = state->prev_min_checked;
|
||||
|
||||
// check the battery voltage once a day...
|
||||
if (date_time.unit.day != state->last_battery_check) {
|
||||
state->last_battery_check = date_time.unit.day;
|
||||
watch_enable_adc();
|
||||
uint16_t voltage = watch_get_vcc_voltage();
|
||||
watch_disable_adc();
|
||||
// 2.2 volts will happen when the battery has maybe 5-10% remaining?
|
||||
// we can refine this later.
|
||||
state->battery_low = (voltage < 2200);
|
||||
}
|
||||
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) {
|
||||
watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
}
|
||||
|
||||
// same minute, skip update
|
||||
if (date_time.unit.minute == prev_min_checked) {
|
||||
break;
|
||||
} else {
|
||||
state->prev_min_checked = date_time.unit.minute;
|
||||
}
|
||||
|
||||
int five_minute_period = (date_time.unit.minute / 5) % 12;
|
||||
|
||||
// If we are 60% to the next 5 interval, move up to the next period
|
||||
if (fmodf(date_time.unit.minute / 5.0f, 1.0f) > 0.5f) {
|
||||
// If we are on the last 5 interval and moving to the next period we need to display the next hour because we are wrapping around
|
||||
if (five_minute_period == 11) {
|
||||
show_next_hour = true;
|
||||
}
|
||||
|
||||
five_minute_period = (five_minute_period + 1) % 12;
|
||||
}
|
||||
|
||||
// same five_minute_period, skip update
|
||||
if (five_minute_period == prev_five_minute_period) {
|
||||
break;
|
||||
}
|
||||
|
||||
// we don't want to modify date_time.unit.hour just in case other watch faces use it
|
||||
close_enough_hour = date_time.unit.hour;
|
||||
|
||||
// move from "MM(mins) P HH" to "MM(mins) 2 HH+1"
|
||||
if (five_minute_period >= hour_switch_index || show_next_hour) {
|
||||
close_enough_hour = (close_enough_hour + 1) % 24;
|
||||
}
|
||||
|
||||
if (!movement_clock_mode_24h()) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
if (close_enough_hour < 12) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
|
||||
close_enough_hour %= 12;
|
||||
if (close_enough_hour == 0) {
|
||||
close_enough_hour = 12;
|
||||
}
|
||||
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) {
|
||||
date_time.unit.hour = 12;
|
||||
}
|
||||
}
|
||||
|
||||
char first_word[3];
|
||||
char second_word[3];
|
||||
char third_word[3];
|
||||
if (five_minute_period == 0) { // "HH OC",
|
||||
sprintf(first_word, "%2d", close_enough_hour);
|
||||
strncpy(second_word, words[five_minute_period], 3);
|
||||
strncpy(third_word, oclock_word, 3);
|
||||
} else {
|
||||
int words_length = sizeof(words) / sizeof(words[0]);
|
||||
|
||||
strncpy(
|
||||
first_word,
|
||||
five_minute_period >= hour_switch_index ?
|
||||
words[words_length - five_minute_period] :
|
||||
words[five_minute_period],
|
||||
3
|
||||
);
|
||||
strncpy(
|
||||
second_word,
|
||||
five_minute_period >= hour_switch_index ?
|
||||
to_word : past_word,
|
||||
3
|
||||
);
|
||||
sprintf(third_word, "%2d", close_enough_hour);
|
||||
}
|
||||
|
||||
sprintf(
|
||||
buf,
|
||||
"%s%2d%s%s%s",
|
||||
watch_utility_get_weekday(date_time),
|
||||
date_time.unit.day,
|
||||
first_word,
|
||||
second_word,
|
||||
third_word
|
||||
);
|
||||
|
||||
watch_display_string(buf, 0);
|
||||
state->prev_five_minute_period = five_minute_period;
|
||||
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != movement_alarm_enabled()) {
|
||||
_update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void close_enough_clock_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Ruben Nic
|
||||
*
|
||||
* 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 CLOSE_ENOUGH_CLOCK_FACE_H_
|
||||
#define CLOSE_ENOUGH_CLOCK_FACE_H_
|
||||
|
||||
/*
|
||||
* CLOSE ENOUGH CLOCK FACE
|
||||
*
|
||||
* Displays the current time; but only in periods of 5.
|
||||
* Just in the in the formats of:
|
||||
* - "10 past 5"
|
||||
* - "15 to 7"
|
||||
* - "6 o'clock"
|
||||
*
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
typedef struct {
|
||||
int prev_five_minute_period;
|
||||
int prev_min_checked;
|
||||
uint8_t last_battery_check;
|
||||
bool battery_low;
|
||||
bool alarm_enabled;
|
||||
} close_enough_clock_state_t;
|
||||
|
||||
void close_enough_clock_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void close_enough_clock_face_activate(void *context);
|
||||
bool close_enough_clock_face_loop(movement_event_t event, void *context);
|
||||
void close_enough_clock_face_resign(void *context);
|
||||
|
||||
#define close_enough_clock_face ((const watch_face_t){ \
|
||||
close_enough_clock_face_setup, \
|
||||
close_enough_clock_face_activate, \
|
||||
close_enough_clock_face_loop, \
|
||||
close_enough_clock_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // CLOSE_ENOUGH_CLOCK_FACE_H_
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Wesley Aptekar-Cassels
|
||||
*
|
||||
* 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 <math.h>
|
||||
#include "day_night_percentage_face.h"
|
||||
#include "watch_utility.h"
|
||||
#include "sunriset.h"
|
||||
|
||||
// fmod but handle negatives right
|
||||
static double better_fmod(double x, double y) {
|
||||
return fmod(fmod(x, y) + y, y);
|
||||
}
|
||||
|
||||
static void recalculate(watch_date_time_t utc_now, day_night_percentage_state_t *state) {
|
||||
movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
|
||||
|
||||
if (movement_location.reg == 0) {
|
||||
state->result = -2;
|
||||
return;
|
||||
}
|
||||
|
||||
// Weird quirky unsigned things were happening when I tried to cast these directly to doubles below.
|
||||
// it looks redundant, but extracting them to local int16's seemed to fix it.
|
||||
int16_t lat_centi = (int16_t)movement_location.bit.latitude;
|
||||
int16_t lon_centi = (int16_t)movement_location.bit.longitude;
|
||||
|
||||
double lat = (double)lat_centi / 100.0;
|
||||
double lon = (double)lon_centi / 100.0;
|
||||
|
||||
state->daylen = day_length(utc_now.unit.year + WATCH_RTC_REFERENCE_YEAR, utc_now.unit.month, utc_now.unit.day, lon, lat);
|
||||
|
||||
state->result = sun_rise_set(utc_now.unit.year + WATCH_RTC_REFERENCE_YEAR, utc_now.unit.month, utc_now.unit.day, lon, lat, &state->rise, &state->set);
|
||||
}
|
||||
|
||||
void day_night_percentage_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(day_night_percentage_state_t));
|
||||
day_night_percentage_state_t *state = (day_night_percentage_state_t *)*context_ptr;
|
||||
watch_date_time_t utc_now = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_get_current_timezone_offset(), 0);
|
||||
recalculate(utc_now, state);
|
||||
}
|
||||
}
|
||||
|
||||
void day_night_percentage_face_activate(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
|
||||
bool day_night_percentage_face_loop(movement_event_t event, void *context) {
|
||||
day_night_percentage_state_t *state = (day_night_percentage_state_t *)context;
|
||||
|
||||
char buf[12];
|
||||
watch_date_time_t date_time = watch_rtc_get_date_time();
|
||||
watch_date_time_t utc_now = watch_utility_date_time_convert_zone(date_time, movement_get_current_timezone_offset(), 0);
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
if ((utc_now.unit.hour == 0 && utc_now.unit.minute == 0 && utc_now.unit.second == 0) || state->result == -2) {
|
||||
recalculate(utc_now, state);
|
||||
}
|
||||
|
||||
if (state->result == -2) {
|
||||
watch_display_string(" no Loc", 0);
|
||||
break;
|
||||
}
|
||||
|
||||
const char* weekday = watch_utility_get_weekday(date_time);
|
||||
if (state->result != 0) {
|
||||
if (state->result == 1) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
sprintf(buf, "%s%2dEtrnal", weekday, date_time.unit.day);
|
||||
watch_display_string(buf, 0);
|
||||
} else {
|
||||
double day_hours_decimal = utc_now.unit.hour + (utc_now.unit.minute + (utc_now.unit.second / 60.0)) / 60.0;
|
||||
|
||||
double day_percentage = (24.0 - better_fmod(state->rise - day_hours_decimal, 24.0)) / state->daylen;
|
||||
double night_percentage = (24.0 - better_fmod(state->set - day_hours_decimal, 24.0)) / (24 - state->daylen);
|
||||
|
||||
uint16_t percentage;
|
||||
if (day_percentage > 0.0 && day_percentage < 1.0) {
|
||||
percentage = day_percentage * 10000;
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
percentage = night_percentage * 10000;
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
|
||||
sprintf(buf, "%s%2d %02d ", weekday, date_time.unit.day, percentage / 100);
|
||||
} else {
|
||||
sprintf(buf, "%s%2d %04d", weekday, date_time.unit.day, percentage);
|
||||
}
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void day_night_percentage_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Wesley Aptekar-Cassels
|
||||
*
|
||||
* 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 DAY_NIGHT_PERCENTAGE_FACE_H_
|
||||
#define DAY_NIGHT_PERCENTAGE_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
* Day/night percentage face
|
||||
*
|
||||
* Shows the percentage of the way through the day/night the current time is.
|
||||
*
|
||||
* The time digits show the percentage of the way through the day/night it is,
|
||||
* with decimals in the smaller seconds digits. If the day or night will last
|
||||
* for a full 24 hours, the text "Etrnal" is displayed instead of a percentage.
|
||||
* The "PM" indicator is set when it is currently nighttime. The weekday and
|
||||
* day digits display the weekday and day, as one would expect.
|
||||
*
|
||||
* This face does not currently offer any configuration. You must set the
|
||||
* location register with some other face.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
int result; // -1, 0, 1: result from sun_rise_set, -2: no location set
|
||||
double rise;
|
||||
double set;
|
||||
double daylen;
|
||||
} day_night_percentage_state_t;
|
||||
|
||||
void day_night_percentage_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void day_night_percentage_face_activate(void *context);
|
||||
bool day_night_percentage_face_loop(movement_event_t event, void *context);
|
||||
void day_night_percentage_face_resign(void *context);
|
||||
|
||||
#define day_night_percentage_face ((const watch_face_t){ \
|
||||
day_night_percentage_face_setup, \
|
||||
day_night_percentage_face_activate, \
|
||||
day_night_percentage_face_loop, \
|
||||
day_night_percentage_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // DAY_NIGHT_PERCENTAGE_FACE_H_
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Curtis J. Brown <mrbrown8@juno.com>
|
||||
*
|
||||
* 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 "decimal_time_face.h"
|
||||
#include "watch.h"
|
||||
|
||||
|
||||
|
||||
void decimal_time_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
// These next two lines just silence the compiler warnings associated with unused parameters.
|
||||
// We have no use for the settings or the watch_face_index, so we make that explicit here.
|
||||
(void) watch_face_index;
|
||||
(void) context_ptr;
|
||||
// At boot, context_ptr will be NULL indicating that we don't have anyplace to store our context.
|
||||
if (*context_ptr == NULL) {
|
||||
// in this case, we allocate an area of memory sufficient to store the stuff we need to track.
|
||||
*context_ptr = malloc(sizeof(decimal_time_face_state_t));
|
||||
decimal_time_face_state_t *state = (decimal_time_face_state_t *)*context_ptr;
|
||||
state->chime_enabled = false;
|
||||
state->features_to_show = 0 ;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void decimal_time_face_activate(void *context) {
|
||||
|
||||
// same as above: silence the warning, we don't need to check the settings.
|
||||
|
||||
// we do however need to set some things in our context. Here we cast it to the correct type...
|
||||
decimal_time_face_state_t *state = (decimal_time_face_state_t *)context;
|
||||
|
||||
watch_set_indicator(WATCH_INDICATOR_24H); // This face is always 24h, so just set the indicators
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
|
||||
if (state->chime_enabled) {
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool decimal_time_face_loop(movement_event_t event, void *context) {
|
||||
|
||||
decimal_time_face_state_t *state = (decimal_time_face_state_t *)context;
|
||||
|
||||
char buf[16];
|
||||
uint8_t centihours, decimal_seconds;
|
||||
watch_date_time_t date_time;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
// on activate and tick
|
||||
|
||||
date_time = watch_rtc_get_date_time();
|
||||
|
||||
centihours = (( date_time.unit.minute * 60 + date_time.unit.second ) * 100 ) / 3600; // Integer division, fractions get dropped, no need for abs() (bonus)
|
||||
|
||||
decimal_seconds = ( date_time.unit.minute * 60 + date_time.unit.second ) % 36 ;
|
||||
|
||||
switch (state->features_to_show) {
|
||||
case 0:
|
||||
sprintf( buf, "dT %02d%02d ", date_time.unit.hour, centihours );
|
||||
break;
|
||||
case 1:
|
||||
sprintf( buf, "dT %02d%02d%2d", date_time.unit.hour, centihours, decimal_seconds );
|
||||
break;
|
||||
case 2:
|
||||
sprintf( buf, "dT%2d%02d%02d ", date_time.unit.day, date_time.unit.hour, centihours );
|
||||
break;
|
||||
case 3:
|
||||
sprintf( buf, "dT%2d%02d%02d%2d", date_time.unit.day, date_time.unit.hour, centihours, decimal_seconds );
|
||||
break;
|
||||
}
|
||||
|
||||
watch_display_string(buf, 0); // display calculated time
|
||||
|
||||
// at the top of every hour...
|
||||
if (date_time.unit.minute == 0 && date_time.unit.second == 0 && state->chime_enabled) {
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 15);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
state->chime_enabled = !state->chime_enabled; // just like from simple_watch_face
|
||||
if (state->chime_enabled)
|
||||
watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else
|
||||
watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
break;
|
||||
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
state->features_to_show += 1 ; // cycle thru what's to be shown
|
||||
break;
|
||||
|
||||
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
// This low energy mode update occurs once a minute, if the watch face is in the
|
||||
// foreground when Movement enters low energy mode. We have the option of supporting
|
||||
// this mode, but since our watch face animates once a second, the "Hello there" face
|
||||
// isn't very useful in this mode. So we choose not to support it. (continued below)
|
||||
break;
|
||||
|
||||
case EVENT_TIMEOUT:
|
||||
// ... Instead, we respond to the timeout event. This event happens after a configurable
|
||||
// interval on screen (1-30 minutes). The watch will give us this event as a chance to
|
||||
// resign control if we want to, and in this case, we do.
|
||||
// This function will return the watch to the first screen (usually a simple clock),
|
||||
// and it will do it long before the watch enters low energy mode. This ensures we
|
||||
// won't be on screen, and thus opts us out of getting the EVENT_LOW_ENERGY_UPDATE above.
|
||||
//movement_move_to_face(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
movement_default_loop_handler(event);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void decimal_time_face_resign(void *context) {
|
||||
// our watch face, like most watch faces, has nothing special to do when resigning.
|
||||
// there are no peripherals or sensors here to worry about turning off.
|
||||
(void) context;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// void decimal_time_face_advise() {
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Curtis J. Brown <mrbrown8@juno.com>
|
||||
*
|
||||
* 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 DECIMAL_TIME_FACE_H_
|
||||
#define DECIMAL_TIME_FACE_H_
|
||||
|
||||
/*
|
||||
* DECIMAL TIME face
|
||||
*
|
||||
* This face presents the current time as hours and hundredths of an hour. Every hundreth of an hour, or "centihour",
|
||||
* occurs every 36 seconds. Because they range from 0 to 99, centihours, in the seventies range, will be displayed with a lowercase 7.
|
||||
*
|
||||
* See https://en.wikipedia.org/wiki/Decimal_time#Decimal_hours
|
||||
*
|
||||
* This method of timekeeping is used by the United States Postal Service.
|
||||
* http://www.branch38nalc.com/sitebuildercontent/sitebuilderfiles/CONVERSION_TABLE_TIME.pdf
|
||||
* https://postalemployeenetwork.com/time-conversion-print.htm
|
||||
*
|
||||
* This method may be used by other organizations as well
|
||||
* https://www.labor.nc.gov/workplace-rights/employer-responsibilities/time-conversion-chart-minutes-decimal-hours
|
||||
* https://uh.edu/office-of-finance/payroll/time_conversion_chart_minutes_to_decimalhours.pdf
|
||||
* https://www.placer.ca.gov/DocumentCenter/View/3860/Decimals-to-Minutes-Conversion-Table-PDF
|
||||
* https://hr.colostate.edu/minute-to-decimal-conversion-chart/
|
||||
*
|
||||
* Many thanks go to Joey Castillo for making this project happen.
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
typedef struct {
|
||||
bool chime_enabled; // did the user enable hourly chime for this face?
|
||||
uint8_t features_to_show : 2 ; // what features are to be displayed?
|
||||
} decimal_time_face_state_t;
|
||||
|
||||
void decimal_time_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void decimal_time_face_activate(void *context);
|
||||
bool decimal_time_face_loop(movement_event_t event, void *context);
|
||||
void decimal_time_face_resign(void *context);
|
||||
// void decimal_time_face_advise();
|
||||
|
||||
|
||||
#define decimal_time_face ((const watch_face_t){ \
|
||||
decimal_time_face_setup, \
|
||||
decimal_time_face_activate, \
|
||||
decimal_time_face_loop, \
|
||||
decimal_time_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // DECIMAL_TIME_FACE_H_
|
||||
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 CarpeNoctem
|
||||
*
|
||||
* 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 "french_revolutionary_face.h"
|
||||
|
||||
void french_revolutionary_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(french_revolutionary_state_t));
|
||||
memset(*context_ptr, 0, sizeof(french_revolutionary_state_t));
|
||||
// Do any one-time tasks in here; the inside of this conditional happens only at boot.
|
||||
french_revolutionary_state_t *state = (french_revolutionary_state_t *)*context_ptr;
|
||||
state->use_am_pm = false;
|
||||
state->show_seconds = true;
|
||||
state->display_type = 0;
|
||||
state->colon_set_after_splash = false;
|
||||
}
|
||||
// Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
|
||||
}
|
||||
|
||||
void french_revolutionary_face_activate(void *context) {
|
||||
french_revolutionary_state_t *state = (french_revolutionary_state_t *)context;
|
||||
|
||||
// Handle any tasks related to your watch face coming on screen.
|
||||
state->colon_set_after_splash = false;
|
||||
}
|
||||
|
||||
bool french_revolutionary_face_loop(movement_event_t event, void *context) {
|
||||
french_revolutionary_state_t *state = (french_revolutionary_state_t *)context;
|
||||
|
||||
char buf[11];
|
||||
watch_date_time_t date_time;
|
||||
fr_decimal_time decimal_time;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
// Initial UI - Show a quick "splash screen"
|
||||
watch_clear_display();
|
||||
watch_display_string("FR dECimL", 0);
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
|
||||
date_time = watch_rtc_get_date_time();
|
||||
|
||||
decimal_time = get_decimal_time(&date_time);
|
||||
|
||||
set_display_buffer(buf, state, &decimal_time, &date_time);
|
||||
|
||||
// If we're in low-energy mode, don't write out the seconds. Also start the LE tick animation if it's not already going.
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
buf[8] = ' ';
|
||||
buf[9] = ' ';
|
||||
if (!watch_sleep_animation_is_running()) { watch_start_sleep_animation(500); }
|
||||
}
|
||||
|
||||
// Update the display with our decimal time
|
||||
watch_display_string(buf, 0);
|
||||
|
||||
// Oh, and a one-off to set the colon after the "splash screen"
|
||||
if (!state->colon_set_after_splash) {
|
||||
watch_set_colon();
|
||||
state->colon_set_after_splash = true;
|
||||
}
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
state->display_type += 1 ; // cycle through the display types
|
||||
if (state->display_type > 2) { state->display_type = 0; } // but return to 0 after 2
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
// I originally had chiming on the decimal-hour enabled, and this would enable/disable that chime, just like on
|
||||
// the simple clock and decimal time faces. But because decimal seconds don't always line up with normal seconds,
|
||||
// I assume the (decimal-)hourly chime could sometimes be missed. Additionally, I need this button for other purposes,
|
||||
// now that I added seconds on/off toggle and upper normal-time with the ability to toggle that between 12/24hr format.
|
||||
state->show_seconds = !state->show_seconds;
|
||||
if (!state->show_seconds) { watch_display_string(" ", 8); }
|
||||
else { watch_display_string("--", 8); }
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
// In case anyone really wants that upper time in 12-hour format. I thought about using the global setting (movement_clock_mode_24h())
|
||||
// for this preference, but thought someone who prefers 12-hour format normally, might prefer 24hr when compared to a 10hr decimal day,
|
||||
// so this is separate for now.
|
||||
state->use_am_pm = !state->use_am_pm;
|
||||
if (state->use_am_pm) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
date_time = watch_rtc_get_date_time();
|
||||
if (date_time.unit.hour < 12) { watch_clear_indicator(WATCH_INDICATOR_PM); }
|
||||
else { watch_set_indicator(WATCH_INDICATOR_PM); }
|
||||
} else {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Movement's default loop handler will step in for any cases you don't handle above:
|
||||
// * EVENT_LIGHT_BUTTON_DOWN lights the LED
|
||||
// * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
|
||||
// * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
|
||||
// You can override any of these behaviors by adding a case for these events to this switch statement.
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
// return true if the watch can enter standby mode. Generally speaking, you should always return true.
|
||||
// Exceptions:
|
||||
// * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
|
||||
// * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
|
||||
// Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
|
||||
// movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
|
||||
return true;
|
||||
}
|
||||
|
||||
void french_revolutionary_face_resign(void *context) {
|
||||
(void) context;
|
||||
|
||||
// handle any cleanup before your watch face goes off-screen.
|
||||
}
|
||||
|
||||
// Calculate decimal time from normal (24hr) time
|
||||
fr_decimal_time get_decimal_time(watch_date_time_t *date_time) {
|
||||
uint32_t current_24hr_secs, current_decimal_seconds;
|
||||
fr_decimal_time decimal_time;
|
||||
// Current 24-hr time in seconds (There are 86400 of these in a day.)
|
||||
current_24hr_secs = date_time->unit.hour * 3600 + date_time->unit.minute * 60 + date_time->unit.second;
|
||||
|
||||
// Current Decimal Time in seconds. There are 100000 seconds in a 10-hr decimal-time day.
|
||||
// current_decimal_seconds = current_24hr_seconds * 100000 / 86400, or = current_24_seconds * 1000 / 864;
|
||||
// By chopping the extra zeros off the end, we can use uint32 instead of uint64.
|
||||
current_decimal_seconds = current_24hr_secs * 1000 / 864;
|
||||
|
||||
decimal_time.hour = current_decimal_seconds / 10000;
|
||||
// Remove the hours from total seconds and keep the remainder for below.
|
||||
current_decimal_seconds = current_decimal_seconds - decimal_time.hour * 10000;
|
||||
|
||||
decimal_time.minute = current_decimal_seconds / 100;
|
||||
// Remove the minutes from total seconds and keep the remaining seconds
|
||||
// Note: I think I used an extra seconds variable here because sprintf or movement weren't liking a uint32...
|
||||
decimal_time.second = current_decimal_seconds - decimal_time.minute * 100;
|
||||
return decimal_time;
|
||||
}
|
||||
|
||||
// Fills in the display buffer, depending on the currently-selected display option (and sub-options):
|
||||
// - Decimal-time only
|
||||
// - Decimal-time with date in top-right
|
||||
// - Decimal-time with normal time in the top (minutes first, then hours, due to display limitations)
|
||||
// TODO: There is some power-saving stuff that simple clock does here around not redrawing characters that haven't changed, but we're not doing that here.
|
||||
// I'll try to add that optimization could be added in a future commit.
|
||||
void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time_t *date_time) {
|
||||
switch (state->display_type) {
|
||||
// Decimal time only
|
||||
case 0:
|
||||
// Originally I had the day slot set to "FR" (French Revolutionary time), but my brain kept thinking "Friday" whenever I saw it,
|
||||
// so I changed it to dT (Decimal Time) to avoid that confusion. Apologies to anyone who has the other decimal_time face and this one
|
||||
// installed concurrently. Maybe the splash screen will help a little.
|
||||
sprintf( buf, "dT %2d%02d%02d", decimal_time->hour, decimal_time->minute, decimal_time->second );
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
break;
|
||||
// Decimal time and date
|
||||
case 1:
|
||||
sprintf( buf, "dT%2d%2d%02d%02d", date_time->unit.day, decimal_time->hour, decimal_time->minute, decimal_time->second );
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
break;
|
||||
// Decimal time on bottom, normal time above
|
||||
case 2:
|
||||
if (state->use_am_pm) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
if (date_time->unit.hour < 12) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
date_time->unit.hour %= 12;
|
||||
if (date_time->unit.hour == 0) date_time->unit.hour = 12;
|
||||
} else {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
}
|
||||
// Note, the date digits don't display a leading zero well, so we don't use it.
|
||||
sprintf( buf, "%02d%2d%2d%02d%02d", date_time->unit.minute, date_time->unit.hour, decimal_time->hour, decimal_time->minute, decimal_time->second );
|
||||
|
||||
// Make the second character of the Day area more readable
|
||||
buf[1] = fix_character_one(buf[1]);
|
||||
break;
|
||||
}
|
||||
// Finally, if show_seconds is disabled, trim those off.
|
||||
if (!state->show_seconds) {
|
||||
buf[8] = ' ';
|
||||
buf[9] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// Sadly, the second character of the Day field cannot show all numbers, so we make some replacements.
|
||||
// See https://www.sensorwatch.net/docs/wig/display/#limitations-of-the-weekday-digits
|
||||
char fix_character_one(char digit) {
|
||||
char return_char = digit; // We don't need to update this for 0, 1, 3, 7 and 8.
|
||||
switch(digit) {
|
||||
case '2':
|
||||
// Roman numeral / tally representation of 2
|
||||
return_char = '|'; // Thanks, Joey, for already having this in the character set.
|
||||
break;
|
||||
case '4':
|
||||
// Looks almost like a 4 - just missing the top-left segment.
|
||||
// 0b01000110
|
||||
return_char = '&'; // Slight hack - I want 0b01000110, but 0b01000100 is already in the character set and will do, since B and C segments are linked in this position.
|
||||
break;
|
||||
case '5':
|
||||
return_char = 'F'; // F for Five
|
||||
break;
|
||||
case '6':
|
||||
return_char = 'E'; // Looks almost like a 6 - just missing the bottom-right segment. Not super happy with it, but liked it best of the options I tried.
|
||||
break;
|
||||
case '9':
|
||||
return_char = 'N'; // N for Nine
|
||||
break;
|
||||
}
|
||||
return return_char;
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 CarpeNoctem
|
||||
*
|
||||
* 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 FRENCH_REVOLUTIONARY_FACE_H_
|
||||
#define FRENCH_REVOLUTIONARY_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
* French Revolutionary Decimal Time
|
||||
*
|
||||
* Similar to the Decimal Time face, but with the day divided into ten hours instead of twenty four.
|
||||
* Each hour is divided into one hundred minutes, and those minutes are divided into 100 seconds.
|
||||
* I came across this one the Svalbard watch site here: https://svalbard.watch/pages/about_decimal_time.html
|
||||
* More info here as well: https://en.wikipedia.org/wiki/Decimal_time
|
||||
*
|
||||
* By default, the face just displays the current decimal time. Pressing the alarm button will toggle through other display options:
|
||||
* 1) Just decimal time (with dT indicator at top)
|
||||
* 2) Decimal time, with dT indicator and date above.
|
||||
* 3) Decimal time, with 24-hr time above (where Day and Date would normally be displayed), BUT minutes first then hours.
|
||||
* Sadly, the first character of the date area only goes up to 3 (see https://www.sensorwatch.net/docs/wig/display/#the-day-digits)
|
||||
* I was going to begrudgindly leave this display option out when I realized that, but thought it would be better to have this backwards
|
||||
* representation of the "normal" time than not at all.
|
||||
*
|
||||
* A long-press of the light button will toggle the upper time between 12-hr AM/PM and 24-hr mode. I thought of reading the main setting for this,
|
||||
* but thought that a person could normally prefer 12hr time, but next to a 10hr day want to see the normal time in the 24hr format.
|
||||
*
|
||||
* A long-press of the alarm button will toggle the seconds off and on.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
bool use_am_pm; // Use 12-hr AM/PM for upper display instead of 24-hr? (Default is 24-hr)
|
||||
bool show_seconds;
|
||||
bool colon_set_after_splash;
|
||||
uint8_t display_type : 2;
|
||||
} french_revolutionary_state_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t second : 8; // 0-99
|
||||
uint8_t minute : 8; // 0-99
|
||||
uint8_t hour : 5; // 0-10
|
||||
} fr_decimal_time;
|
||||
|
||||
void french_revolutionary_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void french_revolutionary_face_activate(void *context);
|
||||
bool french_revolutionary_face_loop(movement_event_t event, void *context);
|
||||
void french_revolutionary_face_resign(void *context);
|
||||
char fix_character_one(char digit);
|
||||
fr_decimal_time get_decimal_time(watch_date_time_t *date_time);
|
||||
void set_display_buffer(char *buf, french_revolutionary_state_t *state, fr_decimal_time *decimal_time, watch_date_time_t *date_time);
|
||||
|
||||
|
||||
#define french_revolutionary_face ((const watch_face_t){ \
|
||||
french_revolutionary_face_setup, \
|
||||
french_revolutionary_face_activate, \
|
||||
french_revolutionary_face_loop, \
|
||||
french_revolutionary_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // FRENCH_REVOLUTIONARY_FACE_H_
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "watch_utility.h"
|
||||
#include "mars_time_face.h"
|
||||
|
||||
// note: lander coordinates come from Mars24's `marslandmarks.xml` file
|
||||
static double site_longitudes[MARS_TIME_NUM_SITES] = {
|
||||
0, // Mars Coordinated Time, at the meridian
|
||||
360.0 - 109.9, // Zhurong lander site
|
||||
360.0 - 77.45088572, // Perseverance lander site
|
||||
360.0 - 135.623447, // InSight lander site
|
||||
360.0 - 137.441635, // Curiosity lander site
|
||||
};
|
||||
|
||||
static char site_names[MARS_TIME_NUM_SITES][3] = {
|
||||
"MC",
|
||||
"ZH",
|
||||
"PE",
|
||||
"IN",
|
||||
"CU",
|
||||
};
|
||||
|
||||
static uint16_t landing_sols[MARS_TIME_NUM_SITES] = {
|
||||
0,
|
||||
52387,
|
||||
52304,
|
||||
51511,
|
||||
49269,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
} mars_clock_hms_t;
|
||||
|
||||
static void _h_to_hms(mars_clock_hms_t *date_time, double h) {
|
||||
unsigned int seconds = (unsigned int)(h * 3600.0);
|
||||
date_time->hour = seconds / 3600;
|
||||
seconds = seconds % 3600;
|
||||
date_time->minute = floor(seconds / 60);
|
||||
date_time->second = round(seconds % 60);
|
||||
}
|
||||
|
||||
static void _update(mars_time_state_t *state) {
|
||||
char buf[11];
|
||||
watch_date_time_t date_time = watch_rtc_get_date_time();
|
||||
uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
|
||||
// TODO: I'm skipping over some steps here.
|
||||
// https://www.giss.nasa.gov/tools/mars24/help/algorithm.html
|
||||
double jdut = 2440587.5 + ((double)now / 86400.0);
|
||||
double jdtt = jdut + ((37.0 + 32.184) / 86400.0);
|
||||
double jd2k = jdtt - 2451545.0;
|
||||
double msd = ((jd2k - 4.5) / 1.0274912517) + 44796.0 - 0.0009626;
|
||||
double mtc = fmod(24 * msd, 24);
|
||||
double lmt;
|
||||
|
||||
if (state->current_site == 0) {
|
||||
lmt = mtc;
|
||||
} else {
|
||||
double longitude = site_longitudes[state->current_site];
|
||||
double lmst = mtc - ((longitude * 24.0) / 360.0);
|
||||
lmt = fmod(lmst + 24, 24);
|
||||
}
|
||||
|
||||
if (state->displaying_sol) {
|
||||
// TODO: this is not right, mission sol should turn over at midnight local time?
|
||||
uint16_t sol = floor(msd) - landing_sols[state->current_site];
|
||||
if (sol < 1000) sprintf(&buf[0], "%s Sol%3d", site_names[state->current_site], sol);
|
||||
else sprintf(&buf[0], "%s $%6d", site_names[state->current_site], sol);
|
||||
watch_clear_colon();
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
} else {
|
||||
mars_clock_hms_t mars_time;
|
||||
_h_to_hms(&mars_time, lmt);
|
||||
sprintf(&buf[0], "%s %02d%02d%02d", site_names[state->current_site], mars_time.hour, mars_time.minute, mars_time.second);
|
||||
watch_set_colon();
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
}
|
||||
|
||||
watch_display_string(buf, 0);
|
||||
}
|
||||
|
||||
void mars_time_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(mars_time_state_t));
|
||||
memset(*context_ptr, 0, sizeof(mars_time_state_t));
|
||||
}
|
||||
}
|
||||
|
||||
void mars_time_face_activate(void *context) {
|
||||
mars_time_state_t *state = (mars_time_state_t *)context;
|
||||
(void) state;
|
||||
}
|
||||
|
||||
bool mars_time_face_loop(movement_event_t event, void *context) {
|
||||
mars_time_state_t *state = (mars_time_state_t *)context;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
_update(state);
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
state->displaying_sol = !state->displaying_sol;
|
||||
_update(state);
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
movement_illuminate_led();
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
state->current_site = (state->current_site + 1) % MARS_TIME_NUM_SITES;
|
||||
_update(state);
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
// TODO: make this lower power so we can avoid timeout
|
||||
movement_move_to_face(0);
|
||||
break;
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
// TODO: low energy update
|
||||
// watch_start_sleep_animation(500);
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
// don't light up every time light is hit
|
||||
break;
|
||||
default:
|
||||
movement_default_loop_handler(event);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mars_time_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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 MARS_TIME_FACE_H_
|
||||
#define MARS_TIME_FACE_H_
|
||||
|
||||
/*
|
||||
* MARS TIME face
|
||||
*
|
||||
* This watch face is dedicated to Martian timekeeping.
|
||||
* It has several modes, and can display either a time or a date.
|
||||
*
|
||||
* Pressing the ALARM button cycles through different time zones on Mars:
|
||||
* MC - Mars Coordinated Time, the time at Airy-0 Crater on the Martian prime meridian
|
||||
* ZH - Local mean solar time for the Zhurong rover
|
||||
* PE - LMST for the Perseverance rover
|
||||
* IN - LMST for the Insight lander
|
||||
* CU - LMST for the Curiosity rover
|
||||
*
|
||||
* Press the LIGHT button to toggle between displaying time and date:
|
||||
* MC S - the Mars Sol Date, Martian days since December 29, 1873
|
||||
* ZH Sol - Mission sol for the Zhurong rover
|
||||
* PE Sol - Mission sol for the Perseverance rover
|
||||
* IN S - Mission sol for the InSight lander
|
||||
* CU S - Mission sol for the Curiosity rover
|
||||
*
|
||||
* Note that where the mission sol is below 1000, this watch face displays
|
||||
* the word “Sol” on the bottom line. When the mission sol is over 1000, the
|
||||
* word “Sol” will not fit and so it displays a stylized letter S at the top
|
||||
* right.
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
typedef enum {
|
||||
MARS_TIME_MERIDIAN,
|
||||
MARS_TIME_ZHURONG_SITE,
|
||||
MARS_TIME_PERSEVERANCE_SITE,
|
||||
MARS_TIME_INSIGHT_SITE,
|
||||
MARS_TIME_CURIOSITY_SITE,
|
||||
MARS_TIME_NUM_SITES,
|
||||
} mars_time_site_t;
|
||||
|
||||
typedef struct {
|
||||
mars_time_site_t current_site;
|
||||
bool displaying_sol;
|
||||
} mars_time_state_t;
|
||||
|
||||
void mars_time_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void mars_time_face_activate(void *context);
|
||||
bool mars_time_face_loop(movement_event_t event, void *context);
|
||||
void mars_time_face_resign(void *context);
|
||||
|
||||
#define mars_time_face ((const watch_face_t){ \
|
||||
mars_time_face_setup, \
|
||||
mars_time_face_activate, \
|
||||
mars_time_face_loop, \
|
||||
mars_time_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // MARS_TIME_FACE_H_
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Dennisman219
|
||||
*
|
||||
* 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 "minimal_clock_face.h"
|
||||
|
||||
static void _minimal_clock_face_update_display() {
|
||||
watch_date_time_t date_time = watch_rtc_get_date_time();
|
||||
char buffer[11];
|
||||
|
||||
if (!movement_clock_mode_24h()) {
|
||||
date_time.unit.hour %= 12;
|
||||
sprintf(buffer, "%2d%02d ", date_time.unit.hour, date_time.unit.minute);
|
||||
} else {
|
||||
sprintf(buffer, "%02d%02d ", date_time.unit.hour, date_time.unit.minute);
|
||||
}
|
||||
|
||||
watch_display_string(buffer, 4);
|
||||
}
|
||||
|
||||
void minimal_clock_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(minimal_clock_state_t));
|
||||
memset(*context_ptr, 0, sizeof(minimal_clock_state_t));
|
||||
// Do any one-time tasks in here; the inside of this conditional happens only at boot.
|
||||
}
|
||||
// Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
|
||||
}
|
||||
|
||||
void minimal_clock_face_activate(void *context) {
|
||||
(void) context;
|
||||
// Handle any tasks related to your watch face coming on screen.
|
||||
watch_set_colon();
|
||||
}
|
||||
|
||||
bool minimal_clock_face_loop(movement_event_t event, void *context) {
|
||||
(void) context;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
// Show your initial UI here.
|
||||
_minimal_clock_face_update_display();
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
// If needed, update your display here.
|
||||
_minimal_clock_face_update_display();
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
// You can use the Light button for your own purposes. Note that by default, Movement will also
|
||||
// illuminate the LED in response to EVENT_LIGHT_BUTTON_DOWN; to suppress that behavior, add an
|
||||
// empty case for EVENT_LIGHT_BUTTON_DOWN.
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
// Just in case you have need for another button.
|
||||
break;
|
||||
case EVENT_TIMEOUT:
|
||||
// Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
|
||||
// you may uncomment this line to move back to the first watch face in the list:
|
||||
// movement_move_to_face(0);
|
||||
break;
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
// If you did not resign in EVENT_TIMEOUT, you can use this event to update the display once a minute.
|
||||
// Avoid displaying fast-updating values like seconds, since the display won't update again for 60 seconds.
|
||||
// You should also consider starting the tick animation, to show the wearer that this is sleep mode:
|
||||
// watch_start_sleep_animation(500);
|
||||
_minimal_clock_face_update_display();
|
||||
break;
|
||||
default:
|
||||
// Movement's default loop handler will step in for any cases you don't handle above:
|
||||
// * EVENT_LIGHT_BUTTON_DOWN lights the LED
|
||||
// * EVENT_MODE_BUTTON_UP moves to the next watch face in the list
|
||||
// * EVENT_MODE_LONG_PRESS returns to the first watch face (or skips to the secondary watch face, if configured)
|
||||
// You can override any of these behaviors by adding a case for these events to this switch statement.
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
// return true if the watch can enter standby mode. Generally speaking, you should always return true.
|
||||
// Exceptions:
|
||||
// * If you are displaying a color using the low-level watch_set_led_color function, you should return false.
|
||||
// * If you are sounding the buzzer using the low-level watch_set_buzzer_on function, you should return false.
|
||||
// Note that if you are driving the LED or buzzer using Movement functions like movement_illuminate_led or
|
||||
// movement_play_alarm, you can still return true. This guidance only applies to the low-level watch_ functions.
|
||||
return true;
|
||||
}
|
||||
|
||||
void minimal_clock_face_resign(void *context) {
|
||||
(void) context;
|
||||
|
||||
// handle any cleanup before your watch face goes off-screen.
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Dennisman219
|
||||
*
|
||||
* 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 MINIMAL_CLOCK_FACE_H_
|
||||
#define MINIMAL_CLOCK_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
* MINIMAL CLOCK FACE
|
||||
*
|
||||
* A minimal clock face that just shows hours and minutes.
|
||||
* There is nothing to configure. The face follows the 12h/24h setting
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
// Anything you need to keep track of, put it here!
|
||||
uint8_t unused;
|
||||
} minimal_clock_state_t;
|
||||
|
||||
void minimal_clock_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void minimal_clock_face_activate(void *context);
|
||||
bool minimal_clock_face_loop(movement_event_t event, void *context);
|
||||
void minimal_clock_face_resign(void *context);
|
||||
|
||||
#define minimal_clock_face ((const watch_face_t){ \
|
||||
minimal_clock_face_setup, \
|
||||
minimal_clock_face_activate, \
|
||||
minimal_clock_face_loop, \
|
||||
minimal_clock_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // MINIMAL_CLOCK_FACE_H_
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Jonas Termeau - original repetition_minute_face
|
||||
* Copyright (c) 2023 Brian Blakley - modified minute_repeater_decimal_face
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This face, minute_repeater_decimal_face, is a modification of the original
|
||||
* repetition_minute_face by Jonas Termeau.
|
||||
*
|
||||
* This version was created by BrianBinFL to use a decimal minute repeater pattern
|
||||
* (hours, tens, and minutes) instead of the traditional pattern (hours, quarters,
|
||||
* minutes).
|
||||
*
|
||||
* Also 500ms delays were added after the hours segment and after the tens segment
|
||||
* to make it easier for the user to realize that the counting for the current
|
||||
* segment has ended.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "minute_repeater_decimal_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
#include "watch_private_display.h"
|
||||
|
||||
void mrd_play_hour_chime(void) {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
|
||||
}
|
||||
|
||||
void mrd_play_tens_chime(void) {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 150);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 750);
|
||||
}
|
||||
|
||||
void mrd_play_minute_chime(void) {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
|
||||
}
|
||||
|
||||
static void _update_alarm_indicator(bool settings_alarm_enabled, minute_repeater_decimal_state_t *state) {
|
||||
state->alarm_enabled = settings_alarm_enabled;
|
||||
if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
}
|
||||
|
||||
void minute_repeater_decimal_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(minute_repeater_decimal_state_t));
|
||||
minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)*context_ptr;
|
||||
state->signal_enabled = false;
|
||||
state->watch_face_index = watch_face_index;
|
||||
}
|
||||
}
|
||||
|
||||
void minute_repeater_decimal_face_activate(void *context) {
|
||||
minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
|
||||
|
||||
if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
|
||||
|
||||
if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
// handle chime indicator
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
|
||||
// show alarm indicator if there is an active alarm
|
||||
_update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
|
||||
watch_set_colon();
|
||||
|
||||
// this ensures that none of the timestamp fields will match, so we can re-render them all.
|
||||
state->previous_date_time = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool minute_repeater_decimal_face_loop(movement_event_t event, void *context) {
|
||||
minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
|
||||
char buf[11];
|
||||
uint8_t pos;
|
||||
|
||||
watch_date_time_t date_time;
|
||||
uint32_t previous_date_time;
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
date_time = watch_rtc_get_date_time();
|
||||
previous_date_time = state->previous_date_time;
|
||||
state->previous_date_time = date_time.reg;
|
||||
|
||||
// check the battery voltage once a day...
|
||||
if (date_time.unit.day != state->last_battery_check) {
|
||||
state->last_battery_check = date_time.unit.day;
|
||||
watch_enable_adc();
|
||||
uint16_t voltage = watch_get_vcc_voltage();
|
||||
watch_disable_adc();
|
||||
// 2.2 volts will happen when the battery has maybe 5-10% remaining?
|
||||
// we can refine this later.
|
||||
state->battery_low = (voltage < 2200);
|
||||
}
|
||||
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
|
||||
if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before seconds is the same, don't waste cycles setting those segments.
|
||||
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
|
||||
watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
|
||||
break;
|
||||
} else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before minutes is the same.
|
||||
pos = 6;
|
||||
sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
|
||||
} else {
|
||||
// other stuff changed; let's do it all.
|
||||
if (!movement_clock_mode_24h()) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
if (date_time.unit.hour < 12) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
}
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
|
||||
sprintf(buf, "%s%2d%2d%02d ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
|
||||
} else {
|
||||
sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
state->signal_enabled = !state->signal_enabled;
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
break;
|
||||
case EVENT_BACKGROUND_TASK:
|
||||
movement_play_signal();
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_UP:
|
||||
/*
|
||||
* Howdy neighbors, this is the actual complication. Like an actual
|
||||
* (very expensive) watch with a repetition minute complication it's
|
||||
* boring at 00:00 or 1:00 and very quite musical at 23:59 or 12:59.
|
||||
*/
|
||||
|
||||
date_time = watch_rtc_get_date_time();
|
||||
|
||||
|
||||
int hours = date_time.unit.hour;
|
||||
int tens = date_time.unit.minute / 10;
|
||||
int minutes = date_time.unit.minute % 10;
|
||||
|
||||
// chiming hours
|
||||
if (!movement_clock_mode_24h()) {
|
||||
hours = date_time.unit.hour % 12;
|
||||
if (hours == 0) hours = 12;
|
||||
}
|
||||
if (hours > 0) {
|
||||
int count = 0;
|
||||
for(count = hours; count > 0; --count) {
|
||||
mrd_play_hour_chime();
|
||||
}
|
||||
// do a little pause before proceeding to tens
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
|
||||
}
|
||||
|
||||
// chiming tens (if needed)
|
||||
if (tens > 0) {
|
||||
int count = 0;
|
||||
for(count = tens; count > 0; --count) {
|
||||
mrd_play_tens_chime();
|
||||
}
|
||||
// do a little pause before proceeding to minutes
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
|
||||
}
|
||||
|
||||
// chiming minutes (if needed)
|
||||
if (minutes > 0) {
|
||||
int count = 0;
|
||||
for(count = minutes; count > 0; --count) {
|
||||
mrd_play_minute_chime();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void minute_repeater_decimal_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
|
||||
movement_watch_face_advisory_t minute_repeater_decimal_face_advise(void *context) {
|
||||
minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
|
||||
movement_watch_face_advisory_t retval = { 0 };
|
||||
|
||||
if (state->signal_enabled) {
|
||||
watch_date_time_t date_time = watch_rtc_get_date_time();
|
||||
retval.wants_background_task = date_time.unit.minute == 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Jonas Termeau - original repetition_minute_face
|
||||
* Copyright (c) 2023 Brian Blakley - modified minute_repeater_decimal_face
|
||||
*
|
||||
* 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 MINUTE_REPEATER_DECIMAL_FACE_H_
|
||||
#define MINUTE_REPEATER_DECIMAL_FACE_H_
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
/*
|
||||
* A hopefully useful complication for friendly neighbors in the dark
|
||||
*
|
||||
* Originating from 1676 from reverend and mechanician Edward Barlow, and
|
||||
* perfected in 1820 by neighbor Abraham Breguet, a minute repeater or
|
||||
* "repetition minute" is a complication in a mechanical watch or clock that
|
||||
* chimes the hours and often minutes at the press of a button. There are many
|
||||
* types of repeater, from the simple repeater which merely strikes the number
|
||||
* of hours, to the minute repeater which chimes the time down to the minute,
|
||||
* using separate tones for hours, decimal hours, and minutes. They originated
|
||||
* before widespread artificial illumination, to allow the time to be determined
|
||||
* in the dark, and were also used by the visually impaired.
|
||||
*
|
||||
*
|
||||
* How to use it :
|
||||
*
|
||||
* Long press the light button to get an auditive reading of the time like so :
|
||||
* 0..23 (1..12 if 24-hours format isn't enabled) low beep(s) for the hours
|
||||
* 0..9 low-high couple pitched beeps for the tens of minutes
|
||||
* 0..9 high pitched beep(s) for the remaining minutes (ones of minutes)
|
||||
*
|
||||
* Prerequisite : a watch with a working buzzer
|
||||
*
|
||||
* ~ Only in the darkness can you see the stars. - Martin Luther King ~
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
uint32_t previous_date_time;
|
||||
uint8_t last_battery_check;
|
||||
uint8_t watch_face_index;
|
||||
bool signal_enabled;
|
||||
bool battery_low;
|
||||
bool alarm_enabled;
|
||||
} minute_repeater_decimal_state_t;
|
||||
|
||||
void mrd_play_hour_chime(void);
|
||||
void mrd_play_tens_chime(void);
|
||||
void mrd_play_minute_chime(void);
|
||||
void minute_repeater_decimal_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void minute_repeater_decimal_face_activate(void *context);
|
||||
bool minute_repeater_decimal_face_loop(movement_event_t event, void *context);
|
||||
void minute_repeater_decimal_face_resign(void *context);
|
||||
movement_watch_face_advisory_t minute_repeater_decimal_face_advise(void *context);
|
||||
|
||||
#define minute_repeater_decimal_face ((const watch_face_t){ \
|
||||
minute_repeater_decimal_face_setup, \
|
||||
minute_repeater_decimal_face_activate, \
|
||||
minute_repeater_decimal_face_loop, \
|
||||
minute_repeater_decimal_face_resign, \
|
||||
minute_repeater_decimal_face_advise, \
|
||||
})
|
||||
|
||||
#endif // MINUTE_REPEATER_DECIMAL_FACE_H_
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Jonas Termeau
|
||||
*
|
||||
* 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 "repetition_minute_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
#include "watch_private_display.h"
|
||||
|
||||
void play_hour_chime(void) {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
|
||||
}
|
||||
|
||||
void play_quarter_chime(void) {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 150);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C6, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 750);
|
||||
}
|
||||
|
||||
void play_minute_chime(void) {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_E6, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 500);
|
||||
}
|
||||
|
||||
static void _update_alarm_indicator(bool settings_alarm_enabled, repetition_minute_state_t *state) {
|
||||
state->alarm_enabled = settings_alarm_enabled;
|
||||
if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
}
|
||||
|
||||
void repetition_minute_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(repetition_minute_state_t));
|
||||
repetition_minute_state_t *state = (repetition_minute_state_t *)*context_ptr;
|
||||
state->signal_enabled = false;
|
||||
state->watch_face_index = watch_face_index;
|
||||
}
|
||||
}
|
||||
|
||||
void repetition_minute_face_activate(void *context) {
|
||||
repetition_minute_state_t *state = (repetition_minute_state_t *)context;
|
||||
|
||||
if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
|
||||
|
||||
if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
// handle chime indicator
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
|
||||
// show alarm indicator if there is an active alarm
|
||||
_update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
|
||||
watch_set_colon();
|
||||
|
||||
// this ensures that none of the timestamp fields will match, so we can re-render them all.
|
||||
state->previous_date_time = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool repetition_minute_face_loop(movement_event_t event, void *context) {
|
||||
repetition_minute_state_t *state = (repetition_minute_state_t *)context;
|
||||
char buf[11];
|
||||
uint8_t pos;
|
||||
|
||||
watch_date_time_t date_time;
|
||||
uint32_t previous_date_time;
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
date_time = watch_rtc_get_date_time();
|
||||
previous_date_time = state->previous_date_time;
|
||||
state->previous_date_time = date_time.reg;
|
||||
|
||||
// check the battery voltage once a day...
|
||||
if (date_time.unit.day != state->last_battery_check) {
|
||||
state->last_battery_check = date_time.unit.day;
|
||||
watch_enable_adc();
|
||||
uint16_t voltage = watch_get_vcc_voltage();
|
||||
watch_disable_adc();
|
||||
// 2.2 volts will happen when the battery has maybe 5-10% remaining?
|
||||
// we can refine this later.
|
||||
state->battery_low = (voltage < 2200);
|
||||
}
|
||||
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
|
||||
if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before seconds is the same, don't waste cycles setting those segments.
|
||||
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
|
||||
watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
|
||||
break;
|
||||
} else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before minutes is the same.
|
||||
pos = 6;
|
||||
sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
|
||||
} else {
|
||||
// other stuff changed; let's do it all.
|
||||
if (!movement_clock_mode_24h()) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
if (date_time.unit.hour < 12) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
}
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
|
||||
sprintf(buf, "%s%2d%2d%02d ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
|
||||
} else {
|
||||
sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
state->signal_enabled = !state->signal_enabled;
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
break;
|
||||
case EVENT_BACKGROUND_TASK:
|
||||
// uncomment this line to snap back to the clock face when the hour signal sounds:
|
||||
// movement_move_to_face(state->watch_face_index);
|
||||
movement_play_signal();
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_UP:
|
||||
/*
|
||||
* Howdy neighbors, this is the actual complication. Like an actual
|
||||
* (very expensive) watch with a repetition minute complication it's
|
||||
* boring at 00:00 or 1:00 and very quite musical at 23:59 or 12:59.
|
||||
*/
|
||||
|
||||
date_time = watch_rtc_get_date_time();
|
||||
|
||||
|
||||
int hours = date_time.unit.hour;
|
||||
int quarters = date_time.unit.minute / 15;
|
||||
int minutes = date_time.unit.minute % 15;
|
||||
|
||||
// chiming hours
|
||||
if (!movement_clock_mode_24h()) {
|
||||
hours = date_time.unit.hour % 12;
|
||||
if (hours == 0) hours = 12;
|
||||
}
|
||||
if (hours > 0) {
|
||||
int count = 0;
|
||||
for(count = hours; count > 0; --count) {
|
||||
play_hour_chime();
|
||||
}
|
||||
}
|
||||
|
||||
// chiming quarters (if needed)
|
||||
if (quarters > 0) {
|
||||
int count = 0;
|
||||
for(count = quarters; count > 0; --count) {
|
||||
play_quarter_chime();
|
||||
}
|
||||
}
|
||||
|
||||
// chiming minutes (if needed)
|
||||
if (minutes > 0) {
|
||||
int count = 0;
|
||||
for(count = minutes; count > 0; --count) {
|
||||
play_minute_chime();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void repetition_minute_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
|
||||
movement_watch_face_advisory_t repetition_minute_face_advise(void *context) {
|
||||
repetition_minute_state_t *state = (repetition_minute_state_t *)context;
|
||||
movement_watch_face_advisory_t retval = { 0 };
|
||||
|
||||
if (state->signal_enabled) {
|
||||
watch_date_time_t date_time = watch_rtc_get_date_time();
|
||||
retval.wants_background_task = date_time.unit.minute == 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Jonas Termeau
|
||||
*
|
||||
* 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 REPETITION_MINUTE_FACE_H_
|
||||
#define REPETITION_MINUTE_FACE_H_
|
||||
|
||||
/*
|
||||
* REPETITION MINUTE face
|
||||
*
|
||||
* A hopefully useful complication for friendly neighbors in the dark
|
||||
*
|
||||
* Originating from 1676 from reverend and mechanician Edward Barlow, and
|
||||
* perfected in 1820 by neighbor Abraham Breguet, a minute repeater or
|
||||
* "repetition minute" is a complication in a mechanical watch or clock that
|
||||
* chimes the hours and often minutes at the press of a button. There are many
|
||||
* types of repeater, from the simple repeater which merely strikes the number
|
||||
* of hours, to the minute repeater which chimes the time down to the minute,
|
||||
* using separate tones for hours, quarter hours, and minutes. They originated
|
||||
* before widespread artificial illumination, to allow the time to be determined
|
||||
* in the dark, and were also used by the visually impaired.
|
||||
*
|
||||
* How to use it :
|
||||
*
|
||||
* Long press the light button to get an auditive reading of the time like so :
|
||||
* 0..23 (1..12 if 24-hours format isn't enabled) low beep(s) for the hours
|
||||
* 0..3 low-high couple pitched beeps for the quarters
|
||||
* 0..14 high pitched beep(s) for the remaining minutes
|
||||
*
|
||||
* Prerequisite : a watch with a working buzzer
|
||||
*
|
||||
* ~ Only in the darkness can you see the stars. - Martin Luther King ~
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t previous_date_time;
|
||||
uint8_t last_battery_check;
|
||||
uint8_t watch_face_index;
|
||||
bool signal_enabled;
|
||||
bool battery_low;
|
||||
bool alarm_enabled;
|
||||
} repetition_minute_state_t;
|
||||
|
||||
void play_hour_chime(void);
|
||||
void play_quarter_chime(void);
|
||||
void play_minute_chime(void);
|
||||
void repetition_minute_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void repetition_minute_face_activate(void *context);
|
||||
bool repetition_minute_face_loop(movement_event_t event, void *context);
|
||||
void repetition_minute_face_resign(void *context);
|
||||
movement_watch_face_advisory_t repetition_minute_face_advise(void *context);
|
||||
|
||||
#define repetition_minute_face ((const watch_face_t){ \
|
||||
repetition_minute_face_setup, \
|
||||
repetition_minute_face_activate, \
|
||||
repetition_minute_face_loop, \
|
||||
repetition_minute_face_resign, \
|
||||
repetition_minute_face_advise, \
|
||||
})
|
||||
|
||||
#endif // REPETITION_MINUTE_FACE_H_
|
||||
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Andreas Nebinger, based on Joey Castillo's simple clock face
|
||||
*
|
||||
* 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 "simple_clock_bin_led_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
#include "watch_private_display.h"
|
||||
|
||||
static void _update_alarm_indicator(bool settings_alarm_enabled, simple_clock_bin_led_state_t *state) {
|
||||
state->alarm_enabled = settings_alarm_enabled;
|
||||
if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
}
|
||||
|
||||
static void _display_left_aligned(uint8_t value) {
|
||||
if (value >= 10) {
|
||||
watch_display_character('0' + value / 10, 4);
|
||||
watch_display_character('0' + value % 10, 5);
|
||||
} else {
|
||||
watch_display_character('0' + value, 4);
|
||||
watch_display_character(' ', 5);
|
||||
}
|
||||
}
|
||||
|
||||
void simple_clock_bin_led_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(simple_clock_bin_led_state_t));
|
||||
memset(*context_ptr, 0, sizeof(simple_clock_bin_led_state_t));
|
||||
simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)*context_ptr;
|
||||
state->watch_face_index = watch_face_index;
|
||||
}
|
||||
}
|
||||
|
||||
void simple_clock_bin_led_face_activate(void *context) {
|
||||
simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context;
|
||||
|
||||
if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
|
||||
|
||||
if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
// handle chime indicator
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
|
||||
// show alarm indicator if there is an active alarm
|
||||
_update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
|
||||
watch_set_colon();
|
||||
|
||||
// this ensures that none of the timestamp fields will match, so we can re-render them all.
|
||||
state->previous_date_time = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool simple_clock_bin_led_face_loop(movement_event_t event, void *context) {
|
||||
simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context;
|
||||
char buf[11];
|
||||
uint8_t pos;
|
||||
|
||||
watch_date_time_t date_time;
|
||||
uint32_t previous_date_time;
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
date_time = watch_rtc_get_date_time();
|
||||
if (state->flashing_state > 0) {
|
||||
if (state->ticks) {
|
||||
state->ticks--;
|
||||
} else {
|
||||
|
||||
if (state->flashing_state & 64) {
|
||||
// start led on for current bit
|
||||
state->flashing_state &= 63;
|
||||
state->ticks = (state->flashing_value & 1 ? 7 : 1);
|
||||
movement_illuminate_led();
|
||||
} else {
|
||||
// indicate first or switch to next bit
|
||||
watch_set_led_off();
|
||||
if ((state->flashing_state & 128) == 0) state->flashing_value = state->flashing_value >> 1;
|
||||
if (state->flashing_value || (state->flashing_state & 128)) {
|
||||
state->flashing_state &= 127;
|
||||
state->flashing_state |= 64;
|
||||
state->ticks = 6;
|
||||
} else if (state->flashing_state & 1) {
|
||||
// transition to minutes
|
||||
state->flashing_state = 2 + 128;
|
||||
state->flashing_value = date_time.unit.minute;
|
||||
_display_left_aligned(state->flashing_value);
|
||||
state->ticks = 9;
|
||||
} else {
|
||||
// end flashing
|
||||
state->flashing_state = 0;
|
||||
state->previous_date_time = 0xFFFFFFFF;
|
||||
movement_request_tick_frequency(1);
|
||||
watch_set_colon();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
previous_date_time = state->previous_date_time;
|
||||
state->previous_date_time = date_time.reg;
|
||||
|
||||
// check the battery voltage once a day...
|
||||
if (date_time.unit.day != state->last_battery_check) {
|
||||
state->last_battery_check = date_time.unit.day;
|
||||
watch_enable_adc();
|
||||
uint16_t voltage = watch_get_vcc_voltage();
|
||||
watch_disable_adc();
|
||||
// 2.2 volts will happen when the battery has maybe 5-10% remaining?
|
||||
// we can refine this later.
|
||||
state->battery_low = (voltage < 2200);
|
||||
}
|
||||
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
|
||||
if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before seconds is the same, don't waste cycles setting those segments.
|
||||
watch_display_character_lp_seconds('0' + date_time.unit.second / 10, 8);
|
||||
watch_display_character_lp_seconds('0' + date_time.unit.second % 10, 9);
|
||||
break;
|
||||
} else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before minutes is the same.
|
||||
pos = 6;
|
||||
sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
|
||||
} else {
|
||||
// other stuff changed; let's do it all.
|
||||
if (!movement_clock_mode_24h()) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
if (date_time.unit.hour < 12) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
}
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
|
||||
sprintf(buf, "%s%2d%2d%02d ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
|
||||
} else {
|
||||
sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
}
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
state->signal_enabled = !state->signal_enabled;
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
break;
|
||||
case EVENT_BACKGROUND_TASK:
|
||||
// uncomment this line to snap back to the clock face when the hour signal sounds:
|
||||
// movement_move_to_face(state->watch_face_index);
|
||||
movement_play_signal();
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
if (state->flashing_state == 0) {
|
||||
date_time = watch_rtc_get_date_time();
|
||||
state->flashing_state = 1 + 128;
|
||||
state->ticks = 4;
|
||||
if (!movement_clock_mode_24h()) {
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
}
|
||||
watch_display_string(" ", 4);
|
||||
_display_left_aligned(date_time.unit.hour);
|
||||
state->flashing_value = date_time.unit.hour > 12 ? date_time.unit.hour - 12 : date_time.unit.hour;
|
||||
watch_set_led_off();
|
||||
watch_clear_colon();
|
||||
movement_request_tick_frequency(8);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void simple_clock_bin_led_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
|
||||
movement_watch_face_advisory_t simple_clock_bin_led_face_advise(void *context) {
|
||||
simple_clock_bin_led_state_t *state = (simple_clock_bin_led_state_t *)context;
|
||||
movement_watch_face_advisory_t retval = { 0 };
|
||||
|
||||
if (state->signal_enabled) {
|
||||
watch_date_time_t date_time = watch_rtc_get_date_time();
|
||||
retval.wants_background_task = date_time.unit.minute == 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Andreas Nebinger, based on Joey Castillo's simple clock face
|
||||
*
|
||||
* 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 SIIMPLE_CLOCK_BIN_LED_FACE_H_
|
||||
#define SIIMPLE_CLOCK_BIN_LED_FACE_H_
|
||||
|
||||
/*
|
||||
* BINARY LED CLOCK FACE
|
||||
*
|
||||
* A "fork" of the simple clock face, which provides the functionality of showing
|
||||
* the current time by flashing the LED using binary representation.
|
||||
*
|
||||
* This feature serves as a practical solution to compensate for the admittedly
|
||||
* subpar backlight of the F91w watch and is especially useful if your eyesight
|
||||
* is not the best. By pressing and holding the light button long enough, the
|
||||
* watch will illuminate the LED to showcase the current time.
|
||||
*
|
||||
* How to interpret the flashing led:
|
||||
* - Firstly, the hour is presented as a binary number, with the lowest bit displayed
|
||||
* first. A short flash signifies 0, while a longer flash represents 1. If you use
|
||||
* the watch in 24h mode, please note that the indicated value may be decreased
|
||||
* by 12, to keep things simple and short. For example, 22h would be translated
|
||||
* to 10h.
|
||||
* - After showing the hour, a lengthier pause indicates that minutes will be shown
|
||||
* next.
|
||||
* - Similar to the hour representation, minutes are displayed in binary format,
|
||||
* starting with the lowest bits. A short flash denotes 0, a longer flash
|
||||
* represents 1.
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t previous_date_time;
|
||||
uint8_t last_battery_check;
|
||||
uint8_t watch_face_index;
|
||||
bool signal_enabled;
|
||||
bool battery_low;
|
||||
bool alarm_enabled;
|
||||
uint8_t flashing_state; // bitmap representing the flashing state. Bit 0 = hours showing, bit 1 = minutes showing,
|
||||
// bit 6 = short break between flashing bits, bit 7 = long break between hours and minutes
|
||||
uint8_t flashing_value;
|
||||
uint8_t ticks;
|
||||
} simple_clock_bin_led_state_t;
|
||||
|
||||
void simple_clock_bin_led_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void simple_clock_bin_led_face_activate(void *context);
|
||||
bool simple_clock_bin_led_face_loop(movement_event_t event, void *context);
|
||||
void simple_clock_bin_led_face_resign(void *context);
|
||||
movement_watch_face_advisory_t simple_clock_bin_led_face_advise(void *context);
|
||||
|
||||
#define simple_clock_bin_led_face ((const watch_face_t){ \
|
||||
simple_clock_bin_led_face_setup, \
|
||||
simple_clock_bin_led_face_activate, \
|
||||
simple_clock_bin_led_face_loop, \
|
||||
simple_clock_bin_led_face_resign, \
|
||||
simple_clock_bin_led_face_advise, \
|
||||
})
|
||||
|
||||
#endif // SIIMPLE_CLOCK_BIN_LED_FACE_H_
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "weeknumber_clock_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
static void _update_alarm_indicator(bool settings_alarm_enabled, weeknumber_clock_state_t *state) {
|
||||
state->alarm_enabled = settings_alarm_enabled;
|
||||
if (state->alarm_enabled) watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
}
|
||||
|
||||
void weeknumber_clock_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(weeknumber_clock_state_t));
|
||||
weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)*context_ptr;
|
||||
state->signal_enabled = false;
|
||||
state->watch_face_index = watch_face_index;
|
||||
}
|
||||
}
|
||||
|
||||
void weeknumber_clock_face_activate(void *context) {
|
||||
weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)context;
|
||||
|
||||
if (watch_sleep_animation_is_running()) watch_stop_sleep_animation();
|
||||
|
||||
if (movement_clock_mode_24h()) watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
// handle chime indicator
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
|
||||
// show alarm indicator if there is an active alarm
|
||||
_update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
|
||||
watch_set_colon();
|
||||
|
||||
// this ensures that none of the timestamp fields will match, so we can re-render them all.
|
||||
state->previous_date_time = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool weeknumber_clock_face_loop(movement_event_t event, void *context) {
|
||||
weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)context;
|
||||
char buf[11];
|
||||
uint8_t pos;
|
||||
|
||||
watch_date_time_t date_time;
|
||||
uint32_t previous_date_time;
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
date_time = watch_rtc_get_date_time();
|
||||
previous_date_time = state->previous_date_time;
|
||||
state->previous_date_time = date_time.reg;
|
||||
|
||||
// check the battery voltage once a day...
|
||||
if (date_time.unit.day != state->last_battery_check) {
|
||||
state->last_battery_check = date_time.unit.day;
|
||||
watch_enable_adc();
|
||||
uint16_t voltage = watch_get_vcc_voltage();
|
||||
watch_disable_adc();
|
||||
// 2.2 volts will happen when the battery has maybe 5-10% remaining?
|
||||
// we can refine this later.
|
||||
state->battery_low = (voltage < 2200);
|
||||
}
|
||||
|
||||
// ...and set the LAP indicator if low.
|
||||
if (state->battery_low) watch_set_indicator(WATCH_INDICATOR_LAP);
|
||||
|
||||
if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
// everything before minutes is the same.
|
||||
pos = 6;
|
||||
sprintf(buf, "%02d%02d", date_time.unit.minute, watch_utility_get_weeknumber(date_time.unit.year, date_time.unit.month, date_time.unit.day));
|
||||
} else {
|
||||
// other stuff changed; let's do it all.
|
||||
if (!movement_clock_mode_24h()) {
|
||||
// if we are in 12 hour mode, do some cleanup.
|
||||
if (date_time.unit.hour < 12) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0) date_time.unit.hour = 12;
|
||||
}
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
if (!watch_sleep_animation_is_running()) watch_start_sleep_animation(500);
|
||||
sprintf(buf, "%s%2d%2d%02d ", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute);
|
||||
} else {
|
||||
sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, date_time.unit.hour, date_time.unit.minute, watch_utility_get_weeknumber(date_time.unit.year, date_time.unit.month, date_time.unit.day));
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
// handle alarm indicator
|
||||
if (state->alarm_enabled != movement_alarm_enabled()) _update_alarm_indicator(movement_alarm_enabled(), state);
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
state->signal_enabled = !state->signal_enabled;
|
||||
if (state->signal_enabled) watch_set_indicator(WATCH_INDICATOR_BELL);
|
||||
else watch_clear_indicator(WATCH_INDICATOR_BELL);
|
||||
break;
|
||||
case EVENT_BACKGROUND_TASK:
|
||||
// uncomment this line to snap back to the clock face when the hour signal sounds:
|
||||
// movement_move_to_face(state->watch_face_index);
|
||||
movement_play_signal();
|
||||
break;
|
||||
default:
|
||||
movement_default_loop_handler(event);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void weeknumber_clock_face_resign(void *context) {
|
||||
(void) context;
|
||||
}
|
||||
|
||||
movement_watch_face_advisory_t weeknumber_clock_face_advise(void *context) {
|
||||
weeknumber_clock_state_t *state = (weeknumber_clock_state_t *)context;
|
||||
movement_watch_face_advisory_t retval = { 0 };
|
||||
|
||||
if (state->signal_enabled) {
|
||||
watch_date_time_t date_time = watch_rtc_get_date_time();
|
||||
retval.wants_background_task = date_time.unit.minute == 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* 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 WEEKNUMBER_CLOCK_FACE_H_
|
||||
#define WEEKNUMBER_CLOCK_FACE_H_
|
||||
|
||||
/*
|
||||
* WEEK-NUMBER WATCH FACE
|
||||
*
|
||||
* Same as simple clock, but has iso 8601 week number instead of seconds counter.
|
||||
*
|
||||
* Long-press ALARM to toggle the hourly chime.
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t previous_date_time;
|
||||
uint8_t last_battery_check;
|
||||
uint8_t watch_face_index;
|
||||
bool signal_enabled;
|
||||
bool battery_low;
|
||||
bool alarm_enabled;
|
||||
} weeknumber_clock_state_t;
|
||||
|
||||
void weeknumber_clock_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void weeknumber_clock_face_activate(void *context);
|
||||
bool weeknumber_clock_face_loop(movement_event_t event, void *context);
|
||||
void weeknumber_clock_face_resign(void *context);
|
||||
movement_watch_face_advisory_t weeknumber_clock_face_advise(void *context);
|
||||
|
||||
#define weeknumber_clock_face ((const watch_face_t){ \
|
||||
weeknumber_clock_face_setup, \
|
||||
weeknumber_clock_face_activate, \
|
||||
weeknumber_clock_face_loop, \
|
||||
weeknumber_clock_face_resign, \
|
||||
weeknumber_clock_face_advise, \
|
||||
})
|
||||
|
||||
#endif // SIMPLE_CLOCK_FACE_H_
|
||||
@@ -1,374 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Konrad Rieck
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "world_clock2_face.h"
|
||||
#include "watch.h"
|
||||
#include "watch_utility.h"
|
||||
#include "watch_utility.h"
|
||||
|
||||
static bool refresh_face;
|
||||
|
||||
/* Simple macros for navigation */
|
||||
#define FORWARD +1
|
||||
#define BACKWARD -1
|
||||
|
||||
/* Activate refresh of time */
|
||||
#define REFRESH_TIME 0xffffffff
|
||||
|
||||
/* List of all time zone names */
|
||||
const char *zone_names[] = {
|
||||
"UTC", // 0 : 0:00:00 (UTC)
|
||||
"CET", // 1 : 1:00:00 (Central European Time)
|
||||
"SAST", // 2 : 2:00:00 (South African Standard Time)
|
||||
"ARST", // 3 : 3:00:00 (Arabia Standard Time)
|
||||
"IRST", // 4 : 3:30:00 (Iran Standard Time)
|
||||
"GET", // 5 : 4:00:00 (Georgia Standard Time)
|
||||
"AFT", // 6 : 4:30:00 (Afghanistan Time)
|
||||
"PKT", // 7 : 5:00:00 (Pakistan Standard Time)
|
||||
"IST", // 8 : 5:30:00 (Indian Standard Time)
|
||||
"NPT", // 9 : 5:45:00 (Nepal Time)
|
||||
"KGT", // 10 : 6:00:00 (Kyrgyzstan time)
|
||||
"MYST", // 11 : 6:30:00 (Myanmar Time)
|
||||
"THA", // 12 : 7:00:00 (Thailand Standard Time)
|
||||
"CST", // 13 : 8:00:00 (China Standard Time, Australian Western Standard Time)
|
||||
"ACWS", // 14 : 8:45:00 (Australian Central Western Standard Time)
|
||||
"JST", // 15 : 9:00:00 (Japan Standard Time, Korea Standard Time)
|
||||
"ACST", // 16 : 9:30:00 (Australian Central Standard Time)
|
||||
"AEST", // 17 : 10:00:00 (Australian Eastern Standard Time)
|
||||
"LHST", // 18 : 10:30:00 (Lord Howe Standard Time)
|
||||
"SBT", // 19 : 11:00:00 (Solomon Islands Time)
|
||||
"NZST", // 20 : 12:00:00 (New Zealand Standard Time)
|
||||
"CHAS", // 21 : 12:45:00 (Chatham Standard Time)
|
||||
"TOT", // 22 : 13:00:00 (Tonga Time)
|
||||
"CHAD", // 23 : 13:45:00 (Chatham Daylight Time)
|
||||
"LINT", // 24 : 14:00:00 (Line Islands Time)
|
||||
"BIT", // 25 : -12:00:00 (Baker Island Time)
|
||||
"NUT", // 26 : -11:00:00 (Niue Time)
|
||||
"HST", // 27 : -10:00:00 (Hawaii-Aleutian Standard Time)
|
||||
"MART", // 28 : -9:30:00 (Marquesas Islands Time)
|
||||
"AKST", // 29 : -9:00:00 (Alaska Standard Time)
|
||||
"PST", // 30 : -8:00:00 (Pacific Standard Time)
|
||||
"MST", // 31 : -7:00:00 (Mountain Standard Time)
|
||||
"CST", // 32 : -6:00:00 (Central Standard Time)
|
||||
"EST", // 33 : -5:00:00 (Eastern Standard Time)
|
||||
"VET", // 34 : -4:30:00 (Venezuelan Standard Time)
|
||||
"AST", // 35 : -4:00:00 (Atlantic Standard Time)
|
||||
"NST", // 36 : -3:30:00 (Newfoundland Standard Time)
|
||||
"BRT", // 37 : -3:00:00 (Brasilia Time)
|
||||
"NDT", // 38 : -2:30:00 (Newfoundland Daylight Time)
|
||||
"FNT", // 39 : -2:00:00 (Fernando de Noronha Time)
|
||||
"AZOT", // 40 : -1:00:00 (Azores Standard Time)
|
||||
};
|
||||
|
||||
/* Modulo function */
|
||||
static inline unsigned int mod(int a, int b)
|
||||
{
|
||||
int r = a % b;
|
||||
return r < 0 ? r + b : r;
|
||||
}
|
||||
|
||||
/* Find the next selected time zone */
|
||||
static inline uint8_t find_selected_zone(world_clock2_state_t *state, int direction)
|
||||
{
|
||||
uint8_t i = state->current_zone;
|
||||
|
||||
do {
|
||||
i = mod(i + direction, NUM_TIME_ZONES);
|
||||
/* Could not find a selected zone. Return UTC */
|
||||
if (i == state->current_zone) {
|
||||
return 0;
|
||||
}
|
||||
} while (!state->zones[i].selected);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Beep when zone is enabled. An octave up */
|
||||
static void beep_enable() {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_G7, 50);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
|
||||
}
|
||||
|
||||
/* Beep when zone id disable. An octave down */
|
||||
static void beep_disable() {
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_REST, 75);
|
||||
watch_buzzer_play_note(BUZZER_NOTE_G7, 75);
|
||||
}
|
||||
|
||||
void world_clock2_face_setup(uint8_t watch_face_index, void **context_ptr)
|
||||
{
|
||||
(void) watch_face_index;
|
||||
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(world_clock2_state_t));
|
||||
memset(*context_ptr, 0, sizeof(world_clock2_state_t));
|
||||
|
||||
/* Start in settings mode */
|
||||
world_clock2_state_t *state = (world_clock2_state_t *) * context_ptr;
|
||||
state->current_mode = WORLD_CLOCK2_MODE_SETTINGS;
|
||||
}
|
||||
}
|
||||
|
||||
void world_clock2_face_activate(void *context)
|
||||
{
|
||||
world_clock2_state_t *state = (world_clock2_state_t *) context;
|
||||
|
||||
if (watch_sleep_animation_is_running())
|
||||
watch_stop_sleep_animation();
|
||||
|
||||
switch (state->current_mode) {
|
||||
case WORLD_CLOCK2_MODE_DISPLAY:
|
||||
/* Normal tick frequency */
|
||||
movement_request_tick_frequency(1);
|
||||
break;
|
||||
case WORLD_CLOCK2_MODE_SETTINGS:
|
||||
/* Faster frequency for blinking effect */
|
||||
movement_request_tick_frequency(4);
|
||||
break;
|
||||
}
|
||||
refresh_face = true;
|
||||
}
|
||||
|
||||
static bool mode_display(movement_event_t event, world_clock2_state_t *state)
|
||||
{
|
||||
char buf[11];
|
||||
uint8_t pos;
|
||||
|
||||
uint32_t timestamp;
|
||||
uint32_t previous_date_time;
|
||||
watch_date_time_t date_time;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
/* Update indicators and colon on refresh */
|
||||
if (refresh_face) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
watch_set_colon();
|
||||
if (movement_clock_mode_24h())
|
||||
watch_set_indicator(WATCH_INDICATOR_24H);
|
||||
|
||||
state->previous_date_time = REFRESH_TIME;
|
||||
refresh_face = false;
|
||||
}
|
||||
|
||||
/* Determine current time at time zone and store date/time */
|
||||
date_time = watch_rtc_get_date_time();
|
||||
timestamp = watch_utility_date_time_to_unix_time(date_time, movement_get_current_timezone_offset());
|
||||
date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->current_zone] * 60);
|
||||
previous_date_time = state->previous_date_time;
|
||||
state->previous_date_time = date_time.reg;
|
||||
|
||||
if ((date_time.reg >> 6) == (previous_date_time >> 6) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
/* Everything before seconds is the same, don't waste cycles setting those segments. */
|
||||
pos = 8;
|
||||
sprintf(buf, "%02d", date_time.unit.second);
|
||||
} else if ((date_time.reg >> 12) == (previous_date_time >> 12) && event.event_type != EVENT_LOW_ENERGY_UPDATE) {
|
||||
/* Everything before minutes is the same. */
|
||||
pos = 6;
|
||||
sprintf(buf, "%02d%02d", date_time.unit.minute, date_time.unit.second);
|
||||
} else {
|
||||
/* Other stuff changed; Let's do it all. */
|
||||
if (!movement_clock_mode_24h()) {
|
||||
/* If we are in 12 hour mode, do some cleanup. */
|
||||
if (date_time.unit.hour < 12) {
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
} else {
|
||||
watch_set_indicator(WATCH_INDICATOR_PM);
|
||||
}
|
||||
date_time.unit.hour %= 12;
|
||||
if (date_time.unit.hour == 0)
|
||||
date_time.unit.hour = 12;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
if (event.event_type == EVENT_LOW_ENERGY_UPDATE) {
|
||||
if (!watch_sleep_animation_is_running())
|
||||
watch_start_sleep_animation(500);
|
||||
|
||||
sprintf(buf, "%.2s%2d%2d%02d ",
|
||||
zone_names[state->current_zone],
|
||||
date_time.unit.day,
|
||||
date_time.unit.hour,
|
||||
date_time.unit.minute);
|
||||
} else {
|
||||
sprintf(buf, "%.2s%2d%2d%02d%02d",
|
||||
zone_names[state->current_zone],
|
||||
date_time.unit.day,
|
||||
date_time.unit.hour,
|
||||
date_time.unit.minute,
|
||||
date_time.unit.second);
|
||||
}
|
||||
}
|
||||
watch_display_string(buf, pos);
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
state->current_zone = find_selected_zone(state, FORWARD);
|
||||
state->previous_date_time = REFRESH_TIME;
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
/* Do nothing. */
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
state->current_zone = find_selected_zone(state, BACKWARD);
|
||||
state->previous_date_time = REFRESH_TIME;
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
movement_illuminate_led();
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
/* Switch to settings mode */
|
||||
state->current_mode = WORLD_CLOCK2_MODE_SETTINGS;
|
||||
refresh_face = true;
|
||||
movement_request_tick_frequency(1);
|
||||
|
||||
if (movement_button_should_sound())
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
|
||||
break;
|
||||
case EVENT_MODE_BUTTON_UP:
|
||||
/* Reset frequency and move to next face */
|
||||
movement_request_tick_frequency(1);
|
||||
movement_move_to_next_face();
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mode_settings(movement_event_t event, world_clock2_state_t *state)
|
||||
{
|
||||
char buf[11];
|
||||
int8_t hours, minutes;
|
||||
uint8_t zone;
|
||||
div_t result;
|
||||
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
case EVENT_TICK:
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
/* Update indicator and colon on refresh */
|
||||
if (refresh_face) {
|
||||
watch_clear_colon();
|
||||
watch_clear_indicator(WATCH_INDICATOR_24H);
|
||||
watch_clear_indicator(WATCH_INDICATOR_PM);
|
||||
refresh_face = false;
|
||||
}
|
||||
result = div(movement_timezone_offsets[state->current_zone], 60);
|
||||
hours = result.quot;
|
||||
minutes = result.rem;
|
||||
|
||||
/*
|
||||
* Display time zone. The range of the parameters is reduced
|
||||
* to avoid accidentally overflowing the buffer and to suppress
|
||||
* corresponding compiler warnings.
|
||||
*/
|
||||
sprintf(buf, "%.2s%2d %c%02d%02d",
|
||||
zone_names[state->current_zone],
|
||||
state->current_zone % 100,
|
||||
hours < 0 ? '-' : '+',
|
||||
abs(hours) % 24,
|
||||
abs(minutes) % 60);
|
||||
|
||||
/* Let the zone number blink */
|
||||
if (event.subsecond % 2)
|
||||
buf[2] = buf[3] = ' ';
|
||||
|
||||
if (state->zones[state->current_zone].selected)
|
||||
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
else
|
||||
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
|
||||
|
||||
watch_display_string(buf, 0);
|
||||
break;
|
||||
case EVENT_ALARM_BUTTON_UP:
|
||||
state->current_zone = mod(state->current_zone + FORWARD, NUM_TIME_ZONES);
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_UP:
|
||||
state->current_zone = mod(state->current_zone + BACKWARD, NUM_TIME_ZONES);
|
||||
break;
|
||||
case EVENT_LIGHT_BUTTON_DOWN:
|
||||
/* Do nothing */
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
/* Find next selected zone */
|
||||
if (!state->zones[state->current_zone].selected)
|
||||
state->current_zone = find_selected_zone(state, FORWARD);
|
||||
|
||||
/* Switch to display mode */
|
||||
state->current_mode = WORLD_CLOCK2_MODE_DISPLAY;
|
||||
refresh_face = true;
|
||||
movement_request_tick_frequency(1);
|
||||
|
||||
if (movement_button_should_sound())
|
||||
watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
|
||||
break;
|
||||
case EVENT_LIGHT_LONG_PRESS:
|
||||
/* Toggle selection of current zone */
|
||||
zone = state->current_zone;
|
||||
state->zones[zone].selected = !state->zones[zone].selected;
|
||||
|
||||
if (movement_button_should_sound()) {
|
||||
if (state->zones[zone].selected) {
|
||||
beep_enable();
|
||||
} else {
|
||||
beep_disable();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVENT_MODE_BUTTON_UP:
|
||||
/* Reset frequency and move to next face */
|
||||
movement_request_tick_frequency(1);
|
||||
movement_move_to_next_face();
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool world_clock2_face_loop(movement_event_t event, void *context)
|
||||
{
|
||||
world_clock2_state_t *state = (world_clock2_state_t *) context;
|
||||
switch (state->current_mode) {
|
||||
case WORLD_CLOCK2_MODE_DISPLAY:
|
||||
return mode_display(event, state);
|
||||
case WORLD_CLOCK2_MODE_SETTINGS:
|
||||
return mode_settings(event, state);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void world_clock2_face_resign(void *context)
|
||||
{
|
||||
(void) context;
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Konrad Rieck
|
||||
* 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 WORLD_CLOCK2_FACE_H_
|
||||
#define WORLD_CLOCK2_FACE_H_
|
||||
|
||||
/*
|
||||
* WORLD CLOCK 2
|
||||
*
|
||||
* This is an alternative world clock face that allows the user to cycle
|
||||
* through a list of selected time zones. It extends the original
|
||||
* implementation by Joey Castillo. The face has two modes: display mode
|
||||
* and settings mode.
|
||||
*
|
||||
* Settings mode
|
||||
*
|
||||
* When the clock face is activated for the first time, it enters settings
|
||||
* mode. Here, the user can select the time zones they want to display. The
|
||||
* face shows a summary of the current time zone:
|
||||
* * The top of the face displays the first two letters of the time zone
|
||||
* abbreviation, such as "PS" for Pacific Standard Time or CE for
|
||||
* "Central European Time".
|
||||
* * The upper-right corner shows the index number of the time zone. This
|
||||
* helps avoid confusion when multiple time zones have the same two-letter
|
||||
* abbreviation.
|
||||
* * The main display shows the offset from UTC, with a "+" indicating a
|
||||
* positive offset and a "-" indicating a negative offset. For example,
|
||||
* the offset for Japanese Standard Time is displayed as "+9:00".
|
||||
*
|
||||
* The user can navigate through the time zones and select them using the
|
||||
* following buttons:
|
||||
* * The ALARM button moves forward to the next time zone, while the LIGHT
|
||||
* button moves backward to the previous zone. This way, the user can
|
||||
* cycle through all 41 supported time zones.
|
||||
* * A long press on the LIGHT button selects the current time zone, and
|
||||
* the signal indicator appears at the top left. Another long press of
|
||||
* the LIGHT button deselects the time zone.
|
||||
* * A long press on the ALARM button exits settings mode and returns to
|
||||
* display mode.
|
||||
*
|
||||
* Display mode
|
||||
*
|
||||
* In the display mode, the face shows the time of the currently selected
|
||||
* time zone. The face includes the following components:
|
||||
* * The top of the face displays the first two letters of the time zone
|
||||
* abbreviation, such as "PS" for Pacific Standard Time or "CE" for
|
||||
* Central European Time.
|
||||
* * The upper-right corner shows the current day of the month, which helps
|
||||
* indicate time zones that cross the international date line with respect
|
||||
* to the local time.
|
||||
* * The main display shows the time in the selected time zone in either
|
||||
* 12-hour or 24-hour form. There is no timeout, allowing users to keep
|
||||
* the chosen time zone displayed for as long as they wish.
|
||||
*
|
||||
* The user can navigate through the selected time zones using the following
|
||||
* buttons:
|
||||
* * The ALARM button moves to the next selected time zone, while the LIGHT
|
||||
* button moves to the previous zone. If no time zone is selected, the
|
||||
* face simply shows UTC.
|
||||
* * A long press on the ALARM button enters settings mode and enables the
|
||||
* user to re-configure the selected time zones.
|
||||
* * A long press on the LIGHT button activates the LED illumination of the
|
||||
* watch.
|
||||
*/
|
||||
|
||||
/* Number of zones. See movement_timezone_offsets. */
|
||||
#define NUM_TIME_ZONES 41
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
typedef enum {
|
||||
WORLD_CLOCK2_MODE_DISPLAY,
|
||||
WORLD_CLOCK2_MODE_SETTINGS
|
||||
} world_clock2_mode_t;
|
||||
|
||||
typedef struct {
|
||||
bool selected;
|
||||
} world_clock2_zone_t;
|
||||
|
||||
typedef struct {
|
||||
world_clock2_zone_t zones[NUM_TIME_ZONES];
|
||||
world_clock2_mode_t current_mode;
|
||||
uint8_t current_zone;
|
||||
uint32_t previous_date_time;
|
||||
} world_clock2_state_t;
|
||||
|
||||
void world_clock2_face_setup(uint8_t watch_face_index, void **context_ptr);
|
||||
void world_clock2_face_activate(void *context);
|
||||
bool world_clock2_face_loop(movement_event_t event, void *context);
|
||||
void world_clock2_face_resign(void *context);
|
||||
|
||||
#define world_clock2_face ((const watch_face_t){ \
|
||||
world_clock2_face_setup, \
|
||||
world_clock2_face_activate, \
|
||||
world_clock2_face_loop, \
|
||||
world_clock2_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif /* WORLD_CLOCK2_FACE_H_ */
|
||||
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 <#author_name#>
|
||||
*
|
||||
* 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 "wyoscan_face.h"
|
||||
#include "watch_private_display.h"
|
||||
|
||||
/*
|
||||
Slowly render the current time from left to right,
|
||||
scanning across its liquid crystal face, completing 1 cycle every 2 seconds.
|
||||
|
||||
Created to mimic the wyoscan watch that was produced by Halmos and designed by Dexter Sinister
|
||||
It looks like this https://www.o-r-g.com/apps/wyoscan
|
||||
|
||||
You’ll notice that reading this watch requires more attention than usual,
|
||||
as the seven segments of each digit are lit one by one across its display.
|
||||
This speed may be adjusted until it reaches the limits of your perception.
|
||||
You and your watch are now in tune.
|
||||
|
||||
This is a relatively generic way of animating a time display.
|
||||
If you want to modify the animation, you can change the segment_map
|
||||
the A-F are corresponding to the segments on the watch face
|
||||
A
|
||||
F B
|
||||
G
|
||||
E C
|
||||
D
|
||||
the X's are the frames that will be skipped in the animation
|
||||
This particular segment_map allocates 8 frames to display each number
|
||||
this is to achieve the 2 second cycle time.
|
||||
8 frames per number * 6 numbers + the trailing 16 frames = 64 frames
|
||||
at 32 frames per second, this is a 2 second cycle time.
|
||||
|
||||
I tried to make the animation of each number display similar to if you were
|
||||
to draw the number on the watch face with a pen, pausing with 'X'
|
||||
when your pen might turn a corner or when you might cross over
|
||||
a line you've already drawn. It is vaguely top to bottom and counter,
|
||||
clockwise when possible.
|
||||
*/
|
||||
static char *segment_map[] = {
|
||||
"AXFBDEXC", // 0
|
||||
"BXXXCXXX", // 1
|
||||
"ABGEXXXD", // 2
|
||||
"ABGXXXCD", // 3
|
||||
"FXGBXXXC", // 4
|
||||
"AXFXGXCD", // 5
|
||||
"AXFEDCXG", // 6
|
||||
"AXXBXXCX", // 7
|
||||
"AFGCDEXB", // 8
|
||||
"AFGBXXCD" // 9
|
||||
};
|
||||
|
||||
/*
|
||||
This is the mapping of input to the watch_set_pixel() function
|
||||
for each position in hhmmss it defines the 2 dimention input at each of A-F*/
|
||||
static const int32_t clock_mapping[6][7][2] = {
|
||||
// hour 1
|
||||
{{1,18}, {2,19}, {0,19}, {1,18}, {0,18}, {2,18}, {1,19}},
|
||||
// hour 2
|
||||
{{2,20}, {2,21}, {1,21}, {0,21}, {0,20}, {1,17}, {1,20}},
|
||||
// minute 1
|
||||
{{0,22}, {2,23}, {0,23}, {0,22}, {1,22}, {2,22}, {1,23}},
|
||||
// minute 2
|
||||
{{2,1}, {2,10}, {0,1}, {0,0}, {1,0}, {2,0}, {1,1}},
|
||||
// second 1
|
||||
{{2,2}, {2,3}, {0,4}, {0,3}, {0,2}, {1,2}, {1,3}},
|
||||
// second 2
|
||||
{{2,4}, {2,5}, {1,6}, {0,6}, {0,5}, {1,4}, {1,5}},
|
||||
};
|
||||
|
||||
void wyoscan_face_setup(uint8_t watch_face_index, void ** context_ptr) {
|
||||
(void) watch_face_index;
|
||||
if (*context_ptr == NULL) {
|
||||
*context_ptr = malloc(sizeof(wyoscan_state_t));
|
||||
memset(*context_ptr, 0, sizeof(wyoscan_state_t));
|
||||
// Do any one-time tasks in here; the inside of this conditional happens only at boot.
|
||||
}
|
||||
// Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
|
||||
}
|
||||
|
||||
void wyoscan_face_activate(void *context) {
|
||||
wyoscan_state_t *state = (wyoscan_state_t *)context;
|
||||
movement_request_tick_frequency(32);
|
||||
state->total_frames = 64;
|
||||
}
|
||||
|
||||
bool wyoscan_face_loop(movement_event_t event, void *context) {
|
||||
wyoscan_state_t *state = (wyoscan_state_t *)context;
|
||||
|
||||
watch_date_time_t date_time;
|
||||
switch (event.event_type) {
|
||||
case EVENT_ACTIVATE:
|
||||
break;
|
||||
case EVENT_TICK:
|
||||
if (!state->animate) {
|
||||
date_time = watch_rtc_get_date_time();
|
||||
state->start = 0;
|
||||
state->end = 0;
|
||||
state->animation = 0;
|
||||
state->animate = true;
|
||||
state->time_digits[0] = date_time.unit.hour / 10;
|
||||
state->time_digits[1] = date_time.unit.hour % 10;
|
||||
state->time_digits[2] = date_time.unit.minute / 10;
|
||||
state->time_digits[3] = date_time.unit.minute % 10;
|
||||
state->time_digits[4] = date_time.unit.second / 10;
|
||||
state->time_digits[5] = date_time.unit.second % 10;
|
||||
}
|
||||
if ( state->animate ) {
|
||||
// if we have reached the max number of illuminated segments, we clear the oldest one
|
||||
if ((state->end + 1) % MAX_ILLUMINATED_SEGMENTS == state->start) {
|
||||
// clear the oldest pixel if it's not 'X'
|
||||
if (state->illuminated_segments[state->start][0] != 99 && state->illuminated_segments[state->start][1] != 99) {
|
||||
watch_clear_pixel(state->illuminated_segments[state->start][0], state->illuminated_segments[state->start][1]);
|
||||
}
|
||||
// increment the start index to point to the next oldest pixel
|
||||
state->start = (state->start + 1) % MAX_ILLUMINATED_SEGMENTS;
|
||||
}
|
||||
if (state->animation < state->total_frames - MAX_ILLUMINATED_SEGMENTS) {
|
||||
if (state->animation % 32 == 0) {
|
||||
if (state->colon) {
|
||||
watch_set_colon();
|
||||
} else {
|
||||
watch_clear_colon();
|
||||
}
|
||||
state->colon = !state->colon;
|
||||
}
|
||||
|
||||
// calculate the start position for the current frame
|
||||
state->position = (state->animation / 8) % 6;
|
||||
// calculate the current segment for the current digit
|
||||
state->segment = state->animation % strlen(segment_map[state->time_digits[state->position]]);
|
||||
// get the segments for the current digit
|
||||
state->segments = segment_map[state->time_digits[state->position]];
|
||||
|
||||
if (state->segments[state->segment] == 'X') {
|
||||
// if 'X', skip this frame
|
||||
state->illuminated_segments[state->end][0] = 99;
|
||||
state->illuminated_segments[state->end][1] = 99;
|
||||
state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
|
||||
state->animation = (state->animation + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
// calculate the animation frame
|
||||
state->x = clock_mapping[state->position][state->segments[state->segment]-'A'][0];
|
||||
state->y = clock_mapping[state->position][state->segments[state->segment]-'A'][1];
|
||||
|
||||
// set the new pixel
|
||||
watch_set_pixel(state->x, state->y);
|
||||
|
||||
// store this pixel in the buffer
|
||||
state->illuminated_segments[state->end][0] = state->x;
|
||||
state->illuminated_segments[state->end][1] = state->y;
|
||||
// increment the end index to the next position
|
||||
state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
|
||||
}
|
||||
else if (state->animation >= state->total_frames - MAX_ILLUMINATED_SEGMENTS && state->animation < state->total_frames) {
|
||||
state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
|
||||
}
|
||||
else {
|
||||
// reset the animation state
|
||||
state->animate = false;
|
||||
}
|
||||
state->animation = (state->animation + 1);
|
||||
}
|
||||
break;
|
||||
case EVENT_LOW_ENERGY_UPDATE:
|
||||
break;
|
||||
case EVENT_ALARM_LONG_PRESS:
|
||||
break;
|
||||
case EVENT_BACKGROUND_TASK:
|
||||
break;
|
||||
default:
|
||||
return movement_default_loop_handler(event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wyoscan_face_resign(void *context) {
|
||||
(void) context;
|
||||
|
||||
// handle any cleanup before your watch face goes off-screen.
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 <#author_name#>
|
||||
*
|
||||
* 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 WYOSCAN_FACE_H_
|
||||
#define WYOSCAN_FACE_H_
|
||||
|
||||
/*
|
||||
* WYOSCAN .5 hz watchface
|
||||
*
|
||||
* This is a recreation of the Wyoscan watch, which was a $175 watch in 2014.
|
||||
* It was an f-91w pcb replacement.
|
||||
*
|
||||
* Video: https://user-images.githubusercontent.com/1795778/252550124-e07f0ed1-e328-4337-a654-fa1ee65d883f.mp4
|
||||
* Background information: https://artmetropole.com/shop/11460
|
||||
* Demo of what it looks like: https://www.o-r-g.com/apps/wyoscan
|
||||
*
|
||||
* 8 frames per number * 6 numbers + the trailing 16 frames = 64 frames
|
||||
* at 32 frames per second, this is a 2-second cycle time or 0.5 Hz.
|
||||
*
|
||||
* It is giving me a stack overflow after about 2.5 cycles of the time display
|
||||
* in the emulator, but it works fine on the watch.
|
||||
*
|
||||
* I'd like to make something for the low energy mode, but I haven't thought
|
||||
* about how that might work, right now it just freezes in low energy mode
|
||||
* until you press the 12-24HR button.
|
||||
*
|
||||
* There are no controls; it simply animates as long as the page is active.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "movement.h"
|
||||
|
||||
#define MAX_ILLUMINATED_SEGMENTS 16
|
||||
|
||||
typedef struct {
|
||||
uint32_t previous_date_time;
|
||||
uint8_t last_battery_check;
|
||||
uint8_t watch_face_index;
|
||||
bool signal_enabled;
|
||||
bool battery_low;
|
||||
bool alarm_enabled;
|
||||
uint8_t animation;
|
||||
bool animate;
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
uint32_t total_frames;
|
||||
bool colon;
|
||||
uint8_t position, segment;
|
||||
char *segments;
|
||||
uint8_t x, y;
|
||||
uint32_t time_digits[6];
|
||||
uint32_t illuminated_segments[MAX_ILLUMINATED_SEGMENTS][2];
|
||||
} wyoscan_state_t;
|
||||
|
||||
void wyoscan_face_setup(uint8_t watch_face_index, void ** context_ptr);
|
||||
void wyoscan_face_activate(void *context);
|
||||
bool wyoscan_face_loop(movement_event_t event, void *context);
|
||||
void wyoscan_face_resign(void *context);
|
||||
movement_watch_face_advisory_t wyoscan_face_advise(void *context);
|
||||
|
||||
#define wyoscan_face ((const watch_face_t){ \
|
||||
wyoscan_face_setup, \
|
||||
wyoscan_face_activate, \
|
||||
wyoscan_face_loop, \
|
||||
wyoscan_face_resign, \
|
||||
NULL, \
|
||||
})
|
||||
|
||||
#endif // WYOSCAN_FACE_H_
|
||||
|
||||
Reference in New Issue
Block a user