Skip to content

Commit

Permalink
Merge branch 'next'
Browse files Browse the repository at this point in the history
This merge adds numerous individually small fixes and improvements
which amount to a sizeable set of features.

 - New hourly chime tunes added
 - Old hourly chime tunes improved
 - Display of characters on the stock LCD improved
 - Documentation improved
 - Simulator improved
 - Leap year handling improved
 - Months and their days sanity checked and fixed
 - More compile time configurable defaults added
 - Excessively exact time checks relaxed
 - Clock face indicators matched to original watch
 - Hardware interface issue fixed

The most significant new feature however is software debouncing.
The sensor watch now properly handles hardware switch bouncing,
making the button inputs much more precise at the cost of a small amount
of latency, greatly improving usability. Any watch faces which require
holding down buttons as part of their user interface, the pulsometer
for example, should see huge improvements in their usability.

 * 9c093f9 Merge PR #387 - configurable default birthdate/location
 * 879c48c Merge PR #417 - improve 24h only mode
 * db4097b Merge PR #426 - add temperature input to simulator
 * dea0566 Merge PR #428 - fix issues in sunrise/sunset
 * c8ca0d3 Merge PR #431 - fix wrong number of days in month
 * 95ca374 Merge PR #433 - fix clock face indicators
 * 663cd72 Merge PR #434 - fix leap years
 * a715265 Merge PR #437 - debouncing logic
 * c741332 Merge PR #439 - fix scheduled task misses
 * 657ff72 Merge PR #440 - fix countdown face issues
 * c8a87d3 Merge PR #441 - update documentation
 * dd04443 Merge PR #443 - improved t and y character display
 * 42dc151 Merge PR #447 - improve kim possible chime
 * fa0cdef Merge PR #450 - sync after enabling RTC
 * a67076f Merge PR #458 - add layla tune
 * 23c422b Merge PR #459 - add power rangers tune
 * a2e5417 Merge PR #461 - improve t/y special case docs

Tested-on-hardware-by: Alex Maestas <[email protected]>
Tested-on-hardware-by: Matheus Afonso Martins Moreira <[email protected]>
Tested-on-hardware-by: Wesley Ellis <[email protected]>
GitHub-Pull-Request: #460
  • Loading branch information
matheusmoreira committed Sep 3, 2024
2 parents 0194044 + a67076f commit d4bd10b
Show file tree
Hide file tree
Showing 21 changed files with 287 additions and 100 deletions.
9 changes: 3 additions & 6 deletions apps/beats-time/app.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <string.h>
#include <math.h>
#include "watch.h"
#include "watch_utility.h"

const int8_t UTC_OFFSET = 4; // set to your current UTC offset to see correct beats time
const uint8_t BEAT_REFRESH_FREQUENCY = 8;
Expand Down Expand Up @@ -203,7 +204,6 @@ void set_time_mode_handle_primary_button(void) {

void set_time_mode_handle_secondary_button(void) {
watch_date_time date_time = watch_rtc_get_date_time();
const uint8_t days_in_month[12] = {31, 28, 31, 30, 31, 30, 30, 31, 30, 31, 30, 31};

switch (application_state.page) {
case 0: // hour
Expand All @@ -224,13 +224,10 @@ void set_time_mode_handle_secondary_button(void) {
break;
case 5: // day
date_time.unit.day = date_time.unit.day + 1;
// can't set to the 29th on a leap year. if it's february 29, set to 11:59 on the 28th.
// and it should roll over.
if (date_time.unit.day > days_in_month[date_time.unit.month - 1]) {
date_time.unit.day = 1;
}
break;
}
if (date_time.unit.day > days_in_month(date_time.unit.month, date_time.unit.year + WATCH_RTC_REFERENCE_YEAR))
date_time.unit.day = 1;
watch_rtc_set_date_time(date_time);
}

Expand Down
123 changes: 109 additions & 14 deletions movement/movement.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@
*/

#define MOVEMENT_LONG_PRESS_TICKS 64
#define DEBOUNCE_TICKS_DOWN 0
#define DEBOUNCE_TICKS_UP 0
/*
DEBOUNCE_TICKS_DOWN and DEBOUNCE_TICKS_UP are in terms of fast_cb ticks after a button is pressed.
The logic is that pressed of a button are ignored until the cb_fast_tick function runs this variable amount of times.
Without modifying the code, the cb_fast_tick frequency is 128Hz, or 7.8125ms.
It is not suggested to set this value to one for debouncing, as the callback occurs asynchronously of the button's press,
meaning that if a button was pressed and 7ms passed since th elast time cb_fast_tick was called, then there will be only 812.5us
of debounce time.
*/

#include <stdio.h>
#include <string.h>
Expand Down Expand Up @@ -95,6 +105,31 @@
#define MOVEMENT_DEFAULT_LED_DURATION 1
#endif

// Default to no set location latitude
#ifndef MOVEMENT_DEFAULT_LATITUDE
#define MOVEMENT_DEFAULT_LATITUDE 0
#endif

// Default to no set location longitude
#ifndef MOVEMENT_DEFAULT_LONGITUDE
#define MOVEMENT_DEFAULT_LONGITUDE 0
#endif

// Default to no set birthdate year
#ifndef MOVEMENT_DEFAULT_BIRTHDATE_YEAR
#define MOVEMENT_DEFAULT_BIRTHDATE_YEAR 0
#endif

// Default to no set birthdate month
#ifndef MOVEMENT_DEFAULT_BIRTHDATE_MONTH
#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0
#endif

// Default to no set birthdate day
#ifndef MOVEMENT_DEFAULT_BIRTHDATE_DAY
#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
#endif

#if __EMSCRIPTEN__
#include <emscripten.h>
#endif
Expand Down Expand Up @@ -169,6 +204,9 @@ static inline void _movement_reset_inactivity_countdown(void) {
static inline void _movement_enable_fast_tick_if_needed(void) {
if (!movement_state.fast_tick_enabled) {
movement_state.fast_ticks = 0;
movement_state.debounce_ticks_light = 0;
movement_state.debounce_ticks_alarm = 0;
movement_state.debounce_ticks_mode = 0;
watch_rtc_register_periodic_callback(cb_fast_tick, 128);
movement_state.fast_tick_enabled = true;
}
Expand All @@ -177,6 +215,7 @@ static inline void _movement_enable_fast_tick_if_needed(void) {
static inline void _movement_disable_fast_tick_if_possible(void) {
if ((movement_state.light_ticks == -1) &&
(movement_state.alarm_ticks == -1) &&
((movement_state.debounce_ticks_light + movement_state.debounce_ticks_mode + movement_state.debounce_ticks_alarm) == 0) &&
((movement_state.light_down_timestamp + movement_state.mode_down_timestamp + movement_state.alarm_down_timestamp) == 0)) {
movement_state.fast_tick_enabled = false;
watch_rtc_disable_periodic_callback(128);
Expand All @@ -201,7 +240,7 @@ static void _movement_handle_scheduled_tasks(void) {

for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) {
if (scheduled_tasks[i].reg) {
if (scheduled_tasks[i].reg == date_time.reg) {
if (scheduled_tasks[i].reg <= date_time.reg) {
scheduled_tasks[i].reg = 0;
movement_event_t background_event = { EVENT_BACKGROUND_TASK, 0 };
watch_faces[i].loop(background_event, &movement_state.settings, watch_face_contexts[i]);
Expand Down Expand Up @@ -328,6 +367,14 @@ static void end_buzzing_and_disable_buzzer(void) {
watch_disable_buzzer();
}

static void set_initial_clock_mode(void) {
#ifdef CLOCK_FACE_24H_ONLY
movement_state.settings.bit.clock_mode_24h = true;
#else
movement_state.settings.bit.clock_mode_24h = MOVEMENT_DEFAULT_24H_MODE;
#endif
}

void movement_play_signal(void) {
void *maybe_disable_buzzer = end_buzzing_and_disable_buzzer;
if (watch_is_buzzer_or_led_enabled()) {
Expand Down Expand Up @@ -376,14 +423,18 @@ void app_init(void) {
#endif

memset(&movement_state, 0, sizeof(movement_state));

movement_state.settings.bit.clock_mode_24h = MOVEMENT_DEFAULT_24H_MODE;
set_initial_clock_mode();
movement_state.settings.bit.led_red_color = MOVEMENT_DEFAULT_RED_COLOR;
movement_state.settings.bit.led_green_color = MOVEMENT_DEFAULT_GREEN_COLOR;
movement_state.settings.bit.button_should_sound = MOVEMENT_DEFAULT_BUTTON_SOUND;
movement_state.settings.bit.to_interval = MOVEMENT_DEFAULT_TIMEOUT_INTERVAL;
movement_state.settings.bit.le_interval = MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL;
movement_state.settings.bit.led_duration = MOVEMENT_DEFAULT_LED_DURATION;
movement_state.location.bit.latitude = MOVEMENT_DEFAULT_LATITUDE;
movement_state.location.bit.longitude = MOVEMENT_DEFAULT_LONGITUDE;
movement_state.birthdate.bit.year = MOVEMENT_DEFAULT_BIRTHDATE_YEAR;
movement_state.birthdate.bit.month = MOVEMENT_DEFAULT_BIRTHDATE_MONTH;
movement_state.birthdate.bit.day = MOVEMENT_DEFAULT_BIRTHDATE_DAY;
movement_state.light_ticks = -1;
movement_state.alarm_ticks = -1;
movement_state.next_available_backup_register = 4;
Expand All @@ -406,10 +457,14 @@ void app_init(void) {

void app_wake_from_backup(void) {
movement_state.settings.reg = watch_get_backup_data(0);
movement_state.location.reg = watch_get_backup_data(1);
movement_state.birthdate.reg = watch_get_backup_data(2);
}

void app_setup(void) {
watch_store_backup_data(movement_state.settings.reg, 0);
watch_store_backup_data(movement_state.location.reg, 1);
watch_store_backup_data(movement_state.birthdate.reg, 2);

static bool is_first_launch = true;

Expand Down Expand Up @@ -462,6 +517,7 @@ void app_wake_from_standby(void) {

static void _sleep_mode_app_loop(void) {
movement_state.needs_wake = false;
movement_state.ignore_alarm_btn_after_sleep = true;
// as long as le_mode_ticks is -1 (i.e. we are in low energy mode), we wake up here, update the screen, and go right back to sleep.
while (movement_state.le_mode_ticks == -1) {
// we also have to handle background tasks here in the mini-runloop
Expand Down Expand Up @@ -627,29 +683,66 @@ static movement_event_type_t _figure_out_button_event(bool pin_level, movement_e
// now that that's out of the way, handle falling edge
uint16_t diff = movement_state.fast_ticks - *down_timestamp;
*down_timestamp = 0;
_movement_disable_fast_tick_if_possible();
// any press over a half second is considered a long press. Fire the long-up event
if (diff > MOVEMENT_LONG_PRESS_TICKS) return button_down_event_type + 3;
else return button_down_event_type + 1;
}
}

static movement_event_type_t btn_action(bool pin_level, int code, uint16_t *timestamp) {
_movement_reset_inactivity_countdown();
return _figure_out_button_event(pin_level, code, timestamp);
}

static void light_btn_action(bool pin_level) {
event.event_type = btn_action(pin_level, EVENT_LIGHT_BUTTON_DOWN, &movement_state.light_down_timestamp);
}

static void mode_btn_action(bool pin_level) {
event.event_type = btn_action(pin_level, EVENT_MODE_BUTTON_DOWN, &movement_state.mode_down_timestamp);
}

static void alarm_btn_action(bool pin_level) {
uint8_t event_type = btn_action(pin_level, EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp);
if (movement_state.ignore_alarm_btn_after_sleep){
if (event_type == EVENT_ALARM_BUTTON_UP || event_type == EVENT_ALARM_LONG_UP) movement_state.ignore_alarm_btn_after_sleep = false;
return;
}
event.event_type = event_type;
}

static void debounce_btn_press(uint8_t pin, uint8_t *debounce_ticks, uint16_t *down_timestamp, void (*function)(bool)) {
if (*debounce_ticks == 0) {
bool pin_level = watch_get_pin_level(pin);
function(pin_level);
*debounce_ticks = pin_level ? DEBOUNCE_TICKS_DOWN : DEBOUNCE_TICKS_UP;
if (*debounce_ticks != 0) _movement_enable_fast_tick_if_needed();
}
else
*down_timestamp = 0;
}

static void disable_if_needed(uint8_t *ticks) {
if (*ticks > 0 && --*ticks == 0)
_movement_disable_fast_tick_if_possible();
}

static void movement_disable_if_debounce_complete(void) {
disable_if_needed(&movement_state.debounce_ticks_light);
disable_if_needed(&movement_state.debounce_ticks_alarm);
disable_if_needed(&movement_state.debounce_ticks_mode);
}

void cb_light_btn_interrupt(void) {
bool pin_level = watch_get_pin_level(BTN_LIGHT);
_movement_reset_inactivity_countdown();
event.event_type = _figure_out_button_event(pin_level, EVENT_LIGHT_BUTTON_DOWN, &movement_state.light_down_timestamp);
debounce_btn_press(BTN_LIGHT, &movement_state.debounce_ticks_light, &movement_state.light_down_timestamp, light_btn_action);
}

void cb_mode_btn_interrupt(void) {
bool pin_level = watch_get_pin_level(BTN_MODE);
_movement_reset_inactivity_countdown();
event.event_type = _figure_out_button_event(pin_level, EVENT_MODE_BUTTON_DOWN, &movement_state.mode_down_timestamp);
debounce_btn_press(BTN_MODE, &movement_state.debounce_ticks_mode, &movement_state.mode_down_timestamp, mode_btn_action);
}

void cb_alarm_btn_interrupt(void) {
bool pin_level = watch_get_pin_level(BTN_ALARM);
_movement_reset_inactivity_countdown();
event.event_type = _figure_out_button_event(pin_level, EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp);
debounce_btn_press(BTN_ALARM, &movement_state.debounce_ticks_alarm, &movement_state.alarm_down_timestamp, alarm_btn_action);
}

void cb_alarm_btn_extwake(void) {
Expand All @@ -662,7 +755,9 @@ void cb_alarm_fired(void) {
}

void cb_fast_tick(void) {
movement_state.fast_ticks++;
movement_disable_if_debounce_complete();
if (movement_state.debounce_ticks_light + movement_state.debounce_ticks_mode + movement_state.debounce_ticks_alarm == 0)
movement_state.fast_ticks++;
if (movement_state.light_ticks > 0) movement_state.light_ticks--;
if (movement_state.alarm_ticks > 0) movement_state.alarm_ticks--;
// check timestamps and auto-fire the long-press events
Expand Down
6 changes: 6 additions & 0 deletions movement/movement.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ typedef struct {
typedef struct {
// properties stored in BACKUP register
movement_settings_t settings;
movement_location_t location;
movement_birthdate_t birthdate;

// transient properties
int16_t current_face_idx;
Expand Down Expand Up @@ -270,6 +272,10 @@ typedef struct {

// low energy mode countdown
int32_t le_mode_ticks;
uint8_t debounce_ticks_light;
uint8_t debounce_ticks_alarm;
uint8_t debounce_ticks_mode;
bool ignore_alarm_btn_after_sleep;

// app resignation countdown (TODO: consolidate with LE countdown?)
int16_t timeout_ticks;
Expand Down
30 changes: 23 additions & 7 deletions movement/movement_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ const watch_face_t watch_faces[] = {
/* Set the timeout before switching to low energy mode
* Valid values are:
* 0: Never
* 1: 1 hour
* 2: 2 hours
* 3: 6 hours
* 4: 12 hours
* 5: 1 day
* 6: 2 days
* 1: 10 mins
* 2: 1 hour
* 3: 2 hours
* 4: 6 hours
* 5: 12 hours
* 6: 1 day
* 7: 7 days
*/
#define MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL 1
#define MOVEMENT_DEFAULT_LOW_ENERGY_INTERVAL 2

/* Set the led duration
* Valid values are:
Expand All @@ -95,4 +95,20 @@ const watch_face_t watch_faces[] = {
*/
#define MOVEMENT_DEFAULT_LED_DURATION 1

/* The latitude and longitude used for the wearers location
* Set signed values in 1/100ths of a degree
*/
#define MOVEMENT_DEFAULT_LATITUDE 0
#define MOVEMENT_DEFAULT_LONGITUDE 0

/* The wearers birthdate
* Valid values:
* Year: 1 - 4095
* Month: 1 - 12
* Day: 1 - 31
*/
#define MOVEMENT_DEFAULT_BIRTHDATE_YEAR 0
#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0
#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0

#endif // MOVEMENT_CONFIG_H_
43 changes: 39 additions & 4 deletions movement/movement_custom_signal_tunes.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,53 @@ int8_t signal_tune[] = {
#ifdef SIGNAL_TUNE_KIM_POSSIBLE
int8_t signal_tune[] = {
BUZZER_NOTE_G7, 6,
BUZZER_NOTE_REST, 1,
BUZZER_NOTE_G4, 3,
BUZZER_NOTE_G4, 2,
BUZZER_NOTE_REST, 5,
BUZZER_NOTE_G7, 6,
BUZZER_NOTE_REST, 1,
BUZZER_NOTE_G4, 3,
BUZZER_NOTE_G4, 2,
BUZZER_NOTE_REST, 5,
BUZZER_NOTE_A7SHARP_B7FLAT, 6,
BUZZER_NOTE_REST, 2,
BUZZER_NOTE_G7, 6,
BUZZER_NOTE_G4, 2,
0
};
#endif // SIGNAL_TUNE_KIM_POSSIBLE

#ifdef SIGNAL_TUNE_POWER_RANGERS
int8_t signal_tune[] = {
BUZZER_NOTE_D8, 6,
BUZZER_NOTE_REST, 8,
BUZZER_NOTE_D8, 6,
BUZZER_NOTE_REST, 8,
BUZZER_NOTE_C8, 6,
BUZZER_NOTE_REST, 2,
BUZZER_NOTE_D8, 6,
BUZZER_NOTE_REST, 8,
BUZZER_NOTE_F8, 6,
BUZZER_NOTE_REST, 8,
BUZZER_NOTE_D8, 6,
0
};
#endif // SIGNAL_TUNE_POWER_RANGERS

#ifdef SIGNAL_TUNE_LAYLA
int8_t signal_tune[] = {
BUZZER_NOTE_A6, 5,
BUZZER_NOTE_REST, 1,
BUZZER_NOTE_C7, 5,
BUZZER_NOTE_REST, 1,
BUZZER_NOTE_D7, 5,
BUZZER_NOTE_REST, 1,
BUZZER_NOTE_F7, 5,
BUZZER_NOTE_REST, 1,
BUZZER_NOTE_D7, 5,
BUZZER_NOTE_REST, 1,
BUZZER_NOTE_C7, 5,
BUZZER_NOTE_REST, 1,
BUZZER_NOTE_D7, 20,
0
};
#endif // SIGNAL_TUNE_LAYLA

#endif // MOVEMENT_CUSTOM_SIGNAL_TUNES_H_
Loading

0 comments on commit d4bd10b

Please sign in to comment.