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
114 changed files with 2531 additions and 699 deletions

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 {