At one point or another, the vast majority of us have all played the “Simon Says” game. In this game, one person, Simon (or Susan, Chad, or whoever happens to be the leader), will say “Simon says” and tell all of the other players to do something, such as raise their left hand. The catch in this game is that if the leader tells the other players to do something without saying the words “Simon says” and the other players do it anyway, they’re out. I personally really enjoy playing Simon Says, but I thought it could even be more fun if you could play Simon Says with a bunch of LEDs…
Step 1: The Project Parts
So that’s exactly what I did.
Using a set of nine LEDs, two PmodBTNs (four user buttons on each), a single additional button, and a shift register, I was able to create Simon Says with it being just as fun as I was hoping it would be. Granted, the chipKIT uC32 I designed the game on always gets to be Simon and pressing buttons to make LEDs light up in the same pattern that the uC32 made them isn’t a super intensive cardio workout, but I’m okay with that.
Step 2: Setting Up the Circuit
To get the circuit set up the way we would like it to be, i.e. in a working state, we will need to do a little bit of wiring first. The connections that I will be listing are specific to the way that I designed my code; you are free to make your own connections if you so desire, although you will need to appropriately adjust the code (provided in a later step) to reflect these changes.
Let's start with the shift register.
There are four digital connections that we need to make: the serial input (SER), the register clock (RCLK), the serial clock (SRCLK), and the serial clear (SRCLR). Connect SER to digital pin 30, RCLK to pin 32, SRCLK to pin 34, and SRCLR to pin 36. Once we have those connections, we also need to provide a source of power to shift register as well as attaching the outputs to our set of LEDs.
To pin 16 on the shift register (the VCC next to the little notch on the shift register) we need to supply a positive power supply; I used a 3.3V source from my chipKIT uC32 since that was the most appropriate option I had to power my LEDs. I also supplied a ground source (0V) to pin 8 (GND) and pin 13 (OE) on the shift register. OE is tied to ground so that all of the output pins are continually enabled.
Once all of that is prepared, we can then wire up the 8 LEDs that will correspond to our 8 buttons (that we will wire up in a bit). Each of the LEDs will need their anode (the longer leg) connected to one of the outputs on the shift register (Qa through Qh) and the shorter leg (the cathode) connected to ground.
Then we need to hook the PmodBTN's to act as our 8 (conveniently) debounced buttons that we can use for the "echoing" portion of the Simon Says game. The four inputs on each of the PmodBTNs will be attached to digital pins 11 through 4 on the uC32. With the way I designed my code (you are free to re-arrange the code if you so desire) BTN0 through BTN3 on one of the PmodBTNs are attached from pins 4-7, respectively and control the green LEDs in the third photo. BTN0 through BTN3 on the second Pmod are attached to pins 8-11 respectively and control the red LEDs in the third photo.
Correspondingly, BTN3 on both Pmods control the leftmost LED in their set and BTN0 controls the rightmost LED in their set.
Our button that we will use to submit our final answer will have one side of the button (the left hand side in my case, as per the third photo) attached to the 3.3V line and the right hand side of the button pulled to ground via a small resistor (220 Ohms). On the side pulled to ground, a wire is also connecting that side to digital pin 26 as well as an LED spanning from that side of the button to ground. This way, when the final answer button is pressed, the LED will turn on and the microcontroller will receive a logic high signal indicating that the user has submitted their final answer.
Step 3: Keeping Track of the Patterns
What some of you may be interested in is how I made the game work–how the game compared the user guess to the original pattern, how the person playing the game knew if they got the answer right or wrong, and how the microcontroller came up with the random patterns in the first place.
The comparison between the user guess and the original pattern of LEDs that was provided was done through two arrays. One array kept track of what numbers the uC32 had generated, which corresponded to which LEDs were lit up in the pattern. A second array similarly kept track of which LED was lit up through the player pressing the corresponding button. Once the player was done lighting up the LEDs in the (hopefully) correct order, they press the isolated button off to the far left to light up the single yellow LED, thus submitting their “final answer”.
Step 4: Checking for Success
The uC32 then compares the two arrays, number by number, to see if they match. If the arrays do match, the four green LEDs are flashed a few times to show the player that they got the pattern right and the length of the pattern that they have to match increases by 1.
However, if the two arrays do not match, the red LEDs will flash and the length of the pattern goes back down to just one LED. To make sure that the two arrays aren’t accidentally different somehow, the values of the arrays are both internally reset to all zeros before a new pattern of LEDs (of any length) is displayed.
Step 5: Wait, Isn't "random" Essentially Impossible With Microcontrollers?
But how did the microcontroller come up with the “random” patterns?
Electronic chips, such as the PIC32 chip from Microchip, do not have any way to arbitrarily come up with a number of some kind, but what the chip on the uC32 does have is a very long sequence of numbers. This sequence of numbers is accessed through the random() function, which chooses a number based on the bounds that you give it. For example, with the eight LEDs that could be lit up, the random() function is written as random(1,9) to choose a number ranging from 1 to 8. The upper number given in the random() function is presumed to be 1 greater than the desired output range of the random() function.
While this is nice, we would find that every time we play Simon Says that the pattern of LEDs that light up will always be the same because the number sequence which random() pulls from is always the same. To get around this and make the game more “random” we can call the randomSeed() function before using the random() function. RandomSeed() will tell the microcontroller where to start pulling numbers from in the number sequence based on what value we give it. Thus, we could tell randomSeed() to get a value based off the analog noise of one of the I/O pins on the microcontroller, getting us a “random” place to start in our number sequence with the random() function.
However, it has been my experience that the electrical noise on a pin tends to be restricted to a very small range of numbers, such as 0 to 5, which is not quite as arbitrary as I would like. So, I instead have the randomSeed() collect its number from the function millis(), which reports the value on the continually running time counter that lasts for nearly 50 days. By using millis(), where the random() function pulls its values from is inherently dependent on how long the player takes to submit their answer. Presuming the player is human instead of another microcontroller, this method guarantees that a different place in the number sequence will be chosen every time the game is played.