Code

Here is my latest code to copy and paste into the arduino mega . It will change over time. It may also contain features that are not in use yet (and may give the result I though it would. Gemini is a great assistant to explain what is going on with it.

This code will work to create a music and sound effect player, keep score, have a hurry up feature, and run a topper

The name of this code is mega130. Just cut and paste into your Mega

below is the code– Uses Arduino Mega, has 2 dfmini players

*********************************************************************

 

/*********************************************************************

 * ARDUINO MEGA – 4-PLAYER HEE HAW PINBALL SYSTEM

 * Version: 1.9.6 (Added Supplemental Hurry Up LEDs on 44, 45)

 *********************************************************************/

#include <EEPROM.h>

const String SKETCH_VERSION = “1.9.6”;

// — PIN DEFINITIONS —

const int relayPin = 11;      

const int triggerPin = 53;    

const int resetScorePin = 52;

const int hurryUpLedPin = 51;  

const int hurryUpLed2 = 44;    // New LED Pin

const int hurryUpLed3 = 45;    // New LED Pin

const int hurryUpStartPin = 50;

const int bonusTargetPin = 49;  

const int busyPin1 = 12;      

const int playPin  = 2;        // Start Button

const int stopPin  = 3;      

const int scorePins[3] = {8, 9, 10};

const int selectPins[4] = {4, 5, 6, 7};

const int p2Pins[11] = { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };

// — NON-BLOCKING TIMERS —

unsigned long lastSfxTrigger[11];      

unsigned long lastScoreTrigger[3];    

unsigned long lastBonusTrigger = 0;    

unsigned long lastSelectTrigger = 0;

bool lastScoreState[3] = {HIGH, HIGH, HIGH};

bool lastSfxState[11];                

bool lastBonusPinState = HIGH;

const long sfxLockout = 20;            

const long scoreLockout = 80;          

const long bonusLockout = 200;        

const long selectLockout = 200;        

// — GAME VARIABLES —

long playerScores[4] = {0, 0, 0, 0};

int reel10[4] = {0, 0, 0, 0}, reel100[4] = {0, 0, 0, 0}, reel1000[4] = {0, 0, 0, 0};

int activePlayer = 0;                  

long allTimeHighScore = 0;

const int EEPROM_ADDR = 0;

// — TIMING & AUDIO —

unsigned long hurryUpEndTime = 0;

bool isHurryUpActive = false;

unsigned long lastLedFlash = 0, relayOffTime = 0, lastTriggerTime = 0, lastFadeMillis = 0;

const long relayTimerDuration = 3000, debounceTime = 300;

int currentVolume1 = 0;

const uint8_t maxVolume = 29;

bool isPlayingMode = false, isFadingOut = false, relayIsTimedActive = false;

unsigned long musicStartDebounce = 0;

bool ledState = false;

// ======================= HELPERS =======================

void multiPrint(String msg, bool newLine = true) {

  if (newLine) { Serial.println(msg); Serial3.println(msg); }

  else { Serial.print(msg); Serial3.print(msg); }

}

long getDisplayScore(int p) {

  return playerScores[p] + (reel1000[p] * 1000L) + (reel100[p] * 100L) + (reel10[p] * 10L);

}

void displayAllScores() {

  Serial.print(“\033[2J\033[1;1H”);

  Serial3.print(“\033[2J\033[1;1H”);

  multiPrint(“************”);

  if(isHurryUpActive) multiPrint(“* HEE HAW: HURRY UP *”);

  else                multiPrint(“* HEE HAW: SCORE    *”);

  multiPrint(“*************”);

  for (int i = 0; i < 4; i++) {

    char buffer[22];

    long currentScore = getDisplayScore(i);

    if (i == activePlayer) sprintf(buffer, “>> P%d: %-7ld <<“, i + 1, currentScore);

    else sprintf(buffer, ”   P%d: %-7ld   “, i + 1, currentScore);

    multiPrint(buffer);

  }

  multiPrint(“******************”);

  multiPrint(” REC: ” + String(allTimeHighScore));

}

void setVolume(HardwareSerial &port, uint8_t vol) {

  if (vol > 30) vol = 30;

  uint8_t checksum = (0xAA + 0x13 + 0x01 + vol) & 0xFF;

  port.write(0xAA); port.write(0x13); port.write(0x01); port.write(vol); port.write(checksum);

  if (&port == &Serial1) currentVolume1 = vol;

}

void sendPlayTrack(HardwareSerial &port, uint8_t track) {

  uint8_t cmd[] = {0xAA, 0x07, 0x02, 0x00, track, 0x00};

  cmd[5] = (0xAA + 0x07 + 0x02 + 0x00 + track) & 0xFF;

  port.write(cmd, 6);

}

void showFinalTally() {

  Serial.print(“\033[2J\033[1;1H”);

  multiPrint(“=== GAME OVER ===”);

  long highestThisGame = 0;

  for (int i = 0; i < 4; i++) {

    long finalScore = getDisplayScore(i);

    multiPrint(“P” + String(i + 1) + “: ” + String(finalScore));

    if (finalScore > highestThisGame) highestThisGame = finalScore;

  }

  digitalWrite(relayPin, HIGH);

  relayIsTimedActive = true;

  if (highestThisGame > allTimeHighScore) {

    allTimeHighScore = highestThisGame;

    EEPROM.put(EEPROM_ADDR, allTimeHighScore);

    multiPrint(“NEW ALL-TIME REC!”);

    relayOffTime = millis() + 15000;

  } else {

    relayOffTime = millis() + 10000;

  }

}

void addThousandPoints() { reel1000[activePlayer]++; if (reel1000[activePlayer] >= 10) { reel1000[activePlayer] = 0; playerScores[activePlayer] += 10000; } }

void addHundredPoints() { reel100[activePlayer]++; if (reel100[activePlayer] >= 10) { reel100[activePlayer] = 0; addThousandPoints(); } }

void addTenPoints() { reel10[activePlayer]++; if (reel10[activePlayer] >= 10) { reel10[activePlayer] = 0; addHundredPoints(); } }

// ======================= SETUP =======================

void setup() {

  Serial.begin(115200); Serial1.begin(9600); Serial2.begin(9600); Serial3.begin(115200);

  EEPROM.get(EEPROM_ADDR, allTimeHighScore);

  if (allTimeHighScore < 0) allTimeHighScore = 0;

  pinMode(relayPin, OUTPUT);

  pinMode(hurryUpLedPin, OUTPUT);

  pinMode(hurryUpLed2, OUTPUT); // Initialize new LED

  pinMode(hurryUpLed3, OUTPUT); // Initialize new LED

  pinMode(triggerPin, INPUT_PULLUP); pinMode(resetScorePin, INPUT_PULLUP);

  pinMode(hurryUpStartPin, INPUT_PULLUP); pinMode(bonusTargetPin, INPUT_PULLUP);

  pinMode(playPin, INPUT_PULLUP); pinMode(stopPin, INPUT_PULLUP);

  pinMode(busyPin1, INPUT_PULLUP);

  for (int i = 0; i < 3; i++) {

    pinMode(scorePins[i], INPUT_PULLUP);

    lastScoreTrigger[i] = 0;

  }

  for (int i = 0; i < 4; i++) pinMode(selectPins[i], INPUT_PULLUP);

  for (int i = 0; i < 11; i++) {

    pinMode(p2Pins[i], INPUT_PULLUP);

    lastSfxState[i] = HIGH;

    lastSfxTrigger[i] = 0;

  }

  randomSeed(analogRead(A0));

  setVolume(Serial1, 0); setVolume(Serial2, 29);

  displayAllScores();

}

// ======================= MAIN LOOP =======================

void loop() {

  unsigned long currentMillis = millis();

  // — 1. START / STOP / MUSIC —

  if (digitalRead(playPin) == LOW && currentMillis – lastTriggerTime > debounceTime) {

    lastTriggerTime = currentMillis;

    if (!isPlayingMode) {

      isPlayingMode = true; isFadingOut = false;

      activePlayer = 0;

      for(int i=0; i<4; i++) {

        playerScores[i] = 0; reel10[i] = 0; reel100[i] = 0; reel1000[i] = 0;

      }

      setVolume(Serial1, maxVolume);

      sendPlayTrack(Serial1, random(1, 9));

      musicStartDebounce = currentMillis;

      displayAllScores();

    }

  }

  if (isPlayingMode && !isFadingOut && (currentMillis – musicStartDebounce > 1000)) {

    if (digitalRead(busyPin1) == HIGH) {

        sendPlayTrack(Serial1, random(1, 9));

        musicStartDebounce = currentMillis;

    }

  }

  if (digitalRead(stopPin) == LOW && isPlayingMode && !isFadingOut) {

    isFadingOut = true; lastFadeMillis = currentMillis;

  }

  if (isFadingOut && currentMillis – lastFadeMillis >= 100) {

    lastFadeMillis = currentMillis;

    if (currentVolume1 > 0) {

      setVolume(Serial1, –currentVolume1);

    } else {

      uint8_t stopCmd[] = {0xAA, 0x04, 0x00, 0xAE};

      Serial1.write(stopCmd, 4);

      isFadingOut = false; isPlayingMode = false;

      showFinalTally();

    }

  }

  // — 2. INSTANT SCORING —

  for (int i = 0; i < 3; i++) {

    bool currentState = digitalRead(scorePins[i]);

    if (currentState == LOW && lastScoreState[i] == HIGH) {

      if (currentMillis – lastScoreTrigger[i] > scoreLockout) {

        if (i == 0) addTenPoints();

        if (i == 1) addHundredPoints();

        if (i == 2) addThousandPoints();

        displayAllScores();

        lastScoreTrigger[i] = currentMillis;

      }

    }

    lastScoreState[i] = currentState;

  }

  // — 3. INSTANT SFX —

  for (int i = 0; i < 11; i++) {

    int currentPin = p2Pins[i];

    bool currentState = digitalRead(currentPin);

    if (currentState == LOW && lastSfxState[i] == HIGH) {

      if (currentMillis – lastSfxTrigger[i] > sfxLockout) {

        if (currentPin == 24) sendPlayTrack(Serial2, 4);

        else if (currentPin == 25) sendPlayTrack(Serial2, 5);

        else if (currentPin == 26) sendPlayTrack(Serial2, 6);

        else sendPlayTrack(Serial2, i + 1);

        lastSfxTrigger[i] = currentMillis;

      }

    }

    lastSfxState[i] = currentState;

  }

  // — 4. MISSION: HURRY UP —

  if (digitalRead(hurryUpStartPin) == LOW && isPlayingMode && !isHurryUpActive) {

      isHurryUpActive = true;

      hurryUpEndTime = currentMillis + 8000;

      displayAllScores();

  }

  if (isHurryUpActive) {

    if (currentMillis < hurryUpEndTime) {

      if (currentMillis – lastLedFlash > 100) {

        lastLedFlash = currentMillis;

        ledState = !ledState;

        // Toggle all three pins simultaneously

        digitalWrite(hurryUpLedPin, ledState);

        digitalWrite(hurryUpLed2, ledState);

        digitalWrite(hurryUpLed3, ledState);

      }

      bool bonusState = digitalRead(bonusTargetPin);

      if (bonusState == LOW && lastBonusPinState == HIGH) {

        if (currentMillis – lastBonusTrigger > bonusLockout) {

          sendPlayTrack(Serial2, 1);

          playerScores[activePlayer] += 10000;

          displayAllScores();

          lastBonusTrigger = currentMillis;

        }

      }

      lastBonusPinState = bonusState;

    } else {

      isHurryUpActive = false;

      // Turn all three pins off

      digitalWrite(hurryUpLedPin, LOW);

      digitalWrite(hurryUpLed2, LOW);

      digitalWrite(hurryUpLed3, LOW);

      displayAllScores();

    }

  }

  // — 5. SYSTEM CONTROLS —

  if (digitalRead(resetScorePin) == LOW && currentMillis – lastSelectTrigger > selectLockout) {

      allTimeHighScore = 0; EEPROM.put(EEPROM_ADDR, allTimeHighScore);

      displayAllScores();

      lastSelectTrigger = currentMillis;

  }

  for (int i = 0; i < 4; i++) {

    if (digitalRead(selectPins[i]) == LOW && activePlayer != i) {

      if (currentMillis – lastSelectTrigger > selectLockout) {

        activePlayer = i; displayAllScores();

        lastSelectTrigger = currentMillis;

      }

    }

  }

  if (digitalRead(triggerPin) == LOW) {

    digitalWrite(relayPin, HIGH); relayIsTimedActive = true;

    relayOffTime = currentMillis + relayTimerDuration;

  }

  if (relayIsTimedActive && currentMillis >= relayOffTime) {

    digitalWrite(relayPin, LOW); relayIsTimedActive = false;

  }

}