Added Timeout; Ability to turn off LED and Sound; Added doublespeed mode. (#1)

* Check that color is valid

Instead of merely checking that COLOR is set, check that it is one of
RED, BLUE or GREEN

* Added ability to turn off sound and timer with modes

* Added enum for mode

---------

Co-authored-by: Wesley Ellis <tahnok@gmail.com>
This commit is contained in:
voloved 2024-07-07 19:23:31 -04:00 committed by GitHub
parent 9794f86430
commit 3eaf807590
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 119 additions and 11 deletions

View file

@ -213,6 +213,12 @@ ifndef COLOR
$(error Set the COLOR variable to RED, BLUE, or GREEN depending on what board you have.)
endif
COLOR_VALID := $(filter $(COLOR),RED BLUE GREEN)
ifeq ($(COLOR_VALID),)
$(error COLOR must be RED, BLUE, or GREEN)
endif
ifeq ($(COLOR), BLUE)
CFLAGS += -DWATCH_IS_BLUE_BOARD
endif

View file

@ -33,6 +33,10 @@
#endif
static char _simon_display_buf[12];
static uint8_t _timer;
static uint16_t _delay_beep;
static uint16_t _timeout;
static uint8_t _secSub;
static inline uint8_t _simon_get_rand_num(uint8_t num_values) {
#if __EMSCRIPTEN__
@ -55,7 +59,26 @@ static void _simon_not_playing_display(simon_state_t *state) {
_simon_clear_display(state);
sprintf(_simon_display_buf, "SI %d", state->best_score);
if (!state->soundOff)
watch_set_indicator(WATCH_INDICATOR_BELL);
else
watch_clear_indicator(WATCH_INDICATOR_BELL);
if (!state->lightOff)
watch_set_indicator(WATCH_INDICATOR_SIGNAL);
else
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
watch_display_string(_simon_display_buf, 0);
switch (state->mode)
{
case SIMON_MODE_EASY:
watch_display_string("E", 9);
break;
case SIMON_MODE_HARD:
watch_display_string("H", 9);
break;
default:
break;
}
}
static void _simon_reset(simon_state_t *state) {
@ -91,18 +114,30 @@ static void _simon_play_note(SimonNote note, simon_state_t *state, bool skip_res
_simon_display_note(note, state);
switch (note) {
case SIMON_LED_NOTE:
watch_set_led_yellow();
watch_buzzer_play_note(BUZZER_NOTE_D3, 300);
if (!state->lightOff) watch_set_led_yellow();
if (state->soundOff)
delay_ms(_delay_beep);
else
watch_buzzer_play_note(BUZZER_NOTE_D3, _delay_beep);
break;
case SIMON_MODE_NOTE:
watch_set_led_red();
watch_buzzer_play_note(BUZZER_NOTE_E4, 300);
if (!state->lightOff) watch_set_led_red();
if (state->soundOff)
delay_ms(_delay_beep);
else
watch_buzzer_play_note(BUZZER_NOTE_E4, _delay_beep);
break;
case SIMON_ALARM_NOTE:
watch_set_led_green();
watch_buzzer_play_note(BUZZER_NOTE_C3, 300);
if (!state->lightOff) watch_set_led_green();
if (state->soundOff)
delay_ms(_delay_beep);
else
watch_buzzer_play_note(BUZZER_NOTE_C3, _delay_beep);
break;
case SIMON_WRONG_NOTE:
if (state->soundOff)
delay_ms(800);
else
watch_buzzer_play_note(BUZZER_NOTE_A1, 800);
break;
}
@ -111,7 +146,7 @@ static void _simon_play_note(SimonNote note, simon_state_t *state, bool skip_res
if (note != SIMON_WRONG_NOTE) {
_simon_clear_display(state);
if (!skip_rest) {
watch_buzzer_play_note(BUZZER_NOTE_REST, 200);
watch_buzzer_play_note(BUZZER_NOTE_REST, (_delay_beep * 2)/3);
}
}
}
@ -134,6 +169,7 @@ static void _simon_listen(SimonNote note, simon_state_t *state) {
if (state->sequence[state->listen_index] == note) {
_simon_play_note(note, state, true);
state->listen_index++;
_timer = 0;
if (state->listen_index == state->sequence_length) {
state->playing_state = SIMON_READY_FOR_NEXT_NOTE;
@ -149,6 +185,22 @@ static void _simon_begin_listening(simon_state_t *state) {
state->listen_index = 0;
}
static void _simon_change_speed(simon_state_t *state){
switch (state->mode)
{
case SIMON_MODE_HARD:
_delay_beep = DELAY_FOR_TONE_MS / 2;
_secSub = SIMON_FACE_FREQUENCY / 2;
_timeout = (TIMER_MAX * SIMON_FACE_FREQUENCY) / 2;
break;
default:
_delay_beep = DELAY_FOR_TONE_MS;
_secSub = SIMON_FACE_FREQUENCY;
_timeout = TIMER_MAX * SIMON_FACE_FREQUENCY;
break;
}
}
void simon_face_setup(movement_settings_t *settings, uint8_t watch_face_index,
void **context_ptr) {
(void)settings;
@ -171,6 +223,10 @@ void simon_face_setup(movement_settings_t *settings, uint8_t watch_face_index,
void simon_face_activate(movement_settings_t *settings, void *context) {
(void) settings;
(void) context;
simon_state_t *state = (simon_state_t *)context;
_simon_change_speed(state);
movement_request_tick_frequency(SIMON_FACE_FREQUENCY);
_timer = 0;
}
bool simon_face_loop(movement_event_t event, movement_settings_t *settings,
@ -183,9 +239,16 @@ bool simon_face_loop(movement_event_t event, movement_settings_t *settings,
_simon_reset(state);
break;
case EVENT_TICK:
if (state->playing_state == SIMON_READY_FOR_NEXT_NOTE) {
_simon_setup_next_note(state);
} else if (state->playing_state == SIMON_TEACHING) {
if (state->playing_state == SIMON_LISTENING_BACK && state->mode != SIMON_MODE_EASY)
{
_timer++;
if(_timer >= (_timeout)){
_timer = 0;
_simon_play_note(SIMON_WRONG_NOTE, state, true);
_simon_reset(state);
}
}
else if (state->playing_state == SIMON_TEACHING && event.subsecond == 0) {
SimonNote note = state->sequence[state->teaching_index];
// if this is the final note in the sequence, don't play the rest to let
// the player jump in faster
@ -196,12 +259,32 @@ bool simon_face_loop(movement_event_t event, movement_settings_t *settings,
_simon_begin_listening(state);
}
}
else if (state->playing_state == SIMON_READY_FOR_NEXT_NOTE && (event.subsecond % _secSub) == 0) {
_timer = 0;
_simon_setup_next_note(state);
}
break;
case EVENT_LIGHT_BUTTON_DOWN:
break;
case EVENT_LIGHT_LONG_PRESS:
if (state->playing_state == SIMON_NOT_PLAYING) {
state->lightOff = !state->lightOff;
_simon_not_playing_display(state);
}
break;
case EVENT_ALARM_LONG_PRESS:
if (state->playing_state == SIMON_NOT_PLAYING) {
state->soundOff = !state->soundOff;
_simon_not_playing_display(state);
if (!state->soundOff)
watch_buzzer_play_note(BUZZER_NOTE_D3, _delay_beep);
}
break;
case EVENT_LIGHT_BUTTON_UP:
if (state->playing_state == SIMON_NOT_PLAYING) {
state->sequence_length = 0;
watch_clear_indicator(WATCH_INDICATOR_BELL);
watch_clear_indicator(WATCH_INDICATOR_SIGNAL);
_simon_setup_next_note(state);
} else if (state->playing_state == SIMON_LISTENING_BACK) {
_simon_listen(SIMON_LED_NOTE, state);
@ -226,6 +309,11 @@ bool simon_face_loop(movement_event_t event, movement_settings_t *settings,
if (state->playing_state == SIMON_LISTENING_BACK) {
_simon_listen(SIMON_ALARM_NOTE, state);
}
else if (state->playing_state == SIMON_NOT_PLAYING){
state->mode = (state->mode + 1) % SIMON_MODE_TOTAL;
_simon_change_speed(state);
_simon_not_playing_display(state);
}
break;
case EVENT_TIMEOUT:
movement_move_to_face(0);

View file

@ -71,12 +71,22 @@ typedef enum SimonPlayingState {
SIMON_READY_FOR_NEXT_NOTE
} SimonPlayingState;
typedef enum SimonMode {
SIMON_MODE_NORMAL = 0, // 5 Second timeout if nothing is input
SIMON_MODE_EASY, // There is no timeout in this mode
SIMON_MODE_HARD, // The speed of the teaching is doubled and th etimeout is halved
SIMON_MODE_TOTAL
} SimonMode;
typedef struct {
uint8_t best_score;
SimonNote sequence[MAX_SEQUENCE];
uint8_t sequence_length;
uint8_t teaching_index;
uint8_t listen_index;
bool soundOff;
bool lightOff;
uint8_t mode:6;
SimonPlayingState playing_state;
} simon_state_t;
@ -94,4 +104,8 @@ void simon_face_resign(movement_settings_t *settings, void *context);
NULL, \
})
#define TIMER_MAX 5
#define SIMON_FACE_FREQUENCY 8
#define DELAY_FOR_TONE_MS 300
#endif // SIMON_FACE_H_