Build quiz buzzers for two players

Two printed buzzers, one with a touch sensor and one with a push button. Whoever is first gets their light and a beep, and the quiz master awards points or passes the question on.
  • Level:Intermediate
  • Duration: 50 min
  • Ages 10+
  • Platform: Arduino

Tools for this lesson

Helpful for this build. Open a tool to see tips and more projects that use it.

Build quiz buzzers for two players – EEMENTOR STEM™ lesson

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

Breadboard wiring diagram: Build quiz buzzers for two players
Breadboard view. Check each connection against the wiring table below.
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

  1. Open the Serial Monitor at 9600 baud and read out a question.
  2. The first player to touch or press gets their light. The other buzzer does nothing now.
  3. 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.
  4. 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.

Build it yourself

Everything used in this lesson comes in one kit, with parts tested by our engineers.

45-in-1 Sensor Kit | Compatible with Arduino IDE and ESP32 $37.00

Shop the kit