Arcade button Simon game on an Arduino Uno
An arcade button Simon game: four lit buttons and a piezo, a sequence that grows one color a round, a note per color and the best round kept in EEPROM. The whole build, an Arduino Uno and 5 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate Runs in your browser. Free, and no account needed.
The parts retail for about $37. On Mokxi they're free.
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Simon Says: four lit arcade buttons and a piezo on an Arduino Uno.
//
// button switch lamp note
// green pin 12 pin 11 E5 659 Hz
// red pin 10 pin 9 C#5 554 Hz
// yellow pin 6 pin 5 A4 440 Hz
// blue pin 3 pin 2 E4 330 Hz
// piezo pin 13 and GND (the board's L lamp flickers along with it)
//
// Each arcade button has two parts inside: a switch (one leg to GND, one to
// its pin, read with INPUT_PULLUP so a press reads LOW) and a 5 V lamp with
// its resistor built in (L+ to its pin, L- to GND). The four notes are an A
// major chord, so any sequence sounds like a tune.
//
// How a game goes:
//
// 1. The lamps chase round while it waits. Press any button to start.
// 2. Simon plays a sequence, one more color each round. The Serial
// Monitor spells it out too, so you can check what you saw.
// 3. Play it back. Every press lights its lamp and plays its note.
// 4. A wrong button, or 3 seconds with no press, ends the game with a low
// buzz. The best round is kept in the EEPROM, so it survives a reset.
//
// Simon speeds up at rounds 5, 9 and 13, as the handheld game does.
#include <EEPROM.h>
const uint8_t BUTTON[4] = {12, 10, 6, 3};
const uint8_t LAMP[4] = {11, 9, 5, 2};
const unsigned int NOTE[4] = {659, 554, 440, 330};
const char *const NAME[4] = {"green", "red", "yellow", "blue"};
const uint8_t PIEZO = 13;
const uint8_t MAX_ROUNDS = 32;
const unsigned long ANSWER_MS = 3000;
const int BEST_ADDRESS = 0;
uint8_t sequence[MAX_ROUNDS];
uint8_t length = 0;
uint8_t best = 0;
// Light one lamp and play its note, or turn everything off with -1.
void show(int8_t color) {
for (uint8_t i = 0; i < 4; i++) digitalWrite(LAMP[i], i == color ? HIGH : LOW);
if (color < 0) {
noTone(PIEZO);
} else {
tone(PIEZO, NOTE[color]);
}
}
// The button held down right now, or -1 for none.
int8_t pressed() {
for (uint8_t i = 0; i < 4; i++) {
if (digitalRead(BUTTON[i]) == LOW) return i;
}
return -1;
}
// Wait for a press and its release, lighting the lamp while it is held.
// Returns the color, or -1 if nothing was pressed for `limit` ms.
int8_t waitForPress(unsigned long limit) {
unsigned long started = millis();
while (pressed() < 0) {
if (millis() - started > limit) return -1;
}
delay(20); // let the contacts settle
int8_t color = pressed();
if (color < 0) return waitForPress(limit);
show(color);
unsigned long down = millis();
while (pressed() == color) {
}
// A quick tap still shows for a moment, so you can see what you pressed.
while (millis() - down < 150) {
}
show(-1);
delay(20);
return color;
}
// Chase round the lamps until someone presses a button.
void attract() {
Serial.println("Simon Says: press any button to start");
uint8_t lamp = 0;
while (true) {
for (uint8_t i = 0; i < 4; i++) digitalWrite(LAMP[i], i == lamp ? HIGH : LOW);
lamp = (lamp + 1) % 4;
unsigned long until = millis() + 250;
while (millis() < until) {
if (pressed() >= 0) {
show(-1);
while (pressed() >= 0) {
}
delay(300);
return;
}
}
}
}
void playSequence() {
// Faster as the sequence grows: 420, 320, 240 then 180 ms a color.
unsigned int on = 420;
if (length >= 5) on = 320;
if (length >= 9) on = 240;
if (length >= 13) on = 180;
Serial.print("round ");
Serial.print(length);
Serial.print(":");
for (uint8_t i = 0; i < length; i++) {
Serial.print(' ');
Serial.print(NAME[sequence[i]]);
}
Serial.println();
for (uint8_t i = 0; i < length; i++) {
show(sequence[i]);
delay(on);
show(-1);
delay(on / 4 + 50);
}
}
void gameOver(uint8_t reached) {
Serial.print("game over: you reached round ");
Serial.println(reached);
for (uint8_t i = 0; i < 4; i++) digitalWrite(LAMP[i], HIGH);
tone(PIEZO, 110);
delay(1000);
show(-1);
if (reached > best) {
best = reached;
EEPROM.write(BEST_ADDRESS, best);
Serial.print("new best: round ");
} else {
Serial.print("best so far: round ");
}
Serial.println(best);
delay(1000);
}
void setup() {
Serial.begin(115200);
for (uint8_t i = 0; i < 4; i++) {
pinMode(BUTTON[i], INPUT_PULLUP);
pinMode(LAMP[i], OUTPUT);
}
pinMode(PIEZO, OUTPUT);
// A blank EEPROM reads 255: no best score yet.
best = EEPROM.read(BEST_ADDRESS);
if (best > MAX_ROUNDS) best = 0;
// Nothing is wired to A0, so it reads noise: a different game each time.
randomSeed(analogRead(A0));
}
void loop() {
attract();
length = 0;
while (true) {
if (length == MAX_ROUNDS) {
Serial.println("you win: all 32 rounds!");
gameOver(length);
return;
}
sequence[length++] = random(4);
playSequence();
for (uint8_t i = 0; i < length; i++) {
int8_t color = waitForPress(ANSWER_MS);
if (color != sequence[i]) {
if (color < 0) {
Serial.println("too slow");
} else {
Serial.print("you pressed ");
Serial.print(NAME[color]);
Serial.print(", Simon said ");
Serial.println(NAME[sequence[i]]);
}
gameOver(length - 1);
return;
}
}
Serial.println("correct!");
delay(600);
}
}
Parts list
6 parts, plus the jumper wires. They retail for about $37; on Mokxi they're free.
- 1 × Arduino Uno R3
- 4 × LED arcade button
- 1 × Piezo speaker
How it is wired
18 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.
- Arduino Uno R3 pin 13; Piezo speaker pin 1
- Arduino Uno R3 pin 12; LED arcade button (1) pin 2
- Arduino Uno R3 pin 11; LED arcade button (1) pin L+
- Arduino Uno R3 pin 10; LED arcade button (2) pin 2
- Arduino Uno R3 pin 9; LED arcade button (2) pin L+
- Arduino Uno R3 pin 6; LED arcade button (3) pin 2
- Arduino Uno R3 pin 5; LED arcade button (3) pin L+
- Arduino Uno R3 pin 3; LED arcade button (4) pin 2
- Arduino Uno R3 pin 2; LED arcade button (4) pin L+
- Ground: LED arcade button (1) pin 1
- Ground: LED arcade button (1) pin L-
- Ground: LED arcade button (2) pin 1
- Ground: LED arcade button (2) pin L-
- Ground: LED arcade button (3) pin 1
- Ground: LED arcade button (3) pin L-
- Ground: LED arcade button (4) pin 1
- Ground: LED arcade button (4) pin L-
- Ground: Piezo speaker pin 2
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.