summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--make.mk4
-rw-r--r--movement/make/Makefile1
-rw-r--r--movement/movement_faces.h1
-rw-r--r--movement/watch_faces/clock/wyoscan_face.c210
-rw-r--r--movement/watch_faces/clock/wyoscan_face.h74
-rw-r--r--movement/watch_faces/complication/flashlight_face.c5
-rw-r--r--movement/watch_faces/complication/randonaut_face.c4
-rw-r--r--movement/watch_faces/settings/nanosec_face.c10
-rw-r--r--rules.mk1
9 files changed, 305 insertions, 5 deletions
diff --git a/make.mk b/make.mk
index 35af3422..4f9e65dc 100644
--- a/make.mk
+++ b/make.mk
@@ -207,6 +207,10 @@ ifeq ($(LED), BLUE)
CFLAGS += -DWATCH_IS_BLUE_BOARD
endif
+ifeq ($(LED), RED)
+CFLAGS += -DWATCH_INVERT_LED_POLARITY
+endif
+
ifdef FIRMWARE
CFLAGS += -DMOVEMENT_FIRMWARE_$(FIRMWARE)=1
CFLAGS += -DMOVEMENT_FIRMWARE=MOVEMENT_FIRMWARE_$(FIRMWARE)
diff --git a/movement/make/Makefile b/movement/make/Makefile
index 8b056a02..f4ffa546 100644
--- a/movement/make/Makefile
+++ b/movement/make/Makefile
@@ -116,6 +116,7 @@ SRCS += \
../watch_faces/complication/geomancy_face.c \
../watch_faces/clock/simple_clock_bin_led_face.c \
../watch_faces/complication/flashlight_face.c \
+ ../watch_faces/clock/wyoscan_face.c \
# New watch faces go above this line.
# Leave this line at the bottom of the file; it has all the targets for making your project.
diff --git a/movement/movement_faces.h b/movement/movement_faces.h
index bf63732e..9b83f141 100644
--- a/movement/movement_faces.h
+++ b/movement/movement_faces.h
@@ -93,6 +93,7 @@
#include "dual_timer_face.h"
#include "simple_clock_bin_led_face.h"
#include "flashlight_face.h"
+#include "wyoscan_face.h"
// New includes go above this line.
#endif // MOVEMENT_FACES_H_
diff --git a/movement/watch_faces/clock/wyoscan_face.c b/movement/watch_faces/clock/wyoscan_face.c
new file mode 100644
index 00000000..fd87b911
--- /dev/null
+++ b/movement/watch_faces/clock/wyoscan_face.c
@@ -0,0 +1,210 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 <#author_name#>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "wyoscan_face.h"
+#include "watch_private_display.h"
+
+/*
+Slowly render the current time from left to right,
+scanning across its liquid crystal face, completing 1 cycle every 2 seconds.
+
+Created to mimic the wyoscan watch that was produced by Halmos and designed by Dexter Sinister
+It looks like this https://www.o-r-g.com/apps/wyoscan
+
+You’ll notice that reading this watch requires more attention than usual,
+as the seven segments of each digit are lit one by one across its display.
+This speed may be adjusted until it reaches the limits of your perception.
+You and your watch are now in tune.
+
+This is a relatively generic way of animating a time display.
+If you want to modify the animation, you can change the segment_map
+the A-F are corresponding to the segments on the watch face
+ A
+F B
+ G
+E C
+ D
+the X's are the frames that will be skipped in the animation
+This particular segment_map allocates 8 frames to display each number
+this is to achieve the 2 second cycle time.
+8 frames per number * 6 numbers + the trailing 16 frames = 64 frames
+at 32 frames per second, this is a 2 second cycle time.
+
+I tried to make the animation of each number display similar to if you were
+to draw the number on the watch face with a pen, pausing with 'X'
+when your pen might turn a corner or when you might cross over
+a line you've already drawn. It is vaguely top to bottom and counter,
+clockwise when possible.
+*/
+static char *segment_map[] = {
+ "AXFEDCBX", // 0
+ "BXXXCXXX", // 1
+ "ABGEXXXD", // 2
+ "ABGXXXCD", // 3
+ "FXGBXXXC", // 4
+ "AXFXGXCD", // 5
+ "AXFEDCXG", // 6
+ "AXXBXXCX", // 7
+ "AFGCDEXB", // 8
+ "AFGBXXCD" // 9
+};
+
+/*
+This is the mapping of input to the watch_set_pixel() function
+for each position in hhmmss it defines the 2 dimention input at each of A-F*/
+static const int32_t clock_mapping[6][7][2] = {
+ // hour 1
+ {{1,18}, {2,19}, {0,19}, {1,18}, {0,18}, {2,18}, {1,19}},
+ // hour 2
+ {{2,20}, {2,21}, {1,21}, {0,21}, {0,20}, {1,17}, {1,20}},
+ // minute 1
+ {{0,22}, {2,23}, {0,23}, {0,22}, {1,22}, {2,22}, {1,23}},
+ // minute 2
+ {{2,1}, {2,10}, {0,1}, {0,0}, {1,0}, {2,0}, {1,1}},
+ // second 1
+ {{2,2}, {2,3}, {0,4}, {0,3}, {0,2}, {1,2}, {1,3}},
+ // second 2
+ {{2,4}, {2,5}, {1,6}, {0,6}, {0,5}, {1,4}, {1,5}},
+};
+
+void wyoscan_face_setup(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(wyoscan_state_t));
+ memset(*context_ptr, 0, sizeof(wyoscan_state_t));
+ // Do any one-time tasks in here; the inside of this conditional happens only at boot.
+ }
+ // Do any pin or peripheral setup here; this will be called whenever the watch wakes from deep sleep.
+}
+
+void wyoscan_face_activate(movement_settings_t *settings, void *context) {
+ (void) settings;
+ wyoscan_state_t *state = (wyoscan_state_t *)context;
+ movement_request_tick_frequency(32);
+ state->total_frames = 64;
+}
+
+bool wyoscan_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
+ wyoscan_state_t *state = (wyoscan_state_t *)context;
+
+ watch_date_time date_time;
+ switch (event.event_type) {
+ case EVENT_ACTIVATE:
+ break;
+ case EVENT_TICK:
+ if (!state->animate) {
+ date_time = watch_rtc_get_date_time();
+ state->start = 0;
+ state->end = 0;
+ state->animation = 0;
+ state->animate = true;
+ state->time_digits[0] = date_time.unit.hour / 10;
+ state->time_digits[1] = date_time.unit.hour % 10;
+ state->time_digits[2] = date_time.unit.minute / 10;
+ state->time_digits[3] = date_time.unit.minute % 10;
+ state->time_digits[4] = date_time.unit.second / 10;
+ state->time_digits[5] = date_time.unit.second % 10;
+ }
+ if ( state->animate ) {
+ // if we have reached the max number of illuminated segments, we clear the oldest one
+ if ((state->end + 1) % MAX_ILLUMINATED_SEGMENTS == state->start) {
+ // clear the oldest pixel if it's not 'X'
+ if (state->illuminated_segments[state->start][0] != 99 && state->illuminated_segments[state->start][1] != 99) {
+ watch_clear_pixel(state->illuminated_segments[state->start][0], state->illuminated_segments[state->start][1]);
+ }
+ // increment the start index to point to the next oldest pixel
+ state->start = (state->start + 1) % MAX_ILLUMINATED_SEGMENTS;
+ }
+ if (state->animation < state->total_frames - MAX_ILLUMINATED_SEGMENTS) {
+ if (state->animation % 32 == 0) {
+ if (state->colon) {
+ watch_set_colon();
+ } else {
+ watch_clear_colon();
+ }
+ state->colon = !state->colon;
+ }
+
+ // calculate the start position for the current frame
+ state->position = (state->animation / 8) % 6;
+ // calculate the current segment for the current digit
+ state->segment = state->animation % strlen(segment_map[state->time_digits[state->position]]);
+ // get the segments for the current digit
+ state->segments = segment_map[state->time_digits[state->position]];
+
+ if (state->segments[state->segment] == 'X') {
+ // if 'X', skip this frame
+ state->illuminated_segments[state->end][0] = 99;
+ state->illuminated_segments[state->end][1] = 99;
+ state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
+ state->animation = (state->animation + 1);
+ break;
+ }
+
+ // calculate the animation frame
+ state->x = clock_mapping[state->position][state->segments[state->segment]-'A'][0];
+ state->y = clock_mapping[state->position][state->segments[state->segment]-'A'][1];
+
+ // set the new pixel
+ watch_set_pixel(state->x, state->y);
+
+ // store this pixel in the buffer
+ state->illuminated_segments[state->end][0] = state->x;
+ state->illuminated_segments[state->end][1] = state->y;
+ // increment the end index to the next position
+ state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
+ }
+ else if (state->animation >= state->total_frames - MAX_ILLUMINATED_SEGMENTS && state->animation < state->total_frames) {
+ state->end = (state->end + 1) % MAX_ILLUMINATED_SEGMENTS;
+ }
+ else {
+ // reset the animation state
+ state->animate = false;
+ }
+ state->animation = (state->animation + 1);
+ }
+ break;
+ case EVENT_LOW_ENERGY_UPDATE:
+ break;
+ case EVENT_ALARM_LONG_PRESS:
+ break;
+ case EVENT_BACKGROUND_TASK:
+ break;
+ default:
+ return movement_default_loop_handler(event, settings);
+ }
+
+ return true;
+}
+
+void wyoscan_face_resign(movement_settings_t *settings, void *context) {
+ (void) settings;
+ (void) context;
+
+ // handle any cleanup before your watch face goes off-screen.
+}
+
diff --git a/movement/watch_faces/clock/wyoscan_face.h b/movement/watch_faces/clock/wyoscan_face.h
new file mode 100644
index 00000000..68db9eed
--- /dev/null
+++ b/movement/watch_faces/clock/wyoscan_face.h
@@ -0,0 +1,74 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2023 <#author_name#>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef WYOSCAN_FACE_H_
+#define WYOSCAN_FACE_H_
+
+#include "movement.h"
+
+/*
+ * A DESCRIPTION OF YOUR WATCH FACE
+ *
+ * and a description of how use it
+ *
+ */
+
+#define MAX_ILLUMINATED_SEGMENTS 16
+
+typedef struct {
+ uint32_t previous_date_time;
+ uint8_t last_battery_check;
+ uint8_t watch_face_index;
+ bool signal_enabled;
+ bool battery_low;
+ bool alarm_enabled;
+ uint8_t animation;
+ bool animate;
+ uint32_t start;
+ uint32_t end;
+ uint32_t total_frames;
+ bool colon;
+ uint8_t position, segment;
+ char *segments;
+ uint8_t x, y;
+ uint32_t time_digits[6];
+ uint32_t illuminated_segments[MAX_ILLUMINATED_SEGMENTS][2];
+} wyoscan_state_t;
+
+void wyoscan_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
+void wyoscan_face_activate(movement_settings_t *settings, void *context);
+bool wyoscan_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
+void wyoscan_face_resign(movement_settings_t *settings, void *context);
+bool wyoscan_face_wants_background_task(movement_settings_t *settings, void *context);
+
+#define wyoscan_face ((const watch_face_t){ \
+ wyoscan_face_setup, \
+ wyoscan_face_activate, \
+ wyoscan_face_loop, \
+ wyoscan_face_resign, \
+ NULL, \
+})
+
+#endif // WYOSCAN_FACE_H_
+
diff --git a/movement/watch_faces/complication/flashlight_face.c b/movement/watch_faces/complication/flashlight_face.c
index 5788d32f..4922faaf 100644
--- a/movement/watch_faces/complication/flashlight_face.c
+++ b/movement/watch_faces/complication/flashlight_face.c
@@ -28,6 +28,7 @@
void flashlight_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(flashlight_state_t));
memset(*context_ptr, 0, sizeof(flashlight_state_t));
@@ -38,14 +39,14 @@ void flashlight_face_setup(movement_settings_t *settings, uint8_t watch_face_ind
void flashlight_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
- flashlight_state_t *state = (flashlight_state_t *)context;
+ (void) context;
watch_enable_digital_output(A2);
watch_set_pin_level(A2, false);
}
bool flashlight_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
- flashlight_state_t *state = (flashlight_state_t *)context;
+ (void) context;
switch (event.event_type) {
case EVENT_ACTIVATE:
diff --git a/movement/watch_faces/complication/randonaut_face.c b/movement/watch_faces/complication/randonaut_face.c
index 1a3eb21d..bca334fb 100644
--- a/movement/watch_faces/complication/randonaut_face.c
+++ b/movement/watch_faces/complication/randonaut_face.c
@@ -278,14 +278,14 @@ static void _randonaut_face_display(randonaut_state_t *state) {
sprintf(buf, "BE # %d", state->point.bearing );
break;
case 3: // latitude DD._____
- sprintf(state->scratchpad, "%07d", abs(state->point.latitude));
+ sprintf(state->scratchpad, "%07d", abs((int32_t)(state->point.latitude)));
sprintf(buf, "LA #%c %c%c ", state->point.latitude < 0 ? '-' : '+', state->scratchpad[0], state->scratchpad[1]);
break;
case 4: // latitude __.DDDDD
sprintf(buf, "LA , %c%c%c%c%c", state->scratchpad[2], state->scratchpad[3],state->scratchpad[4], state->scratchpad[5],state->scratchpad[6]);
break;
case 5: // longitude DD._____
- sprintf(state->scratchpad, "%08d", abs(state->point.longitude));
+ sprintf(state->scratchpad, "%08d", abs((int32_t)(state->point.longitude)));
sprintf(buf, "LO #%c%c%c%c ", state->point.longitude < 0 ? '-' : '+',state->scratchpad[0], state->scratchpad[1], state->scratchpad[2]);
break;
case 6: // longitude __.DDDDD
diff --git a/movement/watch_faces/settings/nanosec_face.c b/movement/watch_faces/settings/nanosec_face.c
index 87f567d3..b9655c1d 100644
--- a/movement/watch_faces/settings/nanosec_face.c
+++ b/movement/watch_faces/settings/nanosec_face.c
@@ -249,6 +249,10 @@ static void value_increase(int16_t delta) {
break;
case 4: // Profile
nanosec_state.correction_profile = (nanosec_state.correction_profile + delta) % nanosec_profile_count;
+ // if ALARM decreases profile below 0, roll back around
+ if (nanosec_state.correction_profile < 0) {
+ nanosec_state.correction_profile += nanosec_profile_count;
+ }
break;
case 5: // Cadence
switch (nanosec_state.correction_cadence) {
@@ -330,7 +334,11 @@ bool nanosec_face_loop(movement_event_t event, movement_settings_t *settings, vo
value_increase(-1);
break;
case EVENT_ALARM_LONG_PRESS:
- value_increase(-50);
+ if (nanosec_screen == 4) { // If we are in profile - still decrease by 1
+ value_increase(-1);
+ } else {
+ value_increase(-50);
+ }
break;
case EVENT_TIMEOUT:
// Your watch face will receive this event after a period of inactivity. If it makes sense to resign,
diff --git a/rules.mk b/rules.mk
index 2c4acec1..f04f3f46 100644
--- a/rules.mk
+++ b/rules.mk
@@ -18,6 +18,7 @@ $(BUILD)/$(BIN).html: $(OBJS)
@echo HTML $@
@$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ \
-s ASYNCIFY=1 \
+ -s EXPORTED_RUNTIME_METHODS=lengthBytesUTF8,printErr \
-s EXPORTED_FUNCTIONS=_main \
--shell-file=$(TOP)/watch-library/simulator/shell.html