ESP32 Bench
Boards: ESP32, S2, S3, C3, C6
Flash and monitor: Web Serial
← All projects

Simon memory game

Four lights, four buttons, four tones. Repeat the growing pattern for as long as you can; the high score is saved.

Intermediate40 min115200 baud
Board
Fig. 1. Pins this project uses on the ESP32-DevKitC V4, 38 pin. USB connector at the bottom.

Wiring

FromTo boardNote
Green LED, through 220 ΩGPIO 16LED cathodes to GND
Red LED, through 220 ΩGPIO 17
Yellow LED, through 220 ΩGPIO 18
Blue LED, through 220 ΩGPIO 19
Green buttonGPIO 25Other side of each button to GND. The internal pull-ups do the rest
Red buttonGPIO 26
Yellow buttonGPIO 27
Blue buttonGPIO 32
Buzzer +GPIO 23Buzzer - 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);
}

Parts

QuantityPartWhere to buy
1×ESP32 dev boardBuy at Amazon
4×LEDs: green, red, yellow and blue
4×220 Ω resistors
4×Push buttonsBuy at Adafruit
1×Passive buzzer or piezoBuy 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

  1. In the Arduino IDE, install the esp32 board package and pick the board named above the sketch.
  2. Paste the sketch, choose the board's port and upload. Hold BOOT if the upload stalls at Connecting.
  3. 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.