In a quiz, the hardest part is often deciding who was first. Let the UNO decide! Each player has a buzzer: one is the touch sensor from the sensor kit, the other is the button module. The first to press gets their light and a beep, and the other buzzer is locked. Then the quiz master uses one button: a short press means "wrong, the other player may try", a long press means "right, one point". The scores appear in the Serial Monitor.
What you'll learn
- How to check two inputs often enough that the fastest press always wins
- How a "state" variable controls what a game is waiting for
- How to tell a short press from a long press
- How to lock out a player who has already answered
What you need
- UNO R3 board and USB cable (UNO R3 Starter Kit)
- Touch sensor module (45-in-1 Sensor Kit)
- Button module (45-in-1 Sensor Kit)
- Active buzzer module (45-in-1 Sensor Kit)
- Red and blue LEDs and 2 resistors, 220 Ω (UNO R3 Starter Kit)
- 1 push button for the quiz master (UNO R3 Starter Kit)
- Long female-to-male jumper wires
- The printed parts from the 3D printing box below (buzzer domes), or the cardboard version described there
- Breadboard and jumper wires (UNO R3 Starter Kit)
Wiring

| From | To |
|---|---|
| Touch module DO (player 1) | UNO pin 2 |
| Button module S (player 2) | UNO pin 3 |
| Quiz master button, one leg | UNO pin 4 |
| Quiz master button, diagonally opposite leg | GND |
| UNO pin 5 | 220 Ω resistor, then the red LED's long leg (player 1) |
| UNO pin 6 | 220 Ω resistor, then the blue LED's long leg (player 2) |
| Both LED short legs | GND |
| Active buzzer module S | UNO pin 8 |
| All modules + and − | 5V and GND |
Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.
The sketch
const int PLAYER_PINS[2] = {2, 3}; // touch module, button module
const int LED_PINS[2] = {5, 6};
const int HOST_PIN = 4;
const int BUZZER_PIN = 8;
int rest[2]; // reading when nobody presses
int score[2] = {0, 0};
bool lockedOut[2] = {false, false};
int answering = -1; // -1: waiting for a buzz, 0 or 1: that player answers
bool pressing(int p) {
return digitalRead(PLAYER_PINS[p]) != rest[p];
}
void beep(int ms) {
digitalWrite(BUZZER_PIN, HIGH);
delay(ms);
digitalWrite(BUZZER_PIN, LOW);
}
void printScores() {
Serial.print("Player 1 (touch): ");
Serial.print(score[0]);
Serial.print(" Player 2 (button): ");
Serial.println(score[1]);
}
void newQuestion() {
answering = -1;
lockedOut[0] = lockedOut[1] = false;
digitalWrite(LED_PINS[0], LOW);
digitalWrite(LED_PINS[1], LOW);
Serial.println("--- Next question! ---");
}
// Returns how long the quiz master held the button, in ms (0 = not pressed)
unsigned long hostPress() {
if (digitalRead(HOST_PIN) == HIGH) return 0;
unsigned long start = millis();
while (digitalRead(HOST_PIN) == LOW) {
if (millis() - start > 800) digitalWrite(LED_PINS[answering], (millis() / 100) % 2); // blink: "point!"
}
return millis() - start;
}
void setup() {
Serial.begin(9600);
for (int p = 0; p < 2; p++) {
pinMode(PLAYER_PINS[p], INPUT);
pinMode(LED_PINS[p], OUTPUT);
}
pinMode(HOST_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
delay(300);
rest[0] = digitalRead(PLAYER_PINS[0]);
rest[1] = digitalRead(PLAYER_PINS[1]);
Serial.println("Quiz buzzers ready.");
newQuestion();
}
void loop() {
if (answering < 0) {
// Read both players in the same moment; the first one to change wins
bool p0 = pressing(0) && !lockedOut[0];
bool p1 = pressing(1) && !lockedOut[1];
if (p0 || p1) {
answering = p0 ? 0 : 1; // a real tie (same microsecond) goes to player 1
digitalWrite(LED_PINS[answering], HIGH);
beep(300);
Serial.print("Player ");
Serial.print(answering + 1);
Serial.println(" answers!");
}
if (digitalRead(HOST_PIN) == LOW) { // nobody knows: skip the question
while (digitalRead(HOST_PIN) == LOW) delay(5);
newQuestion();
}
return;
}
unsigned long held = hostPress();
if (held == 0) return;
if (held > 800) { // long press: correct
score[answering]++;
beep(80); delay(80); beep(80);
printScores();
newQuestion();
} else if (held > 30) { // short press: wrong, the other player may try
lockedOut[answering] = true;
digitalWrite(LED_PINS[answering], LOW);
Serial.println("Wrong! The other player may answer.");
answering = -1;
if (lockedOut[0] && lockedOut[1]) newQuestion();
}
}
Play
- Open the Serial Monitor at 9600 baud and read out a question.
- The first player to touch or press gets their light. The other buzzer does nothing now.
- Right answer: hold the quiz master button until the light blinks (about one second). Wrong answer: press it briefly, and the other player gets a chance.
- If nobody buzzes, a short press on the quiz master button skips to the next question.
How it works
The game is always in one of two states: waiting for a buzz (answering is -1) or waiting for the quiz master (answering is 0 or 1). loop() does something different in each state. Thinking in states like this keeps a game sketch tidy, even when it gets bigger.
While waiting for a buzz, loop() does almost nothing else, so it reads both players many thousand times a second. A human reaction takes around 200 ms, so whoever is first by even a thousandth of a second wins. The lockedOut array remembers who has already given a wrong answer, so they can't buzz again for the same question.
The quiz master's button is timed: hostPress() waits until it is released and returns how long it was held. Over 800 ms counts as a long press. The LED starts blinking at that moment, so the quiz master knows they can let go.
Try this next
- Add a third player with the sensor kit's knock sensor.
- Show the scores on the 1602 LCD instead of the Serial Monitor.
- Give each player 10 seconds to answer, with a countdown beep.
Troubleshooting
- A player wins without pressing: restart the UNO without touching any buzzer, so it learns the resting values.
- The touch buzzer doesn't react: touch the metal plate with your fingertip, and turn the module's screw if needed.
- Long presses count as short ones: hold the button until the LED starts blinking.
- A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.