added idle rainbow effect

master
Ondřej Hruška 7 years ago
parent b69004db7b
commit 3588e2fd1d
  1. 302
      game.c
  2. 2
      game.h
  3. 9
      leds.c
  4. 2
      leds.h
  5. 8
      main.c

302
game.c

@ -25,16 +25,21 @@
#define C_OKGREEN rgb24(20,160,0) #define C_OKGREEN rgb24(20,160,0)
#define C_CRIMSON rgb24(220,0,5) #define C_CRIMSON rgb24(220,0,5)
#define C_DIMRED rgb24(100,0,0) //#define C_DIMRED rgb24(100,0,0)
#define C_DIMGREEN rgb24(0,100,0) //#define C_DIMGREEN rgb24(0,100,0)
#define C_DIMBLUE rgb24(0,0,80) //#define C_DIMBLUE rgb24(0,0,80)
#define C_DIMYELLOW rgb24(50,45,0) //#define C_DIMYELLOW rgb24(50,45,0)
#define C_BRTRED rgb24(255,0,0) #define C_BRTRED rgb24(255,0,0)
#define C_BRTGREEN rgb24(0,255,0) #define C_BRTGREEN rgb24(0,255,0)
#define C_BRTBLUE rgb24(0,0,255) #define C_BRTBLUE rgb24(0,0,255)
#define C_BRTYELLOW rgb24(127,110,0) #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 // assign to positions
#define C_DIM1 C_DIMRED #define C_DIM1 C_DIMRED
@ -51,6 +56,7 @@
enum GameState_enum { enum GameState_enum {
STATE_NEW_GAME, // new game, waiting for key STATE_NEW_GAME, // new game, waiting for key
STATE_GAME_STARTING, // new game, initial anim
STATE_REPLAY, // showing sequence STATE_REPLAY, // showing sequence
STATE_USER_INPUT, // waiting for user input of repeated sequence STATE_USER_INPUT, // waiting for user input of repeated sequence
STATE_SUCCESS_EFFECT, // entered OK, show some fireworks STATE_SUCCESS_EFFECT, // entered OK, show some fireworks
@ -58,48 +64,95 @@ enum GameState_enum {
}; };
/** Current game state */ /** 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 */ /** Screen colors */
uint32_t screen[4] = {0, 0, 0, 0}; static 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}; static const uint32_t brt[4] = {C_BRT1, C_BRT2, C_BRT3, C_BRT4};
const uint32_t dark[4] = {C_DARK, C_DARK, C_DARK, C_DARK}; static const uint32_t dim[4] = {C_DIM1, C_DIM2, C_DIM3, C_DIM4};
const uint32_t dimwhite[4] = {C_DIMWHITE, C_DIMWHITE, C_DIMWHITE, C_DIMWHITE}; //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 400
#define REPLAY_INTERVAL_GAP 75 #define REPLAY_INTERVAL_GAP 75
#define SUC_EFF_TIME 500 #define SUC_EFF_TIME 500
#define FAIL_EFF_TIME 1000
/** Nr of revealed colors in sequence */ /** Nr of revealed colors in sequence */
uint8_t game_revealed_n; static uint8_t game_revealed_n;
/** Nr of next color to replay/input */ /** Nr of next color to replay/input */
uint8_t game_replay_n; static uint8_t game_replay_n;
/** Nr of succ repeated colors */ /** 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 */ /** Show current screen colors */
void show_screen() static void show_screen()
{ {
leds_set(screen); leds_set(screen);
} }
/** Enter state - callback for delayed state change */ /** Enter state - callback for delayed state change */
void deferred_enter_state(void *state) static void delayedEnterStateCb(void *state)
{ {
// clear flag that button was held // clear flag that button was held
holding_new_game_button = false; 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 */ /** Future task CB in replay seq */
void replay_callback(void *onOff) static void replaySequenceCb(void *onOff)
{ {
bool on = (bool) onOff; bool on = (bool) onOff;
@ -113,61 +166,82 @@ void replay_callback(void *onOff)
game_replay_n++; game_replay_n++;
screen[color] = brt[color]; screen[color] = brt[color];
show_screen(); show_screen();
schedule_task(replay_callback, (void *) 0, REPLAY_INTERVAL, false); schedule_task(replaySequenceCb, (void *) 0, REPLAY_INTERVAL, false);
} else { } else {
// turning off // turning off
show_screen(); show_screen();
// Schedule next turning ON // Schedule next turning ON
if (game_replay_n < game_revealed_n) { 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 { } else {
enter_state(STATE_USER_INPUT); // fade in the input hints
//schedule_task(deferred_enter_state, (void *) STATE_USER_INPUT, 50, false); led_brightness_mul = 0;
memcpy(screen, dim, sizeof(screen));
show_screen();
enterGameState(STATE_USER_INPUT);
schedule_task(fadeInColorsCb, (void *) 255, 1, false);
} }
} }
} }
/** SUCCESS effect */ /** SUCCESS effect */
void suc_eff_callback(void *onOff) static void successEffectCb(void *onOff)
{ {
bool on = (bool) onOff; bool on = (bool) onOff;
if (on) { if (on) {
display_show_number(game_revealed_n-1); display_show_number(game_revealed_n-1);
for (uint8_t i = 0; i < 4; i++) screen[i] = C_OKGREEN; 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 { } else {
for (uint8_t i = 0; i < 4; i++) screen[i] = C_DARK; 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(); show_screen();
} }
/** ERROR effect */ /** 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) { if (on) {
for (int i = 0; i < 4; i++) screen[i] = C_CRIMSON; for (int i = 0; i < 4; i++) screen[i] = C_CRIMSON;
schedule_task(fail_eff_callback, 0, FAIL_EFF_TIME, false);
} else { } else {
for (int i = 0; i < 4; i++) screen[i] = C_DARK; for (int i = 0; i < 4; i++) screen[i] = C_DARK;
schedule_task(deferred_enter_state, (void *) STATE_NEW_GAME, 250, false);
} }
show_screen(); 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 * @brief Enter a game state
* @param state * @param state
*/ */
void enter_state(enum GameState_enum state) static void enterGameState(enum GameState_enum state)
{ {
GameState = state; GameState = state;
@ -176,22 +250,41 @@ void enter_state(enum GameState_enum state)
usart_puts("State: new game\r\n"); usart_puts("State: new game\r\n");
// new game - idle state before new game is started // new game - idle state before new game is started
// all dimly lit for (int i = 0; i < 4; i++) screen[i] = 0;
for (int i = 0; i < 4; i++) screen[i] = 0; //C_DIMWHITE 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; break;
case STATE_REPLAY: case STATE_REPLAY:
led_brightness_mul = 255;
usart_puts("State: replay\r\n"); usart_puts("State: replay\r\n");
game_replay_n = 0; game_replay_n = 0;
rng_restart(); rng_restart();
display_show_number(game_revealed_n-1);
// Start replay // Start replay
replay_callback((void *) 1); replaySequenceCb((void *) 1);
break; break;
case STATE_USER_INPUT: case STATE_USER_INPUT:
usart_puts("State: repeat\r\n"); usart_puts("State: repeat\r\n");
memcpy(screen, dim, sizeof(screen));
// Start entering & checking // Start entering & checking
game_repeat_n = 0; game_repeat_n = 0;
@ -202,43 +295,44 @@ void enter_state(enum GameState_enum state)
usart_puts("State: succ\r\n"); usart_puts("State: succ\r\n");
memcpy(screen, dim, sizeof(screen)); memcpy(screen, dim, sizeof(screen));
//suc_eff_callback((void *) 1); //suc_eff_callback((void *) 1);
schedule_task(suc_eff_callback, (void *) 1, 250, false); schedule_task(successEffectCb, (void *) 1, 250, false);
break; break;
case STATE_FAIL_EFFECT: case STATE_FAIL_EFFECT:
usart_puts("State: fail\r\n"); usart_puts("State: fail\r\n");
memcpy(screen, dim, sizeof(screen)); memcpy(screen, dim, sizeof(screen));
//fail_eff_callback((void *) 1); //fail_eff_callback((void *) 1);
schedule_task(fail_eff_callback, (void *) 1, 250, false); schedule_task(failEffectCb, (void *) 5, 250, false);
break; break;
} }
show_screen(); 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; volatile uint16_t idle_cnt = 0;
/** game main function */ /** game main function */
void game_main(void) void game_main(void)
{ {
display_show(SEG_G, SEG_G); // two dashes... idle_anim_init();
enter_state(STATE_NEW_GAME);
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 // 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; enum GameState_enum last_state = STATE_NEW_GAME;
while (1) { while (1) {
if (GameState == last_state) { if (GameState == last_state) {
if (GameState == STATE_NEW_GAME) { 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"); 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) { if (idle_cnt == 3000) {
@ -256,9 +350,9 @@ void game_main(void)
if (idle_cnt > 200) { if (idle_cnt > 200) {
// reset state // reset state
usart_puts("game reset, user walked away\r\n"); usart_puts("game reset, user walked away\r\n");
enter_state(STATE_NEW_GAME); enterGameState(STATE_NEW_GAME);
show_screen(); show_screen();
display_show(SEG_G, SEG_G); display_show(0, 0);
idle_cnt = 0; idle_cnt = 0;
} }
} }
@ -268,7 +362,14 @@ void game_main(void)
} }
idle_cnt++; 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 button: button identifier
* @param press: press state (1 = just pressed, 0 = just released) * @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 // convert to 0-3
button--; button--;
@ -287,24 +388,12 @@ void game_button_handler(uint8_t button, bool press)
if (press) { if (press) {
usart_puts("pressed a new-game button\r\n"); usart_puts("pressed a new-game button\r\n");
// feedback // 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; holding_new_game_button = true;
} }
if (!press) { // released if (!press) { // released
usart_puts("game begins\r\n"); enterGameState(STATE_GAME_STARTING);
// 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);
} }
break; break;
@ -328,11 +417,11 @@ void game_button_handler(uint8_t button, bool press)
if (game_repeat_n == game_revealed_n) { if (game_repeat_n == game_revealed_n) {
usart_puts("repeated all, good work!\r\n"); usart_puts("repeated all, good work!\r\n");
game_revealed_n++; game_revealed_n++;
enter_state(STATE_SUCCESS_EFFECT); enterGameState(STATE_SUCCESS_EFFECT);
} }
} else { } else {
usart_puts("oops bad key\r\n"); 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; 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();
}

@ -19,6 +19,6 @@ void game_main(void);
* @param button * @param button
* @param press * @param press
*/ */
void game_button_handler(uint8_t button, bool press); void onGameButton(uint8_t button, bool press);
#endif //FIRMWARE_GAME_H #endif //FIRMWARE_GAME_H

@ -11,6 +11,7 @@
#include "leds.h" #include "leds.h"
volatile uint8_t led_brightness_mul = 255;
static uint32_t leds[4]; static uint32_t leds[4];
void leds_set(const uint32_t *new_leds) void leds_set(const uint32_t *new_leds)
@ -21,9 +22,13 @@ void leds_set(const uint32_t *new_leds)
void leds_show(void) void leds_show(void)
{ {
xrgb_t arr[4]; 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++) { 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].r = (uint8_t) ((float)rgb24_r(leds[i]) * db);
arr[i].g = (uint8_t) ((float)rgb24_g(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); arr[i].b = (uint8_t) ((float)rgb24_b(leds[i]) * db);

@ -7,6 +7,8 @@
#include <stdint.h> #include <stdint.h>
extern volatile uint8_t led_brightness_mul;
/** /**
* Set led colors * Set led colors
* *

@ -70,10 +70,10 @@ void setup_debouncer(void)
{ {
// Debouncer config // Debouncer config
debo_add(PIN_PWR_KEY, key_cb_power); debo_add(PIN_PWR_KEY, key_cb_power);
debo_add(PIN_KEY_1, game_button_handler); debo_add(PIN_KEY_1, onGameButton);
debo_add(PIN_KEY_2, game_button_handler); debo_add(PIN_KEY_2, onGameButton);
debo_add(PIN_KEY_3, game_button_handler); debo_add(PIN_KEY_3, onGameButton);
debo_add(PIN_KEY_4, game_button_handler); debo_add(PIN_KEY_4, onGameButton);
// Timer 1 - CTC, to 16000 (1 ms interrupt) // Timer 1 - CTC, to 16000 (1 ms interrupt)
OCR1A = 16000; OCR1A = 16000;

Loading…
Cancel
Save