Reaction timer
How fast are you? The onboard LED lights after a random wait and you slap the BOOT button. Your best time survives a power cut.
Wiring
| From | To board | Note |
|---|---|---|
| Onboard LED | GPIO 2 | Already on the board. On the classic ESP32 DevKitC, add an LED and 220 Ω from GPIO 2 to GND |
| BOOT button | GPIO 0 | Already on the board |
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
// Reaction timer: the LED lights after a random wait, press BOOT as fast as you can.
#include <Preferences.h>
const int LED = 2; // the board's LED
const bool LED_IS_RGB = false; // an addressable RGB LED rather than a plain one
const int LED_ON = HIGH; // the level that lights a plain LED
const int BUTTON = 0; // the BOOT button
Preferences prefs;
unsigned long best = 0; // best time in ms, 0 until the first round
void led(bool on) {
if (LED_IS_RGB) rgbLedWrite(LED, 0, on ? 64 : 0, 0); // green
else digitalWrite(LED, on ? LED_ON : !LED_ON);
}
bool pressed() {
return digitalRead(BUTTON) == LOW;
}
void waitForRelease() {
while (pressed()) delay(5);
delay(30); // let the contacts settle
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(BUTTON, INPUT_PULLUP);
if (!LED_IS_RGB) pinMode(LED, OUTPUT);
led(false);
prefs.begin("reaction", false);
best = prefs.getULong("best", 0);
randomSeed(esp_random());
Serial.println("Reaction timer. Press BOOT when the LED lights.");
if (best) Serial.printf("best so far: %lu ms\n", best);
}
void loop() {
Serial.println("get ready...");
waitForRelease();
unsigned long wait = random(1500, 5000);
unsigned long start = millis();
while (millis() - start < wait) {
if (pressed()) {
Serial.println("false start! wait for the light");
for (int i = 0; i < 6; i++) {
led(i % 2 == 0);
delay(80);
}
led(false);
waitForRelease();
delay(1000);
return;
}
delay(1);
}
led(true);
unsigned long lit = micros();
while (!pressed()) {
if (micros() - lit > 3000000UL) {
led(false);
Serial.println("too slow, try again");
delay(1000);
return;
}
}
unsigned long ms = (micros() - lit) / 1000;
led(false);
Serial.printf("reaction=%lu ms\n", ms);
if (best == 0 || ms < best) {
best = ms;
prefs.putULong("best", best);
Serial.printf("new best: %lu ms\n", best);
}
waitForRelease();
delay(1500);
}Wiring
| From | To board | Note |
|---|---|---|
| Onboard LED | GPIO 38 | Already on the board. On the classic ESP32 DevKitC, add an LED and 220 Ω from GPIO 2 to GND |
| BOOT button | GPIO 0 | Already on the board |
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
// Reaction timer: the LED lights after a random wait, press BOOT as fast as you can.
#include <Preferences.h>
const int LED = 38; // the board's LED
const bool LED_IS_RGB = true; // an addressable RGB LED rather than a plain one
const int LED_ON = HIGH; // the level that lights a plain LED
const int BUTTON = 0; // the BOOT button
Preferences prefs;
unsigned long best = 0; // best time in ms, 0 until the first round
void led(bool on) {
if (LED_IS_RGB) rgbLedWrite(LED, 0, on ? 64 : 0, 0); // green
else digitalWrite(LED, on ? LED_ON : !LED_ON);
}
bool pressed() {
return digitalRead(BUTTON) == LOW;
}
void waitForRelease() {
while (pressed()) delay(5);
delay(30); // let the contacts settle
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(BUTTON, INPUT_PULLUP);
if (!LED_IS_RGB) pinMode(LED, OUTPUT);
led(false);
prefs.begin("reaction", false);
best = prefs.getULong("best", 0);
randomSeed(esp_random());
Serial.println("Reaction timer. Press BOOT when the LED lights.");
if (best) Serial.printf("best so far: %lu ms\n", best);
}
void loop() {
Serial.println("get ready...");
waitForRelease();
unsigned long wait = random(1500, 5000);
unsigned long start = millis();
while (millis() - start < wait) {
if (pressed()) {
Serial.println("false start! wait for the light");
for (int i = 0; i < 6; i++) {
led(i % 2 == 0);
delay(80);
}
led(false);
waitForRelease();
delay(1000);
return;
}
delay(1);
}
led(true);
unsigned long lit = micros();
while (!pressed()) {
if (micros() - lit > 3000000UL) {
led(false);
Serial.println("too slow, try again");
delay(1000);
return;
}
}
unsigned long ms = (micros() - lit) / 1000;
led(false);
Serial.printf("reaction=%lu ms\n", ms);
if (best == 0 || ms < best) {
best = ms;
prefs.putULong("best", best);
Serial.printf("new best: %lu ms\n", best);
}
waitForRelease();
delay(1500);
}Wiring
| From | To board | Note |
|---|---|---|
| Onboard LED | GPIO 8 | Already on the board. On the classic ESP32 DevKitC, add an LED and 220 Ω from GPIO 2 to GND |
| BOOT button | GPIO 9 | Already on the board |
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
// Reaction timer: the LED lights after a random wait, press BOOT as fast as you can.
#include <Preferences.h>
const int LED = 8; // the board's LED
const bool LED_IS_RGB = true; // an addressable RGB LED rather than a plain one
const int LED_ON = HIGH; // the level that lights a plain LED
const int BUTTON = 9; // the BOOT button
Preferences prefs;
unsigned long best = 0; // best time in ms, 0 until the first round
void led(bool on) {
if (LED_IS_RGB) rgbLedWrite(LED, 0, on ? 64 : 0, 0); // green
else digitalWrite(LED, on ? LED_ON : !LED_ON);
}
bool pressed() {
return digitalRead(BUTTON) == LOW;
}
void waitForRelease() {
while (pressed()) delay(5);
delay(30); // let the contacts settle
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(BUTTON, INPUT_PULLUP);
if (!LED_IS_RGB) pinMode(LED, OUTPUT);
led(false);
prefs.begin("reaction", false);
best = prefs.getULong("best", 0);
randomSeed(esp_random());
Serial.println("Reaction timer. Press BOOT when the LED lights.");
if (best) Serial.printf("best so far: %lu ms\n", best);
}
void loop() {
Serial.println("get ready...");
waitForRelease();
unsigned long wait = random(1500, 5000);
unsigned long start = millis();
while (millis() - start < wait) {
if (pressed()) {
Serial.println("false start! wait for the light");
for (int i = 0; i < 6; i++) {
led(i % 2 == 0);
delay(80);
}
led(false);
waitForRelease();
delay(1000);
return;
}
delay(1);
}
led(true);
unsigned long lit = micros();
while (!pressed()) {
if (micros() - lit > 3000000UL) {
led(false);
Serial.println("too slow, try again");
delay(1000);
return;
}
}
unsigned long ms = (micros() - lit) / 1000;
led(false);
Serial.printf("reaction=%lu ms\n", ms);
if (best == 0 || ms < best) {
best = ms;
prefs.putULong("best", best);
Serial.printf("new best: %lu ms\n", best);
}
waitForRelease();
delay(1500);
}Wiring
| From | To board | Note |
|---|---|---|
| Onboard LED | GPIO 18 | Already on the board. On the classic ESP32 DevKitC, add an LED and 220 Ω from GPIO 2 to GND |
| BOOT button | GPIO 0 | Already on the board |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32S2 Dev Module
// Reaction timer: the LED lights after a random wait, press BOOT as fast as you can.
#include <Preferences.h>
const int LED = 18; // the board's LED
const bool LED_IS_RGB = true; // an addressable RGB LED rather than a plain one
const int LED_ON = HIGH; // the level that lights a plain LED
const int BUTTON = 0; // the BOOT button
Preferences prefs;
unsigned long best = 0; // best time in ms, 0 until the first round
void led(bool on) {
if (LED_IS_RGB) rgbLedWrite(LED, 0, on ? 64 : 0, 0); // green
else digitalWrite(LED, on ? LED_ON : !LED_ON);
}
bool pressed() {
return digitalRead(BUTTON) == LOW;
}
void waitForRelease() {
while (pressed()) delay(5);
delay(30); // let the contacts settle
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(BUTTON, INPUT_PULLUP);
if (!LED_IS_RGB) pinMode(LED, OUTPUT);
led(false);
prefs.begin("reaction", false);
best = prefs.getULong("best", 0);
randomSeed(esp_random());
Serial.println("Reaction timer. Press BOOT when the LED lights.");
if (best) Serial.printf("best so far: %lu ms\n", best);
}
void loop() {
Serial.println("get ready...");
waitForRelease();
unsigned long wait = random(1500, 5000);
unsigned long start = millis();
while (millis() - start < wait) {
if (pressed()) {
Serial.println("false start! wait for the light");
for (int i = 0; i < 6; i++) {
led(i % 2 == 0);
delay(80);
}
led(false);
waitForRelease();
delay(1000);
return;
}
delay(1);
}
led(true);
unsigned long lit = micros();
while (!pressed()) {
if (micros() - lit > 3000000UL) {
led(false);
Serial.println("too slow, try again");
delay(1000);
return;
}
}
unsigned long ms = (micros() - lit) / 1000;
led(false);
Serial.printf("reaction=%lu ms\n", ms);
if (best == 0 || ms < best) {
best = ms;
prefs.putULong("best", best);
Serial.printf("new best: %lu ms\n", best);
}
waitForRelease();
delay(1500);
}Wiring
| From | To board | Note |
|---|---|---|
| Onboard LED | GPIO 8 | Already on the board. On the classic ESP32 DevKitC, add an LED and 220 Ω from GPIO 2 to GND |
| BOOT button | GPIO 9 | Already on the board |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32C6 Dev Module
// Reaction timer: the LED lights after a random wait, press BOOT as fast as you can.
#include <Preferences.h>
const int LED = 8; // the board's LED
const bool LED_IS_RGB = true; // an addressable RGB LED rather than a plain one
const int LED_ON = HIGH; // the level that lights a plain LED
const int BUTTON = 9; // the BOOT button
Preferences prefs;
unsigned long best = 0; // best time in ms, 0 until the first round
void led(bool on) {
if (LED_IS_RGB) rgbLedWrite(LED, 0, on ? 64 : 0, 0); // green
else digitalWrite(LED, on ? LED_ON : !LED_ON);
}
bool pressed() {
return digitalRead(BUTTON) == LOW;
}
void waitForRelease() {
while (pressed()) delay(5);
delay(30); // let the contacts settle
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(BUTTON, INPUT_PULLUP);
if (!LED_IS_RGB) pinMode(LED, OUTPUT);
led(false);
prefs.begin("reaction", false);
best = prefs.getULong("best", 0);
randomSeed(esp_random());
Serial.println("Reaction timer. Press BOOT when the LED lights.");
if (best) Serial.printf("best so far: %lu ms\n", best);
}
void loop() {
Serial.println("get ready...");
waitForRelease();
unsigned long wait = random(1500, 5000);
unsigned long start = millis();
while (millis() - start < wait) {
if (pressed()) {
Serial.println("false start! wait for the light");
for (int i = 0; i < 6; i++) {
led(i % 2 == 0);
delay(80);
}
led(false);
waitForRelease();
delay(1000);
return;
}
delay(1);
}
led(true);
unsigned long lit = micros();
while (!pressed()) {
if (micros() - lit > 3000000UL) {
led(false);
Serial.println("too slow, try again");
delay(1000);
return;
}
}
unsigned long ms = (micros() - lit) / 1000;
led(false);
Serial.printf("reaction=%lu ms\n", ms);
if (best == 0 || ms < best) {
best = ms;
prefs.putULong("best", best);
Serial.printf("new best: %lu ms\n", best);
}
waitForRelease();
delay(1500);
}Wiring
| From | To board | Note |
|---|---|---|
| Onboard LED | GPIO 21 | Already on the board. On the classic ESP32 DevKitC, add an LED and 220 Ω from GPIO 2 to GND |
| BOOT button | GPIO 0 | Already on the board |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for XIAO_ESP32S3
// Reaction timer: the LED lights after a random wait, press BOOT as fast as you can.
#include <Preferences.h>
const int LED = 21; // the board's LED
const bool LED_IS_RGB = false; // an addressable RGB LED rather than a plain one
const int LED_ON = LOW; // the level that lights a plain LED
const int BUTTON = 0; // the BOOT button
Preferences prefs;
unsigned long best = 0; // best time in ms, 0 until the first round
void led(bool on) {
if (LED_IS_RGB) rgbLedWrite(LED, 0, on ? 64 : 0, 0); // green
else digitalWrite(LED, on ? LED_ON : !LED_ON);
}
bool pressed() {
return digitalRead(BUTTON) == LOW;
}
void waitForRelease() {
while (pressed()) delay(5);
delay(30); // let the contacts settle
}
void setup() {
Serial.begin(115200);
delay(500);
pinMode(BUTTON, INPUT_PULLUP);
if (!LED_IS_RGB) pinMode(LED, OUTPUT);
led(false);
prefs.begin("reaction", false);
best = prefs.getULong("best", 0);
randomSeed(esp_random());
Serial.println("Reaction timer. Press BOOT when the LED lights.");
if (best) Serial.printf("best so far: %lu ms\n", best);
}
void loop() {
Serial.println("get ready...");
waitForRelease();
unsigned long wait = random(1500, 5000);
unsigned long start = millis();
while (millis() - start < wait) {
if (pressed()) {
Serial.println("false start! wait for the light");
for (int i = 0; i < 6; i++) {
led(i % 2 == 0);
delay(80);
}
led(false);
waitForRelease();
delay(1000);
return;
}
delay(1);
}
led(true);
unsigned long lit = micros();
while (!pressed()) {
if (micros() - lit > 3000000UL) {
led(false);
Serial.println("too slow, try again");
delay(1000);
return;
}
}
unsigned long ms = (micros() - lit) / 1000;
led(false);
Serial.printf("reaction=%lu ms\n", ms);
if (best == 0 || ms < best) {
best = ms;
prefs.putULong("best", best);
Serial.printf("new best: %lu ms\n", best);
}
waitForRelease();
delay(1500);
}Parts
| Quantity | Part | Where to buy |
|---|---|---|
| 1× | ESP32 dev board | Buy at Amazon |
| 1× | LED and 220 Ω resistor, only on a board without its own LED |
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 classic ESP32-DevKitC V4 has no user LED. Many 30-pin clones have one on GPIO 2; otherwise add an LED and a 220 Ω resistor from GPIO 2 to GND.
- The S3, C3, S2 and C6 DevKits have an addressable RGB LED instead, driven with rgbLedWrite(). On the first S3-DevKitC-1 revision it is on GPIO 48, not 38.
- The XIAO ESP32S3's LED lights when its pin is low, which is why LED_ON is LOW there.
- Do not hold BOOT while the board resets: that puts it in download mode. Start pressing once it prints 'get ready'.
- The best time is kept with Preferences, so it survives a power cut. Erasing the whole flash clears it.
- Most people land between 200 and 300 ms. Under 150 ms is usually a lucky guess.
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.