Need help with coding - making a toy for my little one

Am making a toy for my kid and need some help with the coding for it.
A few members here have already helped in parts of the code for this project but I still need some guidance if I am doing it right .

Working : - So its basically like this, the status strip is divided into 2 halves (4 leds each) each half shows a different color , the user is supposed to guess the resultant color which we get when mixing the colors shown in the 2 halves, this is done using the 3 buttons and 3 leds on the front which is called the buttons strip. the 3 leds show 3 possible answers and the user is supposed to select the right one of the 3.

The right answer advances the user to the next level and he gets a new set of colors on the status strip and the game moves on and on like this. Each level gives the user 30 secs to give his input after which the game will fail and start over. If game will reduce the time given for answering after every 5 levels by 10 seconds .

My implementation :-
I am using an Arduino Pro Mini and 3 AAA batteries to power this toy.
I have 2 LED strips , one for the buttons which is 3 LED's long and will be called buttons strip here on and another LED strip around the toy which is 9 LED's long but I will need to use only 8 LEDS of it (am leaving the centermost led of this strip which I dont need to use here (marked in grey in the picture above).
I am using the Fastled library and created 3 arrays of type CRGB to hold values for the buttons strip , status strip and the correct answer.

I also cannot use the delay function as the function that controls the leds and the function that checks for the button press have to run simultaneously , i also plan to include a function to generate sounds via a speaker to indicate the different actions in the game later on .

My programming skill is quite basic and I still have a lot to learn from the brilliant people on this forum, so please bear with me if I seem to ask silly questions in the discussions .

The below is my code I have written till now. Please suggest if any issues in my code and any improvements that i can make . the code compiles fine by the way

#include <FastLED.h>


//configure the buttons and variables to record the button state
const int buttonPin1 = 1;
const int buttonPin2 = 2;
const int buttonPin3 = 3;
int button1State = 0;
int button2State = 0;
int button3State = 0;

//configure the 2 led strips
#define BUTTONS_LED_PIN     9
#define STATUS_LED_PIN     7
#define NUMOFLEDS_BUTTON    3
#define NUMOFLEDS_STATUS    9
#define BRIGHTNESS  50
//#define LED_TYPE    WS2811
//#define COLOR_ORDER GRB

// Define the array of leds for both the button strip and status strip
CRGB leds_buttons[NUMOFLEDS_BUTTON];
CRGB leds_status[NUMOFLEDS_STATUS];

//Assign time varibales to use with millis()
unsigned long time;
unsigned long currentTime;
unsigned long previousTime = 0;
long levelTimeout = 30000;
long levelFailedDuration = 2000;
long levelFailedBlink = 100;

//gameSpeed is a work in rogress variable and is currently not being used
int gameSpeed;
int gameLevel;

//create arrays for the different levels
//colorsOnButtons defines the colors that will be displayed on any given level
CRGB colorsOnButtons[3][3] = {
  {{ 255,  0,  0}, {0, 255, 0}, {0, 0, 255}},
  {{0, 255, 0}, {0, 0, 255}, { 255,  0,  0}},
  {{0, 0, 255}, { 255,  0,  0}, {0, 255, 0}}
};
//checkCorrectColor holds the combination of colors with which we need to compare in order to find if a level was won or not.
CRGB checkCorrectColor[3][3] = {
  {{ 255,  0,  0}, {0, 200, 0}, {0, 0, 255}},
  {{0, 255, 0}, {0, 0, 255}, { 255,  0,  0}},
  {{0, 0, 255}, { 255,  0,  0}, {0, 255, 0}}
};
//colorsOnStatus defines what colors are shown on the status led strip, this strip will be devided into 2 halves 0-3 LED and 5-8 LED , the LED number 4 will not be used here
CRGB colorsOnStatus[3][2] = {
  {{ 255,  0,  0}, {0, 255, 0}},
  {{0, 255, 0}, {0, 0, 255}},
  {{0, 0, 255}, { 255,  0,  0}}
};

void(* resetFunc) (void) = 0;//declare reset function at address 0

void setup() {
  // Uncomment/edit one of the following lines for your leds arrangement.
  delay( 500 );
  gameSpeed = 1;
  gameLevel = 0;

  //Initialize the buttons as Input
  pinMode(buttonPin1, INPUT);
  pinMode(buttonPin2, INPUT);
  pinMode(buttonPin3, INPUT);

  FastLED.addLeds<NEOPIXEL, BUTTONS_LED_PIN>(leds_buttons, NUMOFLEDS_BUTTON);  // GRB ordering is assumed
  FastLED.addLeds<NEOPIXEL, STATUS_LED_PIN>(leds_status, NUMOFLEDS_STATUS);
  FastLED.setBrightness(  BRIGHTNESS );
  //start();
  //cycling through the led's on the strip and buttons on start of the game just for fun
  for ( int i = 0; i < NUMOFLEDS_STATUS; i++) {
    leds_status[i] = ColorFromPalette( RainbowColors_p, 1, BRIGHTNESS, LINEARBLEND);
  }
  for ( int i = 0; i < NUMOFLEDS_BUTTON; i++) {
    leds_buttons[i] = ColorFromPalette( RainbowColors_p, 1, BRIGHTNESS, LINEARBLEND);
  }
  FastLED.show();
  delay(3000);
  fill_solid(leds_buttons, NUMOFLEDS_BUTTON, CRGB::Black);
  fill_solid(leds_status, NUMOFLEDS_STATUS, CRGB::Black);
  FastLED.show();
}

void loop() {
  currentTime = millis();
  //if(currentTime <= levelTimeout)
  showColors(gameSpeed, gameLevel);
  checkButtonPressed(gameLevel);

}
//this function checks all 3 buttons and then checks if the right answer was given by comparing the led corresponding to the button with the checkAnswer array 

void checkButtonPressed(int x) {
  if (currentTime <= levelTimeout) {
    button1State = digitalRead(buttonPin1);
    button2State = digitalRead(buttonPin2);
    button3State = digitalRead(buttonPin3);

    if (button1State == HIGH) {
      if (colorsOnButtons[x, 0] == checkCorrectColor[x, 0]) {
        levelWin();
        return;
      }
      else
        levelLose();
    }
    else if (button2State == HIGH) {
      if (colorsOnButtons[x, 1] == checkCorrectColor[x, 1]) {
        levelWin();
        return;
      }
      else
        levelLose();
    }
    else if (button3State == HIGH) {
      if (colorsOnButtons[x, 2] == checkCorrectColor[x, 2]) {
        levelWin();
        return;
      }
      else
        levelLose();
    }

  }
  else levelLose();
}

//this function generates the colors which are being shown on the 2 led strips
void showColors(int x, int y) {

  int count = 1;
  //to show colors on the buttons and selecting the colors for the buttons from the array defined
  for (int i = 0; i < 3; i++) {
    leds_buttons[i] = colorsOnButtons[y, i];
  }
  FastLED.show();

// the below code was written to make sure the last 5 seconds of the game time both halves of the status strip alternate between ON and OFF state to indicate that time is running out. 

  if (currentTime - previousTime >= levelTimeout - 5000) {

    long timeDiff = currentTime - previousTime;
    //to show the colors for the strip and selecting the colors from the array defined
    if (timeDiff >= 500 && timeDiff <= 1000) {
      for (int i = 0; i < 4; i++) {
        leds_buttons[i] = colorsOnButtons[y, 0];
      }
      for (int i = 5 ; i < 9; i++) {
        leds_buttons[i] = CRGB::Black;
      }
      FastLED.show();

    }
    else {
      for (int i = 0; i < 4; i++) {
        leds_buttons[i] = CRGB::Black;
      }
      for (int i = 5 ; i < 9; i++) {
        leds_buttons[i] = colorsOnButtons[y, 1];
      }
      FastLED.show();
    }
  }
  else {
    for (int i = 0; i < 4; i++) {
      leds_buttons[i] = colorsOnButtons[y, 0];
    }
    for (int i = 5 ; i < 9; i++) {
      leds_buttons[i] = colorsOnButtons[y, 1];
    }
    FastLED.show();

  }
}

void levelWin() {
  gameLevel++;
  if (gameLevel >= 6) {
    gameSpeed++;
    levelTimeout = 20000;
  }
  else if (gameLevel >= 10) {
    gameSpeed++;
    levelTimeout = 10000;
  }

  FastLED.clear();
  for (int i = 0; i < NUMOFLEDS_STATUS; i++) {
    leds_status[i] = CRGB::Green;
  }
  FastLED.show();
  for (int i = 0; i < NUMOFLEDS_STATUS; i++) {
    leds_status[i] = CRGB::Black;
    delay(100);
    FastLED.show();
  }
}

// when the game is lost the the arduino is reset and the game starts from beginning again 
void levelLose() {
  FastLED.clear();
  for (int i = 0; i < NUMOFLEDS_STATUS; i++) {
    leds_status[i] = CRGB::Red;

  }
  FastLED.show();
  for (int i = 0; i < NUMOFLEDS_STATUS; i++) {
    leds_status[i] = CRGB::Black;
    delay(100);
    FastLED.show();
  }

  resetFunc();
}

lotta code (novices often over complicate)

not sure how the button relates to the LED(s) being displayed

don't understand the purpose of colorsOnButtons [] and checkCorrectColor [] in checkButtonPressed. for each button press, why not have a variable indicating the correct button?

don't understand the purpose of colorsOnButtons [] and checkCorrectColor []. looks like they contain a 3 field values describing an LED color. why not a value 1, 2, 3?

i can understand the need to debounce, but wouldn't it be unnecessary if there's a pause to indicate that either the correct or wrong button was pressed before the next iteration

should there be a timeout if the button is not pressed soon enough?

seems the sequence is

  • display a color led(s)
  • check for either a button press or timeout
  • report either right or wrong (your levelWin, levelLose)
  • repeat

buttons can be in an array and checked in a for loop.

buttons switches are conventionally wired between the pin and ground, the pin configured as INPUT_PULLUP to enable the interal pullup resistor which pulls the pin HIGH and the pin is pulled LOW when the button pressed.

don't understand how showColors () selects (?) the colors to display

here it just picks the colors from the colorsOnButtons[] array as per the level in the game .

this array colorsOnButtons is only to hold the color values to display on the button led strip
the array checkCorrectColor is wrong , it will simply be one dimension array that holds the correct color value for each level , so if I have 15 levels then this array will have 15 levels

[quote="gcjr, post:2, topic:1009485"]
but wouldn't it be unnecessary if there's a pause to indicate that either the correct or wrong button was pressed before the next iteration [/quote]
when a correct or wrong button is pressed the function either moves to the levelWin or levelLose which will indicate the game status based on the red or green colors displayed on the status strip.

There is a timeout provided in the checkButtonPressed function where it checks for this condition while (currentTime <= levelTimeout) but it is not defined correctly , i changed this to if (currentTime <= levelTimeout) and in the else part mentioned to call the levelLose().

can you explain with example for the last parts where you say buttons can be in an array and we can use the pullup resistor, I didnt quite catch that

FWIW, the OP says that this thread is an offshoot of:
https://forum.arduino.cc/t/coding-help-required-trying-to-blink-pixels/1007852/16
Just in case there is additional information there...

there could be an array with just the 3 color settings and the other arrays (why 2?) just have indices 0-2

CRGB colors [] =   {
{ 255,  0,  0}, {0, 255, 0}, {0, 0, 255}
};

why compare

    if (colorsOnButtons[x, 1] == checkCorrectColor[x, 1])

instead of simply a button index?

why there?

consider following code. you don't want a case, simply a comparison of checkButtons and a value

// check multiple buttons and toggle LEDs

enum { Off = HIGH, On = LOW };

byte pinsLed [] = { 10, 11, 12 };
byte pinsBut [] = { A1, A2, A3 };
#define N_BUT   sizeof(pinsBut)

byte butState [N_BUT];

// -----------------------------------------------------------------------------
int
chkButtons ()
{
    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        byte but = digitalRead (pinsBut [n]);

        if (butState [n] != but)  {
            butState [n] = but;

            delay (10);     // debounce

            if (On == but)
                return n;
        }
    }
    return -1;
}

// -----------------------------------------------------------------------------
void
loop ()
{
    switch (chkButtons ())  {
    case 2:
        digitalWrite (pinsLed [2], ! digitalRead (pinsLed [2]));
        break;

    case 1:
        digitalWrite (pinsLed [1], ! digitalRead (pinsLed [1]));
        break;

    case 0:
        digitalWrite (pinsLed [0], ! digitalRead (pinsLed [0]));
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        pinMode (pinsBut [n], INPUT_PULLUP);
        butState [n] = digitalRead (pinsBut [n]);
    }

    for (unsigned n = 0; n < sizeof(pinsLed); n++)  {
        digitalWrite (pinsLed [n], Off);
        pinMode      (pinsLed [n], OUTPUT);
    }
}

how well written is that?

What's your point?

That is my post which was about a part of the code belonging to this very game

I didnt understand what you mean

My question was to @gcjr.

It doesn't makes sense to me.

long levelTimeout = 30000;

void loop()
{
  currentTime = millis();
}
void checkButtonPressed(int x)
{
  while (currentTime <= levelTimeout)
  {
  }
}

millis is always growing so if the board is powered for more than 30s this function will be useless until a restart.

BTW millis is an unsigned long.

ive updated my code here to use the if function to make sure that its running for 30 seconds only after which if no button press is detected it will goto the levellose function and the game will reset

Please don't do that again, editing previously posted code. It makes nonsense out of the replies.

Also, you didn't say anything at all about the impact of the changes you made to your code, whether it works or not, whether there are outstanding issues...

the code compiles fine , i still need to build a prototype to test if this works , is there any software simulation available to test such projects before they can be built.

My only need is for the code improvement

Improving code that hasn't been tested, could waste a lot of time. Lots of improvements might apply to features or methods that don't work and have to be discarded or changed.

Also, what kind of improvement are you looking for?

There is simulation software available, just Google for it. I've heard good things about Wokwi (sp?). I don't use any simulators, it's too easy to just hook up a real device. :slight_smile:

I once didn't, but now using a simulator is in fact easier for me… I use it for forum things and I use it for early drafts of stuff that will maybe get real-ized.

It's wokwi, find it at

At the moment it is the best simulator available, and only getting better. There are most of the common parts you'd need to "wire" up something to test. The dev team is responsive in rare events of needing to wonder about one thing or another.

You don't need any account.

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.