BeginnerArduino about 45 min

Arduino Reaction-Time Game

Wait for the onboard LED, press a button, and see your reaction time. Learn debouncing, false-start detection and state machines.

Arduino Reaction-Time Game
Illustrative cover. Follow the written wiring steps.

Steps

  1. Prepare the board and button

    Use an Arduino UNO-compatible board, its matching USB cable, a breadboard, a momentary tactile button and jumper wires. The onboard LED is the visual cue, so no external LED or resistor is needed. Four-legged tactile switches have two internally connected leg pairs; use contacts from opposite switched sides, not two legs already joined. Disconnect USB before wiring.

  2. Wire an active-low input

    Connect UNO D2 to one switched side of the button and GND to the other. Do not connect the button to 5V. INPUT_PULLUP keeps the released input HIGH; pressing connects it to ground and reads LOW. If the input never changes, check the button leg pairs with a continuity meter while unpowered.

  3. Upload and open the monitor

    Select your UNO board and port in Arduino IDE. Upload the included sketch and open Serial Monitor at 115200 baud. Release the button after power-up. The Ready message means you can begin a round. The sketch confirms input changes after 25 ms to reject contact bounce.

  4. Play one round

    Press and release to start. Wait for the onboard LED to light, then press again. The waiting time varies from roughly two to five seconds. The cue is withheld while the start button is held. The result uses the initial edge time of the debounced response, reducing the added confirmation delay. Release before starting another round.

  5. Check error handling

    Press again before the cue to test a false start. Hold the start button and verify that the cue does not appear until release. Ignore the cue for ten seconds to test the round timeout. A long held response must produce one result, not repeated results.

  6. Compare several trials

    Record five results, include their units, and compare the median rather than only your fastest score. This is a learning experiment, not a calibrated psychological measurement. Compare the state-machine guide with the four phases in the code before adding a display or another player.

Code

#include <Arduino.h>
const uint8_t BUTTON = 2;
const uint32_t DEBOUNCE_MS = 25;
enum Phase { RELEASE, READY, WAIT, GO };
Phase phase = RELEASE;
bool rawState = HIGH, stableState = HIGH;
uint32_t changedAt = 0, startedAt = 0, waitMs = 0, goAt = 0;
void setup() {
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  Serial.begin(115200);
  Serial.println("Release button, then press to start. Wait for LED, then press again.");
}
void loop() {
  uint32_t now = millis();
  bool raw = digitalRead(BUTTON);
  if (raw != rawState) { rawState = raw; changedAt = now; }
  bool pressed = false;
  if (rawState != stableState && uint32_t(now - changedAt) >= DEBOUNCE_MS) {
    stableState = rawState;
    pressed = stableState == LOW;
  }
  if (phase == RELEASE) {
    if (rawState == HIGH && stableState == HIGH && uint32_t(now - changedAt) >= DEBOUNCE_MS) {
      phase = READY; Serial.println("Ready: press to start.");
    }
    return;
  }
  if (phase == READY && pressed) {
    // Variation comes from when the player starts; not a security random source.
    waitMs = 2000UL + (micros() % 3001UL);
    startedAt = now; phase = WAIT; Serial.println("Wait..."); return;
  }
  if (phase == WAIT) {
    if (pressed) {
      Serial.println("False start. Release and try again."); phase = RELEASE; return;
    }
    // Do not give the cue while the start button is still held.
    if (uint32_t(now - startedAt) >= waitMs && rawState == HIGH && stableState == HIGH) {
      digitalWrite(LED_BUILTIN, HIGH); goAt = now; phase = GO;
    }
    return;
  }
  if (phase == GO && pressed) {
    // Timestamp the first raw edge, before the debounce confirmation delay.
    Serial.print("Reaction ms: "); Serial.println(uint32_t(changedAt - goAt));
    digitalWrite(LED_BUILTIN, LOW); phase = RELEASE;
  } else if (phase == GO && uint32_t(now - goAt) >= 10000UL) {
    Serial.println("Round timed out. Release and try again.");
    digitalWrite(LED_BUILTIN, LOW); phase = RELEASE;
  }
}
Notes. Onboard LED only. One tactile switch is used, but the catalog minimum purchase quantity is currently ten; the cart applies this automatically. USB timing, button travel and software polling affect results. No target-board or physical hardware verification has been performed. Cover is an existing illustrative Arduino workshop image.