The Reaction Game Using Multi-Function Shield

The Reaction Game Using Multi-Function Shield

HARDWARE REQUIRED:

  • PICUNO Microcontroller board
  • 1 × Multi-Function Shield
  • USB cable

DESCRIPTION:

This project turns your Multifunction Shield into a fun game to test your reaction speed. The program will wait for a random amount of time and then suddenly light up one of the first three LEDs. The player's goal is to press the button directly underneath the lit LED as quickly as possible. The game provides instant feedback with sounds from the buzzer for success or failure.

CIRCUIT DETAILS:

  • Plug the Multi-function Shield directly on top of the PICUNO board. The shield's components have fixed connections to the board's pins.
  • LEDs (D1-D4) are connected to GPIO 13, 12, 11, and 10.
  • Buttons (S1-S3) are connected to Analog Pins A1, A2, and A3.
  • Buzzer is connected to GPIO 3.

SCHEMATIC:

No external wiring is required. Place the Multi-Function shield directly on top of the PICUNO board.

Multi-Function shield:

LEDs (D1-D4) → GPIO 13, 12, 11, 10.

Buttons (S1-S3) → A1, A2, A3.

Buzzer → GPIO 3

CODE -- C:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// --- Pin Definitions ---
const int ledPins[] = {13, 12, 11}; 
const int buttonPins[] = {A1, A2, A3};
const int BUZZER_PIN = 3;

void setup() {
  for (int i = 0; i < 3; i++) {
    pinMode(ledPins[i], OUTPUT);
    digitalWrite(ledPins[i], HIGH); 
    pinMode(buttonPins[i], INPUT_PULLUP);
  }
  pinMode(BUZZER_PIN, OUTPUT);
  randomSeed(analogRead(A0)); // Seed the random number generator
  Serial.begin(9600);
  Serial.println("Reaction Game Started!");
  delay(1000);
}

void loop() {
  Serial.println("Get ready...");
  for (int i = 0; i < 3; i++) {
    digitalWrite(ledPins[i], HIGH); 
  }

  // Wait for a random time between 1 and 2 seconds
  delay(random(1000, 2000));

  int ledToPress = random(0, 3);
  digitalWrite(ledPins[ledToPress], LOW); 

  Serial.print("LED ");
  Serial.print(ledToPress + 1);
  Serial.println(" is ON!");

  unsigned long startTime = millis();
  unsigned long timeLimit = 1000; 
  bool answered = false;
  bool playerWon = false;

  while ((millis() - startTime < timeLimit) && !answered) {
    for (int i = 0; i < 3; i++) {
      if (digitalRead(buttonPins[i]) == LOW) { 
        answered = true;
        if (i == ledToPress) {
          playerWon = true;
        }
        break; // Exit the for loop
      }
    }
  }

  if (playerWon) {
    Serial.println("Correct!");
    tone(BUZZER_PIN, 1500, 150); // Success sound
  } else {
    Serial.println("Too slow or wrong button!");
    tone(BUZZER_PIN, 200, 300); // Fail sound
  }

  delay(2000); 
}
randomSeed(analogRead(A0)) - This command uses the random electrical noise from an unconnected Analog pin (A0) to "seed" the random number generator, ensuring that the sequence of lit LEDs is different every time you turn the device on.

random(min, max) - This function generates a random number within a specified range. It's used to create the random delay before an LED lights up and to choose which of the three LEDs will be the target.

digitalRead(buttonPins[i]) == LOW - This is how the code checks if a button has been pressed. Because the pins are configured with INPUT_PULLUP, their default state is HIGH.

tone(BUZZER_PIN, frequency, duration) - This function creates the sound effects. It generates a sound wave of a specific frequency on the buzzer pin for a set duration, providing instant audio feedback for a correct or incorrect answer.

CODE -- PYTHON:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
from machine import Pin, PWM
from time import sleep_ms, ticks_ms, ticks_diff
import urandom
import time 

# --- Pin Definitions ---
leds = [Pin(13, Pin.OUT, value=1), Pin(12, Pin.OUT, value=1), Pin(11, Pin.OUT, value=1)]
buttons = [Pin(27, Pin.IN, Pin.PULL_UP), Pin(28, Pin.IN, Pin.PULL_UP), Pin(29, Pin.IN, Pin.PULL_UP)]

buzzer = PWM(Pin(3))

def play_tone(freq, duration):
    buzzer.freq(freq)
    buzzer.duty_u16(32768) # 50% volume
    sleep_ms(duration)
    buzzer.duty_u16(0)

print("Reaction Game Started!")
sleep_ms(1000)

while True:
    print("Get ready...")
    for led in leds:
        led.high()
    
    # random time between 1 and 2 seconds
    sleep_ms(urandom.randint(1000, 2000))
    
    # Pick a random LED to light up
    led_to_press = urandom.randint(0, 2)
    leds[led_to_press].low() 
    
    start_time = ticks_ms()
    time_limit = 700 # 1 second to react
    player_won = False
    answered = False
    
    print(f"LED {led_to_press + 1} is ON!")
    
    # Game loop to wait for player's reaction
    while ticks_diff(ticks_ms(), start_time) < time_limit and not answered:
        for i in range(3):
            if buttons[i].value() == 0: 
                answered = True
                if i == led_to_press:
                    # Correct button
                    player_won = True
                break
    
    if player_won:
        print("Correct!")
        play_tone(1500, 150) # Success sound
    else:
        print("Too slow or wrong button!")
        play_tone(200, 300) # Fail sound

    sleep_ms(2000)
urandom.randint() - This function is used to make the game unpredictable. It generates a random number for the delay time and for which LED to light up.

leds[led_to_press].low() - Turns on the target LED. The LEDs on this shield are "active low," meaning they light up when the pin is set to a LOW voltage.

Game Logic - The code waits for a button press. It checks if the button that was pressed (i) corresponds to the LED that was lit (led_to_press).

Timer (ticks_ms) - The game gives the player a 1-second time limit to press the correct button.