Projects

Follow The Leader

Mike Hansell

Issue 6, December 2017

This article includes additional downloadable resources.
Please log in to access.

Log in

A touch-interactive game of skill, with adjustable difficulty levels.

What can you make in a few hours with 4 illuminated push button switches, 4 transistors and 12 resistors? We’re not sure about you but we recreated a project from the dim dark ages of the late 1970s, only this time it can run off a small battery and can fit in one hand. The original used a S100 computer, which was the size of a modern gaming PC. I can hear you thinking, “Yeah, so what?” Well, with some code running on the Arduino we can make an interactive game suitable for children, or just the young-at-heart!

THE BROAD OVERVIEW

The four switches are arranged in a diamond pattern. Under control of the Arduino, a progressively more complicated pattern of LEDs and sounds is presented to the user. Now it’s your turn to press the buttons to reproduce the sequence. Pressing the buttons will illuminate the LEDs. If you get the sequence correct, another step is added and the process continues. You win the game if you can repeat the whole sequence without error.

We have chosen to use a red LED at the top, then a green LED to its right, a blue LED at the bottom, and a yellow LED on the left. Each LED is housed in a push button switch, and each LED is connected to an Arduino digital pin, configured as outputs. The four switches are each connected to Arduino digital pins, configured as inputs. There are three difficulty levels: level 1 has 8 steps in it’s LED/tone sequence; level 2 has 12, and level 3 has 16 steps. The difficulty level is set by the position of a potentiometer, which is connected across the 5V power and 0V. The sketch detects the position of the wiper (the centre pin) of the pot, and divides the value read into three distinct levels. The first third is level 1 difficulty, the second third is medium difficulty, and... we’re sure you can work out level 3.

THE BUILD

Parts Required: Jaycar Altronics
1 x Arduino UNO or Nano XC4410 Z6280
1 x Red Push Button Switch SP0662 S1670
1 x Green Push Button Switch SP0665 S1671
1 x Blue Push Button Switch SP0666 S1674
1 x Yellow Push Button Switch SP0664 S1675
4 x 2N2222 Transistors ZT2298 Z1040
1 x 220Ω Resistor Pack RR0556 R7542
1 x 10kΩ Resistor Pack RR0596 Z6280
1 x 10k Linear Potentiometer RP3510 R2243
1 x 78L05 Low Power 5V Regulator ZV1539 Z0460
1 x Piezo Buzzer XC4424 Z6280
1 x 9V Battery Clip Connector PH9230 P0455
1 x 9V Battery SB2423 S4970B
1 x Plastic Box HB6128 H0152
1 x 2 Position Switch ST0335 S1310

You’ll also need standard prototyping equipment like a breadboard and jumper leads.

We built the whole prototype on a large breadboard but this is not the preferred setup. While the prototype works fine, the object is to have the LEDs and switches exposed and accessible. The rest of the electronics can be hidden, well, out of the way anyway.

There are a variety of Jiffy boxes available that would make an ideal enclosure, or you could 3D print one if you like. When you choose your enclosure, consider the layout of the LEDs/switches; you don’t want them too close to each other.

You’ll have 10 wires coming from the surface that you mount the LEDs/switches on. We used matching coloured wires to make our life easier. The 10 wires are made up of 4 for the LEDs, 4 for the switches plus +5V and ground. As one side of each switch is connected to ground (0V) you could wire them together. All of the LED anodes are connected to +5V so they can be wired together too.

inside the box
Keeping things tidy on the inside helps, but isn't always required.

As you’ll be connecting to a breadboard and an Arduino, breadboard jumpers are ideal for the interconnects. You can easily solder the male pins to the LEDs and switches, and then the other ends can be inserted into the breadboard and Arduino as necessary.

The transistors and their associated resistors can be mounted on the breadboard. The pot to set the difficulty level can be mounted on the breadboard too, but is probably best to mount directly on the enclosure. It may not be obvious but there’s no real need to be able to vary the difficulty level. You can easily set the difficulty level in the sketch, and ignore the bit of code that reads the pot.

The last component to go on the breadboard is a 78L05 voltage regulator, which is used to reduce the 9V from the battery to 5V for the Arduino. This is important. The smoke needs to stay inside!

A schematic showing how the momentary switches are integrated with transistors, in order to drive bright lights
A schematic showing how the momentary switches are integrated with transistors, in order to drive bright lights.

The electronics could be mounted on a Vero or similar prototyping board too. A bit of double-sided tape will hold the battery, Arduino and breadboard in place. The Piezo buzzer can be mounted on the breadboard or glued to the enclosure. If you find it too loud, add a series resistor of 220ohm or maybe double that if it’s still too loud.

The full wiring diagram. It's best to breadboard before installing into your box.
The full wiring diagram. It's best to breadboard before installing into your box.

THE CODE

For the sake of clarity we've removed an amount of debugging code in the sketch. We find the Serial.print and Serial.println functions to be invaluable for debugging.

char seqcolours[5] = { ‘!’, ‘Y’, ‘B’, ‘G’, ‘R’ };
int seqtones[5] = { 0, 1000, 750, 500, 250 };
byte sequence[17];              
// an array to hold up to 16 random values of 1 
// to 4 representing the colours in the sequence
byte diff = 16;                  
// This is the difficulty value 
// will be reset in loop()
// pins assigned to control LEDs
const byte red = 13;
const byte green = 12;
const byte blue = 11;
const byte yellow = 10;
// pins assigned as inputs to read buttons
const byte redbut = 9;
const byte greenbut = 8;
const byte bluebut = 7;
const byte yellowbut = 6;
const int redtone = 250;        
// these values define the 
// frequency of the tones played
const int greentone = 500;
const int bluetone = 750;
const int yellowtone = 1000;
const byte tonepin = 5;         
// Connect piezo buzzer to this pin 
const byte APin = 1;            
// Read the pot on analog pin 1
int d = 500;                    
// A general purpose delay value  

First we initialise the global variables and constants that will be used in the sketch.

OUR SETUP FUNCTION

void setup()
{
  pinMode(13, OUTPUT);  // output for red LED
  pinMode(12, OUTPUT);  // output for green LED
  pinMode(11, OUTPUT);  // output for blue LED
  pinMode(10, OUTPUT);  // output for yellow LED
  pinMode(9, INPUT);  // input fo red button 
  pinMode(8, INPUT);  // input for green button
  pinMode(7, INPUT);  // input for blue button
  pinMode(6, INPUT);  // vinput for yellow button
  
  randomSeed(analogRead(0));
} // End of setup()  

A very interesting issue popped up during development. We tried using millis() to come up with a random number to seed (initialise) the random number generator, but it always came up with the same value and played the same game. The following was found in the Arduino docs (yes, we know, read the docs first! This info is modified a little to make more general purpose...):

“If an analog input pin is disconnected, analog noise will cause a random value to be read. This value can then be used with randomSeed() to generate a different series of random number each time the sketch runs."

void loop()
{
  int rc, pot;
  byte i, j, k, counter = 0;
  pot = analogRead(APin);
  if (pot > 699) diff = 16;
  if ((pot > 299) && (pot < 700)) 
    {
    diff = 12;
    }
   if (pot < 300) diff = 8;
  // diff = 16;   

This is the start of the loop function. Here you can see that we determine the difficulty level by reading the voltage across a potentiometer (pot), or via a switch with two resistors forming a voltage divider.

The commented out line // diff = 16; is a convenient place for you to set the difficulty and eliminate the need to set it via the pot.

counter += 1;
ToneTest();
for (j = 1; j < diff + 1; j++)
{
  sequence[j] = random(10, 14); // gens random integers between 10 and 13    
}
delay(2000); 

counter += 1 is used to set the initial sequence of LEDs and tones to just 1. You wouldn’t want to start with 8 or 10, would you?

ToneTest() plays the intro sequence of LEDs going from red to green, etc with accompanying tones and then backwards. This shows that the game has started.

The “for” loop here generates a number of values in the range 10 to 13, which correspond to the output pins on the Arduino:

Pin 10 is yellow,

Pin 11 is blue,

Pin 12 is green,

Pin 13 is red.

The number of values generated is determined by the variable diff, which is short for “difficulty”.

for (i = 1; i < diff + 1; i++)    
// for number of colours in sequence for 
//  set difficulty (8, 12, 16)
  {
  // show user part of the sequence 
  for (j = 0; j < counter + 1; j++)  
// for number of colours in sequence for 
// set difficulty (8, 12, 16)
    {
    digitalWrite(sequence[j], HIGH);  
    // turn the LED on
    tone(tonepin, seqtones[
sequence[j] - 9], 1000);
    delay(500);
    digitalWrite(sequence[j], LOW);  
    // turn the LED off
    delay(1000);
    }
  rc = GetUserButtons(j - 1);
  if (rc == false) break;
  counter += 1;
  delay(1000);  

This is the core of the sketch. It shows a part of the colour sequence and plays the appropriate sound. With each loop an extra colour/sound is added, until either the user gets the sequence wrong or the user wins the game by entering all of the colours in the correct sequence. It’s not as easy as it sounds.

if (rc == false)
  {
  PlaneCrash();  
  // distinctive tone sequence to
  // let user know they lost
  }

// End of loop()  

This is the remainder of the loop() code, which only runs when the complete sequence has been entered successfully or you got the sequence wrong.

int GetUserButtons(int NumToGet)
{
int i, rc;
  for (i = 1; i < NumToGet + 1; i++) // Get NumToGet buttons from user
    {
    rc = WaitForBut();
    delay(500);
    if (rc != sequence[i])   
      // wrong button
      {
      return(false);
      }
    }
  return(true);
}  
// End of GetUserButtons()  

GetUserButtons() simply gets the number of button presses (NumToGet) passed to it.

int WaitForBut()
{
  int rc;
  while(1)
    {
    rc = digitalRead(redbut);
    if (rc == 0) 
      {
      tone(tonepin, redtone, 250);          
        return(red);
      }
    rc = digitalRead(greenbut);
    if (rc == 0)
      {
      tone(tonepin, greentone, 250);          
        return(green);
      }
    rc = digitalRead(bluebut);
    if (rc == 0) 
      {
      tone(tonepin, bluetone, 250);          
        return(blue);
      }
    rc = digitalRead(yellowbut);
    if (rc == 0) 
      {
      tone(tonepin, yellowtone, 250);          
        return(yellow);
      }
    delay(100);
    }
} // End of WaitForBut()

This function simply waits for a button press and returns the value of the button pressed. Pressing a switch will illuminate the appropriate LED and play the appropriate sound.

void PlaneCrash()
{
  byte j;
  for (j = 21; j > 1; j--)
    {
    tone(tonepin, j * 250, 500);
    delay(200);
    }
}  

PlaneCrash? Well, that’s pushing it, but as close as we could get in a reasonable time. It just makes a series of tones from 5kHz to 250Hz. If you stretch your imagination maybe you can hear a plane dive.

digitalWrite(red, HIGH); 
// turn the red LED on
  tone(tonepin, 250, 1000);
  delay(1000);
  digitalWrite(red, LOW); // turn the red LED off
// repeat above for green then blue
 digitalWrite(yellow, HIGH);      
// turn the yellow LED on
   tone(tonepin, 1000, 1000);
   delay(1000);
   digitalWrite(yellow, LOW);       
// turn the yellow LED off
// now we go back again, but at a faster rate
   digitalWrite(yellow, HIGH);      
// turn the LED on
   tone(tonepin, 1000, 250);
   delay(250);
   digitalWrite(yellow, LOW);       
// turn the LED on
// repeat above for blue then green
   digitalWrite(red, HIGH); // turn the LED on
   tone(tonepin, 250, 250);
   delay(250);
   digitalWrite(red, LOW); // turn the LED on

The ToneTest() is quite simple as it’s just a bunch of LED on/off with accompanying tones. The name comes from the original function, where we were testing various alternatives.

int CheckForButs()
{
  int rc;
  rc = digitalRead(redbut);
  if (rc != 0) return(redbut);
  rc = digitalRead(greenbut);
  if (rc != 0) return(greenbut);
  rc = digitalRead(bluebut);
  if (rc != 0) return(bluebut);
  rc = digitalRead(yellowbut);
  if (rc != 0) return(yellowbut);
} // End of CheckForButts()   

CheckForButs() checks the state of each button and if any is pressed returns the value assigned to that button.

THE BOX

We wouldn't be getting too far if it wasn't for a nice, creative box. Initially we created a tight-fitting box, that we could print in one-go on our LulzBot TAZ 6 3D printer.

Unfortunately when we mounted everything, while theoretically it should have fit, it didn't entirely get the lid closed. The large arcade buttons we used also needed a little more room, to make sure we didn't accidentally hit the wrong button, but also to display things nicely.

As a result, we settled on a large friction-lid box. There are holes to neatly mount the arcade switches, “difficulty” potentiometer, and power switch. We've made the holes a fraction over-size. It ensures we still get a fairly snug fit when everything is mounted, while reducing any requirement for manual work on the box. If we make the hole sizes too accurate, they'll usually require some reaming to make things fit properly due to the tolerances in the printer filament and how sizes are calculated.

the box
Our complete box doesn't fit on our LulzBot TAZ 6, so we've split the file to enable printing on a small printer too.

As you can see from the rendering above, however, is that the box won't print in one-go, even on our TAZ 6 (the blue area shows the print bed size). In the resources, you'll find files for the lid and box separately, so it should print on a much smaller printer without much problem too.

Of course, if you're keen to make your own box, different from this one - we show you how in our What's In A Box article

WHERE TO FROM HERE?

A lot of the old Sci-Fi movies had controls activated by hand gestures. Having built a project last month using PIR sensors (and I’m a Sci-Fi nut!), the idea of using them instead of switches sounds like a lot of fun to me, but they’re probably prone to being too sensitive. A great add-on would be a sound card. Maybe RGB LEDs might make an interesting addition too, as an attract sequence (i.e., “Look at me, I’m pretty!”).