#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int greenLEDPin = 8;
const int redLEDPin = 9;
const int buttonPin = 2;
const int speakerPin = 10;
unsigned long ledOnTime;
bool waitingForReaction = false;
bool isGreen = false;
unsigned long bestTime = 9999;
unsigned long worstTime = 0;
void playHappyTune() {
tone(speakerPin, 523, 200); delay(200);
tone(speakerPin, 659, 200); delay(200);
tone(speakerPin, 784, 300); delay(300);
noTone(speakerPin);
}
void playNewBestTune() {
tone(speakerPin, 784, 200); delay(200);
tone(speakerPin, 880, 200); delay(200);
tone(speakerPin, 988, 300); delay(300);
tone(speakerPin, 1047, 400); delay(400);
noTone(speakerPin);
}
void playSadTune() {
tone(speakerPin, 220, 300); delay(300);
tone(speakerPin, 196, 300); delay(300);
tone(speakerPin, 174, 400); delay(400);
noTone(speakerPin);
}
void setup() {
pinMode(greenLEDPin, OUTPUT);
pinMode(redLEDPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(speakerPin, OUTPUT);
lcd.begin(16, 2);
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Reaction Timer");
delay(2000);
lcd.clear();
randomSeed(analogRead(A0)); // Improve randomness
}
void loop() {
digitalWrite(greenLEDPin, LOW);
digitalWrite(redLEDPin, LOW);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Get ready...");
delay(1000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Wait for signal");
// Wait randomly, and watch for early button press
int waitTime = random(2000, 5000);
unsigned long waitStart = millis();
while (millis() - waitStart < waitTime) {
if (digitalRead(buttonPin) == LOW) {
// Too early!
digitalWrite(redLEDPin, HIGH);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Too fast!");
playSadTune();
delay(2000);
digitalWrite(redLEDPin, LOW);
lcd.clear();
return; // Restart loop
}
}
// Start actual round
isGreen = random(0, 2) == 1;
if (isGreen) {
digitalWrite(greenLEDPin, HIGH);
} else {
digitalWrite(redLEDPin, HIGH);
}
ledOnTime = millis();
waitingForReaction = true;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("GO!");
bool redLEDTimeoutHandled = false;
while (waitingForReaction) {
unsigned long currentTime = millis();
if (digitalRead(buttonPin) == LOW) {
waitingForReaction = false;
digitalWrite(greenLEDPin, LOW);
digitalWrite(redLEDPin, LOW);
lcd.clear();
if (isGreen) {
unsigned long reactionTime = currentTime - ledOnTime;
bool isNewBest = reactionTime < bestTime;
if (isNewBest) bestTime = reactionTime;
if (reactionTime > worstTime) worstTime = reactionTime;
lcd.setCursor(0, 0);
lcd.print("You: ");
lcd.print(reactionTime);
lcd.print("ms");
lcd.setCursor(0, 1);
lcd.print("B:");
lcd.print(bestTime);
lcd.print(" W:");
lcd.print(worstTime);
isNewBest ? playNewBestTune() : playHappyTune();
} else {
lcd.setCursor(0, 0);
lcd.print("Wrong LED!");
playSadTune();
}
delay(3000);
lcd.clear();
}
if (!isGreen && !redLEDTimeoutHandled && (currentTime - ledOnTime >= 3000)) {
waitingForReaction = false;
redLEDTimeoutHandled = true;
digitalWrite(redLEDPin, LOW);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Good job!");
delay(3000);
lcd.clear();
}
}
}