-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added flowtime watch face and instructions
- Loading branch information
Showing
9 changed files
with
392 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
build/ | ||
build-sim/ | ||
build-sim/ | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,49 @@ | ||
# flowtime-watch | ||
# Flowtime Watch 🌀 | ||
|
||
The Flowtime Watch is an implementation of the [Flowtime](https://github.com/Ucodia/flowtime) on a classic Casio watch. | ||
|
||
This project depends on the `movement` framework from the [Sensor Watch](https://github.com/joeycastillo/Sensor-Watch) project. | ||
|
||
## Dependencies | ||
|
||
To build this project, you need to install the `arm-none-eabi` variant of the Arm GNU Toolchain installed on your machine: [Arm GNU Toolchain Downloads](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) | ||
|
||
## Building | ||
|
||
After cloning this repository, make sure you have pulled the submodules for `sensor-watch` and its submodules. | ||
|
||
``` | ||
git submodule update --init | ||
cd sensor-watch | ||
git submodule update --init | ||
``` | ||
|
||
Hack: These files need to be removed as make does not seem to respect priority of header directory as defined in the Makefile | ||
|
||
``` | ||
rm sensor-watch/movement/movement_config.h | ||
rm sensor-watch/movement/movement_faces.h | ||
``` | ||
|
||
Find which Sensor Watch board you are building for (RED, GREEN or BLUE), then run the following command by specifying the proper color | ||
|
||
``` | ||
cd make | ||
make COLOR=RED | ||
``` | ||
|
||
## Testing | ||
|
||
To test in the emulator, run the following command | ||
|
||
``` | ||
cd make | ||
emmake make COLOR=RED | ||
python3 -m http.server -d build-sim | ||
``` | ||
|
||
The emulator should be available at http://localhost:8000/watch.html | ||
|
||
## Flashing | ||
|
||
To flash your Sensor Watch with the Flowtime watch firmware, copy the `movement/make/build/watch.uf2` firmware file to your board. Read more in the official [Sensor Watch documentation](https://www.sensorwatch.net/docs/firmware/flashing/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include "flowtime.h" | ||
#include "watch.h" | ||
|
||
double nextLcgValue(Lcg *lcg) | ||
{ | ||
lcg->z = (lcg->multiplier * lcg->z + lcg->increment) % lcg->modulus; | ||
return (double)lcg->z / lcg->modulus; | ||
} | ||
|
||
Lcg createLcg(long long seed, long long modulus, long long multiplier, long long increment) | ||
{ | ||
Lcg lcg = {seed, modulus, multiplier, increment}; | ||
return lcg; | ||
} | ||
|
||
void getSeedsFromDate(watch_date_time date, long long *minuteSeed, long long *hourSeed) | ||
{ | ||
char buffer[20]; | ||
sprintf(buffer, "%04d%02d%02d%02d", date.unit.year + 2020, date.unit.month, date.unit.day, date.unit.hour); | ||
*minuteSeed = atoll(buffer); | ||
|
||
sprintf(buffer, "%04d%02d%02d", date.unit.year + 2020, date.unit.month, date.unit.day); | ||
*hourSeed = atoll(buffer); | ||
} | ||
|
||
void getSequenceFromLcg(Lcg lcg, int length, int *sequence) | ||
{ | ||
double *values = (double *)malloc(length * sizeof(double)); | ||
for (int i = 0; i < length; i++) | ||
{ | ||
values[i] = nextLcgValue(&lcg); | ||
} | ||
|
||
for (int i = 0; i < length; i++) | ||
{ | ||
int count = 0; | ||
for (int j = 0; j < length; j++) | ||
{ | ||
if (values[j] < values[i] || (values[j] == values[i] && j < i)) | ||
{ | ||
count++; | ||
} | ||
} | ||
sequence[i] = count; | ||
} | ||
|
||
free(values); | ||
} | ||
|
||
Time date_to_flowtime(watch_date_time date) | ||
{ | ||
long long minuteSeed, hourSeed; | ||
getSeedsFromDate(date, &minuteSeed, &hourSeed); | ||
|
||
Lcg minuteLcg = createLcg(minuteSeed, 2147483647, 48271, 0); | ||
Lcg hourLcg = createLcg(hourSeed, 4294967296, 1664525, 1013904223); | ||
|
||
int minuteSequence[60]; | ||
int hourSequence[24]; | ||
|
||
getSequenceFromLcg(minuteLcg, 60, minuteSequence); | ||
getSequenceFromLcg(hourLcg, 24, hourSequence); | ||
|
||
Time result = {hourSequence[date.unit.hour], minuteSequence[date.unit.minute], date.unit.second}; | ||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#include "watch.h" | ||
|
||
typedef struct Lcg | ||
{ | ||
long long z; | ||
long long modulus; | ||
long long multiplier; | ||
long long increment; | ||
} Lcg; | ||
|
||
typedef struct Time | ||
{ | ||
int hour; | ||
int minute; | ||
int second; | ||
} Time; | ||
|
||
double nextLcgValue(Lcg *lcg); | ||
Lcg createLcg(long long seed, long long modulus, long long multiplier, long long increment); | ||
void getSeedsFromDate(watch_date_time date, long long *minuteSeed, long long *hourSeed); | ||
void getSequenceFromLcg(Lcg lcg, int length, int *sequence); | ||
Time date_to_flowtime(watch_date_time date); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#include <stdlib.h> | ||
#include "flowtime_face.h" | ||
#include "watch.h" | ||
#include "watch_utility.h" | ||
#include "watch_private_display.h" | ||
#include "flowtime.h" | ||
|
||
void flowtime_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(flowtime_state_t)); | ||
flowtime_state_t *state = (flowtime_state_t *)*context_ptr; | ||
state->watch_face_index = watch_face_index; | ||
state->reality_check = false; | ||
} | ||
} | ||
|
||
void flowtime_face_activate(movement_settings_t *settings, void *context) | ||
{ | ||
flowtime_state_t *state = (flowtime_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); | ||
|
||
if (state->reality_check) | ||
watch_set_indicator(WATCH_INDICATOR_BELL); | ||
else | ||
watch_clear_indicator(WATCH_INDICATOR_BELL); | ||
|
||
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 flowtime_face_loop(movement_event_t event, movement_settings_t *settings, void *context) | ||
{ | ||
flowtime_state_t *state = (flowtime_state_t *)context; | ||
char buf[11]; | ||
uint8_t pos; | ||
|
||
watch_date_time date_time; | ||
Time flow_time; | ||
bool alarm_button_down; | ||
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(); | ||
flow_time = date_to_flowtime(date_time); | ||
alarm_button_down = watch_get_pin_level(2); | ||
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); | ||
|
||
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, alarm_button_down ? date_time.unit.hour : flow_time.hour, alarm_button_down ? date_time.unit.minute : flow_time.minute); | ||
} | ||
else | ||
{ | ||
sprintf(buf, "%s%2d%2d%02d%02d", watch_utility_get_weekday(date_time), date_time.unit.day, alarm_button_down ? date_time.unit.hour : flow_time.hour, alarm_button_down ? date_time.unit.minute : flow_time.minute, date_time.unit.second); | ||
} | ||
|
||
watch_display_string(buf, pos); | ||
break; | ||
default: | ||
return movement_default_loop_handler(event, settings); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
void flowtime_face_resign(movement_settings_t *settings, void *context) | ||
{ | ||
(void)settings; | ||
(void)context; | ||
} | ||
|
||
bool flowtime_face_wants_background_task(movement_settings_t *settings, void *context) | ||
{ | ||
return false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#ifndef FLOWTIME_FACE_H_ | ||
#define FLOWTIME_FACE_H_ | ||
|
||
#include "movement.h" | ||
|
||
typedef struct | ||
{ | ||
uint32_t previous_date_time; | ||
uint8_t last_battery_check; | ||
uint8_t watch_face_index; | ||
bool battery_low; | ||
bool reality_check; | ||
} flowtime_state_t; | ||
|
||
void flowtime_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr); | ||
void flowtime_face_activate(movement_settings_t *settings, void *context); | ||
bool flowtime_face_loop(movement_event_t event, movement_settings_t *settings, void *context); | ||
void flowtime_face_resign(movement_settings_t *settings, void *context); | ||
bool flowtime_face_wants_background_task(movement_settings_t *settings, void *context); | ||
|
||
#define flowtime_face ((const watch_face_t){ \ | ||
flowtime_face_setup, \ | ||
flowtime_face_activate, \ | ||
flowtime_face_loop, \ | ||
flowtime_face_resign, \ | ||
flowtime_face_wants_background_task, \ | ||
}) | ||
|
||
#endif // FLOWTIME_FACE_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#ifndef MOVEMENT_CONFIG_H_ | ||
#define MOVEMENT_CONFIG_H_ | ||
|
||
#include "movement_faces.h" | ||
|
||
const watch_face_t watch_faces[] = { | ||
flowtime_face, | ||
simple_clock_face, | ||
stopwatch_face, | ||
preferences_face, | ||
set_time_face, | ||
}; | ||
|
||
#define MOVEMENT_NUM_FACES (sizeof(watch_faces) / sizeof(watch_face_t)) | ||
|
||
/* Determines what face to go to from the first face on long press of the Mode button. | ||
* Also excludes these faces from the normal rotation. | ||
* In the default firmware, this lets you access temperature and battery voltage with a long press of Mode. | ||
* Some folks also like to use this to hide the preferences and time set faces from the normal rotation. | ||
* If you don't want any faces to be excluded, set this to 0 and a long Mode press will have no effect. | ||
*/ | ||
#define MOVEMENT_SECONDARY_FACE_INDEX (MOVEMENT_NUM_FACES - 2) // or (0) | ||
|
||
/* Custom hourly chime tune. Check movement_custom_signal_tunes.h for options */ | ||
#define SIGNAL_TUNE_DEFAULT | ||
|
||
#endif // MOVEMENT_CONFIG_H_ |
Oops, something went wrong.