Simon memory game
Four lights, four buttons, four tones. Repeat the growing pattern for as long as you can; the high score is saved.
Wiring
| From | To board | Note |
|---|---|---|
| Green LED, through 220 Ω | GPIO 16 | LED cathodes to GND |
| Red LED, through 220 Ω | GPIO 17 | |
| Yellow LED, through 220 Ω | GPIO 18 | |
| Blue LED, through 220 Ω | GPIO 19 | |
| Green button | GPIO 25 | Other side of each button to GND. The internal pull-ups do the rest |
| Red button | GPIO 26 | |
| Yellow button | GPIO 27 | |
| Blue button | GPIO 32 | |
| Buzzer + | GPIO 23 | Buzzer - to GND |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32 Dev Module
// Simon: repeat the growing sequence of lights and tones.
#include <Preferences.h>
const int LEDS[] = {16, 17, 18, 19}; // green, red, yellow, blue
const int BUTTONS[] = {25, 26, 27, 32}; // in the same order
const int BUZZER = 23;
const int TONES[] = {415, 310, 252, 209}; // the classic Simon pitches
const int MAX_STEPS = 64;
Preferences prefs;
int sequence[MAX_STEPS];
int length = 0;
int best = 0;
void show(int i, int ms) {
digitalWrite(LEDS[i], HIGH);
tone(BUZZER, TONES[i]);
delay(ms);
noTone(BUZZER);
digitalWrite(LEDS[i], LOW);
}
// Wait for one button press and release. Returns the button, or -1 after 4 s.
int readButton() {
unsigned long start = millis();
while (millis() - start < 4000) {
for (int i = 0; i < 4; i++) {
if (digitalRead(BUTTONS[i]) == LOW) {
digitalWrite(LEDS[i], HIGH);
tone(BUZZER, TONES[i]);
delay(30);
while (digitalRead(BUTTONS[i]) == LOW) delay(5);
noTone(BUZZER);
digitalWrite(LEDS[i], LOW);
delay(30);
return i;
}
}
delay(2);
}
return -1;
}
void gameOver(int score) {
tone(BUZZER, 110, 800);
for (int k = 0; k < 3; k++) {
for (int i = 0; i < 4; i++) digitalWrite(LEDS[i], HIGH);
delay(150);
for (int i = 0; i < 4; i++) digitalWrite(LEDS[i], LOW);
delay(150);
}
if (score > best) {
best = score;
prefs.putInt("best", best);
}
Serial.printf("score=%d best=%d\n", score, best);
Serial.println("Press any button to start.");
length = 0;
}
void setup() {
Serial.begin(115200);
delay(500);
for (int i = 0; i < 4; i++) {
pinMode(LEDS[i], OUTPUT);
pinMode(BUTTONS[i], INPUT_PULLUP);
}
prefs.begin("simon", false);
best = prefs.getInt("best", 0);
randomSeed(esp_random());
Serial.println("Simon. Press any button to start.");
}
void loop() {
if (length == 0) {
while (readButton() < 0) {
}
delay(600);
}
if (length == MAX_STEPS) {
Serial.println("You beat the game!");
gameOver(MAX_STEPS);
return;
}
sequence[length++] = random(4);
Serial.printf("round=%d\n", length);
int speed = max(150, 450 - length * 15); // it speeds up as you go
for (int i = 0; i < length; i++) {
show(sequence[i], speed);
delay(speed / 2);
}
for (int i = 0; i < length; i++) {
int pressed = readButton();
if (pressed != sequence[i]) {
Serial.println(pressed < 0 ? "too slow! game over" : "wrong button! game over");
gameOver(length - 1);
return;
}
}
delay(700);
}Wiring
| From | To board | Note |
|---|---|---|
| Green LED, through 220 Ω | GPIO 4 | LED cathodes to GND |
| Red LED, through 220 Ω | GPIO 5 | |
| Yellow LED, through 220 Ω | GPIO 6 | |
| Blue LED, through 220 Ω | GPIO 7 | |
| Green button | GPIO 15 | Other side of each button to GND. The internal pull-ups do the rest |
| Red button | GPIO 16 | |
| Yellow button | GPIO 17 | |
| Blue button | GPIO 18 | |
| Buzzer + | GPIO 10 | Buzzer - to GND |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32S3 Dev Module
// Simon: repeat the growing sequence of lights and tones.
#include <Preferences.h>
const int LEDS[] = {4, 5, 6, 7}; // green, red, yellow, blue
const int BUTTONS[] = {15, 16, 17, 18}; // in the same order
const int BUZZER = 10;
const int TONES[] = {415, 310, 252, 209}; // the classic Simon pitches
const int MAX_STEPS = 64;
Preferences prefs;
int sequence[MAX_STEPS];
int length = 0;
int best = 0;
void show(int i, int ms) {
digitalWrite(LEDS[i], HIGH);
tone(BUZZER, TONES[i]);
delay(ms);
noTone(BUZZER);
digitalWrite(LEDS[i], LOW);
}
// Wait for one button press and release. Returns the button, or -1 after 4 s.
int readButton() {
unsigned long start = millis();
while (millis() - start < 4000) {
for (int i = 0; i < 4; i++) {
if (digitalRead(BUTTONS[i]) == LOW) {
digitalWrite(LEDS[i], HIGH);
tone(BUZZER, TONES[i]);
delay(30);
while (digitalRead(BUTTONS[i]) == LOW) delay(5);
noTone(BUZZER);
digitalWrite(LEDS[i], LOW);
delay(30);
return i;
}
}
delay(2);
}
return -1;
}
void gameOver(int score) {
tone(BUZZER, 110, 800);
for (int k = 0; k < 3; k++) {
for (int i = 0; i < 4; i++) digitalWrite(LEDS[i], HIGH);
delay(150);
for (int i = 0; i < 4; i++) digitalWrite(LEDS[i], LOW);
delay(150);
}
if (score > best) {
best = score;
prefs.putInt("best", best);
}
Serial.printf("score=%d best=%d\n", score, best);
Serial.println("Press any button to start.");
length = 0;
}
void setup() {
Serial.begin(115200);
delay(500);
for (int i = 0; i < 4; i++) {
pinMode(LEDS[i], OUTPUT);
pinMode(BUTTONS[i], INPUT_PULLUP);
}
prefs.begin("simon", false);
best = prefs.getInt("best", 0);
randomSeed(esp_random());
Serial.println("Simon. Press any button to start.");
}
void loop() {
if (length == 0) {
while (readButton() < 0) {
}
delay(600);
}
if (length == MAX_STEPS) {
Serial.println("You beat the game!");
gameOver(MAX_STEPS);
return;
}
sequence[length++] = random(4);
Serial.printf("round=%d\n", length);
int speed = max(150, 450 - length * 15); // it speeds up as you go
for (int i = 0; i < length; i++) {
show(sequence[i], speed);
delay(speed / 2);
}
for (int i = 0; i < length; i++) {
int pressed = readButton();
if (pressed != sequence[i]) {
Serial.println(pressed < 0 ? "too slow! game over" : "wrong button! game over");
gameOver(length - 1);
return;
}
}
delay(700);
}Wiring
| From | To board | Note |
|---|---|---|
| Green LED, through 220 Ω | GPIO 0 | LED cathodes to GND |
| Red LED, through 220 Ω | GPIO 1 | |
| Yellow LED, through 220 Ω | GPIO 3 | |
| Blue LED, through 220 Ω | GPIO 4 | |
| Green button | GPIO 2 | Other side of each button to GND. The internal pull-ups do the rest |
| Red button | GPIO 5 | |
| Yellow button | GPIO 7 | |
| Blue button | GPIO 10 | |
| Buzzer + | GPIO 6 | Buzzer - to GND |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32C3 Dev Module
// Simon: repeat the growing sequence of lights and tones.
#include <Preferences.h>
const int LEDS[] = {0, 1, 3, 4}; // green, red, yellow, blue
const int BUTTONS[] = {2, 5, 7, 10}; // in the same order
const int BUZZER = 6;
const int TONES[] = {415, 310, 252, 209}; // the classic Simon pitches
const int MAX_STEPS = 64;
Preferences prefs;
int sequence[MAX_STEPS];
int length = 0;
int best = 0;
void show(int i, int ms) {
digitalWrite(LEDS[i], HIGH);
tone(BUZZER, TONES[i]);
delay(ms);
noTone(BUZZER);
digitalWrite(LEDS[i], LOW);
}
// Wait for one button press and release. Returns the button, or -1 after 4 s.
int readButton() {
unsigned long start = millis();
while (millis() - start < 4000) {
for (int i = 0; i < 4; i++) {
if (digitalRead(BUTTONS[i]) == LOW) {
digitalWrite(LEDS[i], HIGH);
tone(BUZZER, TONES[i]);
delay(30);
while (digitalRead(BUTTONS[i]) == LOW) delay(5);
noTone(BUZZER);
digitalWrite(LEDS[i], LOW);
delay(30);
return i;
}
}
delay(2);
}
return -1;
}
void gameOver(int score) {
tone(BUZZER, 110, 800);
for (int k = 0; k < 3; k++) {
for (int i = 0; i < 4; i++) digitalWrite(LEDS[i], HIGH);
delay(150);
for (int i = 0; i < 4; i++) digitalWrite(LEDS[i], LOW);
delay(150);
}
if (score > best) {
best = score;
prefs.putInt("best", best);
}
Serial.printf("score=%d best=%d\n", score, best);
Serial.println("Press any button to start.");
length = 0;
}
void setup() {
Serial.begin(115200);
delay(500);
for (int i = 0; i < 4; i++) {
pinMode(LEDS[i], OUTPUT);
pinMode(BUTTONS[i], INPUT_PULLUP);
}
prefs.begin("simon", false);
best = prefs.getInt("best", 0);
randomSeed(esp_random());
Serial.println("Simon. Press any button to start.");
}
void loop() {
if (length == 0) {
while (readButton() < 0) {
}
delay(600);
}
if (length == MAX_STEPS) {
Serial.println("You beat the game!");
gameOver(MAX_STEPS);
return;
}
sequence[length++] = random(4);
Serial.printf("round=%d\n", length);
int speed = max(150, 450 - length * 15); // it speeds up as you go
for (int i = 0; i < length; i++) {
show(sequence[i], speed);
delay(speed / 2);
}
for (int i = 0; i < length; i++) {
int pressed = readButton();
if (pressed != sequence[i]) {
Serial.println(pressed < 0 ? "too slow! game over" : "wrong button! game over");
gameOver(length - 1);
return;
}
}
delay(700);
}Parts
| Quantity | Part | Where to buy |
|---|---|---|
| 1× | ESP32 dev board | Buy at Amazon |
| 4× | LEDs: green, red, yellow and blue | |
| 4× | 220 Ω resistors | |
| 4× | Push buttons | Buy at Adafruit |
| 1× | Passive buzzer or piezo | Buy at Adafruit |
| 16× | Jumper wires |
Amazon buy links may include an Associates tag, and this site may earn a commission from qualifying Amazon purchases at no extra cost to you. Adafruit links are direct.
Libraries
Preferences (built in)
Load it
- In the Arduino IDE, install the esp32 board package and pick the board named above the sketch.
- Paste the sketch, choose the board's port and upload. Hold BOOT if the upload stalls at Connecting.
- Close the IDE's serial monitor, then open the bench here to watch the output.
Field notes
- The buttons use the chip's internal pull-ups, so each one only needs a wire to its GPIO and one to GND.
- On the C3 the red button is on GPIO 2, a strapping pin that must be high at reset. An open button does not change that; just do not hold it while the board starts.
- Use a passive buzzer or piezo. An active buzzer plays one fixed pitch whatever tone() asks for.
- The high score is kept with Preferences and survives a power cut.
Not tested yet
- Not tested on hardware yet. Compiles for every board listed with Arduino-ESP32 core 3.3.12.
Built it? Send a build report with your board and what the serial port printed. Working reports become the "Tested on this board" entries above.