Merge branch 'main' into day_one_face

This commit is contained in:
Wesley Aptekar-Cassels 2023-11-27 23:08:28 -05:00 committed by GitHub
commit a2f1ba9171
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
114 changed files with 2531 additions and 699 deletions

View File

@ -6,6 +6,9 @@ on:
branches-ignore:
- gh-pages
env:
COLOR: BLUE
jobs:
build:
container:

View File

@ -62,6 +62,7 @@ CFLAGS += -MD -MP -MT $(BUILD)/$(*F).o -MF $(BUILD)/$(@F).d
LDFLAGS += -mcpu=cortex-m0plus -mthumb
LDFLAGS += -Wl,--gc-sections
LDFLAGS += -Wl,--script=$(TOP)/watch-library/hardware/linker/saml22j18.ld
LDFLAGS += -Wl,--print-memory-usage
LIBS += -lm
@ -207,6 +208,10 @@ ifeq ($(LED), BLUE)
CFLAGS += -DWATCH_IS_BLUE_BOARD
endif
ifndef COLOR
$(error Set the COLOR variable to RED, BLUE, or GREEN depending on what board you have.)
endif
ifeq ($(COLOR), BLUE)
CFLAGS += -DWATCH_IS_BLUE_BOARD
endif

View File

@ -118,6 +118,9 @@ SRCS += \
../watch_faces/complication/flashlight_face.c \
../watch_faces/clock/decimal_time_face.c \
../watch_faces/clock/wyoscan_face.c \
../watch_faces/complication/couch_to_5k_face.c \
../watch_faces/clock/minute_repeater_decimal_face.c \
../watch_faces/complication/tuning_tones_face.c \
# New watch faces go above this line.
# Leave this line at the bottom of the file; it has all the targets for making your project.

View File

@ -294,7 +294,25 @@ void movement_request_wake() {
}
void movement_play_signal(void) {
watch_buzzer_play_sequence(signal_tune, NULL);
bool buzzer_enabled = watch_is_buzzer_or_led_enabled();
if (!buzzer_enabled) {
watch_enable_buzzer();
}
watch_buzzer_play_note(BUZZER_NOTE_C8, 75);
watch_buzzer_play_note(BUZZER_NOTE_REST, 100);
watch_buzzer_play_note(BUZZER_NOTE_C8, 100);
if (!buzzer_enabled) {
watch_disable_buzzer();
}
}
void movement_play_tune(void) {
if (!watch_is_buzzer_or_led_enabled()) {
watch_enable_buzzer();
watch_buzzer_play_sequence(signal_tune, watch_disable_buzzer);
} else {
watch_buzzer_play_sequence(signal_tune, NULL);
}
}
void movement_play_alarm(void) {

View File

@ -307,6 +307,7 @@ void movement_cancel_background_task_for_face(uint8_t watch_face_index);
void movement_request_wake(void);
void movement_play_signal(void);
void movement_play_tune(void);
void movement_play_alarm(void);
void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note);

View File

@ -95,6 +95,9 @@
#include "flashlight_face.h"
#include "decimal_time_face.h"
#include "wyoscan_face.h"
#include "couch_to_5k_face.h"
#include "minute_repeater_decimal_face.h"
#include "tuning_tones_face.h"
// New includes go above this line.
#endif // MOVEMENT_FACES_H_

View File

@ -1,3 +1,27 @@
/*
* MIT License
*
* Copyright (c) 2023 Wesley Ellis <https://github.com/tahnok>
*
* 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 "beats_face.h"

View File

@ -1,6 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2023 Wesley Ellis <https://github.com/tahnok>
*
* 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 BEATS_FACE_H_
#define BEATS_FACE_H_
/*
* BEATS TIME face
*
* The Beat Time face displays the current Swatch Internet Time, or .beat time.
* This is a decimal time system that divides the day into 1000 beats.
*
* The three large digits in the bottom row indicate the current beat, and the
* two smaller digits (normally the seconds in Simple Clock) indicate the
* fractional beat; so for example you can read 67214 as beat 672.14.
*/
#include "movement.h"
typedef struct {

View File

@ -25,10 +25,8 @@
#ifndef DECIMAL_TIME_FACE_H_
#define DECIMAL_TIME_FACE_H_
#include "movement.h"
/*
* DECIMAL TIME FACE
* 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.
@ -46,9 +44,10 @@
* 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?

View File

@ -25,6 +25,32 @@
#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 {

View File

@ -0,0 +1,238 @@
/*
* 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(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) settings;
(void) watch_face_index;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(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(movement_settings_t *settings, void *context) {
minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
if (watch_tick_animation_is_running()) watch_stop_tick_animation();
if (settings->bit.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(settings->bit.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, movement_settings_t *settings, void *context) {
minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
char buf[11];
uint8_t pos;
watch_date_time 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 (!settings->bit.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_tick_animation_is_running()) watch_start_tick_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 != settings->bit.alarm_enabled) _update_alarm_indicator(settings->bit.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 (!settings->bit.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, settings);
}
return true;
}
void minute_repeater_decimal_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
}
bool minute_repeater_decimal_face_wants_background_task(movement_settings_t *settings, void *context) {
(void) settings;
minute_repeater_decimal_state_t *state = (minute_repeater_decimal_state_t *)context;
if (!state->signal_enabled) return false;
watch_date_time date_time = watch_rtc_get_date_time();
return date_time.unit.minute == 0;
}

View File

@ -0,0 +1,84 @@
/*
* 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(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void minute_repeater_decimal_face_activate(movement_settings_t *settings, void *context);
bool minute_repeater_decimal_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void minute_repeater_decimal_face_resign(movement_settings_t *settings, void *context);
bool minute_repeater_decimal_face_wants_background_task(movement_settings_t *settings, 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_wants_background_task, \
})
#endif // MINUTE_REPEATER_DECIMAL_FACE_H_

View File

@ -151,19 +151,7 @@ bool repetition_minute_face_loop(movement_event_t event, movement_settings_t *se
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);
if (watch_is_buzzer_or_led_enabled()) {
// if we are in the foreground, we can just beep.
movement_play_signal();
} else {
// if we were in the background, we need to enable the buzzer peripheral first,
watch_enable_buzzer();
// beep quickly (this call blocks for 275 ms),
movement_play_signal();
// and then turn the buzzer peripheral off again.
watch_disable_buzzer();
}
movement_play_signal();
break;
case EVENT_LIGHT_LONG_UP:
/*

View File

@ -25,9 +25,9 @@
#ifndef REPETITION_MINUTE_FACE_H_
#define REPETITION_MINUTE_FACE_H_
#include "movement.h"
/*
* REPETITION MINUTE face
*
* A hopefully useful complication for friendly neighbors in the dark
*
* Originating from 1676 from reverend and mechanician Edward Barlow, and
@ -40,7 +40,6 @@
* 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 :
@ -51,9 +50,10 @@
* 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;

View File

@ -180,17 +180,7 @@ bool simple_clock_bin_led_face_loop(movement_event_t event, movement_settings_t
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);
if (watch_is_buzzer_or_led_enabled()) {
// if we are in the foreground, we can just beep.
movement_play_signal();
} else {
// if we were in the background, we need to enable the buzzer peripheral first,
watch_enable_buzzer();
// beep quickly (this call blocks for 275 ms),
movement_play_signal();
// and then turn the buzzer peripheral off again.
watch_disable_buzzer();
}
movement_play_signal();
break;
case EVENT_LIGHT_LONG_PRESS:
if (state->flashing_state == 0) {

View File

@ -25,9 +25,9 @@
#ifndef SIIMPLE_CLOCK_BIN_LED_FACE_H_
#define SIIMPLE_CLOCK_BIN_LED_FACE_H_
#include "movement.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.
*
@ -49,6 +49,8 @@
* represents 1.
*/
#include "movement.h"
typedef struct {
uint32_t previous_date_time;
uint8_t last_battery_check;

View File

@ -136,17 +136,11 @@ bool simple_clock_face_loop(movement_event_t event, movement_settings_t *setting
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);
if (watch_is_buzzer_or_led_enabled()) {
// if we are in the foreground, we can just beep.
movement_play_signal();
} else {
// if we were in the background, we need to enable the buzzer peripheral first,
watch_enable_buzzer();
// beep quickly (this call blocks for 275 ms),
movement_play_signal();
// and then turn the buzzer peripheral off again.
watch_disable_buzzer();
}
#ifdef SIGNAL_TUNE_DEFAULT
movement_play_signal();
#else
movement_play_tune();
#endif
break;
default:
return movement_default_loop_handler(event, settings);

View File

@ -25,6 +25,15 @@
#ifndef SIMPLE_CLOCK_FACE_H_
#define SIMPLE_CLOCK_FACE_H_
/*
* SIMPLE CLOCK FACE
*
* Displays the current time, matching the original operation of the watch.
* This is the default display mode in most watch configurations.
*
* Long-press ALARM to toggle the hourly chime.
*/
#include "movement.h"
typedef struct {

View File

@ -130,17 +130,7 @@ bool weeknumber_clock_face_loop(movement_event_t event, movement_settings_t *set
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);
if (watch_is_buzzer_or_led_enabled()) {
// if we are in the foreground, we can just beep.
movement_play_signal();
} else {
// if we were in the background, we need to enable the buzzer peripheral first,
watch_enable_buzzer();
// beep quickly (this call blocks for 275 ms),
movement_play_signal();
// and then turn the buzzer peripheral off again.
watch_disable_buzzer();
}
movement_play_signal();
break;
default:
movement_default_loop_handler(event, settings);

View File

@ -25,6 +25,14 @@
#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 {

View File

@ -23,79 +23,6 @@
* SOFTWARE.
*/
/*
* 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.
*
*/
#include <stdlib.h>
#include <string.h>
#include "world_clock2_face.h"

View File

@ -26,6 +26,65 @@
#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

View File

@ -25,7 +25,29 @@
#ifndef WORLD_CLOCK_FACE_H_
#define WORLD_CLOCK_FACE_H_
/*
* WORLD CLOCK FACE
*
* The World Clock watch face looks similar to the Simple Clock watch face,
* but youll notice that at first launch the day of week indicators are blank.
* Thats because this watch face does not display the day of the week.
* Instead, you may customize these letters to display the name of a time zone
* of your choosing.
*
* To customize this watch face, press and hold the ALARM button. The first
* letter in the top row will begin flashing. Press the ALARM button repeatedly
* to advance through the available letters in the first slot, then press the
* LIGHT button to move to the second letter. Finally, press LIGHT again to move
* to the time zone setting, and press ALARM to cycle through the available time
* zones. Press LIGHT one last time to return to the world clock display.
*
* Note that the second slot cannot display all letters or numbers. Also note
* that at this time, time zones do not automatically update for daylight saving
* time; you will need to manually adjust this field each spring and fall.
*/
#include "movement.h"
typedef union {
struct {
uint8_t char_0;

View File

@ -25,15 +25,32 @@
#ifndef WYOSCAN_FACE_H_
#define WYOSCAN_FACE_H_
#include "movement.h"
/*
* A DESCRIPTION OF YOUR WATCH FACE
* WYOSCAN .5 hz watchface
*
* and a description of how use it
* 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 {

View File

@ -25,10 +25,8 @@
#ifndef ACTIVITY_FACE_H_
#define ACTIVITY_FACE_H_
#include "movement.h"
/*
* ACTIVITY WATCH FACE
* ACTIVITY watch face
*
* The Activity face lets you record activities like you would do with a fitness watch.
* It supports different activities like running, biking, rowing etc., and for each recorded activity
@ -69,9 +67,10 @@
*
* See the top of activity_face.c for some customization options. What you most likely want to do
* is reduce the list of activities shown on the first screen to the ones you are regularly doing.
*
*/
#include "movement.h"
void activity_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void activity_face_activate(movement_settings_t *settings, void *context);
bool activity_face_loop(movement_event_t event, movement_settings_t *settings, void *context);

View File

@ -22,8 +22,6 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
@ -32,31 +30,6 @@
#include "watch_utility.h"
#include "watch_private_display.h"
/*
Implements 16 alarm slots on the sensor watch
Usage:
- In normal mode, the alarm button cycles through all 16 alarms.
- Pressing the alarm button long in normal mode toggles the corresponding alarm on or off.
(Whereas pressing the alarm button extra long brings you back to alarm no. 1.)
- Pressing the light button enters setting mode and cycles through the settings of each alarm.
(Long pressing the light button enters setting mode without illuminating the led.)
- In setting mode an alarm slot is selected by pressing the alarm button when the slot number
in the upper right corner is blinking.
- For each alarm slot, you can select the day. These are the day modes:
- ED = the alarm rings every day
- 1t = the alarm fires only one time and is erased afterwards
- MF = the alarm fires Mondays to Fridays
- WN = the alarm fires on weekends (Sa/Su)
- MO to SU = the alarm fires only on the given day of week
- You can fast cycle through hour or minute setting via long press of the alarm button.
- You can select the tone in which the alarm is played. (Three pitch levels available.)
- You can select how many "beep rounds" are played for each alarm. 1 to 9 rounds, plus extra
long ('L') and extra short ('o') alarms.
- The simple watch face indicates if any alarm is set within the next 24h by showing the signal
indicator.
*/
typedef enum {
alarm_setting_idx_alarm,
alarm_setting_idx_day,

View File

@ -27,11 +27,34 @@
#ifndef ALARM_FACE_H_
#define ALARM_FACE_H_
#include "movement.h"
/*
A face for setting various alarms
*/
* ALARM face
*
* Implements up to 16 alarm slots on the sensor watch
*
* Usage:
* - In normal mode, the alarm button cycles through all 16 alarms.
* - Pressing the alarm button long in normal mode toggles the corresponding alarm on or off.
* (Whereas pressing the alarm button extra long brings you back to alarm no. 1.)
* - Pressing the light button enters setting mode and cycles through the settings of each alarm.
* (Long pressing the light button enters setting mode without illuminating the led.)
* - In setting mode an alarm slot is selected by pressing the alarm button when the slot number
* in the upper right corner is blinking.
* - For each alarm slot, you can select the day. These are the day modes:
* - ED = the alarm rings every day
* - 1t = the alarm fires only one time and is erased afterwards
* - MF = the alarm fires Mondays to Fridays
* - WN = the alarm fires on weekends (Sa/Su)
* - MO to SU = the alarm fires only on the given day of week
* - You can fast cycle through hour or minute setting via long press of the alarm button.
* - You can select the tone in which the alarm is played. (Three pitch levels available.)
* - You can select how many "beep rounds" are played for each alarm. 1 to 9 rounds, plus extra
* long ('L') and extra short ('o') alarms.
* - The simple watch face indicates if any alarm is set within the next 24h by showing the signal
* indicator.
*/
#include "movement.h"
#define ALARM_ALARMS 16 // no of available alarm slots (be aware: only 4 bits reserved for this value in struct below)
#define ALARM_DAY_STATES 11 // no of different day settings

View File

@ -25,6 +25,47 @@
#ifndef ASTRONOMY_FACE_H_
#define ASTRONOMY_FACE_H_
/*
* ASTRONOMY face
*
* The Astronomy watch face is among the most complex watch faces in the
* Movement collection. It allows you to calculate the locations of celestial
* bodies in the sky, as well as distance in astronomical units (or, in the
* case of the Moon, distance in kilometers).
*
* When you arrive at the Astronomy watch face, youll see its name (Astro)
* and an animation of two objects orbiting each other. You will also see SO
* (for Sol) flashing in the top left. The flashing letters indicate the
* currently selected celestial body. Short press Alarm to advance through
* the available celestial bodies:
*
* SO - Sol, the sun
* ME - Mercury
* VE - Venus
* LU - Luna, the Earths moon
* MA - Mars
* JU - Jupiter
* SA - Saturn
* UR - Uranus
* NE - Neptune
*
* Once youve selected the celestial body whose parameters you wish to
* calculate, long press the Alarm button and release it. The letter C will
* flash while the calculation is performed.
*
* When the calculation is complete, the screen will display the altitude
* (aL) of the celestial body. You can cycle through the available parameters
* with repeated short presses on the Alarm button:
*
* aL - Altitude (in degrees), the elevation over the horizon. If negative, it is below the horizon.
* aZ - Azimuth (in degrees), the cardinal direction relative to true north.
* rA - Right Ascension (in hours/minutes/seconds)
* dE - Declination (in degrees/minutes/seconds)
* di - Distance (the digits in the top right will display either aU for astronomical units, or K for kilometers)
*
* Long press on the Alarm button to select another celestial body.
*/
#include "movement.h"
#include "astrolib.h"

View File

@ -25,6 +25,32 @@
#ifndef BLINKY_FACE_H_
#define BLINKY_FACE_H_
/*
* BLINKY LIGHT face
*
* The blinky light watch face was designed as a tutorial for making a watch
* face in Movement, but it actually might be useful to have a blinking light
* in a pinch.
*
* The screen displays the name of the watch face (BL), as well as an S at
* the top right for slow blink or an F for fast blink. The bottom line selects
* the color: green, red or yellow. You can change the speed of the blinking
* light by pressing the Alarm button, and change the color with the Light
* button. A long press on the Alarm button starts the blinking light, and
* another long press stops it.
*
* Note that this will chew through your battery! The green LED uses about
* 450µA at full brightness, which is 45 times the normal power consumption of
* the watch. The red LED is an order of magnitude less efficient (4500 µA),
* and the yellow setting lights both LEDs, which chews through nearly
* 5 milliamperes. This means that one hour of yellow blinking is likely to
* eat up between 2 and 3 percent of the batterys usable life!
*
* Still, if you need to signal your location to someone in a dark forest,
* this watch face could come in handy. Just try to use the green LED as much
* as you can.
*/
#include "movement.h"
typedef struct {

View File

@ -25,6 +25,17 @@
#ifndef BREATHING_FACE_H_
#define BREATHING_FACE_H_
/*
* BOXED BREATHING face
*
* Breathing is a complication for guiding boxed breathing sessions.
* Boxed breathing is a technique to help you stay calm and improve
* concentration in stressful situations.
*
* Usage: Timed messages will cycle as long as this face is active.
* Press ALARM to toggle sound.
*/
#include "movement.h"
void breathing_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

View File

@ -0,0 +1,267 @@
/*
* MIT License
*
* Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
*
* 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 "couch_to_5k_face.h"
// They go: Warmup, Run, Walk, Run, Walk, Run, Walk ... , End (0)
// Time is defined in seconds
// Maybe do /10 to reduce memory usage?
// (i don't want to use floats)
// uint16_t C25K_WEEK_TEST[] = {10, 10, 10, 0};
uint16_t C25K_WEEK_1[] = {300, 60, 90, 60, 90, 60, 90, 60, 90, 60, 90, 60,
90, 60, 90, 60, 90, 0};
uint16_t C25K_WEEK_2[] = {300, 90, 120, 90, 120, 90, 120, 90, 120, 90, 120,
90, 120, 0};
uint16_t C25K_WEEK_3[] = {300, 90, 90, 180, 180, 90, 90, 180, 180, 0};
uint16_t C25K_WEEK_4[] = {300, 180, 90, 300, 150, 180, 90, 300, 0};
uint16_t C25K_WEEK_5_1[] = {300, 300, 180, 300, 180, 300, 0 };
uint16_t C25K_WEEK_5_2[] = {300, 480, 300, 480 , 0};
uint16_t C25K_WEEK_5_3[] = {300, 1200, 0};
uint16_t C25K_WEEK_6_1[] = {300, 300, 180, 480, 180, 300, 0 };
uint16_t C25K_WEEK_6_2[] = {300, 600, 180, 600 , 0};
uint16_t C25K_WEEK_6_3[] = {300, 1500, 0};
uint16_t C25K_WEEK_7[] = {300, 1500, 0};
uint16_t C25K_WEEK_8[] = {300, 1680, 0};
uint16_t C25K_WEEK_9[] = {300, 1800, 0};
#define C25K_SESSIONS_LENGTH 3*9
uint16_t *C25K_SESSIONS[C25K_SESSIONS_LENGTH];
static inline bool _finished(couch_to_5k_state_t *state){
return state->exercise_type == C25K_FINISHED;
}
static inline bool _cleared(couch_to_5k_state_t *state){
return state->timer == C25K_SESSIONS[state->session][0]
&& state->exercise == 0;
}
static inline void _next_session(couch_to_5k_state_t *state){
if (++state->session >= C25K_SESSIONS_LENGTH){
state->session = 0;
}
}
static inline void _assign_exercise_type(couch_to_5k_state_t *state){
if (state->exercise == 0){
state->exercise_type = C25K_WARMUP;
} else if (state->exercise % 2 == 1){
state->exercise_type = C25K_RUN;
} else {
state->exercise_type = C25K_WALK;
}
}
static void _next_exercise(couch_to_5k_state_t *state){
state->exercise++;
state->timer = C25K_SESSIONS[state->session][state->exercise];
// If the new timer starts in zero, it's finished
if (state->timer == 0){
movement_play_alarm_beeps(7, BUZZER_NOTE_C8);
state->exercise_type = C25K_FINISHED;
return;
}
movement_play_alarm_beeps(4, BUZZER_NOTE_A7);
_assign_exercise_type(state);
}
static void _init_session(couch_to_5k_state_t *state){
state->exercise = 0; // Restart exercise counter
state->timer = C25K_SESSIONS[state->session][state->exercise];
_assign_exercise_type(state);
}
static char *_exercise_type_to_str(exercise_type_t t){
switch (t){
case C25K_WARMUP:
return "WU";
case C25K_RUN:
return "RU";
case C25K_WALK:
return "WA";
case C25K_FINISHED:
return "--";
default:
return " ";
}
}
static void _display(couch_to_5k_state_t *state, char *buf){
// TODO only repaint needed parts
uint8_t seconds = state->timer % 60;
sprintf(buf, "%s%2d%2d%02d%02d",
_exercise_type_to_str(state->exercise_type),
(state->session + 1) % 100,
((state->timer - seconds) / 60) % 100,
seconds,
(state->exercise + 1) % 100);
watch_display_string(buf, 0);
}
void couch_to_5k_face_setup(movement_settings_t *settings, uint8_t
watch_face_index, void ** context_ptr) {
(void) settings;
(void) watch_face_index;
if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(couch_to_5k_state_t));
memset(*context_ptr, 0, sizeof(couch_to_5k_state_t));
// Do any one-time tasks in here; the inside of this conditional
// happens only at boot.
// C25K_SESSIONS[0] = C25K_WEEK_TEST;
C25K_SESSIONS[0] = C25K_WEEK_1;
C25K_SESSIONS[1] = C25K_WEEK_1;
C25K_SESSIONS[2] = C25K_WEEK_1;
C25K_SESSIONS[3] = C25K_WEEK_2;
C25K_SESSIONS[4] = C25K_WEEK_2;
C25K_SESSIONS[5] = C25K_WEEK_2;
C25K_SESSIONS[6] = C25K_WEEK_3;
C25K_SESSIONS[7] = C25K_WEEK_3;
C25K_SESSIONS[8] = C25K_WEEK_3;
C25K_SESSIONS[9] = C25K_WEEK_4;
C25K_SESSIONS[10] = C25K_WEEK_4;
C25K_SESSIONS[11] = C25K_WEEK_4;
C25K_SESSIONS[12] = C25K_WEEK_5_1;
C25K_SESSIONS[13] = C25K_WEEK_5_2;
C25K_SESSIONS[14] = C25K_WEEK_5_3;
C25K_SESSIONS[15] = C25K_WEEK_6_1;
C25K_SESSIONS[16] = C25K_WEEK_6_2;
C25K_SESSIONS[17] = C25K_WEEK_6_3;
C25K_SESSIONS[18] = C25K_WEEK_7;
C25K_SESSIONS[19] = C25K_WEEK_7;
C25K_SESSIONS[20] = C25K_WEEK_7;
C25K_SESSIONS[21] = C25K_WEEK_8;
C25K_SESSIONS[22] = C25K_WEEK_8;
C25K_SESSIONS[23] = C25K_WEEK_8;
C25K_SESSIONS[24] = C25K_WEEK_9;
C25K_SESSIONS[25] = C25K_WEEK_9;
C25K_SESSIONS[26] = C25K_WEEK_9;
}
// Do any pin or peripheral setup here; this will be called whenever the
// watch wakes from deep sleep.
}
void couch_to_5k_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
// Handle any tasks related to your watch face coming on screen.
watch_set_colon();
}
bool couch_to_5k_face_loop(movement_event_t event, movement_settings_t *settings,
void *context) {
couch_to_5k_state_t *state = (couch_to_5k_state_t *)context;
static char buf[11];
static bool paused = true;
switch (event.event_type) {
case EVENT_ACTIVATE:
// Show your initial UI here.
movement_request_tick_frequency(1);
_init_session(state);
paused = true;
_display(state, buf);
break;
case EVENT_TICK:
if ( !paused && !_finished(state) ) {
if (state->timer == 0){
_next_exercise(state);
} else {
state->timer--;
}
}
_display(state, buf);
break;
case EVENT_LIGHT_BUTTON_UP:
// This is the next-exercise / reset button.
// When finished move to the next session and leave it paused
if ( _finished(state) ){
_next_session(state);
_init_session(state);
paused = true;
break;
}
// When paused and cleared move to next, when only paused, clear
if ( paused ) {
if ( _cleared(state) ){
_next_session(state);
}
_init_session(state);
}
break;
case EVENT_ALARM_BUTTON_UP:
if (settings->bit.button_should_sound) {
watch_buzzer_play_note(BUZZER_NOTE_C8, 50);
}
paused = !paused;
break;
case EVENT_TIMEOUT:
// Your watch face will receive this event after a period of
// inactivity. If it makes sense to resign,
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_tick_animation(500);
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, settings);
}
// 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 couch_to_5k_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
// handle any cleanup before your watch face goes off-screen.
}

View File

@ -0,0 +1,87 @@
/*
* MIT License
*
* Copyright (c) 2023 Ekaitz Zarraga <ekaitz@elenq.tech>
*
* 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 COUCHTO5K_FACE_H_
#define COUCHTO5K_FACE_H_
#include "movement.h"
/*
* Couch To 5k;
*
*
* The program is designed to train 3 times a week. Each training is a
* *session*. Each of the rounds you have in the training is an *exercise*.
*
* The training goes like this:
* 5min warm-up walk -> Run X minutes -> Walk Y minutes -> ... -> Stop
*
* The watch face shows it like this: The weekday indicator shows if you need
* to Warm Up (`WU`), run (`rU`), walk (`WA`) or stop (`--`).
*
* The month-day indicator shows the session you are in (from 1 to 27).
*
* The timer shows the time you have left in the exercise and the exercise you
* are doing (MM:SS:ee). When an exercise finishes you are notified with an
* alarm. When the whole session finishes, a different tone is played for a
* longer period.
*
* Pressing the ALARM button pauses/resumes the clock.
*
* Pressing the LIGHT button does nothing if the timer is not paused. When it
* is paused it clears the current session (it restarts it to the beginning)
* and if it was already cleared or the current session was finished moves to
* the next session.
*/
typedef enum {
C25K_WARMUP,
C25K_RUN,
C25K_WALK,
C25K_FINISHED
} exercise_type_t;
typedef struct {
// Anything you need to keep track of, put it here!
uint8_t session;
uint8_t exercise;
exercise_type_t exercise_type;
uint16_t timer;
} couch_to_5k_state_t;
void couch_to_5k_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void couch_to_5k_face_activate(movement_settings_t *settings, void *context);
bool couch_to_5k_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void couch_to_5k_face_resign(movement_settings_t *settings, void *context);
#define couch_to_5k_face ((const watch_face_t){ \
couch_to_5k_face_setup, \
couch_to_5k_face_activate, \
couch_to_5k_face_loop, \
couch_to_5k_face_resign, \
NULL, \
})
#endif // COUCHTO5K_FACE_H_

View File

@ -23,27 +23,12 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
#include "countdown_face.h"
#include "watch.h"
#include "watch_utility.h"
/*
Slight extension of the original countdown face by Wesley Ellis.
- Press the light button to enter setting mode and adjust the
countdown timer.
- Start and pause the countdown using the alarm button, similar to the
stopwatch face.
- When paused or terminated, press the light button to restore the
last entered countdown.
*/
#define CD_SELECTIONS 3
#define DEFAULT_MINUTES 3

View File

@ -22,22 +22,27 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#ifndef COUNTDOWN_FACE_H_
#define COUNTDOWN_FACE_H_
#include "movement.h"
/*
A countdown/timer face
Max countdown is 23 hours, 59 minutes and 59 seconds.
Note: we have to prevent the watch from going to deep sleep using
movement_schedule_background_task() while the timer is running.
*/
* COUNTDOWN TIMER face
*
* Slight extension of the original countdown face by Wesley Ellis.
* - Press the light button to enter setting mode and adjust the
* countdown timer.
* - Start and pause the countdown using the alarm button, similar
* to the stopwatch face.
* - When paused or terminated, press the light button to restore the
* last entered countdown.
*
* Max countdown is 23 hours, 59 minutes and 59 seconds.
*
* Note: we have to prevent the watch from going to deep sleep using
* movement_schedule_background_task() while the timer is running.
*/
#include "movement.h"
typedef enum {
cd_paused,

View File

@ -25,9 +25,19 @@
#ifndef COUNTER_FACE_H_
#define COUNTER_FACE_H_
/*
* COUNTER face
*
* Counter face is designed to count the number of running laps during exercises.
*
* Usage:
* Short-press ALARM to increment the counter (loops at 99)
* Long-press ALARM to reset the counter.
* Long-press LIGHT to toggle sound.
*/
#include "movement.h"
// Counter face is designed to count the number of running laps during excercises.
typedef struct {
uint8_t counter_idx;
bool beep_on;

View File

@ -20,8 +20,6 @@
* 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.
*
* Displays some pre-defined data that you might want to remember. Math constants, birthdays, phone numbers...
*/
#include <stdlib.h>
@ -96,12 +94,8 @@ bool databank_face_loop(movement_event_t event, movement_settings_t *settings, v
case EVENT_ACTIVATE:
display();
case EVENT_TICK:
// on activate and tick, if we are animating,
break;
case EVENT_LIGHT_BUTTON_UP:
// when the user presses 'light', we illuminate the LED. We could override this if
// our UI needed an additional button for input, consuming the light button press
// but not illuminating the LED.
databank_state.current_word = (databank_state.current_word + max_words - 1) % max_words;
display();
break;
@ -116,8 +110,6 @@ bool databank_face_loop(movement_event_t event, movement_settings_t *settings, v
display();
break;
case EVENT_ALARM_BUTTON_UP:
// when the user presses 'alarm', we toggle the state of the animation. If animating,
// we stop; if stopped, we resume.
databank_state.current_word = (databank_state.current_word + 1) % max_words;
display();
break;

View File

@ -25,6 +25,23 @@
#ifndef DATABANK_FACE_H_
#define DATABANK_FACE_H_
/*
* DATABANK face
*
* Displays some pre-defined data that you might want to remember
* Math constants, birthdays, phone numbers...
*
* Usage: Edit the global variable `pi_data` in "databank_face.c"
* to the define the data that will be displayed. Each "item" contains
* a two-letter label (using the day-of-week display), then a longer
* string that will be displayed one "word" (six characters) at a time.
*
* Short-press ALARM to display the next word.
* Short-press LIGHT to display the previous word.
* Long-press ALARM to display the next item.
* Long-press LIGHT to display the previous item.
*/
#include "movement.h"
void databank_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

View File

@ -25,10 +25,29 @@
#ifndef DAY_ONE_FACE_H_
#define DAY_ONE_FACE_H_
#include "movement.h"
/*
* DAY ONE face
*
* This watch face displays the number of days since or until a given date.
* It was originally designed to display the number of days youve been alive,
* but technically it can count up from any date in the 20th century or the
* 21st century, so far.
*
* Long press on the Alarm button to enter customization mode. The text YR
* will appear, and will allow you to set the year starting from 1959. Press
* Alarm repeatedly to advance the year. If your birthday is before 1959,
* advance beyond the current year and it will wrap around to 1900.
*
* Once you have set the year, press Light to set the month (MO) and
* day (DA), advancing the value by pressing Alarm repeatedly.
*
* Note that at this time, the Day One face does not display the sleep
* indicator in sleep mode, which may make the watch appear to be
* unresponsive in sleep mode. You can still press the Alarm button to
* wake the watch. This UI quirk will be addressed in a future update.
*/
// The Day One face is designed to count the days since or until a given date. It also functions as an
// interface for setting the birth date register, which other watch faces can use for various purposes.
#include "movement.h"
typedef enum {
PAGE_DISPLAY,

View File

@ -1,7 +1,7 @@
#include <stdlib.h>
#include <string.h>
#include "discgolf_face.h"
#include "watch.h" // Remember to change number of courses in this file
#include "watch.h" // Remember to change number of courses in this file
#include "watch_utility.h"
/*

View File

@ -22,21 +22,24 @@
* SOFTWARE.
*/
/////////////////////////////////////////////////////////////////////////////////////
#ifndef DISCGOLF_FACE_H_
#define DISCGOLF_FACE_H_
/*
/*
* DISC GOLF face
*
* Keep track of scores in discgolf or golf!
* The watch face operates in three different modes:
*
* - dg_setting: Select a course
* Enter this mode by holding down the light button. The screen will display
* the label for the hole and the lowest score since last boot.
* Press alarm to loop through the holes. Press the light button to make a
* the label for the hole and the lowest score since last boot.
* Press alarm to loop through the holes. Press the light button to make a
* selection. This will reset all scores and start a new game in dg_idle mode.
*
* -dg_idle: We're playing a hole
* This either shows your current score relative to par, or the score for a
* particular hole.
* particular hole.
* At the start of a game, press alarm to loop through the holes and leave it
* your starting hole. For optimal experience, play the course linearly after that
* If you're viewing the hole you're supposed to be playing, the watch face will
@ -49,19 +52,15 @@
* -dg_scoring: Input score for a hole
* In this mode, if the score is 0 (hasn't been entered during this round),
* it will blink, indicating we're in scoring mode. Press the alarm button
* to increment the score up until 15, in which case it loops back to 0.
* to increment the score up until 15, in which case it loops back to 0.
* Press the light button to save the score for that hole, advance one hole
* if you're not editing an already input score, and returning to idle mode.
*
* When all scores have been entered, the LAP indicator turns on. At that point, if we enter
* dg_setting to select a course, the score for that round is evaluated against the current
* When all scores have been entered, the LAP indicator turns on. At that point, if we enter
* dg_setting to select a course, the score for that round is evaluated against the current
* lowest score for that course, and saved if it is better.
*/
#ifndef DISCGOLF_FACE_H_
#define DISCGOLF_FACE_H_
#include "movement.h"
#define courses 11
@ -75,7 +74,7 @@ typedef struct {
uint8_t course; // Index for course selection, from 0
uint8_t hole; // Index for current hole, from 1
uint8_t playing; // Current hole
int scores[18]; // Scores for each played hole
int scores[18]; // Scores for each played hole
discgolf_mode_t mode; // Watch face mode
} discgolf_state_t;

View File

@ -26,16 +26,6 @@
#ifndef DUAL_TIMER_FACE_H_
#define DUAL_TIMER_FACE_H_
#include "movement.h"
/*
* IMPORTANT: This watch face uses the same TC2 callback counter as the Stock Stopwatch
* watch-face. It works through calling a global handler function. The two watch-faces
* therefore can't coexist within the same firmware. If you want to compile this watch-face
* then you need to remove the line <../watch_faces/complication/stock_stopwatch_face.c \>
* from the Makefile.
*/
/*
* DUAL TIMER
* ==========
@ -70,8 +60,15 @@
* the timers. In this case LONG PRESSING MODE will move to the next face instead of moving
* back to the default watch face.
*
* IMPORTANT: This watch face uses the same TC2 callback counter as the Stock Stopwatch
* watch-face. It works through calling a global handler function. The two watch-faces
* therefore can't coexist within the same firmware. If you want to compile this watch-face
* then you need to remove the line <../watch_faces/complication/stock_stopwatch_face.c \>
* from the Makefile.
*/
#include "movement.h"
typedef struct {
uint8_t centiseconds : 7; // 0-59
uint8_t seconds : 6; // 0-59

View File

@ -25,9 +25,9 @@
#ifndef FLASHLIGHT_FACE_H_
#define FLASHLIGHT_FACE_H_
#include "movement.h"
/*
* FLASHLIGHT face
*
* A flashlight for use with the Flashlight sensor board.
*
* When the watch face appears, the display will show "FL" in the top two positions.
@ -35,6 +35,8 @@
*
*/
#include "movement.h"
typedef struct {
// Anything you need to keep track of, put it here!
uint8_t unused;

View File

@ -25,10 +25,8 @@
#ifndef GEOMANCY_FACE_H_
#define GEOMANCY_FACE_H_
#include "movement.h"
/*
* GEOMANCY WATCH FACE
* GEOMANCY watch face
*
* A simple and straightforward watch face for the ancient Eastern geomantic divination system
* of I Ching and the western system of "Geomancy". It is an optional addition to the Toss Up
@ -65,6 +63,8 @@
*
*/
#include "movement.h"
typedef struct {
uint8_t bits : 4;
} nibble_t;

View File

@ -25,8 +25,6 @@
#ifndef HABIT_FACE_H_
#define HABIT_FACE_H_
#include "movement.h"
/*
* Habit tracking face
*
@ -36,6 +34,8 @@
*
*/
#include "movement.h"
void habit_face_setup(movement_settings_t *settings, uint8_t watch_face_index,
void **context_ptr);
void habit_face_activate(movement_settings_t *settings, void *context);

View File

@ -22,8 +22,6 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
@ -33,57 +31,6 @@
#include "watch_private_display.h"
#include "watch_buzzer.h"
/*
This face brings 9 customizable interval timers to the sensor watch,
to be used as hiit training device and/or for time management techniques.
- There are 9 interval timer slots, you can cycle through these with the
alarm button (short press). For each timer slot, a short "slideshow"
displaying the relevant details (like length of each phase - see below)
is shown.
- To start an interval timer, press and hold the alarm button.
- To pause a running timer, press the alarm button (short press).
- To completely abort a running timer, press and hold the alarm button.
- Press and hold the light button to enter settings mode for each interval
timer slot.
- Each interval timer has 1 to 4 phases of customizable length like so:
(1) prepare/warum up --> (2) work --> (3) break --> (4) cool down.
When setting up or running a timer, each of these phases is displayed by
the letters "PR" (prepare), "WO" (work), "BR" (break), "CD" (cool down).
- Each of these phases is optional, you can set the corresponding
minutes and seconds to zero. But at least one phase needs to be set, if
you want to use the timer.
- You can define the number of rounds either only for the work
phase and/or for the combination of work + break phase. Let's say you
want an interval timer that counts 3 rounds of 30 seconds work,
followed by 20 seconds rest:
work 30s --> work 30s --> work 30s --> break 20s
You can do this by setting 30s for the "WO"rk phase and setting a 3
in the lower right hand corner of the work page. The "LAP" indicator
lights up at this position, to explain that we are setting laps here.
After that, set the "BR"eak phase to 20s and leave the rest as it is.
- If you want to set up a certain number of "full rounds", consisting
of work phase(s) plus breaks, you can do so at the "BR"eak page. The
number in the lower right hand corner determines the number of full
rounds to be counted. A "-" means, that there is no limit and the
timer keeps alternating between work and break phases.
- This watch face comes with several pre-defined interval timers,
suitable for hiit training (timer slots 1 to 4) as well as doing
work according to the pomodoro principle (timer slots 5 to 6).
Feel free to adjust the timer slots to your own needs (or completely
wipe them ;-)
*/
typedef enum {
interval_setting_0_timer_idx,
interval_setting_1_clear_yn,

View File

@ -22,16 +22,62 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#ifndef INTERVAL_FACE_H_
#define INTERVAL_FACE_H_
#include "movement.h"
/*
A face for customizable interval timers
*/
* INTERVAL TIMER face
*
* This face brings 9 customizable interval timers to the sensor watch,
* to be used as hiit training device and/or for time management techniques.
*
* - There are 9 interval timer slots, you can cycle through these with the
* alarm button (short press). For each timer slot, a short "slideshow"
* displaying the relevant details (like length of each phase - see below)
* is shown.
*
* - To start an interval timer, press and hold the alarm button.
*
* - To pause a running timer, press the alarm button (short press).
*
* - To completely abort a running timer, press and hold the alarm button.
*
* - Press and hold the light button to enter settings mode for each interval
* timer slot.
*
* - Each interval timer has 1 to 4 phases of customizable length like so:
* (1) prepare/warum up --> (2) work --> (3) break --> (4) cool down.
* When setting up or running a timer, each of these phases is displayed by
* the letters "PR" (prepare), "WO" (work), "BR" (break), "CD" (cool down).
*
* - Each of these phases is optional, you can set the corresponding
* minutes and seconds to zero. But at least one phase needs to be set, if
* you want to use the timer.
*
* - You can define the number of rounds either only for the work
* phase and/or for the combination of work + break phase. Let's say you
* want an interval timer that counts 3 rounds of 30 seconds work,
* followed by 20 seconds rest:
* work 30s --> work 30s --> work 30s --> break 20s
* You can do this by setting 30s for the "WO"rk phase and setting a 3
* in the lower right hand corner of the work page. The "LAP" indicator
* lights up at this position, to explain that we are setting laps here.
* After that, set the "BR"eak phase to 20s and leave the rest as it is.
*
* - If you want to set up a certain number of "full rounds", consisting
* of work phase(s) plus breaks, you can do so at the "BR"eak page. The
* number in the lower right hand corner determines the number of full
* rounds to be counted. A "-" means, that there is no limit and the
* timer keeps alternating between work and break phases.
*
* - This watch face comes with several pre-defined interval timers,
* suitable for hiit training (timer slots 1 to 4) as well as doing
* work according to the pomodoro principle (timer slots 5 to 6).
* Feel free to adjust the timer slots to your own needs (or completely
* wipe them ;-)
*/
#include "movement.h"
#define INTERVAL_TIMERS 9 // no of available customizable timers (be aware: only 4 bits reserved for this value in struct below)

View File

@ -25,8 +25,6 @@
#ifndef INVADERS_FACE_H_
#define INVADERS_FACE_H_
#include "movement.h"
/*
* Remake of the "famous" Casio Number Invaders Game
*
@ -60,6 +58,8 @@
*
*/
#include "movement.h"
typedef struct {
uint16_t highscore;
bool sound_on;

View File

@ -25,6 +25,30 @@
#ifndef MOON_PHASE_FACE_H_
#define MOON_PHASE_FACE_H_
/*
* MOON PHASE face
*
* The Moon Phase face is similar to the Sunrise/Sunset face: it displays the
* current phase of the moon, along with the day of the month and a graphical
* representation of the moon on the top row.
*
* This graphical representation is a bit abstract. The segments that turn on
* represent the shape of the moon, waxing from the bottom right and waning at
* the top left. A small crescent at the bottom right will grow into a larger
* crescent, then add lines in the center for a quarter and half moon. All
* segments are on during a full moon. Then gradually the segments at the
* bottom right will turn off, until all that remains is a small waning
* crescent at the top left.
*
* All segments turn off during a new moon.
*
* On this screen you may press the Alarm button repeatedly to move forward
* in time: the day of the month at the top right will advance by one day for
* each button press, and both the text and the graphical representation will
* display the moon phase for that day. Try pressing the Alarm button 27 times
* now, just to visualize what the moon will look like over the next month.
*/
#include "movement.h"
typedef struct {

View File

@ -22,89 +22,6 @@
* SOFTWARE.
*/
/*
## Morse-code-based RPN calculator
The calculator is operated by first composing a **token** in Morse code, then submitting it to the calculator. A token specifies either a calculator operation or a float value.
These two parts of the codebase are totally independent:
1. The Morse-code reader (`mc.h`, `mc.c`)
2. The RPN calculator (`calc.h`, `calc.c`, `calc_fn.h`, `calc_fn.c`, `small_strtod.c`)
The user interface (`morsecalc_face.h`, `morsecalc_face.c`) lets you talk to the RPN calculator through Morse code.
## Controls
- `light` is dash
- `alarm` is dot
- `mode` is "finish character"
- long-press `mode` or submit a blank token to switch faces
- long-press `alarm` to show stack
- long-press `light` to toggle the light
## Morse code token entry
As you enter `.`s and `-`s, the morse code char you've entered will appear in the top center digit.
At the top right is the # of morse code `.`/`-` you've input so far. The character resets at the 6th `.`/`-`.
Once you have the character you want to enter, push `mode` to enter it.
The character will be appended to the current token, whose 6 trailing chars are shown on the main display.
Once you've typed in the token you want, enter a blank Morse code character and then push `mode`.
This submits it to the calculator.
Special characters:
- Backspace is `(` (`-.--.`).
- Clear token input without submitting to calculator is `Start transmission` (`-.-.-`).
## Writing commands
First the calculator will try to interpret the token as a command/stack operation.
Commands are defined in `calc_dict[]` in `movement/lib/morsecalc/calc_fns.h`.
If the command doesn't appear in the dictionary, the calculator tries to interpret the token as a number.
## Writing numbers
Numbers are written like floating point strings.
Entering a number pushes it to the top of the stack if there's room.
This can get long, so for convenience numerals can also be written in binary with .- = 01.
0 1 2 3 4 5 6 7 8 9
. - -. -- -.. -.- --. --- -... -..-
e t n m d k g o b x
- Exponent signs must be entered as "p".
- Decimal place "." can be entered as "h" (code ....)
- Sign "-" can be entered as "Ch digraph" (code ----)
For example: "4.2e-3" can be entered directly, or as "4h2pC3"
similarly, "0.0042" can also be entered as "eheedn"
Once you submit a number to the watch face, it pushes it to the top of the stack if there's room.
## Number display
After a command runs, the top of the stack is displayed in this format:
- Main 4 digits = leading 4 digits
- Last 2 digits = exponent
- Top middle = [Stack location, Sign of number]
- Top right = [Stack exponent, Sign of exponent]
Blank sign digit means positive.
So for example, the watch face might look like this:
[ 0 -5]
[4200 03]
... representing `+4.200e-3` is in stack location 0 (the top) and it's one of five items in the stack.
## Looking at the stack
To show the top of the stack, push and hold `light`/`alarm` or submit a blank token by pushing `mode` a bunch of times.
To show the N-th stack item (0 through 9):
- Put in the Morse code for N without pushing the mode button.
- Push and hold `alarm`.
To show the memory register, use `m` instead of a number.
To see all the calculator operations and their token aliases, see the `calc_dict[]` struct in `calc_fns.h`
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>

View File

@ -25,6 +25,96 @@
#ifndef MORSECALC_FACE_H_
#define MORSECALC_FACE_H_
/*
* MORSECALC face
* Morse-code-based RPN calculator
*
* The calculator is operated by first composing a **token** in Morse code,
* then submitting it to the calculator. A token specifies either a calculator
* operation or a float value.
*
* These two parts of the codebase are totally independent:
* 1. The Morse-code reader (`mc.h`, `mc.c`)
* 2. The RPN calculator (`calc.h`, `calc.c`, `calc_fn.h`, `calc_fn.c`, `small_strtod.c`)
*
* The user interface (`morsecalc_face.h`, `morsecalc_face.c`) lets you talk
* to the RPN calculator through Morse code.
*
* ## Controls
* - `light` is dash
* - `alarm` is dot
* - `mode` is "finish character"
* - long-press `mode` or submit a blank token to switch faces
* - long-press `alarm` to show stack
* - long-press `light` to toggle the light
*
* ## Morse code token entry
* As you enter `.`s and `-`s, the morse code char you've entered will
* appear in the top center digit. At the top right is the # of morse code
* `.`/`-` you've input so far. The character resets at the 6th `.`/`-`.
*
* Once you have the character you want to enter, push `mode` to enter it.
*
* The character will be appended to the current token, whose 6 trailing
* chars are shown on the main display. Once you've typed in the token you
* want, enter a blank Morse code character and then push `mode`.
* This submits it to the calculator.
*
* Special characters:
* - Backspace is `(` (`-.--.`).
* - Clear token input without submitting to calculator is `Start
* transmission` (`-.-.-`).
*
* ## Writing commands
* First the calculator will try to interpret the token as a command/stack operation.
* Commands are defined in `calc_dict[]` in `movement/lib/morsecalc/calc_fns.h`.
* If the command doesn't appear in the dictionary, the calculator tries to interpret the token as a number.
*
* ## Writing numbers
* Numbers are written like floating point strings.
* Entering a number pushes it to the top of the stack if there's room.
* This can get long, so for convenience numerals can also be written in binary with .- = 01.
*
* 0 1 2 3 4 5 6 7 8 9
* . - -. -- -.. -.- --. --- -... -..-
* e t n m d k g o b x
*
* - Exponent signs must be entered as "p".
* - Decimal place "." can be entered as "h" (code ....)
* - Sign "-" can be entered as "Ch digraph" (code ----)
*
* For example: "4.2e-3" can be entered directly, or as "4h2pC3"
* similarly, "0.0042" can also be entered as "eheedn"
* Once you submit a number to the watch face, it pushes it to the top of the stack if there's room.
*
* ## Number display
* After a command runs, the top of the stack is displayed in this format:
*
* - Main 4 digits = leading 4 digits
* - Last 2 digits = exponent
* - Top middle = [Stack location, Sign of number]
* - Top right = [Stack exponent, Sign of exponent]
*
* Blank sign digit means positive.
* So for example, the watch face might look like this:
*
* [ 0 -5]
* [4200 03]
*
* ... representing `+4.200e-3` is in stack location 0 (the top) and it's one of five items in the stack.
*
* ## Looking at the stack
* To show the top of the stack, push and hold `light`/`alarm` or submit a blank token by pushing `mode` a bunch of times.
* To show the N-th stack item (0 through 9):
*
* - Put in the Morse code for N without pushing the mode button.
* - Push and hold `alarm`.
*
* To show the memory register, use `m` instead of a number.
*
* To see all the calculator operations and their token aliases, see the `calc_dict[]` struct in `calc_fns.h`
*/
#define MORSECALC_TOKEN_LEN 32
#define MORSECODE_LEN 5
@ -34,7 +124,7 @@
/*
* MC International Morse Code binary tree
* Levels of the tree are concatenated.
* '.' = 0 and '-' = 1.
* '.' = 0 and '-' = 1.
*
* Capitals denote special characters:
* C = Ch digraph

View File

@ -20,7 +20,6 @@
* 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>

View File

@ -25,6 +25,48 @@
#ifndef ORRERY_FACE_H_
#define ORRERY_FACE_H_
/*
* ORRERY face
*
* The Orrery watch face is similar to the Astronomy watch face in that it
* calculates properties of the planets, but instead of calculating their
* positions in the sky, this watch face calculates their absolute locations
* in the solar system. This is only useful if you want to plot the planets
* on graph paper, but hey, you never know!
*
* The controls are identical to the Astronomy watch face: while the title
* screen (Orrery) is displayed, you can advance through the available
* planets with repeated short presses on the Alarm button. The available
* planets:
*
* ME - Mercury
* VE - Venus
* EA - Earth
* LU - Luna, the Earths moon
* MA - Mars
* JU - Jupiter
* SA - Saturn
* UR - Uranus
* NE - Neptune
*
* Note that the sun is not available in this menu, as the sun is always at
* (0,0,0) in this calculation.
*
* Long press on the Alarm button to calculate the planets location, and
* after a flashing C (for Calculating), you will be presented with the
* planets X coordinate in astronomical units. Short press Alarm to cycle
* through the X, Y and Z coordinates, and then long press Alarm to return
* to planet selection.
*
* The large numbers represent the whole number part, and the two smaller
* numbers (in the seconds place) represent the decimal portion. So if you
* see SA X 736 and SA Y -662, you can read that as an X coordinate of
* 7.36 AU and a Y coordinate of -6.62 AU. You can literally draw a dot at
* (0, 0) to represent the sun, and a dot at (7.36, -6.62) to represent
* Saturn. (The Z coordinates tend to be pretty close to zero, as the
* planets largely orbit on a single plane, the ecliptic.)
*/
#include "movement.h"
typedef enum {

View File

@ -26,12 +26,11 @@
#ifndef planetary_hours_face_H_
#define planetary_hours_face_H_
#include "movement.h"
#include "sunrise_sunset_face.h"
/*
* BACKGROUND
* PLANETARY HOURS face
*
* Background
*
* Both the 24 hour day and the order of our weekdays have quite esoteric roots.
* The ancient Egyptians divided the day up into 12 hours of sunlight and 12 hours
* of night time. Obviously the length of these hours varied throughout the year.
@ -74,6 +73,9 @@
* watch face to work properly!)
*/
#include "movement.h"
#include "sunrise_sunset_face.h"
typedef struct {
// Anything you need to keep track of, put it here!
uint32_t planetary_hours[24];

View File

@ -26,12 +26,11 @@
#ifndef planetary_time_face_H_
#define planetary_time_face_H_
#include "movement.h"
#include "sunrise_sunset_face.h"
/*
* PLANETARY TIME face
*
* BACKGROUND
*
* Both the 24 hour day and the order of our weekdays have quite esoteric roots.
* The ancient Egyptians divided the day up into 12 hours of sunlight and 12 hours
* of night time. Obviously the length of these hours varied throughout the year.
@ -77,6 +76,9 @@
* watch face to work properly!)
*/
#include "movement.h"
#include "sunrise_sunset_face.h"
typedef struct {
// Anything you need to keep track of, put it here!
uint32_t phase_start;

View File

@ -25,6 +25,18 @@
#ifndef PROBABILITY_FACE_H_
#define PROBABILITY_FACE_H_
/*
* PROBABILITY face
*
* This face is a dice-rolling random number generator.
* Supports dice with 2, 4, 6, 8, 10, 12, 20, or 100 sides.
*
* Press LIGHT to cycle through die type.
* The current die size is indicated on the left ("C" for 100)
*
* Press ALARM to roll the selected die.
*/
#include "movement.h"
typedef struct {

View File

@ -25,6 +25,33 @@
#ifndef PULSOMETER_FACE_H_
#define PULSOMETER_FACE_H_
/*
* PULSOMETER face
*
* The Pulsometer is an implementation of a sort of a classic mechanical
* watch complication. A classic pulsometer complication involves a
* chronograph with a scale calibrated for counting a certain number of
* heartbeats (often 30). You start it and begin counting heartbeats, and
* stop it after counting the specified number of beats. Once stopped,
* the needle will point to your heart rate.
*
* The pulsometer on Sensor Watch flashes its instructions at launch:
* Hold Alarm + count 30 beats. Using the hand on the side where you wear
* your watch, touch your carotid artery (in your neck) and feel for your
* pulse. Once you find it, use your other hand to press and hold the Alarm
* button, and count your heartbeats. When you reach 30 beats, release the
* Alarm button. The display will show a number such as 60 bpm; this is
* your heart rate in beats per minute.
*
* Two notes:
* o For the first few seconds of a measurement, the display will read Hi.
* This indicates that its too early for the measured value to be a valid
* heart rate. Once the measurement is below 240 bpm, the display will update.
* o If you hold the button down for more than 45 seconds, the display will
* read Lo. If it took this long for you to count 30 heartbeats, this
* indicates that your heart rate is below 40 beats per minute.
*/
#include "movement.h"
typedef struct {

View File

@ -25,11 +25,8 @@
#ifndef RANDONAUT_FACE_H_
#define RANDONAUT_FACE_H_
#include "movement.h"
#include "place_face.h"
/*
* RANDONAUT FACE
* RANDONAUT face
* ==============
*
* Randonauting is a way to turn the world around you into an adventure and get the user outside
@ -71,6 +68,9 @@
*
*/
#include "movement.h"
#include "place_face.h"
typedef struct {
uint8_t mode :3;
uint8_t location_format :3;

View File

@ -25,6 +25,16 @@
#ifndef RATEMETER_FACE_H_
#define RATEMETER_FACE_H_
/*
* RATE METER face
*
* The rate meter shows the rate per minute at which the ALARM button is
* being pressed. This is particularly useful in sports where cadence
* tracking is useful. For instance, rowing coaches often use a dedicated
* rate meter - clicking the rate button each time the crew puts their oars
* in the water to see the rate (strokes per minute) on the rate meter.
*/
#include "movement.h"
typedef struct {

View File

@ -22,39 +22,6 @@
* SOFTWARE.
*/
/* RPN Calculator alternate face.
*
* Operations appear in the 'day' section; ALARM changes between operations when operation is flashing.
* LIGHT executes current operation.
*
* This is the alternate face because it has a non-traditional number entry system which
* I call 'guess a number'. In number entry mode, the watch tries to guess which number you
* want, and you respond with 'smaller' (left - MODE) or larger (right - ALARM). This means
* that when you _are_ entering a number, MODE will no longer move between faces!
*
* Example of entering the number 27
* - select the NO operation (probably unnecessary, as this is the default),
* and execute it by hitting LIGHT.
* - you are now in number entry mode; you know this because nothing is flashing.
* - Watch displays 10; you hit ALARM to say you want a larger number.
* - Watch displays 100; you hit MODE to say you want a smaller number.
* - Continuing: 50 -> MODE -> 30 -> MODE -> 20 -> ALARM -> 27
* - Hit LIGHT to add the number to the stack (and now 'NO' is flashing
* again, indicating you're back in operation selection mode).
*
* One other thing to watch out for is how quickly it will switch into scientific notation
* due to the limitations of the display when you have large numbers or non-integer values.
* In this mode, the 'colon' serves at the decimal point, and the numbers in the top right
* are the exponent.
*
* As with the main movement firmware, this has the concept of 'secondary' functions which
* you can jump to by a long hold of ALARM on NO. These are functions to do with stack
* manipulation (pop, swap, dupe, clear, size (le)). If you're _not_ on NO, a long
* hold will take you back to it.
*
* See 'functions' below for names of all operations.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>

View File

@ -25,6 +25,40 @@
#ifndef CALCULATOR_FACE_H_
#define CALCULATOR_FACE_H_
/*
* RPN Calculator alternate face.
*
* Operations appear in the 'day' section; ALARM changes between operations when
* operation is flashing. LIGHT executes current operation.
*
* This is the alternate face because it has a non-traditional number entry system which
* I call 'guess a number'. In number entry mode, the watch tries to guess which number you
* want, and you respond with 'smaller' (left - MODE) or larger (right - ALARM). This means
* that when you _are_ entering a number, MODE will no longer move between faces!
*
* Example of entering the number 27
* - select the NO operation (probably unnecessary, as this is the default),
* and execute it by hitting LIGHT.
* - you are now in number entry mode; you know this because nothing is flashing.
* - Watch displays 10; you hit ALARM to say you want a larger number.
* - Watch displays 100; you hit MODE to say you want a smaller number.
* - Continuing: 50 -> MODE -> 30 -> MODE -> 20 -> ALARM -> 27
* - Hit LIGHT to add the number to the stack (and now 'NO' is flashing
* again, indicating you're back in operation selection mode).
*
* One other thing to watch out for is how quickly it will switch into scientific notation
* due to the limitations of the display when you have large numbers or non-integer values.
* In this mode, the 'colon' serves at the decimal point, and the numbers in the top right
* are the exponent.
*
* As with the main movement firmware, this has the concept of 'secondary' functions which
* you can jump to by a long hold of ALARM on NO. These are functions to do with stack
* manipulation (pop, swap, dupe, clear, size (le)). If you're _not_ on NO, a long
* hold will take you back to it.
*
* See 'functions' in "rpn_calculator_alt_face.c" for names of all operations.
*/
#include "movement.h"
#define CALC_MAX_STACK_SIZE 20

View File

@ -25,6 +25,15 @@
#ifndef RPN_CALCULATOR_FACE_H_
#define RPN_CALCULATOR_FACE_H_
/*
* RPN CALCULATOR face
*
* A calculator face using reverse polish notation (RPN).
*
* For usage instructions, please refer to the wiki:
* https://www.sensorwatch.net/docs/watchfaces/complication/#rpn-calculator
*/
#include "movement.h"
#define RPN_CALCULATOR_STACK_SIZE 4

View File

@ -24,45 +24,12 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
#include "sailing_face.h"
#include "watch.h"
#include "watch_utility.h"
/*
Implements a sailing timer.
Usage:
Waiting mode: Light button enters settings, alarm button starts the timer (sailing mode).
Sailing mode:
Alarm button switches to next programmed start signal, long press on light button
resets timer and enters waiting mode. Countdown to zero, then switch to counting mode.
Counting mode:
After the start signal (0s), the duration of the race is counted (like a stopwatch timer).
Alarm button increases the lap counter, alarm long press resets lap counter.
Long press on light button resets timer and enters waiting mode.
Setting mode:
Alarm button increases active (blinking) signal. Goes to 0 if upper boundary
(11 or whatever the signal left to the active one is set to) is met.
10 is printed vertically (letter o plus top segment).
Alarm button long press resets to default minutes (5-4-1-0).
Light button cycles through the signals.
Long press on light button cycles through sound modes:
- Bell indicator: Sound at start (0s) only.
- Signal indicator: Sound at each programmed signal and at start.
- Bell+Signal: Sound at each minute, at 30s and at 10s countdown.
- No indicator: No sound.
*/
#define sl_SELECTIONS 6
#define DEFAULT_MINUTES { 5,4,1,0,0,0 }

View File

@ -24,17 +24,43 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#ifndef SAILING_FACE_H_
#define SAILING_FACE_H_
#include "movement.h"
/*
A sailing sailing/timer face
*/
* SAILING face
* Implements a sailing timer.
*
* Usage:
*
* Waiting mode:
* LIGHT button enters settings
* ALARM button starts the timer (sailing mode).
*
* Sailing mode:
* ALARM button switches to next programmed start signal.
* Long press on LIGHT button resets timer and enters waiting mode.
* Countdown to zero, then switch to counting mode.
*
* Counting mode:
* After the start signal (0s), the duration of the race is counted (like a stopwatch timer).
* ALARM button increases the lap counter, ALARM long press resets lap counter.
* Long press on LIGHT button resets timer and enters waiting mode.
*
* Setting mode:
* ALARM button increases active (blinking) signal. Goes to 0 if upper boundary
* (11 or whatever the signal left to the active one is set to) is met.
* 10 is printed vertically (letter o plus top segment).
* ALARM button long press resets to default minutes (5-4-1-0).
* LIGHT button cycles through the signals.
* Long press on LIGHT button cycles through sound modes:
* - Bell indicator: Sound at start (0s) only.
* - Signal indicator: Sound at each programmed signal and at start.
* - Bell+Signal: Sound at each minute, at 30s and at 10s countdown.
* - No indicator: No sound.
*/
#include "movement.h"
typedef enum {
sl_waiting,

View File

@ -25,9 +25,8 @@
#ifndef SHIPS_BELL_FACE_H_
#define SHIPS_BELL_FACE_H_
#include "movement.h"
/*
* SHIP'S BELL face
* A ship's bell complication.
*
* See: https://en.wikipedia.org/wiki/Ship%27s_bell#Simpler_system
@ -45,6 +44,8 @@
* - long press Alarm button: Cycle through the watches (All/1/2/3)
*/
#include "movement.h"
typedef struct {
bool bell_enabled;
uint8_t on_watch;

View File

@ -25,12 +25,34 @@
#ifndef STOCK_STOPWATCH_FACE_H_
#define STOCK_STOPWATCH_FACE_H_
#include "movement.h"
/*
* STOCK STOPWATCH face
*
* The Stock Stopwatch face implements the original F-91W stopwatch
* functionality, including counting hundredths of seconds and lap timing.
*
* Use the ALARM button to start and stop the stopwatch.
* Press the LIGHT button while the stopwatch is running to view the lap time.
* (The stopwatch continues running in the background, indicated by a blinking colon.)
* Press the LIGHT button again to switch back to the running stopwatch.
* Press the LIGHT button when the timekeeping is stopped to reset the stopwatch.
*
* There are two improvements compared to the original F-91W:
* o When the stopwatch reaches 59:59, the counter does not simply jump back
* to zero but keeps track of hours in the upper right-hand corner
* (up to 24 hours).
* o Long-press the light button to toggle the LED behavior.
* It either turns on with each button press or remains off.
*
* NOTE:
* This watch face relies heavily on static vars in stock_stopwatch.c.
* The disadvantage is that you cannot use more than one instance of this
* watch face on your custom firmware - but then again, who would want that?
* The advantage is that accessing vars is more direct and faster, and we
* can save some precious cpu cycles. :-)
*/
// This watch face relies heavily on static vars in stock_stopwatch.c.
// The disadvantage is that you cannot use more than one instance of this watch face on
// your custom firmware - but then again, who would want that? The advantage is that accessing
// vars is more direct and faster, and we can save some precious cpu cycles :-)
#include "movement.h"
typedef struct {
bool light_on_button; // determines whether the light button actually triggers the led

View File

@ -26,6 +26,17 @@
#ifndef STOPWATCH_FACE_H_
#define STOPWATCH_FACE_H_
/*
* STOPWATCH FACE
*
* The Stopwatch face provides basic stopwatch functionality: you can start
* and stop the stopwatch with the alarm button. Pressing the light button
* when the timer is stopped resets it.
*
* This face does not count sub-seconds.
* See also: "stock_stopwatch_face.h"
*/
#include "movement.h"
typedef struct {

View File

@ -25,10 +25,18 @@
#ifndef SUNRISE_SUNSET_FACE_H_
#define SUNRISE_SUNSET_FACE_H_
#include "movement.h"
/*
* SUNRISE & SUNSET FACE
*
* The Sunrise/Sunset face is designed to display the next sunrise or sunset
* for a given location. It also functions as an interface for setting the
* location register, which other watch faces can use for various purposes.
*
* Refer to the wiki for usage instructions:
* https://www.sensorwatch.net/docs/watchfaces/complication/#sunrisesunset
*/
// The Sunrise/Sunset face is designed to display the next sunrise or sunset for a given location.
// TODO: It also functions as an interface for setting the location register, which other watch faces can use for various purposes.
#include "movement.h"
typedef struct {
uint8_t sign: 1; // 0-1

View File

@ -25,6 +25,49 @@
#ifndef TACHYMETER_FACE_H_
#define TACHYMETER_FACE_H_
/*
* TACHYMETER face
*
* The Tachymeter complication emulates the tachymeter function often
* present in watches, that computes the average speed in [units per hour]
* for a given distance given in [units].
*
* Use case:
* User sets the distance
* User starts the tachymeter when the trip begins
* User stops the tachymeter when the trip ends
* The watch presents the average speed and trip duration in seconds
*
* Usage:
* Go to tachymeter face, TC is shown in the Weekday Digits
* A steady d in the Day Digits indicates the distance to be used.
* To edit the distance:
* Long-press the Alarm button, the distance edition page (d will blink)
* Use the Light button to change the editing (blinking) digit, and press Alarm to increase its value
* Once done, long-press the Alarm button to exit the distance edition page
* Press the Alarm button to start the tachymeter.
* A running animation will appear in the Day Digits
* Press the Alarm button to stop the tachymeter
* The average speed and total time information will alternate.
* The average speed will be shown alongside /h in the Day Digits;
* and the total time will be shown alongside t in the Day Digits.
* Long press the Light button to return to the distance d page,
* and restart the tachymeter from there.
* Long-press the light button in the steady distance page to reset
* the distance to 1.00
*
* Pending design points
* o movement_request_tick_frequency(4) is used to obtain a 4Hz ticking, thus
* having a time resolution of 250 ms. Not sure if using event.subsecond`
* is the proper way to get the fractions of second for the start and
* final times.
* o For distance and average speed, the Second Digits (position 8 and 9)
* can be seen as decimals, thus possible to show distances as short as
* 0.01 km (or miles) and speeds as low as 0.01 km/h (or mph). However,
* if the same idea is used for the total time (showing hundredths),
* this limits the display to 9999.99 seconds (~2h:45m).
*/
#include "movement.h"
typedef struct {

View File

@ -25,11 +25,17 @@
#ifndef TALLY_FACE_H_
#define TALLY_FACE_H_
#include "movement.h"
/*
* TALLY face
*
* Tally face is designed to act as a tally counter.
* Based on the counter_face watch face by Shogo Okamoto.
*
* To advance the counter, press the ALARM button.
* To reset, long press the ALARM button.
*/
// Tally face is designed to act as a tally counter.
// Based on the counter_face watch face by Shogo Okamoto.
// To advance the counter, press the Alarm button. To reset, long press the Alarm button.
#include "movement.h"
typedef struct {
uint32_t tally_idx;

View File

@ -25,10 +25,8 @@
#ifndef TAROT_FACE_H_
#define TAROT_FACE_H_
#include "movement.h"
/*
* Tarot card watch face
* TAROT CARD watch face
*
* Draw from a deck of tarot cards. Can choose between major arcana only or
* entire deck.
@ -62,6 +60,8 @@
* - Light button (long press): go back to Draw screen, for choosing different draw parameters.
*/
#include "movement.h"
#define MAX_CARDS_TO_DRAW 10
typedef struct {

View File

@ -20,11 +20,6 @@
* 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.
*
* Gathers temperature statistics in a chart form. Statistics bins are per hour / per 0.5°C.
* Saved to file every day at 00:00. Can help improve watch precision in the future.
* If you can gather statistics over few months, and then send tempchart.ini to 3@14.by - it
* will help future generations of precision quartz watches.
*/
#include <stdlib.h>

View File

@ -25,6 +25,19 @@
#ifndef TEMPCHART_FACE_H_
#define TEMPCHART_FACE_H_
/*
* TEMPERATURE CHART face
*
* Gathers temperature statistics in a chart form.
* Statistics bins are per hour / per 0.5°C.
*
* Saved to file every day at 00:00.
* Can help improve watch precision in the future.
*
* If you can gather statistics over few months, and then send "tempchart.ini"
* to "3@14.by", it will help future generations of precision quartz watches.
*/
#include "movement.h"
void tempchart_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

View File

@ -25,9 +25,9 @@
#ifndef TIME_LEFT_FACE_H_
#define TIME_LEFT_FACE_H_
#include "movement.h"
/*
* TIME LEFT face
*
* The Time Left Face helps you to visualize how far you have proceeded in a certain
* time span. Much like the Day One Face, you can set your beginning date. In addition
* to that, you also set your target or destination date. You can then use the face
@ -65,6 +65,8 @@
*
*/
#include "movement.h"
typedef struct {
uint8_t current_page;
uint16_t current_year;

View File

@ -22,15 +22,13 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
#include "timer_face.h"
#include "watch.h"
#include "watch_utility.h"
static const uint16_t _default_timer_values[] = {0x200, 0x500, 0xA00, 0x1400, 0x2D02}; // default timers: 2 min, 5 min, 10 min, 20 min, 2 h 45 min
static const uint32_t _default_timer_values[] = {0x000200, 0x000500, 0x000A00, 0x001400, 0x002D02}; // default timers: 2 min, 5 min, 10 min, 20 min, 2 h 45 min
// sound sequence for a single beeping sequence
static const int8_t _sound_seq_beep[] = {BUZZER_NOTE_C8, 3, BUZZER_NOTE_REST, 3, -2, 2, BUZZER_NOTE_C8, 5, BUZZER_NOTE_REST, 25, 0};
@ -199,7 +197,7 @@ void timer_face_setup(movement_settings_t *settings, uint8_t watch_face_index, v
timer_state_t *state = (timer_state_t *)*context_ptr;
memset(*context_ptr, 0, sizeof(timer_state_t));
state->watch_face_index = watch_face_index;
for (uint8_t i = 0; i < sizeof(_default_timer_values) / sizeof(uint16_t); i++) {
for (uint8_t i = 0; i < sizeof(_default_timer_values) / sizeof(uint32_t); i++) {
state->timers[i].value = _default_timer_values[i];
}
}

View File

@ -22,14 +22,11 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#ifndef TIMER_FACE_H_
#define TIMER_FACE_H_
#include "movement.h"
/*
* TIMER face
* Advanced timer/countdown face with pre-set timer lengths
*
* This watch face provides the functionality of starting a countdown by choosing
@ -53,6 +50,8 @@
*
*/
#include "movement.h"
#define TIMER_SLOTS 9 // offer 9 timer slots
typedef enum {

View File

@ -25,6 +25,26 @@
#ifndef TOMATO_FACE_H_
#define TOMATO_FACE_H_
/*
* TOMATO TIMER face
*
* Add a "tomato" timer watch face that alternates between 25 and 5 minute
* timers as in the Pomodoro Technique.
* https://en.wikipedia.org/wiki/Pomodoro_Technique
*
* The top right letter shows mode (f for focus or b for break).
* The bottom right shows how many focus sessions you've completed.
* (You can reset the count with a long press of alarm)
*
* When you show up and it says 25 minutes, you can start it (alarm),
* switch to 5 minute (light) mode or leave (mode).
*
* When it's running you can reset (alarm), or leave (mode).
*
* When it's done, we beep and go back to step 1, changing switching
* mode from focus to break (or break to focus)
*/
#include "movement.h"
typedef enum {

View File

@ -25,10 +25,8 @@
#ifndef TOSS_UP_FACE_H_
#define TOSS_UP_FACE_H_
#include "movement.h"
/*
* TOSS UP FACE
* TOSS UP face
* ============
*
* Playful watch face for games of chance or divination using coins or dice.
@ -75,6 +73,8 @@
*
*/
#include "movement.h"
typedef struct {
// Anything you need to keep track of, put it here!
uint32_t entropy;

View File

@ -1,3 +1,27 @@
/*
* MIT License
*
* Copyright (c) 2022 Wesley Ellis (https://github.com/tahnok)
*
* 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 "totp_face.h"
@ -5,15 +29,6 @@
#include "watch_utility.h"
#include "TOTP.h"
// Use https://cryptii.com/pipes/base32-to-hex to convert base32 to hex
// Use https://github.com/susam/mintotp to generate test codes for verification
// Available algorothms:
// SHA1 (most TOTP codes use this)
// SHA224
// SHA256
// SHA384
// SHA512
////////////////////////////////////////////////////////////////////////////////
// Enter your TOTP key data below
static const uint8_t num_keys = 2;

View File

@ -1,6 +1,58 @@
/*
* MIT License
*
* Copyright (c) 2022 Wesley Ellis (https://github.com/tahnok)
*
* 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 TOTP_FACE_H_
#define TOTP_FACE_H_
/*
* TOTP face
* Time-based one-time password (TOTP) generator
*
* Generate one-time passwords often used for two-factor authentication.
* The secret key must be set by hand, by editing "totp_face.c".
*
* Available algorithms:
* o SHA1 (most TOTP codes use this)
* o SHA224
* o SHA256
* o SHA384
* o SHA512
*
* Instructions:
* o Find your secret key(s) and convert them to the required format.
* o Use https://cryptii.com/pipes/base32-to-hex to convert base32 to hex
* o Use https://github.com/susam/mintotp to generate test codes for verification
* o Edit global variables in "totp_face.c" to configure your stored keys:
* o "keys", "key_sizes", "timesteps", and "algorithms" set the
* cryptographic parameters for each secret key.
* o "labels" sets the two-letter label for each key
* (This replaces the day-of-week indicator)
* o Once finished, remove the two provided examples.
*
* If you have more than one secret key, press ALARM to cycle through them.
*/
#include "movement.h"
typedef struct {

View File

@ -1,3 +1,27 @@
/*
* MIT License
*
* Copyright (c) 2022 Wesley Ellis (https://github.com/tahnok)
*
* 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>
@ -11,24 +35,6 @@
#include "totp_face_lfs.h"
/* Reads from a file totp_uris.txt where each line is what's in a QR code:
* e.g.
* otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
* otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30
* This is also the same as what Aegis exports in plain-text format.
*
* Minimal sanitisation of input, however.
*
* At the moment, to get the records onto the filesystem, start a serial connection and do:
* echo otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example > totp_uris.txt
* echo otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 >> totp_uris.txt
* (note the double >> in the second one)
*
* You may want to customise the characters that appear to identify the 2FA code. These are just the first two characters of the issuer,
* and it's fine to modify the URI.
*/
#define MAX_TOTP_RECORDS 20
#define MAX_TOTP_SECRET_SIZE 48
#define TOTP_FILE "totp_uris.txt"

View File

@ -1,6 +1,54 @@
/*
* MIT License
*
* Copyright (c) 2022 Wesley Ellis (https://github.com/tahnok)
*
* 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 TOTP_FACE_LFS_H_
#define TOTP_FACE_LFS_H_
/*
* TOTP-LFS face
* Time-based one-time password (TOTP) generator using LFS
*
* Reads from a file "totp_uris.txt", containing a single secret key in a
* series of URLs. Each line is what's in a QR code, e.g.:
* otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
* otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30
*
* This is also the same as what Aegis exports in plain-text format.
* This face performs minimal sanitisation of input, however.
*
* At the moment, to get the records onto the filesystem, start a serial connection and do:
* echo otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example > totp_uris.txt
* echo otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30 >> totp_uris.txt
* (note the double >> in the second one)
*
* You may want to customise the characters that appear to identify the 2FA
* code. These are just the first two characters of the issuer, and it's fine
* to modify the URI.
*
* If you have more than one secret key, press ALARM to cycle through them.
*/
#include "movement.h"
typedef struct {

View File

@ -0,0 +1,140 @@
/*
* MIT License
*
* Copyright (c) 2023 Per Waagø
*
* 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 "tuning_tones_face.h"
/*
This face plays a tone that can be used as a reference when tuning
musical instrument.
- The alarm button (short press) starts and stops the tone
- The light button (short press) changes which note is played. The name
of the note is shown in the display.
*/
typedef struct Note {
BuzzerNote note;
char * name;
} Note;
static Note notes[] = {
{ .note = BUZZER_NOTE_C5, .name = "C " },
{ .note = BUZZER_NOTE_C5SHARP_D5FLAT, .name = "Db" },
{ .note = BUZZER_NOTE_D5, .name = "D " },
{ .note = BUZZER_NOTE_D5SHARP_E5FLAT, .name = "Eb" },
{ .note = BUZZER_NOTE_E5, .name = "E " },
{ .note = BUZZER_NOTE_F5, .name = "F " },
{ .note = BUZZER_NOTE_F5SHARP_G5FLAT, .name = "Gb" },
{ .note = BUZZER_NOTE_G5, .name = "G " },
{ .note = BUZZER_NOTE_G5SHARP_A5FLAT, .name = "Ab" },
{ .note = BUZZER_NOTE_A5, .name = "A " },
{ .note = BUZZER_NOTE_A5SHARP_B5FLAT, .name = "Bb" },
{ .note = BUZZER_NOTE_B5, .name = "B " },
};
static size_t note_count = sizeof notes / sizeof *notes;
static void draw(tuning_tones_state_t *state)
{
watch_display_string(notes[state->note_ind].name, 8);
}
void tuning_tones_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {
(void) settings;
(void) watch_face_index;
if (*context_ptr == NULL) {
tuning_tones_state_t *state = malloc(sizeof *state);
memset(state, 0, sizeof *state);
state->note_ind = 9;
*context_ptr = state;
}
}
void tuning_tones_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
}
static void update_buzzer(const tuning_tones_state_t *state)
{
if (state->playing) {
watch_set_buzzer_off();
watch_set_buzzer_period(NotePeriods[notes[state->note_ind].note]);
watch_set_buzzer_on();
}
}
bool tuning_tones_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
tuning_tones_state_t *state = (tuning_tones_state_t *)context;
switch (event.event_type) {
case EVENT_ACTIVATE:
draw(state);
break;
case EVENT_TICK:
break;
case EVENT_LIGHT_BUTTON_DOWN:
state->note_ind++;
if (state->note_ind == note_count) {
state->note_ind = 0;
}
update_buzzer(state);
draw(state);
break;
case EVENT_LIGHT_BUTTON_UP:
break;
case EVENT_ALARM_BUTTON_DOWN:
state->playing = !state->playing;
if (!state->playing) {
watch_set_buzzer_off();
} else {
update_buzzer(state);
}
case EVENT_ALARM_BUTTON_UP:
break;
case EVENT_TIMEOUT:
movement_move_to_face(0);
break;
case EVENT_LOW_ENERGY_UPDATE:
break;
default:
return movement_default_loop_handler(event, settings);
}
return !state->playing;
}
void tuning_tones_face_resign(movement_settings_t *settings, void *context) {
(void) settings;
tuning_tones_state_t *state = (tuning_tones_state_t *)context;
if (state->playing) {
state->playing = false;
watch_set_buzzer_off();
}
}

View File

@ -0,0 +1,57 @@
/*
* MIT License
*
* Copyright (c) 2023 Per Waagø
*
* 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 TUNING_TONES_FACE_H_
#define TUNING_TONES_FACE_H_
#include "movement.h"
/*
* A DESCRIPTION OF YOUR WATCH FACE
*
* and a description of how use it
*
*/
typedef struct {
// Anything you need to keep track of, put it here!
bool playing;
size_t note_ind;
} tuning_tones_state_t;
void tuning_tones_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void tuning_tones_face_activate(movement_settings_t *settings, void *context);
bool tuning_tones_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
void tuning_tones_face_resign(movement_settings_t *settings, void *context);
#define tuning_tones_face ((const watch_face_t){ \
tuning_tones_face_setup, \
tuning_tones_face_activate, \
tuning_tones_face_loop, \
tuning_tones_face_resign, \
NULL, \
})
#endif // TUNING_TONES_FACE_H_

View File

@ -22,24 +22,12 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
// #include <threads.h>
#include "wake_face.h"
#include "watch.h"
#include "watch_utility.h"
/*
UI Notes
º Light advances hour by 1
º Light long press advances hour by 6
º Alarm advances minute by 10
º Alarm long press cycles through signal modes (just one at the moment)
*/
//
// Private
//

View File

@ -22,11 +22,24 @@
* SOFTWARE.
*/
//-----------------------------------------------------------------------------
#ifndef WAKE_FACE_H_
#define WAKE_FACE_H_
/*
* WAKE daily alarm face
*
* Basic daily alarm clock face. Seems useful if nothing else in the interest
* of feature parity with the F-91Ws OEM module, 593.
*
* Also experiments with caret-free UI: One button cycles hours, the other
* minutes, so theres no toggling between display and adjust modes and no
* cycling the caret through the UI.
* º LIGHT advances hour by 1
* º LIGHT long press advances hour by 6
* º ALARM advances minute by 10
* º ALARM long press cycles through signal modes (just one at the moment)
*/
#include "movement.h"
typedef struct {

View File

@ -25,6 +25,17 @@
#ifndef CHARACTER_SET_FACE_H_
#define CHARACTER_SET_FACE_H_
/*
* CHARACTER SET FACE
*
* This watch face displays all of the characters in the Sensor Watch character
* set. You can advance from one character to the next with a short press of the
* ALARM button.
*
* This watch face may be useful to watch face developers, in that it can help
* them to understand which characters will work in different positions.
*/
#include "movement.h"
void character_set_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

View File

@ -25,8 +25,6 @@
#ifndef CHIRPY_DEMO_FACE_H_
#define CHIRPY_DEMO_FACE_H_
#include "movement.h"
/*
* CHIRPY DEMO FACE
*
@ -50,9 +48,10 @@
*
* To record and decode a chirpy transmission on your computer, you can use the web app here:
* https://jealousmarkup.xyz/off/chirpy/rx/
*
*/
#include "movement.h"
void chirpy_demo_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
void chirpy_demo_face_activate(movement_settings_t *settings, void *context);
bool chirpy_demo_face_loop(movement_event_t event, movement_settings_t *settings, void *context);

View File

@ -25,6 +25,17 @@
#ifndef DEMO_FACE_H_
#define DEMO_FACE_H_
/*
* DEMO FACE
*
* This watch was designed for the Crowd Supply marketing team, so they could
* photograph the various functions of Sensor Watch. The Alarm button advances
* through static screens that simulate different watch faces.
*
* This watch face may only be useful to you if you need to photograph Sensor
* Watch, i.e. for a blog post.
*/
#include "movement.h"
void demo_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

View File

@ -25,6 +25,18 @@
#ifndef FREQUENCY_CORRECTION_FACE_H_
#define FREQUENCY_CORRECTION_FACE_H_
/*
* FREQUENCY CORRECTION FACE
*
* While active, this face generates a square-wave on pin A1 of the 9-pin
* connector. The output frequency is adjustable from 64 Hz to 0.5 Hz.
* Long-press ALARM to cycle through available frequencies.
*
* This face also displays the value of the watch's frequency-correction
* register. This setting varies from -127 to +127. Press LIGHT to increment
* or ALARM to decrement the setting.
*/
#include "movement.h"
typedef struct {

View File

@ -25,6 +25,13 @@
#ifndef HELLO_THERE_FACE_H_
#define HELLO_THERE_FACE_H_
/*
* HELLO THERE FACE
*
* A simple demo that displays the word "Hello" and then the word "there",
* on an endless loop. Press ALARM to pause or resume the animation.
*/
#include "movement.h"
typedef struct {

View File

@ -25,6 +25,14 @@
#ifndef LIS2DW_LOGGING_FACE_H_
#define LIS2DW_LOGGING_FACE_H_
/*
* LIS2DW Accelerometer Data Logger
*
* This is an experimental watch face for logging data on the Sensor Watch
* Motion Express board. I will add more documentation for this watch face
* once this sensor board is more widely available.
*/
#include "movement.h"
#include "watch.h"

View File

@ -25,6 +25,17 @@
#ifndef VOLTAGE_FACE_H_
#define VOLTAGE_FACE_H_
/*
* VOLTAGE face
*
* This watch face is very simple and has no controls to speak of. It displays
* the battery voltage as measured by the SAM L22s ADC.
*
* Note that the Simple Clock watch face includes a low battery warning, so you
* dont technically need to this watch face unless you want to track the
* battery level.
*/
#include "movement.h"
void voltage_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

View File

@ -25,6 +25,12 @@
#ifndef ACCELEROMETER_DATA_ACQUISITION_FACE_H_
#define ACCELEROMETER_DATA_ACQUISITION_FACE_H_
/*
* ACCELEROMETER DATA ACQUISITION
*
* TODO: Add description here, including controls.
*/
#include "movement.h"
#define ACCELEROMETER_DATA_ACQUISITION_INVALID ((uint64_t)(0b11)) // all bits are 1 when the flash is erased

View File

@ -22,37 +22,6 @@
* SOFTWARE.
*/
/* Aperture-priority Light Meter Face
*
* Tested with the "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001" flexboard.
* This flexboard could use a revision:
*
* - The thermistor components should be moved west a mm or flipped to the backside
* to avoid stressing the flexboard against the processor so much.
* - The 'no connect' pad falls off easily.
*
* Controls:
*
* - Trigger a measurement by long-pressing Alarm.
* Sensor integration is happening when the Signal indicator is on.
*
* - ISO setting can be cycled by long-pressing Light.
* During integration the current ISO setting will be displayed.
*
* - EV measurement in the top right: "LAP" indicates "half stop".
* So "LAP -1" means EV = -1.5. Likewise "LAP 13" means EV = +13.5
*
* - Aperture in the bottom right: the last 3 main digits are the f-stop.
* Adjust this number in half-stop increments using Alarm = +1/2 and Light = -1/2.
*
* - Best shutter speed in the bottom left: the first 3 digits are the shutter speed.
* Some special chars are needed here: "-" = seconds, "h" = extra half second, "K" = thousands.
* "HI" or "LO" if there's no shutter in the dictionary within 0.5 stops of correct exposure.
*
* - Mode long-press changes the main digits to show raw sensor lux measurements.
*
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>

View File

@ -25,6 +25,37 @@
#ifndef LIGHTMETER_FACE_H_
#define LIGHTMETER_FACE_H_
/*
* Aperture-priority Light Meter Face
*
* Tested with the "Q3Q-SWAB-A1-00 Temperature + Test Points + OPT3001" flexboard.
* This flexboard could use a revision:
*
* - The thermistor components should be moved west a mm or flipped to the backside
* to avoid stressing the flexboard against the processor so much.
* - The 'no connect' pad falls off easily.
*
* Controls:
*
* - Trigger a measurement by long-pressing Alarm.
* Sensor integration is happening when the Signal indicator is on.
*
* - ISO setting can be cycled by long-pressing Light.
* During integration the current ISO setting will be displayed.
*
* - EV measurement in the top right: "LAP" indicates "half stop".
* So "LAP -1" means EV = -1.5. Likewise "LAP 13" means EV = +13.5
*
* - Aperture in the bottom right: the last 3 main digits are the f-stop.
* Adjust this number in half-stop increments using Alarm = +1/2 and Light = -1/2.
*
* - Best shutter speed in the bottom left: the first 3 digits are the shutter speed.
* Some special chars are needed here: "-" = seconds, "h" = extra half second, "K" = thousands.
* "HI" or "LO" if there's no shutter in the dictionary within 0.5 stops of correct exposure.
*
* - Mode long-press changes the main digits to show raw sensor lux measurements.
*/
#include "movement.h"
#include "opt3001.h"

View File

@ -25,6 +25,34 @@
#ifndef THERMISTOR_LOGGING_FACE_H_
#define THERMISTOR_LOGGING_FACE_H_
/*
* THERMISTOR LOGGING (aka Temperature Log)
*
* This watch face automatically logs the temperature once an hour, and
* maintains a 36-hour log of readings. This watch face is admittedly rather
* complex, and bears some explanation.
*
* The main display shows the letters TL in the top left, indicating the
* name of the watch face. At the top right, it displays the index of the
* reading; 0 represents the most recent reading taken, 1 represents one
* hour earlier, etc. The bottom line in this mode displays the logged
* temperature.
*
* A short press of the Alarm button advances to the next oldest reading;
* you will see the number at the top right advance from 0 to 1 to 2, all
* the way to 35, the oldest reading available.
*
* A short press of the Light button will briefly display the timestamp
* of the reading. The letters at the top left will display the word At,
* and the main line will display the timestamp of the currently displayed
* data point. The number in the top right will display the day of the month
* for the given data point; for example, you can read At 22 3:00 PM as
* At 3:00 PM on the 22nd.
*
* If you need to illuminate the LED to read the data point, long press the
* Light button and release it.
*/
#include "movement.h"
#include "watch.h"

View File

@ -25,6 +25,29 @@
#ifndef THERMISTOR_READOUT_FACE_H_
#define THERMISTOR_READOUT_FACE_H_
/*
* THERMISTOR READOUT (aka Temperature Display)
*
* This watch face is designed to work with either the Temperature + GPIO
* sensor board or the Temperature + Light sensor board. It reads the current
* temperature from the thermistor voltage divider on the sensor board, and
* displays the current temperature in degrees Celsius.
*
* When the watch is on your wrist, your body heat interferes with an ambient
* temperature reading, but if you set it on a bedside table, strap it to your
* bike handlebars or place it outside of your tent while camping, this watch
* face can act as a digital thermometer for displaying ambient conditions.
*
* The temperature sensor watch face automatically samples the temperature
* once every five seconds, and it illuminates the Signal indicator just
* before taking a reading.
*
* Pressing the ALARM button toggles the unit display from Celsius to
* Fahrenheit. Technically this sets the global Metric / Imperial flag, so
* any other watch face that displays localizable units will display them in
* the system selected here.
*/
#include "movement.h"
void thermistor_readout_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

View File

@ -28,11 +28,6 @@
#include "thermistor_driver.h"
#include "watch.h"
// This watch face is designed for testing temperature sensor boards.
// It displays temperature readings at a relatively fast rate of 8 Hz,
// and disables low energy mode so my testing device doesn't sleep.
// You more than likely want to use thermistor_readout_face instead.
static void _thermistor_testing_face_update_display(bool in_fahrenheit) {
thermistor_driver_enable();
float temperature_c = thermistor_driver_get_temperature();

View File

@ -25,6 +25,17 @@
#ifndef THERMISTOR_TESTING_FACE_H_
#define THERMISTOR_TESTING_FACE_H_
/*
* THERMISTOR TESTING FACE
*
* This watch face is designed for testing temperature sensor boards.
* It displays temperature readings at a relatively fast rate of 8 Hz,
* and disables low energy mode so my testing device doesn't sleep.
* You more than likely want to use thermistor_readout_face instead.
*
* Press ALARM to toggle display of metric vs. imperial units.
*/
#include "movement.h"
void thermistor_testing_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

View File

@ -20,21 +20,6 @@
* 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.
*
* FineTune face allows to align watch with sub-second precision in 25/250ms accuracy.
* Counts time since previous finetune, and allows to calculate & apply ppm correction for nanosec.
*
* Main screen - adjust delay (light/alarm)
* Long mode press - show hours since previous finetune
* Long mode press - show calculated ppm correction. You can apply it with long light, or just reset finetune timer with long alarm.
*
* Finetune will apply crystal aging correction on every finetune save (as aging is calculated since "last finetune" timestamp) - but you should worry
* about aging only on second/third years of watch calibration (if you are really looking at less than 10 seconds per year of error).
*
* Warning, do not use at the first second of a month, as you might stay at the same month and it will surprise you.
* Just wait 1 second...We are not fully replicating RTC timer behavior when RTC is off.
* Simulating months and years is... too much complexity.
*
*/
#include <stdlib.h>

Some files were not shown because too many files have changed in this diff Show More