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;
}
}