#include #include #define RELAY_ON LOW #define RELAY_OFF HIGH // Setup the LCD display // Connected to SDA and SCL LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Input pins const int startButton = 38; // Start button const int nudgeButtons[] = { 39, 40, 41, 42 }; // Four nudge buttons const int drumSenseData = 29; // 4021 Shift register // Output pins const int drumSelects[] = { 34, 35, 36, 37}; // Switches power to one of the drums to allow position to be read const int nudgeButtonLights[] = { 43, 44, 45, 46 }; // Lights up the nudge buttons when available const int solenoids[] = { 47, 48, 49, 50 }; // Activates a solenoid to allow drum to spin const int startButtonLight = 51; // Turns on start button light const int motorEnable = 52; // Turns on motor const int buzzer = 5; // Buzzer connection const int drumSenseClock = 31; // 4021 Shift register const int drumSenseLatch = 30; // 4021 Shift register const int minSpinTime = 500; const int maxSpinTime = 2000; void setup() { Serial.begin(9600); // initialize the lcd for 16 chars 2 lines, turn on backlight, // and clear anything on display lcd.begin(16,2); lcd.backlight(); lcdLine1(" "); lcdLine2(" "); // ------------- // // Input pins // // ------------- // // Define start button pinMode(startButton, INPUT); // Data from drum sense shift registers pinMode(drumSenseData, INPUT); // Define nudge button inputs for (int button = 0; button < 4; button++) { pinMode(nudgeButtons[button], INPUT); } // ----------- // // Output pins // // ----------- // // Define buzzer pin pinMode(buzzer, OUTPUT); // define pins for drum sense shift registers pinMode(drumSenseClock, OUTPUT); pinMode(drumSenseLatch, OUTPUT); // Define nudge button light relays for (int button = 0; button < 4; button++) { pinMode(nudgeButtonLights[button], OUTPUT); } // Define start button light relay pinMode(startButtonLight, OUTPUT); // Define motor relay pinMode(motorEnable, OUTPUT); // Define solenoid relays for (int solenoid = 0; solenoid < 4; solenoid++) { pinMode(solenoids[solenoid], OUTPUT); } // Define drum select relays // These provide power to each drum in turn to enable the position to be sensed for (int drum = 0; drum < 4; drum++) { pinMode(drumSelects[drum], OUTPUT); } // ------------------ // // Startup conditions // // ------------------ // // Turn on the lcd backlight and say hello lcd.backlight(); lcdLine1("Fruit Machine!"); // Turn off start button light Log("SB light OFF"); digitalWrite(startButtonLight, RELAY_OFF); // Turn off motor Log("Motor ON"); digitalWrite(motorEnable, RELAY_OFF); // Turn off all four solenoids Log("Solenoids OFF"); for (int solenoid = 0; solenoid < 4; solenoid++) { digitalWrite(solenoids[solenoid], RELAY_OFF); } // Turn off drum selects Log("DS OFF"); for (int drum = 0; drum < 4; drum++) { digitalWrite(drumSelects[drum], LOW); } // Turn off all nudge button lights Log("NB OFF"); updateNudgeButtons(0); // Setup random seed randomSeed(analogRead(0)); } void loop() { Log("Starting..."); // Turn on the start button light Log("SB ON"); digitalWrite(startButtonLight, RELAY_ON); // Wait for start button to be pressed Log("Press Start"); lcdLine2("Press Start"); while (digitalRead(startButton) == LOW) { delay(10); } // Turn off start button light as they have pressed it Log("SB OFF"); digitalWrite(startButtonLight, RELAY_OFF); // Turn on motor Log("Motor ON"); digitalWrite(motorEnable, RELAY_ON); // Wait for motor to come up to speed delay(2000); lcdLine2("Spinning!"); // Operate all four solenoids - the solenoids release the catches which allow the drums spin Log("Solenoids ON"); for (int solenoid = 0; solenoid < 4; solenoid++) { digitalWrite(solenoids[solenoid], RELAY_ON); } // Leave the drums spinning for a second at least delay(1000); // Wait for random times and stop each drum in turn for (int p = 0; p < 4; p++) { int pause = minSpinTime + random(maxSpinTime); delay(pause); Log("Solenoid " + String(p) + " OFF"); digitalWrite(solenoids[p], RELAY_OFF); } // Is there is currently a winning line? int winningLine = WinningLine(); if (winningLine) { Log("NB OFF"); updateNudgeButtons(0); } else { Log("NB ON"); lcdLine2("Nudge..."); updateNudgeButtons(1); } // Set max number of nudges and nudge count int maxNudges = 3; int nudgeCount = 0; // Keep going while they can still push nudge buttons and they haven't won yet while (winningLine == 0 && nudgeCount < maxNudges) { // Check for a nudge button pressed. If it is, enable the appropriate solenoid for a moment to let the drum spin one position for (int p = 0; p < 4; p++) { if (digitalRead(nudgeButtons[p]) == HIGH) { Log("Nudge drum " + String(p)); nudgeCount++; digitalWrite(solenoids[p], RELAY_ON); delay(50); // Not sure how long to enable solenoid to allow drum to move digitalWrite(solenoids[p], RELAY_OFF); delay(100); // Check for winning line now a drum have moved winningLine = WinningLine(); } } } // Turn off nudge lights whether we have won or not Log("NB OFF"); updateNudgeButtons(0); // Stop motor Log("Motor OFF"); digitalWrite(motorEnable, RELAY_OFF); // Have we won? if (winningLine == 1) { Log("You've won!"); lcdLine2("You won!"); PlayTune(1); } else { Log("You've lost!"); lcdLine2("You lost!"); PlayTune(0); } // Wait a bit and start again delay(4000); } void PlayTune(int won) { int winSongLength = 18; char winNotes[] = "cdfda ag cdfdg gf "; // a space represents a rest char loseNotes[] = "abcde ff abcde ff "; // a space represents a rest int winBeats[] = {1, 1, 1, 1, 1, 1, 4, 4, 2, 1, 1, 1, 1, 1, 1, 4, 4, 2}; int winTempo = 113; int i, duration; for (i = 0; i < winSongLength; i++) // step through the song arrays { duration = winBeats[i] * winTempo; // length of note/rest in ms if (winNotes[i] == ' ') // is this a rest? { delay(duration); // then pause for a moment } else // otherwise, play the note { tone(buzzer, frequency(winNotes[i]), duration); delay(duration); // wait for tone to finish } delay(winTempo / 10); // brief pause between notes } } void updateNudgeButtons(int state) { for (int button = 0; button < 4; button++) { if (state == 0) { digitalWrite(nudgeButtonLights[button], RELAY_OFF); } else { digitalWrite(nudgeButtonLights[button], RELAY_ON); } } } int frequency(char note) { // This function takes a note character (a-g), and returns the // corresponding frequency in Hz for the tone() function. int i; const int numNotes = 8; // number of notes we're storing // The following arrays hold the note characters and their // corresponding frequencies. The last "C" note is uppercase // to separate it from the first lowercase "c". If you want to // add more notes, you'll need to use unique characters. // For the "char" (character) type, we put single characters // in single quotes. char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' }; int frequencies[] = {262, 294, 330, 349, 392, 440, 494, 523}; // Now we'll search through the letters in the array, and if // we find it, we'll return the frequency for that note. for (i = 0; i < numNotes; i++) // Step through the notes { if (names[i] == note) // Is this the one? { return (frequencies[i]); // Yes! Return the frequency } } return (0); // We looked through everything and didn't find it, // but we still need to return a value, so return 0. } void Log(String s) { Serial.print(s + "\n"); //delay(1000); } int WinningLine() { Log("Checking..."); int won = 0; // Read the drum position for each drum int lines[4]; for (int drum = 0; drum < 4; drum++) { lines[drum] = ReadLine(drum); } if (lines[0] == lines[1] == lines[2] == lines[3]) { won = 1; } return won; } int ReadLine(int drum) { // Ensure all drum selects are off for (int drum = 0; drum < 4; drum++) { digitalWrite(drumSelects[drum], RELAY_OFF); } delay(100); // Latch signals from drum pickups digitalWrite(drumSenseLatch, 1); delayMicroseconds(20); digitalWrite(drumSenseLatch, 0); byte byte1 = myShiftIn(drumSenseData, drumSenseClock); byte byte2 = myShiftIn(drumSenseData, drumSenseClock); byte byte3 = myShiftIn(drumSenseData, drumSenseClock); String bits = ""; digitalWrite(drumSelects[drum], RELAY_ON); delay(100); // Read in from shift register digitalWrite(drumSelects[drum], RELAY_OFF); delay(100); return 0; } void lcdLine1(String s) { lcd.setCursor(0,0); lcd.print(s); } void lcdLine2(String s) { lcd.setCursor(0,1); lcd.print(s); } byte myShiftIn(int myDataPin, int myClockPin) { int i; int temp = 0; int pinState; byte myDataIn = 0; pinMode(myClockPin, OUTPUT); pinMode(myDataPin, INPUT); //we will be holding the clock pin high 8 times (0,..,7) at the //end of each time through the for loop //at the begining of each loop when we set the clock low, it will //be doing the necessary low to high drop to cause the shift //register's DataPin to change state based on the value //of the next bit in its serial information flow. //The register transmits the information about the pins from pin 7 to pin 0 //so that is why our function counts down for (i=7; i>=0; i--) { digitalWrite(myClockPin, 0); delayMicroseconds(0.2); temp = digitalRead(myDataPin); if (temp) { pinState = 1; //set the bit to 0 no matter what myDataIn = myDataIn | (1 << i); } else { //turn it off -- only necessary for debuging //print statement since myDataIn starts as 0 pinState = 0; } //Debuging print statements //Serial.print(pinState); //Serial.print(" "); //Serial.println (dataIn, BIN); digitalWrite(myClockPin, 1); } //debuging print statements whitespace //Serial.println(); //Serial.println(myDataIn, BIN); return myDataIn; }