Reaction Timer
Push It!
Mental chronometry is the study of how fast humans react to different inputs. It takes a few hundred milliseconds for the signal to get from your eyes, to your brain, out to your limbs to respond. The reaction timer is a great project to demonstrate this time delay. It also makes for a fun game between friends!
The principal of the reaction timer is simple: when the user sees the light turn on, press the button! A microcontroller is perfect for this because it can time milliseconds very accurately.
Required Materials
Here's a list of parts used in this project:
- RedBoard
- Big Dome Push Button - Red
- OpenSegment Serial Display - 20mm (Blue)
- JST 3 Wire Assembly
- Enclosure - Flanged (Red)
- 5V Wall Adapter
Wishlist to all of these parts
Suggested Reading
This tutorial assumes you know a few things about Arduino and serial communication. If you're a bit rusty consider checking out these tutorials:
Hardware
The hardware is really simple. The RedBoard is connected to a large dome push button and an OpenSegment four-digit display. OpenSegment has a spot for a 3-pin JST, which makes it easy to connect on the fly. Checkout this page on the OpenSegment tutorial for more info.
The RedBoard has the following connections:
- Pin 2 -> RX Pin on OpenSegment
- Pin 9 -> Anode of LED within large dome button
- Pin 7 <- The normally open (labeled NO) blade of the arcade switch
One input (the button) and two outputs (the LED in the button and the time display) is all you need! We used jumper wires and simply plugged them into the RedBoard.
This hardy red enclosure was used to withstand the endless button pounding. Cutting a hole for the dome button is pretty straight forward, and a dremel rotary tool easily cuts the slot for the 7-segment display. The edges are not very clean, but it was a quick project. If you wanted to get fancy, a printed graphic/bezel combo would provide instructions and give plenty of polish.
A few holes were drilled on the face that points down to allow for a USB cable and a power cable. The RedBoard is hotglued in place, the display is hotglued into the slot, and jumper wires provide the necessary connections. If this timer was being installed outside of SparkFun, we would solder and screw everything down, but this timer has survived quite a lot of abuse already!
The unit can run off a battery pack for a few days, but, since we are permanently installing the timer near the kegerators at work, a 5V wall adapter provides all the power we need.
Firmware
The code that runs the reaction timer is very straight forward. We simply illuminate a light and wait for the user to press the button. Download and install the example sketch.
A timer counts how many milliseconds it requires for the (slow, slow) human to respond. And, because we don't want the human to cheat, we pick a random amount of time before we turn on the light.
Note: This example assumes you are using the latest version of the Arduino IDE on your desktop. If this is your first time using Arduino, please review our tutorial on installing the Arduino IDE.
If you have not previously installed an Arduino library, please check out our installation guide.language:c
/*
Reaction Timer for the World of Wonder Childrens museum in Lafayette, CO
By: Nathan Seidle (SparkFun Electronics)
Date: May 6th, 2013
This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
How to play:
The button will be pulsing with light. Pressing the button will cause the light to turn off and the game will begin.
After a few seconds of darkness, the light will light up and the user must press the button as fast as they can.
Their reaction time is displayed on a 7 segment display.
If the user presses the button before it is illuminated the display will show '-Err' to indicate error.
*/
#include <SoftwareSerial.h>
SoftwareSerial segmentDisplay(3, 2); //RX, TX to the OpenSegment display
int LED = 9;
int button = 7;
long timeDiff; //Global variable keeps track of your score
int idleLoops = 0;
String gameTime; //Contains the last game time
int gamesPlayed; //Contains the total number of games played for the life of the device
//These functions allow the LED to be really bright when on, and
//just barely on when the game is in idle mode
#define LEDON() analogWrite(LED, 255)
#define LEDLOW() analogWrite(LED, 10)
#define LEDOFF() analogWrite(LED, 0)
void setup()
{
Serial.begin(115200);
pinMode(LED, OUTPUT);
LEDOFF(); //Turn off LED
pinMode(button, INPUT_PULLUP);
randomSeed(analogRead(A1)); //Get noise to seed the random number generator
segmentDisplay.begin(9600); //Talk to the Serial7Segment at 9600 bps
segmentDisplay.write('v'); //Reset the display - this forces the cursor to return to the beginning of the display
scrollTitle();
}
void loop()
{
if(digitalRead(button) == LOW)
{
Serial.println("Playing");
playGame();
idleLoops = 0;
}
pulseTheButton(); //If no one is playing, pulse LED to intice them. Function takes 6 seconds to complete.
idleLoops++;
if(idleLoops > 9) //Play a screen saver every 60 seconds.
{
scrollTitle(); //Screen saver = display title
idleLoops = 0;
}
}
void playGame()
{
segmentDisplay.write('v'); //Reset the display
delay(25); //Debounce the button a bit
while(digitalRead(button) == LOW) ; //Wait for user to stop hitting button
LEDLOW(); //Turn LED on low to indicate the beginning of the game
//Get random number of milliseconds
long lightTime = random(2000, 3500); //From 2 to 3.5 seconds
long zeroTime = millis();
while(millis() - zeroTime < lightTime) //Wait for random amount of time to go by
{
//If the user hits the button in this time then error out (cheater!)
if(digitalRead(button) == LOW)
{
segmentDisplay.write('v'); //Reset the display
segmentDisplay.print("-Err");
Serial.println("Err!");
blinkButton();
return;
}
}
//Begin game
Serial.println("Go!");
LEDON();
long beginTime = millis(); //Record this as the beginning of the test
//Wait for user to hit the button
while(digitalRead(button) == HIGH)
{
//Check to see if the user fails to respond in 10 seconds
timeDiff = millis() - beginTime;
if(timeDiff > 9999)
{
timeDiff = 9999;
segmentDisplay.write('v'); //Reset the display
segmentDisplay.print(timeDiff);
blinkButton();
return;
}
}
gameTime = String(timeDiff);
//Right adjust the time
if(timeDiff < 10)
gameTime = " " + gameTime;
else if(timeDiff < 100)
gameTime = " " + gameTime;
else if(timeDiff < 1000)
gameTime = " " + gameTime;
segmentDisplay.write('v'); //Reset the display
segmentDisplay.print(gameTime); //Display the game time
Serial.print("Reaction time:");
Serial.println(gameTime);
blinkButton(); //Blink the LED to indicate the end of the game
//Record that we have played this game
gamesPlayed++;
Serial.print("This time played:");
Serial.println(gamesPlayed);
//After the game is complete, the display will show the gameTime for awhile
}
//If there is no game going on, pulse the LED on/off
//If the user ever presses the button then return immediately
//This function takes approximately 6 seconds to complete
void pulseTheButton(void)
{
//Fade LED on
for(int fadeValue = 0 ; fadeValue <= 255; fadeValue += 5)
{
if(digitalRead(button) == LOW) return;
analogWrite(LED, fadeValue);
delay(30);
}
//Fade LED off
for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -= 5)
{
if(digitalRead(button) == LOW) return;
analogWrite(LED, fadeValue);
delay(30);
}
//Turn LED off for awhile
for(int x = 0 ; x < 100 ; x++)
{
if(digitalRead(button) == LOW) return;
analogWrite(LED, 0);
delay(30);
}
}
//Quickly blinks to button indicating the end of a game
void blinkButton()
{
for(int x = 0 ; x < 7 ; x++)
{
LEDON();
delay(60);
LEDOFF();
delay(60);
}
}
//Quickly scrolls the title across the display
void scrollTitle()
{
String titleStr = String(" reaction speed ");
for(int x = 0 ; x < titleStr.length() - 4 ; x++)
{
String tempStr = titleStr.substring(x, x + 4); //Chop out four letters from the string
segmentDisplay.write('v'); //Reset the display
segmentDisplay.print(tempStr); //Display this substring
for(int y = 0 ; y < 25 ; y++)
{
if(digitalRead(button) == LOW) return; //If the button is pressed, bail!
delay(10);
}
}
segmentDisplay.write('v'); //Reset the display
segmentDisplay.print(gameTime); //Display the last game time
}
Load this code onto the RedBoard, and open the terminal at 115200bps. You should see a series of debug statements such as 'Games Played' and 'Err!'.
Resources and Going Further
This project provides a really quick and solid example of how long it takes a human to react to visual stimulation. We've found that the average around the office is about 160ms, after the morning coffee has kicked in. Hopefully you can find parts of this project to use in your own timer or game.
You may also want to checkout:
- Reaction Timer Poster
- See the full reaction timer code on the github repo
- Bill Porter did a fantastic kids museum exhibit with graphics! Check his very cool Reaction Time Challenge Exhibit
- The Dice Gauntlet
- Using OpenSegment
- Switch Basics
- Connector Basics
- Battery Technologies