DYI Knock Down a Clown Arcade

This is a working project that I wanted to share with the forum as so many of you contributed to the various programming aspects that allowed me, a novice, to successfully write working code. The code is shown below. NOTE: I am not necessarily looking for any help whatsoever but thought several of you would enjoy knowing that your help made this possible.
Click here for YouTube video

My arcade game is similar to a game called "Down the Clown". If you are not familiar with it, the player throws balls at 10 clown targets and scores points when each clown target gets knocked down. When an entire row is knocked down, that row resets after some time passes. This continues for a total of 30 seconds and then the game ends.

The cabinet was handmade using parts either I had lying around or purchased from Home Depot. For the most part, all the electrical components were purchased from Amazon.

The clown targets were salvaged from two arcade games that must have shot balls using an air cannon. Those games had been in a warehouse fire. The original cabinets were severely damaged but the clown targets, air cylinders, and solenoids were only smoke-damaged. You should have seen the clowns before they were cleaned.

The outside of the control panel looks like this:

The inside of the control panel looks like this:

  1. Hand-made PCB board needed to simplify wiring to the individual seven segments on all the eight displays (three digits for score, three digits for high score, and 2 digits for time). This board also incorporates resistors to manipulate the 12 volts incoming voltage from the power supply and reduce it to 7.2 volts needed for the large 3" displays
  2. Hand-made PCB board made up of resistors to further reduce the 7.2 volts coming out of the larger PCB board and reduce it to 2 volts needed for the small 1" displays
  3. Power supply that offers three voltages (5V, 12 V & 24V) needed for various components
  4. Two MP3 players
  5. Two amplifiers for the MP3 players
  6. Arduino Mega
  7. Relays to convert microprocessor signals to 5V needed by the start button light and 24V needed for the air cylinder solenoids
  8. Back side of 3 digit display (1" tall) for high score
  9. Back side of 3 digit display (3" tall) for score
  10. Back side of the start button with LED light
  11. 2 digit display (3" tall) for the time remaining (not shown... see photo above)

The game operation works like this. The clown targets sit on a NC switch. When knocked over, the switch opens the circuit. The Mega monitors the switches and awards points when targets knocked down and bonus points when either and entire row or all targets get knocked down. When needed, one of three solenoids is activated which in turn operates one of three air cylinders to reset the clown targets.

As for the sketch, I overcame the following programming challenges:

  • Power up 3 separate 7 segment displays two of which were voltages higher than 5 volts. @jim-p (thanks!) shared a design for a PCB board that I had to make to handle the higher voltage display requirements. Thanks to @kolaha for help with the 7 segment display programming. Here is a link to a solution: Arduino Forum Thread

  • Code for a start button (see sketch below)

  • Code to ignore "static" of targets being bumped or from normal game vibrations yet still recognize when the target physically gets knocked down. Here is a link to the forum discussion that shows the solution to this problem:Forum Discussion

  • Playing two separate and independent MP3 players (Thanks @J-M-L). Here is a link to a solution to that problem: Forum Discussion

  • Code for general scoring (see sketch below)

Enjoy and please don't beat me up on poorly written/structured code!


// //////////////////////////////////////////////////////////// //
//                                                              //
//            Code for operating Down the Clown Game            //
//                       Revision Rev 1.2                       //
//                       December 21.2023                       //
//                         Deron Butler                         //
//                                                              //
// /////////////////////////////////////////////////////////// ///

// *********************************************************************** //
//                              Libraries
// *********************************************************************** //

#include <SevSeg.h> // SevSeg - Version: 3.7.0
SevSeg sevSeg;

// #include <FastLED.h> // Library for LED controls

// *********************************************************************** //
//                 Definitions for Seven Segment Displays
// *********************************************************************** //

const uint8_t digit[] = {
  // GFEDCBA  Segments      7-segment map:
  0b00111111, // 0   "0"          AAA
  0b00000110, // 1   "1"         F   B
  0b01011011, // 2   "2"         F   B
  0b01001111, // 3   "3"          GGG
  0b01100110, // 4   "4"         E   C
  0b01101101, // 5   "5"         E   C
  0b01111101, // 6   "6"          DDD
  0b00000111, // 7   "7"
  0b01111111, // 8   "8"
  0b01101111, // 9   "9"
  0b00000000 // 32  ' '  BLANK
};

// *********************************************************************** //
//                      Definitions for MP3 Players
// *********************************************************************** //

#define serialOne Serial1 // For YX5300 #1 playing background music
#define serialTwo Serial2 // For YX5300 #2 playing specific tunes when targets knocked down
// Command Structure 0x7E 0xFF 0x06 CMD FBACK DAT1 DAT2 0xEF
byte commandBuffer[8] = {0x7E, 0xFF, 0x06, 0x00, 0x00, 0x00, 0x00, 0xEF} ; // 8 byte variable to be sent to YX5300 MP3 player
byte commandBuffer2[10] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, } ; // 8 byte variable to be sent to YX5300 MP3 player

// *********************************************************************** //
//                       Variables for Set Up
// *********************************************************************** //

// Convention for bool variable in resting state is "false" when circuit normally open and "true" when circuit normally closed
//General variables
int Mode = 5;
bool Data[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW}; // temporary variable for evaluation
int Target[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // Targets 1-10 switches; 1 if up and 0 if down (Target 0 is not used)
//int TargetRow[] = {0, 0, 0, 0};  // 1 if activated and 0 if not (Row 0 is not used)
unsigned long numberToShow = 0; // 8 digit variable for 8 digit displays
const int switchPin[] = {23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; // the number of the switch pin

// *********************************************************************** //
//                   Variables for Start Debounce
// *********************************************************************** //
int reading = 0;
bool StartTriggered = false; // for start button
int buttonState;  // the current reading from the input pin
int lastButtonState = LOW;  // the previous reading from the input pin
unsigned long lastStartDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceStartDelay = 50;    // the debounce time; increase if the output flickers

// *********************************************************************** //
//               Variables for Target Switch Debounce
// *********************************************************************** //

int switchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW}; // the current reading from the input pin
int lastSwitchState[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW}; // the previous reading from the input pin
unsigned long lastDebounceTime[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // the last time the output pin was toggled
bool TargetTriggered[] = {false, false, false, false, false, false, false, false, false, false, false}; // Switch for targets 1-10
unsigned long debounceTargetDelay = 50;    // the debounce time; increase if the output flickers

// *********************************************************************** //
//               Variables for Target Row Debounce
// *********************************************************************** //
bool RowTriggered[] = {false, false, false, false}; // if all targets in row 1 are knocked down, then true otherwise false (0 is not used)
unsigned long debounceRowDelay= 1000; // switch delay time in milliseconds
unsigned long TurnRowOffTime[] = {0, 0, 0, 0}; // in milliseconds (0 is not used)
//bool TurnRowOff= false;

// *********************************************************************** //
//                      Variables for Scoring
// *********************************************************************** //

uint16_t Score = 0; // Primary score between 0 to 990 which gets updated to displays every second
uint16_t HighScore = 0; //0 to 990
int ScoreMultiplierBonus = 1;  // intended to be bonus if all targets in rows 1-3 knocked down once each; 1 if off and 2 if on
bool ScoreMultiplier[] = {0, 0, 0, 0}; // potential bonus for knocking down all of rows 1-3 (0 is not used)

// *********************************************************************** //
//                       Variables for Time
// *********************************************************************** //

byte TimeRemaining = 0; // Values 0 to 30 in Seconds
unsigned long CurrentTime = 0; // Assigns value of relative time in milliseconds
unsigned long StartButtonTime  = 0; // Assigns value of relative time in milliseconds

// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //
// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //
void setup() {
// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //
// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //

  Serial.begin(9600); //temp setup for displaying on monitor through serial port
//  Serial.flush(); // Tests flash memory when needed
  serialOne.begin(9600); // sets baud rate to 9600; used for MP3 Player 1
  serialTwo.begin(9600); // sets baud rate to 9600; used for MP3 Player 2

  delay(1000);
  // Reset both MP3 Players
    commandBuffer[3] = 0x0C; // Sets YX5300 storage device to TF card
    commandBuffer[5] = 0x00; // Default settings for TF card initialization
    commandBuffer[6] = 0x00; // Default settings for TF card initialization
    serialOne.write((const char*) commandBuffer, sizeof commandBuffer);//
    serialTwo.write((const char*) commandBuffer, sizeof commandBuffer);
    delay(1000); // required for initialization
    
  // Initialization required for YX5300 to direct to storage device
    commandBuffer[3] = 0x09; // Sets YX5300 storage device to TF card
    commandBuffer[5] = 0x00; // Default settings for TF card initialization
    commandBuffer[6] = 0x00; // Default settings for TF card initialization
    serialOne.write((const char*) commandBuffer, sizeof commandBuffer);
    serialTwo.write((const char*) commandBuffer, sizeof commandBuffer);
    delay(2000); // required for initialization

  // Set up for seven segment displays
    byte segPins[] = {38, 39, 40, 41, 42, 43, 44}; // Segments A, B, C, D, E, F, G
    byte digitPins[] = {45, 46, 47, 48, 49, 50, 51, 52};  // Common cathodes for digits 1, 2, 3, 4, 5, 6, 7, 8
    sevSeg.begin(N_TRANSISTORS, 8, digitPins, segPins, true, false, false, true);

  // Data input assignments of Arduino board pins
    pinMode(22, INPUT_PULLUP); // NO Switch for Start Button

  // Data output assignments of Arduino board pins
    // pins 23-32 are used for switches 1-10
    for (int ii = 0; ii < 10; ii++) {
      pinMode(switchPin[ii], INPUT_PULLUP); 
    }

    pinMode(33, OUTPUT); // Start button light
    pinMode(34, OUTPUT); // Solenoid1 for Row 1 (5 volt through 24 volt relay)
    pinMode(35, OUTPUT); // Solenoid2 for Row 2 (5 volt through 24 volt relay)
    pinMode(36, OUTPUT); // Solenoid3 for Row 3 (5 volt through 24 volt relay)
    pinMode(37, OUTPUT); // Solenoid4 for Ball Gate (5 volt through 24 volt relay)
    //  pin 38 is defined by SevSeg for seven segment display anode - segment A on all digits
    //  pin 39 is defined by SevSeg for seven segment display anode - segment B on all digits
    //  pin 40 is defined by SevSeg for seven segment display anode - segment C on all digits
    //  pin 41 is defined by SevSeg for seven segment display anode - segment D on all digits
    //  pin 42 is defined by SevSeg for seven segment display anode - segment E on all digits
    //  pin 43 is defined by SevSeg for seven segment display anode - segment F on all digits
    //  pin 44 is defined by SevSeg for seven segment display anode - segment G on all digits
    //  pin 45 is defined by SevSeg for seven segment display cathode - digit 1 (1's digit of time)
    //  pin 46 is defined by SevSeg for seven segment display cathode - digit 2 (10's digit of time)
    //  pin 47 is defined by SevSeg for seven segment display cathode - digit 3 (1's digit of Score)
    //  pin 48 is defined by SevSeg for seven segment display cathode - digit 4 (10's digit of Score)
    //  pin 49 is defined by SevSeg for seven segment display cathode - digit 5 (100's digit of Score)
    //  pin 50 is defined by SevSeg for seven segment display cathode - digit 6 (1's digit of HighScore)
    //  pin 51 is defined by SevSeg for seven segment display cathode - digit 7 (10's digit of HighScore)
    //  pin 52 is defined by SevSeg for seven segment display cathode - digit 8 (100's digit of HighScore)
    //  pinMode(53, OUTPUT); //Reserved for Future LED Lights

} // end set up

// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //
// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //
void loop() {
// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //
// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //
  
//  for (int i = 0; i < 2; i++) { // Loop to ensure displays show solid.  NOTE this may not be necessary
    sevSeg.refreshDisplay(); // Update all displays
//  }
  
  maintainDisplay(); // Call subroutine to do tasks that only get done once a second

//  CurrentTime = millis(); // Update time once a loop

//  Game Modes
//    Mode 1. Initial Game Setup (set up required game parameters)
//    Mode 2. Pre Game Play (first 10 seconds after start button pushed)
//    Mode 3. Game Play (20 to 40 seconds after start button pushed)
//    Mode 4. Post Game Play (set up required post game parameters)
//    Mode 5. Attract Mode (plays until start button pushed again)

  //  Monitor if Start Button pushed to start game
  if (StartTriggered == false) {
    
    int reading = digitalRead(22);

    if (reading != lastButtonState) {
      lastStartDebounceTime = millis(); // reset the debouncing timer
    }

    if ((millis() - lastStartDebounceTime) > debounceStartDelay) {  // whatever the reading is at, it's been there for longer than the debounce delay, so take it as the actual current state:
      
      if (reading != buttonState) {
        buttonState = reading;
        
        if (buttonState == LOW) {
          Mode = 1;
          StartTriggered = true;  // NOTE THIS NEEDS TO BE RESET TO FALSE IN MODE 4
          digitalWrite(33, HIGH); //turns start button light on//
          StartButtonTime = millis();
          // Play Starting Tune
            commandBuffer[3] = 0x22; // Play with volume
            commandBuffer[5] = 0x1E; // Set Volume to 30
            commandBuffer[6] = 0x0C; // Play Coin Accept tune on YX5300 #1
            serialOne.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #1 to play
        }
      }
    }
      
    lastButtonState = reading;  // save the reading. Next time through the loop, it'll be the lastButtonState:
  }

  // *********************************************************************** //
  //                      1.  INITIAL GAME SETUP
  //            RESETS GAME PARAMETERS PRIOR TO EVERY GAME START
  // *********************************************************************** //

  if (Mode == 1) {
    if (Data[1] == LOW) { // Run this loop once
    // variables to be reset each game
        
      Data[1] = HIGH;
      Data[2] = LOW;
      Data[3] = LOW;
      Data[4] = LOW;
      Data[5] = LOW;
      Data[6] = LOW;
      Data[7] = LOW;
      Data[8] = LOW;
      Data[9] = LOW;
      Data[10] = LOW;
      TimeRemaining = 10;      
      Score = 0;
      ScoreMultiplierBonus = 1;
        
      for (int i = 0; i < 10; i++) {
//      TurnOffTriggered[i] = false;
      TargetTriggered[i] = false;
      }
        
      for (int i = 0; i < 4; i++) {
        ScoreMultiplier[i] = 0;
        RowTriggered[i] = false;
      }
        
    //  Play Tune for Mode 1 on MP3 #2
        commandBuffer[3] = 0x22; // Play with volume
        commandBuffer[5] = 0x0A; // Set Volume to 10
        commandBuffer[6] = 0x01; // Play game music tune on YX5300 #2
        serialTwo.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #2 to play
        
        Mode = 2;
    }
  }

  // *********************************************************************** //
  //                         2. PRE GAME PLAY MODE
  //            RELATIVE TIME 0 TO 10 SECONDS PRIOR TO GAME STARTING
  // *********************************************************************** //

  if (Mode == 2) {
        
    if (TimeRemaining < 4) {
      
      if (Data[2] == LOW) { // Run this loop once
        
       //  Play Tune
        commandBuffer[3] = 0x22; // Play with volume
        commandBuffer[5] = 0x1E; // Set Volume to 30
        commandBuffer[6] = 0x0D; // Play Tune 12 on MP3 #1 for Countdown 
        serialOne.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #1 to play

        for (int i=1; i<3; i++) {
          // Energize target reset arm for rows 1-3
          digitalWrite(34, HIGH); // Reset Targets on Row 1
          digitalWrite(35, HIGH); // Reset Targets on Row 2
          digitalWrite(36, HIGH); // Reset Targets on Row 3
        }
        Data[2] = HIGH;
      } 
    }
    
    if (TimeRemaining < 2) {
      if (Data[3] == LOW) { // Run this loop once
        for (int i=1; i<6; i++) {
//      Relax target reset arm for row 1-3 and lower start gate
          digitalWrite(34, LOW); // Reset Targets on Row 1
          digitalWrite(35, LOW); // Reset Targets on Row 2
          digitalWrite(36, LOW); // Reset Targets on Row 3
          digitalWrite(37, HIGH); // Lower start gate
        }
        Mode = 3;
        Data[3] = HIGH;
      }
    }
  }

  // *********************************************************************** //
  //                         3. GAME PLAY MODE
  //         RELATIVE TIME 11 TO 40 SECONDS WHILE GAME IS ACTIVE
  // *********************************************************************** //

  if (Mode == 3) {
    
    if (Data[4] == LOW) { // Runs this loop once
        
      TimeRemaining = 30;

      // Play tune
        commandBuffer[3] = 0x22; // Play with volume
        commandBuffer[5] = 0x0A; // Set Volume to 10
        commandBuffer[6] = 0x02; // Play game music tune on YX5300 #2
        serialTwo.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #2 to play

      Data[4] = HIGH;
    }
      
    // read the state of the switch into a local variable:
    for (int i = 0; i < 10; i++) {
      Target[i] = digitalRead(switchPin[i]);
      
    // If the switch changed, due to noise or pressing:
      if (Target[i] != lastSwitchState[i]) {
        // reset the debouncing timer
        lastDebounceTime[i] = millis();
      }
        
      if ((millis() - lastDebounceTime[i]) > debounceTargetDelay) {
        // whatever the reading is at, it's been there for longer than the debounce

       // if the switch state has changed:
       if (Target[i] != switchState[i]) {
          switchState[i] = Target[i];

        // only perform the following code if the new switch state is HIGH
        if (switchState[i] == HIGH) {

           // Subroutine for music
            commandBuffer[3] = 0x22; // Play with volume
            commandBuffer[5] = 0x1E; // Set Volume to 30
            commandBuffer[6] = commandBuffer2[i]; // Play "i"th tune on YX5300 #1
            serialOne.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #1 to play

          Score = Score + 10; // Updates temp score
          
          }
        }
      }
    }
    
    // save the reading. Next time through the loop, it'll be the lastSwitchState[i]:
      for (int i = 0; i < 10; i++) {
      lastSwitchState[i] = Target[i];
    }              
    
    // Evaluate if all targets in row 1 are down
    if (RowTriggered[1] == false) {
      if (Target[0] == 1 && Target[1] == 1 && Target[2] == 1) {
        if (Data[7] == LOW) {
          Serial.println ("*Row 1 needs to be reset!!!");
          RowTriggered[1] = true;
          TurnRowOffTime[1] = millis();
          for (int i=1; i<6; i++) {
            digitalWrite(34, HIGH); // Reset Targets Rows 1
          }
          Serial.println ("**Row 1 arm is up!!!");
          ScoreMultiplier[1] =  1;
          Score = Score + 100;
//        Score = Score + (10 * ScoreMultiplierBonus);
          Data[7] = HIGH;
        }
      }
    }
    if (RowTriggered[1] == true) {
      if (millis() - TurnRowOffTime[1] >  debounceRowDelay) {
        for (int i=1; i<6; i++) {
          digitalWrite(34, LOW); // Relax row 1 rest arm
        }
        Serial.println ("***Row 1 arm is down!!!");
        RowTriggered[1] = false;
      }
      if (Target[0] == 0 && Target[1] == 0 && Target[2] == 0) {
        Data[7] = LOW;
      }
    }
    
    // Evaluate if all targets in row 2 are down
    if (RowTriggered[2] == false) {
      if (Target[3] == 1 && Target[4] == 1 && Target[5] == 1) {
        if (Data[8] == LOW) {
          Serial.println ("*Row 2 needs to be reset!!!");
          RowTriggered[2] = true;
          TurnRowOffTime[2] = millis();
          for (int i=1; i<6; i++) {
            digitalWrite(35, HIGH); // Reset Targets Rows 2
          }
          Serial.println ("**Row 2 arm is up!!!");
          ScoreMultiplier[2] =  1;
          Score = Score + 100;
//        Score = Score + (10 * ScoreMultiplierBonus);
          Data[8] = HIGH;
        }
      }
    }
    if (RowTriggered[2] == true) {
      if (millis() - TurnRowOffTime[2] >  debounceRowDelay) {
        for (int i=1; i<6; i++) {
          digitalWrite(35, LOW); // Relax row 2 rest arm
        }
        Serial.println ("***Row 2 arm is down!!!");
        RowTriggered[2] = false;
      }
      if (Target[3] == 0 && Target[4] == 0 && Target[5] == 0) {
        Data[8] = LOW;
      }
    }
    
    // Evaluate if all targets in row 3 are down
    if (RowTriggered[3] == false) {
      if (Target[6] == 1 && Target[7] == 1 && Target[8] == 1 && Target[9] == 1) {
        if (Data[9] == LOW) {
          Serial.println ("*Row 3 needs to be reset!!!");
          RowTriggered[3] = true;
          TurnRowOffTime[3] = millis();
          for (int i=1; i<6; i++) {
            digitalWrite(36, HIGH); // Reset Targets Rows 1
          }
          Serial.println ("**Row 3 arm is up!!!");
          ScoreMultiplier[3] =  1;
          Score = Score + 100;
//        Score = Score + (10 * ScoreMultiplierBonus);
          Data[9] = HIGH;
        }
      }
    }
    if (RowTriggered[3] == true) {
      if (millis() - TurnRowOffTime[3] >  debounceRowDelay) {
        for (int i=1; i<6; i++) {
          digitalWrite(36, LOW); // Relax row 1 rest arm
        }
        Serial.println ("***Row 3 arm is down!!!");
        RowTriggered[3] = false;
      }
      if (Target[6] == 0 && Target[7] == 1 && Target[8] == 0 && Target[9] == 0) {
        Data[9] = LOW;
      }
    }
    
    if (ScoreMultiplier[1] + ScoreMultiplier[2] + ScoreMultiplier[3] == 3) {
      if (Data[10] == LOW) {
        ScoreMultiplierBonus = 2;
        // Play tune
          commandBuffer[3] = 0x22; // Play with volume
          commandBuffer[5] = 0x0F; // Set Volume to 30 0x0F
          commandBuffer[6] = 0x0E; // Play 14th tune on YX5300 #1
          serialOne.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #1 to play
        Data[10] = HIGH;
      }
    }

    if (TimeRemaining <= 0) {
      Mode = 4;
    }

  } // End of Mode 3. GAME PLAY MODE

  // *********************************************************************** //
  //                           4. POST GAME PLAY MODE
  //                       RELATIVE TIME 41 TO 100 SECONDS
  // *********************************************************************** //

  if (Mode == 4) { // > 40 seconds after start button pushed
    if (Data[5] == LOW) { // Run this loop once
      TimeRemaining = 0;
      buttonState == LOW;
      lastButtonState == LOW;
      Data[1] = LOW;
      StartTriggered = false;  // Allows start button to be pushed
      digitalWrite(33, LOW); // Turns off start button light
      digitalWrite(37, LOW); // Close Ball Gate

    // Play Music
      commandBuffer[3] = 0x22; // Play with volume  
      commandBuffer[5] = 0x1E; // Set Volume to 30
      commandBuffer[6] = 0x0F; // Play game over tune on YX5300 #1
      serialOne.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #2 to play

      commandBuffer[3] = 0x22; // Play with volume
      commandBuffer[5] = 0x0A; // Set Volume to 10
      commandBuffer[6] = 0x03; // Play post credit tune on YX5300 #2
      serialTwo.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #2 to play
      
      CurrentTime = millis();
      Data[5] = HIGH; 
    }
 
//    if (millis() - CurrentTime > 2000) {
      if (Data[6] == LOW) { // Run this loop once
        
        if (Score > HighScore) {
          HighScore = Score;
        
          // Play Music
          commandBuffer[3] = 0x22; // Play with volume
          commandBuffer[5] = 0x1E; // Set Volume to 30
          commandBuffer[6] = 0x0B; // Play trumpet fanfair tune #12 on YX5300 #1
          serialOne.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #1 to play
        }  

        Mode = 5;
        Data[6] = HIGH; 
      }
//    }
    
    // Post Game Light Sequence
    
  }  // End Game Mode 4. Post Game Play

  // *********************************************************************** //
  //                          5. ATTRACT MODE
  //       RELATIVE TIME >100 SECONDS UNTIL START BUTTON PUSHED AGAIN
  // *********************************************************************** //

  if (Mode == 5) {
  
    if (Data[6] = LOW) { // Run this loop once
    
      // Play Attraction Tune
        commandBuffer[3] = 0x22; // Play with volume
        commandBuffer[5] = 0x0A; // Set Volume to 10
        commandBuffer[6] = 0x04; // Play Attract 1 tune on YX5300 #2
        serialTwo.write((const char*) commandBuffer, sizeof commandBuffer); //command that tells YX5300 #2 to play

        Data[6] = HIGH; 
    }      
    // Attract Light Sequence

  } // End of Mode 5. Attract Mode

}  // End of loop ()

// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //

void maintainDisplay() { // For tasks to be performed once every second
  static const unsigned long REFRESH_INTERVAL2 = 1000; // ms
  static unsigned long lastRefreshTime2 = 0;

  if (millis() - lastRefreshTime2 >= REFRESH_INTERVAL2) 	{
    lastRefreshTime2 += REFRESH_INTERVAL2;
    
    if (TimeRemaining > 0) {
      TimeRemaining = TimeRemaining - 1;
    }      
  
    else {
      TimeRemaining = 0;
      Serial.print ("Game Over");
    }
   
//    Serial.print ("TimeRemaining = ");
//    Serial.println (TimeRemaining);
//    Serial.print ("Mode = ");
//    Serial.println (Mode);
      Serial.print(ScoreMultiplier[1]);
      Serial.print(ScoreMultiplier[2]);
      Serial.println(ScoreMultiplier[3]);
      
    Numbers();
  }
}

// -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ //

void Numbers() {  // Definitions for Displays
  // [2 digts time remaining][3 digits high score][3 digits score] = 8 digit number
  byte T0 = (TimeRemaining / 10) % 10;
  byte T1 = TimeRemaining % 10;
  sevSeg.setSegmentsDigit(7, T0 ? digit[T0] : digit[10]);
  sevSeg.setSegmentsDigit(6, digit[T1]);

  byte H0 = (HighScore / 100) % 10;
  byte H1 = (HighScore / 10) % 10;
  byte H2 = HighScore % 10;
  sevSeg.setSegmentsDigit(5, H0 ? digit[H0] : digit[10]);
  sevSeg.setSegmentsDigit(4, H0 || H1 ? digit[H1] : digit[10]);
  sevSeg.setSegmentsDigit(3, digit[H2]);

  byte S0 = (Score / 100) % 10;
  byte S1 = (Score / 10) % 10;
  byte S2 = Score % 10;
  sevSeg.setSegmentsDigit(2, S0 ? digit[S0] : digit[10]);
  sevSeg.setSegmentsDigit(1, S0 || S1 ? digit[S1] : digit[10]);
  sevSeg.setSegmentsDigit(0, digit[S2]);
}
4 Likes

Another wonderful example of why I love this forum! Thank you for sharing that.

1 Like