#include <SPI.h>
// Pin Definitions
const int buttonPins[] = {2, 3, 4, 5, 6}; // Pins for the buttons
const int buzzerPin = 7; // Pin for the buzzer
const int redLedPin = 11; // Pin for the red LED
const int relayButtonPin = 12; // Pin for the relay control button
const int relayPin = 13; // Pin for the relay
// Score values for each button
const int buttonScores[] = {25, 50, 100, 200, 300};
// 74HC595 shift register pins for score display
const int latchPin = 8; // RCLK (ST_CP) pin
const int clockPin = 9; // SRCLK (SH_CP) pin
const int dataPin = 10; // SER (DS) pin
// 74HC595 shift register pins for Ball In Play display
const int ballLatchPin = A0; // RCLK (ST_CP) pin
const int ballClockPin = A1; // SRCLK (SH_CP) pin
const int ballDataPin = A2; // SER (DS) pin
// Note frequencies for piano notes from A3 to A4 (including sharps)
#define NOTE_A3 220
#define NOTE_A3_SHARP 233
#define NOTE_B3 247
#define NOTE_C4 261
#define NOTE_C4_SHARP 277
#define NOTE_D4 294
#define NOTE_D4_SHARP 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_F4_SHARP 370
#define NOTE_G4 392
#define NOTE_G4_SHARP 415
#define NOTE_A4 440
// Variables
int totalScore = 0; // Total score
unsigned long gameStartTime = 0; // Start time of the game
int totalButtonPresses = 0; // Total number of button presses
bool isFirstButtonPressed = false; // Flag to indicate if the first button is pressed
bool isRelayButtonPressed = false; // Flag to indicate if the relay button has been pressed
bool gameStarted = false; // Flag to indicate if the game has started
int ballInPlay = 1; // Ball in play number
unsigned long previousMillis = 0; // Stores the last time the LED was updated
const long interval = 500; // Interval for blinking the LED (0.5 seconds)
// Variables for relay control
unsigned long relayLastActivated = 0; // Stores the last time the relay was activated
const long relayActivationInterval = 1000; // Interval before the relay can be activated again (1 second)
// Animation segments for the score display
byte animationSegments[] = {
0b11111101, // Segment a
0b11111011, // Segment f
0b11110111, // Segment e
0b11101111, // Segment d
0b11011111, // Segment c
0b11111110 // Segment b
};
// Function prototypes
void resetGame();
void updateScore(int button);
void displayScore(int score);
void displayBallInPlay(int ball);
void clearBallInPlay();
void playHappyMelody();
void playStartGameMelody();
void playTone(int note, int duration, int delayTime);
void playAnimation();
byte digitToSegments(int digit);
void setup() {
Serial.begin(9600); // Initialize Serial communication
// Initialize button pins as inputs with pull-up resistors
for (int i = 0; i < 5; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
pinMode(buzzerPin, OUTPUT); // Initialize buzzer pin as output
pinMode(latchPin, OUTPUT); // Initialize latch pin as output
pinMode(clockPin, OUTPUT); // Initialize clock pin as output
pinMode(dataPin, OUTPUT); // Initialize data pin as output
pinMode(ballLatchPin, OUTPUT); // Initialize ball latch pin as output
pinMode(ballClockPin, OUTPUT); // Initialize ball clock pin as output
pinMode(ballDataPin, OUTPUT); // Initialize ball data pin as output
pinMode(redLedPin, OUTPUT); // Initialize red LED pin as output
digitalWrite(redLedPin, HIGH); // Turn on the red LED initially
pinMode(relayButtonPin, INPUT_PULLUP); // Initialize relay control button pin as input with pull-up resistor
pinMode(relayPin, OUTPUT); // Initialize relay pin as output
digitalWrite(relayPin, LOW); // Ensure the relay is initially off
resetGame(); // Reset the game at startup
}
void loop() {
unsigned long currentMillis = millis(); // Get the current time
// Check if game time is up (60 seconds)
if (currentMillis - gameStartTime > 60000UL) {
resetGame();
}
// Check for button presses to update the score
for (int i = 0; i < 5; i++) {
if (digitalRead(buttonPins[i]) == LOW) {
updateScore(i);
delay(100); // Debounce delay
}
}
// Blink the red LED if the game is waiting for the first button press and relay button has not been pressed
if (!isFirstButtonPressed && !isRelayButtonPressed) {
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
digitalWrite(redLedPin, !digitalRead(redLedPin)); // Toggle the LED state
}
}
// Check for relay control button press
if (digitalRead(relayButtonPin) == LOW && !isRelayButtonPressed) {
digitalWrite(redLedPin, LOW); // Turn off the red LED
isRelayButtonPressed = true; // Set the relay button pressed flag
// Check if enough time has passed since the last activation
if (currentMillis - relayLastActivated >= relayActivationInterval) {
digitalWrite(relayPin, HIGH); // Activate the relay
delay(20); // Hold the relay for 20ms
digitalWrite(relayPin, LOW); // Deactivate the relay
// Play the start game melody and animation sequence simultaneously
playStartGameMelody();
for (int i = 0; i < 2; i++) { // Loop the animation 2 times
playAnimation();
}
// Show the current score
displayScore(totalScore);
// Update the last activated time
relayLastActivated = currentMillis;
// Display the initial Ball In Play number
displayBallInPlay(ballInPlay);
gameStarted = true; // Mark the game as started
}
}
}
void resetGame() {
totalScore = 0; // Reset the total score
totalButtonPresses = 0; // Reset the total number of button presses
ballInPlay = 1; // Reset ball in play number
isFirstButtonPressed = false; // Reset the first button pressed flag
isRelayButtonPressed = false; // Reset the relay button pressed flag
gameStarted = false; // Reset the game started flag
gameStartTime = millis(); // Reset the game start time
displayScore(0); // Reset the score display
clearBallInPlay(); // Clear the ball in play display
digitalWrite(redLedPin, HIGH); // Turn on the red LED
}
void updateScore(int button) {
if (!isFirstButtonPressed) {
digitalWrite(redLedPin, LOW); // Turn off the red LED when the first button is pressed
isFirstButtonPressed = true; // Set the first button pressed flag
}
totalScore += buttonScores[button]; // Update the total score
totalButtonPresses++; // Increment the total button presses
// Increment ball in play number only if total button presses are less than 3
if (totalButtonPresses < 3) {
ballInPlay++;
}
displayScore(totalScore); // Update the score display
// Update the Ball In Play display only if the game has started
if (gameStarted) {
displayBallInPlay(ballInPlay); // Update the ball in play display
}
// Play the corresponding tone for each button
switch(buttonPins[button]) {
case 2:
playTone(NOTE_A3_SHARP, 150, 75);
break;
case 3:
playTone(NOTE_D4_SHARP, 150, 75);
break;
case 4:
playTone(NOTE_F4_SHARP, 150, 75);
break;
case 5:
playTone(NOTE_G4_SHARP, 150, 75);
break;
case 6:
playTone(NOTE_A4, 150, 75);
break;
}
// Check if the total number of button presses is 3
if (totalButtonPresses >= 3) {
digitalWrite(redLedPin, HIGH); // Turn on the red LED before displaying the final score
delay(100); // Short delay to ensure LED is fully on
playHappyMelody(); // Play the happy melody
delay(4000); // Wait for 4 seconds before resetting the game
resetGame(); // Reset the game
}
}
void displayScore(int score) {
// Separate the score into individual digits
int hundreds = score / 100;
int tens = (score / 10) % 10;
int ones = score % 10;
digitalWrite(latchPin, LOW); // Start data transmission
// If the score is 0, only show the ones digit on the rightmost display
if (score == 0) {
shiftOut(dataPin, clockPin, MSBFIRST, B11111111); // Blank
shiftOut(dataPin, clockPin, MSBFIRST, B11111111); // Blank
shiftOut(dataPin, clockPin, MSBFIRST, digitToSegments(0)); // Show 0
} else {
// Align the score to the right
if (hundreds > 0) {
shiftOut(dataPin, clockPin, MSBFIRST, digitToSegments(hundreds)); // Hundreds digit
shiftOut(dataPin, clockPin, MSBFIRST, digitToSegments(tens)); // Tens digit
shiftOut(dataPin, clockPin, MSBFIRST, digitToSegments(ones)); // Ones digit
} else if (tens > 0) {
shiftOut(dataPin, clockPin, MSBFIRST, B11111111); // Blank
shiftOut(dataPin, clockPin, MSBFIRST, digitToSegments(tens)); // Tens digit
shiftOut(dataPin, clockPin, MSBFIRST, digitToSegments(ones)); // Ones digit
} else {
shiftOut(dataPin, clockPin, MSBFIRST, B11111111); // Blank
shiftOut(dataPin, clockPin, MSBFIRST, B11111111); // Blank
shiftOut(dataPin, clockPin, MSBFIRST, digitToSegments(ones)); // Ones digit
}
}
digitalWrite(latchPin, HIGH); // End data transmission
}
void displayBallInPlay(int ball) {
// Send the ball in play number to the shift register
digitalWrite(ballLatchPin, LOW); // Start data transmission
shiftOut(ballDataPin, ballClockPin, MSBFIRST, digitToSegments(ball)); // Ball in play digit
digitalWrite(ballLatchPin, HIGH); // End data transmission
}
void clearBallInPlay() {
// Clear the ball in play display
digitalWrite(ballLatchPin, LOW); // Start data transmission
shiftOut(ballDataPin, ballClockPin, MSBFIRST, B11111111); // Blank display
digitalWrite(ballLatchPin, HIGH); // End data transmission
}
void playHappyMelody() {
// Play the happy melody with specified notes, durations, and delays
playTone(NOTE_A3, 50, 50);
playTone(NOTE_C4_SHARP, 50, 50);
playTone(NOTE_E4, 50, 50);
playTone(NOTE_A4, 50, 100);
playTone(NOTE_E4, 50, 50);
playTone(NOTE_A4, 500, 75);
}
void playStartGameMelody() {
// Play the start game melody with specified notes, durations, and delays
playTone(NOTE_A4, 50, 50);
playTone(NOTE_E4, 50, 50);
playTone(NOTE_D4, 50, 50);
playTone(NOTE_C4_SHARP, 50, 50);
playTone(NOTE_A3, 400, 20);
}
void playTone(int note, int duration, int delayTime) {
tone(buzzerPin, note, duration); // Play the tone
delay(duration); // Wait for the tone to finish
noTone(buzzerPin); // Stop the tone
delay(delayTime); // Delay after the tone
}
void playAnimation() {
for (int i = 0; i < 6; i++) { // Loop through each segment
digitalWrite(latchPin, LOW); // Start data transmission
shiftOut(dataPin, clockPin, MSBFIRST, animationSegments[i]); // Send segment to hundreds digit
shiftOut(dataPin, clockPin, MSBFIRST, animationSegments[i]); // Send segment to tens digit
shiftOut(dataPin, clockPin, MSBFIRST, animationSegments[i]); // Send segment to ones digit
digitalWrite(latchPin, HIGH); // End data transmission
delay(40); // Delay for animation
}
}
byte digitToSegments(int digit) {
// Lookup table for segment values for each digit (common cathode)
byte segments[] = {
B11000000, // 0
B11111001, // 1
B10100100, // 2
B10110000, // 3
B10011001, // 4
B10010010, // 5
B10000010, // 6
B11111000, // 7
B10000000, // 8
B10010000 // 9
};
return segments[digit];
}