Inspiration
For this week’s assignment, I designed and built a mini F1 Reaction Game — a quick reflex challenge that adds a spark of fun and competition to any desk ⚡. It’s a two-player game where you race to hit your button faster than your opponent after a countdown. The winner’s light flashes, and their reaction time is shown instantly on the screen.
I chose this idea because it turns short breaks at the desk into fun, energizing moments instead of mindless scrolling. It was inspired by the intense start lights of Formula 1 races and the excitement of classic reaction games — where every millisecond counts. This little gadget brings that thrill right to my workspace.
Software
Tinkercad
I tested the buzzer loop game on Tinkercad Circuits by setting up the power, wires, and buzzer virtually. It let me see how the design works and fix mistakes safely before building it—like a quick rehearsal before the real thing.
Arduino IDE
I used the Arduino IDE as the main software tool for this assignment. The Arduino IDE is an integrated development environment designed specifically for programming and uploading code to Arduino boards. It provides a simple and user-friendly interface to write, compile, and debug code. It also includes a wide range of built-in libraries that make it easy to work with various sensors, modules, and electronic components. This made it an ideal choice for developing and testing the logic of my project efficiently.
Materials
Cardboard
Glue Gun
Cutter
Scissors
Electronic Components
Arduino UNO
Breadboard
Jumper Wires
LED
220 Ohm Resistor
16x2 LCD Display with I2C Module
Push Button
Circuit Design on Tinkercad
I started by adding the main components I needed:
Arduino UNO
LEDs
Push Buttons
220Ω resistors
Breadboard
16x2 LCD Display with I2C Module
After placing them, I connected the circuit and ran a simulation on Tinkercad to check for possible errors, short circuits, or burnouts. This helped ensure the design was safe and functional before moving forward.
Wiring the Circuit
To wire the mini F1 Reaction game circuit, I followed these steps:
1. Power Rails Setup
Connected the Arduino’s 5V pin to the positive rail (+) of the breadboard.
Connected the GND pin to the negative rail (–) to provide a common ground for all components.
2. Connecting the LCD (I2C)
Connected the VCC pin of the LCD module to the +5V rail and the GND pin to the ground rail.
Connected the SDA pin of the LCD to Arduino A4 and the SCL pin to Arduino A5 (these are the I2C communication lines).
3. Connecting the Buttons
Placed three push buttons on the breadboard: one for Start, one for Player 1, and one for Player 2.
Connected one side of each button to Arduino pins 2, 3, and 4, respectively.
Connected the other side of each button to the ground rail (–).
Enabled internal pull-up resistors in the code (using INPUT_PULLUP), so no external resistors were needed.
4. Connecting the LEDs
Placed three LEDs: green (start indicator) on pin 5, red (Player 1) on pin 6, and red (Player 2) on pin 7.
Connected each LED’s positive (long) leg to its assigned pin through a 220Ω resistor to limit current.
Connected each LED’s negative (short) leg to the ground rail (–).
5. Final Check
Verified all connections were secure, ensuring no loose wires or short circuits.
Powered the Arduino through USB to test the circuit.
Preparing the cardboard
To build the body of the F1 Reaction Game, I prepared the cardboard as follows:
Cutting the Base and Sides
Measured and cut the cardboard pieces for the base and sides as shown in the reference image.
Left 2 cm margins on each side to ensure smooth connections between the cardboard panels.
Placing the Components
Marked and cut spaces for the LCD, buttons, and LEDs so they could be mounted securely.
Positioned each component carefully to make them accessible and visible from the outside.
Hiding the Wiring
Organized and routed the wires inside the enclosure to keep them hidden from view.
Placed any unnecessary components inside the structure to give the project a clean and organized look.
Final Assembly
Used tools like a glue gun to fix the cardboard pieces together securely.
Closed the enclosure and tested the setup to ensure it worked properly.
The Code
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
What it does:
Adds the libraries needed to communicate with the LCD through I2C (which uses only 2 Arduino pins).
Creates an lcd object with the I2C address 0x27, 16 columns, and 2 rows.
This allows you to display messages on the LCD screen.
const int startButton = 2;
const int player1Button = 3;
const int player2Button = 4;
const int greenLED = 5;
const int player1LED = 6;
const int player2LED = 7;
What it does:
Assigns names to each input/output pin.
Using const int ensures these pin numbers never change during the program.
Inputs: Start button, Player1 button, Player2 button
Outputs: Green LED, Player1 LED, Player2 LED
bool gameRunning = false;
int startTime = 0;
bool winnerDeclared = false;
What it does:
gameRunning: Tracks whether the game has started.
startTime: Stores the time (in ms) when the “GO” signal appears to measure reaction time.
winnerDeclared: Makes sure only the first button press counts as the winner.
void setup() {
pinMode(startButton, INPUT_PULLUP);
pinMode(player1Button, INPUT_PULLUP);
pinMode(player2Button, INPUT_PULLUP);
pinMode(greenLED, OUTPUT);
pinMode(player1LED, OUTPUT);
pinMode(player2LED, OUTPUT);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("F1 Reaction Game");
lcd.setCursor(0, 1);
lcd.print("Press Start!");
}
What it does:
Configures the pins as inputs (buttons) or outputs (LEDs).
Initializes and turns on the LCD.
Shows a welcome message when the Arduino powers up.
void loop() {
if (!gameRunning && digitalRead(startButton) == LOW) {
startGame();
}
if (gameRunning && !winnerDeclared) {
checkWinner();
}
}
What it does:
Continuously checks if the start button is pressed → starts the game.
If the game is running and no winner yet → checks who presses their button first.
void startGame() {
gameRunning = true;
winnerDeclared = false;
digitalWrite(player1LED, LOW);
digitalWrite(player2LED, LOW);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Get Ready...");
delay(1000);
digitalWrite(greenLED, HIGH);
for (int i = 1; i <= 3; i++) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Countdown:");
lcd.setCursor(0, 1);
lcd.print(i);
delay(1000);
}
digitalWrite(greenLED, LOW);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("GO!");
lcd.setCursor(0, 1);
lcd.print("Press Button!");
startTime = millis();
}
What it does:
Starts the game, resets LEDs, and displays “Get Ready” then a countdown on the LCD.
Green LED stays on during the countdown.
Shows “GO!” then starts the reaction timer.
void checkWinner() {
if (digitalRead(player1Button) == LOW) {
declareWinner(1);
} else if (digitalRead(player2Button) == LOW) {
declareWinner(2);
}
}
What it does:
Checks which player pressed their button first.
Calls declareWinner() and sends the player number.
void declareWinner(int player) {
winnerDeclared = true;
gameRunning = false;
int reactionTime = millis() - startTime;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Player ");
lcd.print(player);
lcd.print(" Wins!");
lcd.setCursor(0, 1);
lcd.print("Time: ");
lcd.print(reactionTime);
lcd.print(" ms");
if (player == 1) {
digitalWrite(player1LED, HIGH);
} else {
digitalWrite(player2LED, HIGH);
}
}
What it does:
Stops the game and records reaction time.
Displays the winner and their time on the LCD.
Lights up the winner’s LED.
The Code File
Implementation Process
First, I began by building the circuit in real life, making sure it worked the same way as the simulation. After that, I cut the cardboard into the pieces I had planned earlier and prepared them to form the body of the game.
Next, I carefully placed the components inside the box, making sure everything fit neatly. I also connected the game wire—the loop where the ring moves through—to the circuit.
Once everything was secured and connected, the final product was ready.
Final Product
Creating a New Function
When I was writing the code, I initially faced problems because I had many conditions nested inside each other, which made the code confusing and difficult to track. I paused at this point and searched for solutions, and I found that a better approach would be to create separate functions that can be defined once and then called whenever needed. This approach made the code much easier to understand, more organized, reusable, and efficient. By doing this, I was also able to revisit the previous problems I faced and rewrite them more clearly. A common challenge others can avoid is trying to handle all logic inside the loop() function—breaking the code into smaller functions makes it much easier to debug and maintain.