From 3588e2fd1d883de53190a5039e952b0c447e936c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 17 Sep 2017 01:59:51 +0200 Subject: [PATCH] added idle rainbow effect --- game.c | 302 ++++++++++++++++++++++++++++++++++++++++++++------------- game.h | 2 +- leds.c | 9 +- leds.h | 2 + main.c | 8 +- 5 files changed, 246 insertions(+), 77 deletions(-) diff --git a/game.c b/game.c index 89044be..675d290 100644 --- a/game.c +++ b/game.c @@ -25,16 +25,21 @@ #define C_OKGREEN rgb24(20,160,0) #define C_CRIMSON rgb24(220,0,5) -#define C_DIMRED rgb24(100,0,0) -#define C_DIMGREEN rgb24(0,100,0) -#define C_DIMBLUE rgb24(0,0,80) -#define C_DIMYELLOW rgb24(50,45,0) +//#define C_DIMRED rgb24(100,0,0) +//#define C_DIMGREEN rgb24(0,100,0) +//#define C_DIMBLUE rgb24(0,0,80) +//#define C_DIMYELLOW rgb24(50,45,0) #define C_BRTRED rgb24(255,0,0) #define C_BRTGREEN rgb24(0,255,0) #define C_BRTBLUE rgb24(0,0,255) #define C_BRTYELLOW rgb24(127,110,0) +#define C_DIMRED rgb24((int)(255*0.3),0,0) +#define C_DIMGREEN rgb24(0,(int)(255*0.3),0) +#define C_DIMBLUE rgb24(0,0,(int)(255*0.3)) +#define C_DIMYELLOW rgb24((int)(127*0.3),(int)(110*0.3),0) + // assign to positions #define C_DIM1 C_DIMRED @@ -51,6 +56,7 @@ enum GameState_enum { STATE_NEW_GAME, // new game, waiting for key + STATE_GAME_STARTING, // new game, initial anim STATE_REPLAY, // showing sequence STATE_USER_INPUT, // waiting for user input of repeated sequence STATE_SUCCESS_EFFECT, // entered OK, show some fireworks @@ -58,48 +64,95 @@ enum GameState_enum { }; /** Current game state */ -enum GameState_enum GameState = STATE_NEW_GAME; +static enum GameState_enum GameState = STATE_NEW_GAME; -volatile bool holding_new_game_button = false; +static volatile bool holding_new_game_button = false; /** Screen colors */ -uint32_t screen[4] = {0, 0, 0, 0}; -const uint32_t brt[4] = {C_BRT1, C_BRT2, C_BRT3, C_BRT4}; -const uint32_t dim[4] = {C_DIM1, C_DIM2, C_DIM3, C_DIM4}; -const uint32_t dark[4] = {C_DARK, C_DARK, C_DARK, C_DARK}; -const uint32_t dimwhite[4] = {C_DIMWHITE, C_DIMWHITE, C_DIMWHITE, C_DIMWHITE}; +static uint32_t screen[4] = {0, 0, 0, 0}; + +static const uint32_t brt[4] = {C_BRT1, C_BRT2, C_BRT3, C_BRT4}; +static const uint32_t dim[4] = {C_DIM1, C_DIM2, C_DIM3, C_DIM4}; +//static const uint32_t dark[4] = {C_DARK, C_DARK, C_DARK, C_DARK}; +//static const uint32_t dimwhite[4] = {C_DIMWHITE, C_DIMWHITE, C_DIMWHITE, C_DIMWHITE}; #define REPLAY_INTERVAL 400 #define REPLAY_INTERVAL_GAP 75 #define SUC_EFF_TIME 500 -#define FAIL_EFF_TIME 1000 /** Nr of revealed colors in sequence */ -uint8_t game_revealed_n; +static uint8_t game_revealed_n; /** Nr of next color to replay/input */ -uint8_t game_replay_n; +static uint8_t game_replay_n; /** Nr of succ repeated colors */ -uint8_t game_repeat_n; +static uint8_t game_repeat_n; + +static void enterGameState(enum GameState_enum state); -void enter_state(enum GameState_enum state); +void idle_anim_init(void); + +void idle_anim(void) +; /** Show current screen colors */ -void show_screen() +static void show_screen() { leds_set(screen); } /** Enter state - callback for delayed state change */ -void deferred_enter_state(void *state) +static void delayedEnterStateCb(void *state) { // clear flag that button was held holding_new_game_button = false; - enter_state((enum GameState_enum) state); + enterGameState((enum GameState_enum) state); +} + +/** starting a game */ +static void newGameCountdownCb(void *state) +{ + int steps = (int) state; + + if (steps == 0) { + display_show(0,0); // clear all + // start playback with a delay + // this makes it obvious the playback is not a feedback to the pressed button + schedule_task(delayedEnterStateCb, (void *) STATE_REPLAY, 500, false); + return; + } + + display_show_number((uint8_t) steps); + steps--; + + schedule_task(newGameCountdownCb, (void *) steps, 500, false); + +} + +static void fadeInColorsCb(void *stepsToGo) +{ + uint8_t steps = (uint8_t) (int) stepsToGo; + if (led_brightness_mul < 255) led_brightness_mul += 1; + + steps -= 1; + if (steps>0) { + schedule_task(fadeInColorsCb, (void *) (int) steps, 1, false); + } +} + +static void fadeOutColorsCb(void *stepsToGo) +{ + uint8_t steps = (uint8_t) (int) stepsToGo; + if (led_brightness_mul > 0) led_brightness_mul -= 1; + + steps -= 1; + if (steps>0) { + schedule_task(fadeOutColorsCb, (void *) (int) steps, 1, false); + } } /** Future task CB in replay seq */ -void replay_callback(void *onOff) +static void replaySequenceCb(void *onOff) { bool on = (bool) onOff; @@ -113,61 +166,82 @@ void replay_callback(void *onOff) game_replay_n++; screen[color] = brt[color]; show_screen(); - schedule_task(replay_callback, (void *) 0, REPLAY_INTERVAL, false); + schedule_task(replaySequenceCb, (void *) 0, REPLAY_INTERVAL, false); } else { // turning off show_screen(); // Schedule next turning ON if (game_replay_n < game_revealed_n) { - schedule_task(replay_callback, (void *) 1, REPLAY_INTERVAL_GAP, false); + schedule_task(replaySequenceCb, (void *) 1, REPLAY_INTERVAL_GAP, false); } else { - enter_state(STATE_USER_INPUT); - //schedule_task(deferred_enter_state, (void *) STATE_USER_INPUT, 50, false); + // fade in the input hints + led_brightness_mul = 0; + memcpy(screen, dim, sizeof(screen)); + show_screen(); + enterGameState(STATE_USER_INPUT); + + schedule_task(fadeInColorsCb, (void *) 255, 1, false); } } } /** SUCCESS effect */ -void suc_eff_callback(void *onOff) +static void successEffectCb(void *onOff) { bool on = (bool) onOff; if (on) { display_show_number(game_revealed_n-1); for (uint8_t i = 0; i < 4; i++) screen[i] = C_OKGREEN; - schedule_task(suc_eff_callback, 0, SUC_EFF_TIME, false); + schedule_task(successEffectCb, 0, SUC_EFF_TIME, false); } else { for (uint8_t i = 0; i < 4; i++) screen[i] = C_DARK; - schedule_task(deferred_enter_state, (void *) STATE_REPLAY, 250, false); + schedule_task(delayedEnterStateCb, (void *) STATE_REPLAY, 250, false); } show_screen(); } /** ERROR effect */ -void fail_eff_callback(void *onOff) +static void failEffectCb(void *cnt) { - bool on = (bool) onOff; + int cn = (int)cnt; + bool on = ((cn)%2)==1; if (on) { for (int i = 0; i < 4; i++) screen[i] = C_CRIMSON; - schedule_task(fail_eff_callback, 0, FAIL_EFF_TIME, false); } else { for (int i = 0; i < 4; i++) screen[i] = C_DARK; - - schedule_task(deferred_enter_state, (void *) STATE_NEW_GAME, 250, false); } show_screen(); + + cn--; + if (cn == 0) { + schedule_task(delayedEnterStateCb, (void *) STATE_NEW_GAME, 250, false); + } else { + schedule_task(failEffectCb, (void *) cn, 250, false); + } +} + +/** Prepare new sequence, using time for seed. */ +static void prepareNewSequence() +{ + rng_set_seed(time_ms); + rng_restart(); } +task_pid_t fadeout_pid; +task_pid_t fadein_pid; + +volatile bool first_start = true; /** * @brief Enter a game state * @param state */ -void enter_state(enum GameState_enum state) +static void enterGameState(enum GameState_enum state) { GameState = state; @@ -176,22 +250,41 @@ void enter_state(enum GameState_enum state) usart_puts("State: new game\r\n"); // new game - idle state before new game is started - // all dimly lit - for (int i = 0; i < 4; i++) screen[i] = 0; //C_DIMWHITE + for (int i = 0; i < 4; i++) screen[i] = 0; + led_brightness_mul = 0; // fading in... + + if (first_start) { + abort_scheduled_task(fadeout_pid); + fadein_pid = schedule_task(fadeInColorsCb, (void *) 255, 1, false); + } + break; + + case STATE_GAME_STARTING: + first_start = false; + + usart_puts("game begins\r\n"); + // user wants to start playing + prepareNewSequence(); + game_revealed_n = 1; // start with 1 revealed + + abort_scheduled_task(fadein_pid); + fadeout_pid = schedule_task(fadeOutColorsCb, (void *) 255, 1, false); + schedule_task(newGameCountdownCb, (void *) 5, 500, false); break; case STATE_REPLAY: + led_brightness_mul = 255; usart_puts("State: replay\r\n"); game_replay_n = 0; rng_restart(); + display_show_number(game_revealed_n-1); // Start replay - replay_callback((void *) 1); + replaySequenceCb((void *) 1); break; case STATE_USER_INPUT: usart_puts("State: repeat\r\n"); - memcpy(screen, dim, sizeof(screen)); // Start entering & checking game_repeat_n = 0; @@ -202,43 +295,44 @@ void enter_state(enum GameState_enum state) usart_puts("State: succ\r\n"); memcpy(screen, dim, sizeof(screen)); //suc_eff_callback((void *) 1); - schedule_task(suc_eff_callback, (void *) 1, 250, false); + schedule_task(successEffectCb, (void *) 1, 250, false); break; case STATE_FAIL_EFFECT: usart_puts("State: fail\r\n"); memcpy(screen, dim, sizeof(screen)); //fail_eff_callback((void *) 1); - schedule_task(fail_eff_callback, (void *) 1, 250, false); + schedule_task(failEffectCb, (void *) 5, 250, false); break; } show_screen(); } -/** Prepare new sequence, using time for seed. */ -void prepare_sequence() -{ - rng_set_seed(time_ms); - rng_restart(); -} - volatile uint16_t idle_cnt = 0; + + /** game main function */ void game_main(void) { - display_show(SEG_G, SEG_G); // two dashes... - enter_state(STATE_NEW_GAME); + idle_anim_init(); + + display_show(0, 0); + enterGameState(STATE_NEW_GAME); // we'll init the sequence when user first presses a button - the time is used as a seed enum GameState_enum last_state = STATE_NEW_GAME; while (1) { if (GameState == last_state) { if (GameState == STATE_NEW_GAME) { - if (idle_cnt == 25 && !holding_new_game_button) { + if (!first_start && idle_cnt == 50 && !holding_new_game_button) { usart_puts("clear highscore display\r\n"); - display_show(SEG_G, SEG_G); + display_show(0, 0); + + // start fading + abort_scheduled_task(fadeout_pid); + fadein_pid = schedule_task(fadeInColorsCb, (void *) 255, 1, false); } if (idle_cnt == 3000) { @@ -256,9 +350,9 @@ void game_main(void) if (idle_cnt > 200) { // reset state usart_puts("game reset, user walked away\r\n"); - enter_state(STATE_NEW_GAME); + enterGameState(STATE_NEW_GAME); show_screen(); - display_show(SEG_G, SEG_G); + display_show(0, 0); idle_cnt = 0; } } @@ -268,7 +362,14 @@ void game_main(void) } idle_cnt++; - delay_ms(100); + + for (int i = 0; i < 10; i++) { + delay_ms(10); + if (GameState == STATE_NEW_GAME) { + // we can do some idle animation here + idle_anim(); + } + } } } @@ -277,7 +378,7 @@ void game_main(void) * @param button: button identifier * @param press: press state (1 = just pressed, 0 = just released) */ -void game_button_handler(uint8_t button, bool press) +void onGameButton(uint8_t button, bool press) { // convert to 0-3 button--; @@ -287,24 +388,12 @@ void game_button_handler(uint8_t button, bool press) if (press) { usart_puts("pressed a new-game button\r\n"); // feedback - display_show_number(0); // show 0 + display_show(SEG_D|SEG_E|SEG_F|SEG_A, SEG_A|SEG_B|SEG_C|SEG_D); holding_new_game_button = true; } if (!press) { // released - usart_puts("game begins\r\n"); - // user wants to start playing - prepare_sequence(); - game_revealed_n = 1; // start with 1 revealed - - // darken - //memcpy(screen, dark, sizeof(screen)); - //show_screen(); - - // start playback with a delay - // this makes it obvious the playback is not a feedback to the pressed button - schedule_task(deferred_enter_state, (void *) STATE_REPLAY, 500, false); - //enter_state(STATE_REPLAY); + enterGameState(STATE_GAME_STARTING); } break; @@ -328,11 +417,11 @@ void game_button_handler(uint8_t button, bool press) if (game_repeat_n == game_revealed_n) { usart_puts("repeated all, good work!\r\n"); game_revealed_n++; - enter_state(STATE_SUCCESS_EFFECT); + enterGameState(STATE_SUCCESS_EFFECT); } } else { usart_puts("oops bad key\r\n"); - enter_state(STATE_FAIL_EFFECT); + enterGameState(STATE_FAIL_EFFECT); } } @@ -344,3 +433,76 @@ void game_button_handler(uint8_t button, bool press) break; } } + +// ------------------------------- + +const uint8_t anim_step = 1; +const uint8_t anim_max = 60; + +xrgb_t color1; +uint8_t step1; +xrgb_t color2; +uint8_t step2; +xrgb_t color3; +uint8_t step3; +xrgb_t color4; +uint8_t step4; + +void idle_anim_init(void) +{ + color1 = xrgb(anim_max, 0, 0); + step1 = 0; + + color2 = xrgb(anim_max, anim_max, 0); + step2 = 1; + + color3 = xrgb(0, anim_max, 0); + step3 = 2; + + color4 = xrgb(0, anim_max, anim_max); + step4 = 3; +} + +void idle_anim_oneled(xrgb_t *color, uint8_t *step) +{ + switch (*step) { + case 0: + color->g += anim_step; + if (color->g >= anim_max) *step = *step + 1; + break; + case 1: + color->r -= anim_step; + if (color->r == 0) *step = *step + 1; + break; + case 2: + color->b += anim_step; + if (color->b >= anim_max) *step = *step + 1; + break; + case 3: + color->g -= anim_step; + if (color->g == 0) *step = *step + 1; + break; + case 4: + color->r += anim_step; + if (color->r >= anim_max) *step = *step + 1; + break; + default: + color->b -= anim_step; + if (color->b == 0) *step = 0; + break; + } +} + + +void idle_anim(void) +{ + idle_anim_oneled(&color1, &step1); + idle_anim_oneled(&color2, &step2); + idle_anim_oneled(&color3, &step3); + idle_anim_oneled(&color4, &step4); + screen[0] = xrgb_rgb24(color1); + screen[1] = xrgb_rgb24(color2); + screen[2] = xrgb_rgb24(color3); + screen[3] = xrgb_rgb24(color4); + show_screen(); +} diff --git a/game.h b/game.h index 5a3faf7..d7afb86 100644 --- a/game.h +++ b/game.h @@ -19,6 +19,6 @@ void game_main(void); * @param button * @param press */ -void game_button_handler(uint8_t button, bool press); +void onGameButton(uint8_t button, bool press); #endif //FIRMWARE_GAME_H diff --git a/leds.c b/leds.c index 722456c..af2eb27 100644 --- a/leds.c +++ b/leds.c @@ -11,6 +11,7 @@ #include "leds.h" +volatile uint8_t led_brightness_mul = 255; static uint32_t leds[4]; void leds_set(const uint32_t *new_leds) @@ -21,9 +22,13 @@ void leds_set(const uint32_t *new_leds) void leds_show(void) { xrgb_t arr[4]; + + float db = (float)disp_brightness / 255.0f; + if (db < 0.10) db = 0.10; + float db2 = (float)led_brightness_mul / 255.0f; + db *= db2; + for (uint8_t i = 0; i < 4; i++) { - float db = (float)disp_brightness / 255.0f; - if (db < 0.15) db = 0.15; arr[i].r = (uint8_t) ((float)rgb24_r(leds[i]) * db); arr[i].g = (uint8_t) ((float)rgb24_g(leds[i]) * db); arr[i].b = (uint8_t) ((float)rgb24_b(leds[i]) * db); diff --git a/leds.h b/leds.h index e5e39b9..0405d14 100644 --- a/leds.h +++ b/leds.h @@ -7,6 +7,8 @@ #include +extern volatile uint8_t led_brightness_mul; + /** * Set led colors * diff --git a/main.c b/main.c index c4489ea..1b8159a 100644 --- a/main.c +++ b/main.c @@ -70,10 +70,10 @@ void setup_debouncer(void) { // Debouncer config debo_add(PIN_PWR_KEY, key_cb_power); - debo_add(PIN_KEY_1, game_button_handler); - debo_add(PIN_KEY_2, game_button_handler); - debo_add(PIN_KEY_3, game_button_handler); - debo_add(PIN_KEY_4, game_button_handler); + debo_add(PIN_KEY_1, onGameButton); + debo_add(PIN_KEY_2, onGameButton); + debo_add(PIN_KEY_3, onGameButton); + debo_add(PIN_KEY_4, onGameButton); // Timer 1 - CTC, to 16000 (1 ms interrupt) OCR1A = 16000;