Components and supplies
Arduino UNO
Arduino Mega 2560
Project description
Code
functions.ino
arduino
This is a “tab” file for PinballControl_14.ino. It maps each of the switches to the set of functions that it has to initiate.
1void switchPbSetup(){ 2 3 /*Set up the mapping of switches, coils, varaibles for timing and I2S messages. 4 *The structure 'switchPB' and array are declared in types.h. 5 *Elements are in the following order 6 * 1 - switchPin - the pin to which the switch is connected 7 * 2 - coilPin - the pin to which the coil is connected (0 if none connected) 8 * 3 - coilState - *not set here - used to record whether a coil is currently activated 9 * 4 - timeStampHigh - *not set here - used to record the point in time (ms) that a coil was activated 10 * 5 - debounceMs - time in milliseconds to debounce the switch 11 * 6 - soundMsg - I2C message to be sent to the sound board when switch activates 12 * 7 - rouletteMsg - I2C message to be sent to the roulette board 13 * 8 - lightsMsg - I2C message to be sent to the sound board 14 * 9 - scoreIncrement - I2C message to send the default score for the switch 15 * 10 - globalDebounceMs - Number of milliseconds to stop reacting to all switches 16 * *Note* - score may be changed by multipliers in main code. 17 * 11 - lastState - used to record the last state of a switch to ensure a switch held closed 18 * is not repeatedly acted upon. 1 --> Open 0--> Closed 19 */ 20 switchPB[0] = {ENTRY_LANE_S, NOTHING, 0, 0, 0, ORGAN_LOUD_SND, NO_ROULETTE, CURVE_L, 0, 0,1}; 21 22 switchPB[1] = {GATE_LEFT_S, NOTHING, 0, 0, 0, HAMMOND_SND, NO_ROULETTE, DRINK_L, 1000, 0,1}; 23 24 switchPB[2] = {GATE_CENTRE_S, NOTHING, 0, 0, 0, HAMMOND_SND, NO_ROULETTE, SEX_L, 5000, 0,1}; 25 26 switchPB[3] = {GATE_RIGHT_S, NOTHING, 0, 0, 0, HAMMOND_SND, NO_ROULETTE, SMOKE_L, 1000, 0,1}; 27 28 switchPB[4] = {POP_BUMPER_LEFT_S, POP_BUMPER_LEFT_C, 0, 0, 200, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 3000, 200,1}; 29 30 switchPB[5] = {POP_BUMPER_CENTRE_S, POP_BUMPER_CENTRE_C, 0, 0, 200, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 3000, 200,1}; 31 32 switchPB[6] = {POP_BUPER_RIGHT_S, POP_BUMPER_RIGHT_C, 0, 0, 2, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 500, 3000,1}; 33 34 switchPB[7] = {DROP_TARGET_S, NOTHING, 0, 0, 200, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 100000, 0,0}; 35 36 switchPB[8] = {SPINNER_S,NOTHING, 0, 0, 0, NO_SOUND, NO_ROULETTE, SPINNER_L, 3000, 0}; 37 38 switchPB[9] = {BALL_EJECT_S, BALL_EJECT_C, 0, 0, 0, 0, NO_ROULETTE, CURVE_L, 200000, 0,1}; 39 40 switchPB[10] = {TARGET_LEFT_S, NOTHING, 0, 0, 0, ROULETTE_SND, SPIN_CCW, NO_LIGHTS, 30000, 0,1}; 41 42 switchPB[11] = {TARGET_RIGHT_S, NOTHING, 0, 0, 0, ROULETTE_SND, SPIN_CW, NO_LIGHTS, 30000, 0,1}; 43 44 switchPB[12] = {EXIT_LANE_LEFT_S, NOTHING, 0, 0, 0, HAMMOND_SND, NO_ROULETTE, HELL_L, 500, 0,1}; 45 46 switchPB[13] = {EXIT_LANE_RIGHT_S, NOTHING, 0, 0, 0, NO_SOUND, NO_ROULETTE, HELL_L, 500, 0,1}; 47 48 switchPB[14] = {SLINGSHOT_LEFT_S, SLINGSHOT_LEFT_C, 0, 0, 500, NO_SOUND, NO_ROULETTE, DRUGS_L, 1000, 0,1}; 49 50 switchPB[15] = {SLINGSHOT_RIGHT_S, SLINGSHOT_RIGHT_C, 0, 0, 500, NO_SOUND, NO_ROULETTE, DRINK_L, 1000, 0,1}; 51 52 switchPB[16] = {DRAIN_S, NOTHING, 0, 0, 0, NO_SOUND, NO_ROULETTE, HELL_L, 0, 0,1}; 53 54 switchPB[17] = {NEW_BALL_S,NOTHING, 0, 0, 0, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 0, 0,1}; 55 56 switchPB[18] = {NOTHING,NEW_BALL_C, 0, 0, 0, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 0, 0,1}; //special case as no switch assigned 57 58 switchPB[19] = {NOTHING,DROP_TARGET_C, 0, 0, 0, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 0, 0,1}; 59 60 61 62 for (int x = 0; x < TOTAL_SWITCHES; x++){ 63 #ifdef DEBUG 64 Serial.println ("Setting up pins..."); 65 Serial.print("Pin "); 66 Serial.print(switchPB[x].switchPin); 67 #endif 68 if (switchPB[x].switchPin){ 69 #ifdef DEBUG 70 Serial.println( " Input Pullup"); 71 #endif 72 pinMode(switchPB[x].switchPin, INPUT_PULLUP); 73 } else { 74 #ifdef DEBUG 75 Serial.println (" Not Set!"); 76 #endif 77 } 78 79 if (switchPB[x].coilPin) { 80 pinMode(switchPB[x].coilPin, OUTPUT); 81 digitalWrite(switchPB[x].coilPin, LOW); 82 } 83 } 84} 85 86void updateScore(long scoreIncrement){ 87 //Serial.println("sending"); 88 score = score + scoreIncrement; 89 sendScore(); 90} 91 92void sendScore(){ 93 byte longAsBytes[4]; 94 longAsBytes[0] = score >>24; 95 longAsBytes[1] = score >>16; 96 longAsBytes[2] = score >>8; 97 longAsBytes[3] = score; 98 99 Wire.beginTransmission(MSG_SCOREBOARD_ADDR); 100 for (int x = 0; x < 4; x++){ 101 Wire.write(longAsBytes[x]); 102 } 103 Wire.endTransmission(); 104} 105 106void activateCoil(byte index){ 107 //takes in the index for the switchPB array and activates the associate coil 108 #ifdef DEBUG 109 Serial.print("firing coil connected to pin "); 110 Serial.println(switchPB[index].coilPin); 111 #endif 112 digitalWrite(switchPB[index].coilPin, HIGH); 113 switchPB[index].timeStampHigh = millis(); 114 switchPB[index].coilState = 1; 115 if(switchPB[index].globalDebounceMs){ 116 suppressUntilMillis = millis() + switchPB[index].globalDebounceMs; 117 } 118} 119 120
names.ino
arduino
this is a “tab” file for PinballControl_14.ino. It defines: the pins allocated to each of the switches; pins that are outputs to device coils; game control constants.
1 2 3#define VERSION "0.5" 4//#define DEBUG //comment out when deploying full version 5/* V0.2 - Added score, suppressUntilMillis and switchPB.globalDebounceMs variables 6 * Included I2C for scoreboard 7 * ability to ignore all switches for a period of time added to fix rf problems with coils 8 * 9 * V0.3 - corrected message to scoreboard to separate long into 4 bytes 10 * 11 * V0.4 12 * 13 * 14 * Delays have been included in the code for the ball eject and ball return actions. It is 15 * assumed that due to the time taken for the ball to travel that all activated coils e.g. 16 * pop bumpers will have been deactivated before the delays occur. If this is not the case 17 * then the coils will remain activated for the duration of the delay. 18 * !!!END WARNING!!! 19 * 20 * Added delay [BALL_EJECT_MILLIS] for ball eject 21 * 22 * New function [activateCoil(byte index)] created to allow the ball eject and drop target 23 * coils to be activated independently of the switchPB loop. 24 * 25 * New code in setup identifies the elements in the switchPB array that reference the 26 * ball return [newBallIndex] and drop target [dropTargetIndex] coils. 27 * 28 * Game control: 29 * 30 * Lives are counted - when ball goes down drain, a life is deducted until no lives 31 * are left and the ball is automatically returned after a defined period 32 * [BALL_EJECT_DELAY_MILLIS]. 33 * 34 * New ball button is now being used as new game - it is only available if a game is not 35 * in progress [gameInProgress]. When pressed, a new game is initiated with lives [LIVES] 36 * and the score is reset. 37 * 38 * When the entry lane switch is closed a timer now starts that is used to raise the 39 * drop target after a defined period [DROP_TARGET_RAISE_MILLIS] 40 * 41 * V0.5 - added new lastState value to switchPB to allow checking that the swich has closed 42 * before re-triggering. 43 * 44 */ 45 46 47 48/************************** 49 * the following are used to 50 * tune the performance of the machine 51 * 52 *Duration to hold coils high*/ 53#define COIL_MILLIS 100 54//Debounce values 55#define MICROSWITCH_DB 30 56#define PLATE_SWITCH_DB 60 57/************************** 58 */ 59 60//Game control constant 61#define LIVES 3 62 63#define BALL_EJECT_DELAY_MILLIS 3000 64#define DROP_TARGET_RAISE_MILLIS 20000L 65 66 67/* The delay to account for the time between 68 * the drain switch closing and the ball reaching 69 * the ball return mechanism - tune as appropriate 70 */ 71#define DRAIN_TO_BALL_RETURN_MILLIS 1000 72 73#define NOTHING 0 74#define NO_SOUND 0 75#define NO_SCORE 0 76#define NO_LIGHTS 0 77#define NO_ROULETTE 0 78 79 80#define TOTAL_SWITCHES 20 81//Pins allocated to switches 82#define ENTRY_LANE_S 31 83#define GATE_LEFT_S 30 84#define GATE_CENTRE_S 29 85#define GATE_RIGHT_S 28 86#define POP_BUMPER_LEFT_S 27 87#define POP_BUMPER_CENTRE_S 26 88#define POP_BUPER_RIGHT_S 25 89#define DROP_TARGET_S 22 90#define SPINNER_S 34 91#define BALL_EJECT_S 35 92#define TARGET_LEFT_S 36 93#define TARGET_RIGHT_S 37 94#define EXIT_LANE_LEFT_S 38 95#define EXIT_LANE_RIGHT_S 39 96#define SLINGSHOT_LEFT_S 24 97#define SLINGSHOT_RIGHT_S 23 98#define DRAIN_S 40 99#define NEW_BALL_S 42 100 101//Output coil pins 102#define POP_BUMPER_LEFT_C 46 103#define POP_BUMPER_RIGHT_C 53 104#define POP_BUMPER_CENTRE_C 47 105#define SLINGSHOT_LEFT_C 49 106#define SLINGSHOT_RIGHT_C 50 107#define DROP_TARGET_C 52 108#define BALL_EJECT_C 51 109#define NEW_BALL_C 48 110 111 112 113 114
PinballControl_14.ino
arduino
This controls the game. Its triggers are the rollover switches on the playfield and the switches in the devices such as pop bumpers, targets etc. It causes coils within the devices to be energized and also triggers, via 12C comms, actions such as light and sound sequences which are controlled by the slave Arduinos.
1#include <arduino.h> 2#include <Wire.h> 3#include "names.h" 4#include "types.h" 5#include <PBWire.h> 6 7unsigned long suppressUntilMillis = 0; 8/* May be used to ignore all switches for period 9 * defined by the attribute globalDebounceMs as 10 * part of the switchPB structure defined in types.h 11 */ 12 13//Game control variables 14int lives = 0; 15long hiScore = 0; 16unsigned long lifeStart = 0; 17byte gameInProgress = 0; 18byte newBallIndex = 0; 19byte dropTargetIndex = 0; 20byte dropTargetRaised = 0; 21 22long score = 0; 23 24void setup() { 25 26 Serial.begin(9600); 27 Wire.begin(); 28 Serial.println("Starting Lost & Found"); 29 Serial.print("Version: "); 30 Serial.println(VERSION); 31 32 33 switchPbSetup(); 34 //identify the array elements for drop target and new ball coils 35 for(int x = 0; x < TOTAL_SWITCHES; x++){ 36 if(switchPB[x].coilPin == NEW_BALL_C){ 37 newBallIndex = x; 38 } 39 if(switchPB[x].coilPin == DROP_TARGET_C){ 40 dropTargetIndex = x; 41 } 42 } 43 44 //!!Start up lights messages here 45 Wire.beginTransmission(MSG_LIGHTS_ADDR); 46 //Wire.write(YOUR_MESSAGE_HERE); 47 Wire.endTransmission(); 48 #ifdef DEBUG 49 Serial.println("Startup message sent to lights: "); 50 #endif 51} 52 53 54void loop() { 55 //is it time to raise the drop target? 56 if(!dropTargetRaised){ 57 if(millis() > lifeStart + DROP_TARGET_RAISE_MILLIS){ 58 activateCoil(dropTargetIndex); 59 dropTargetRaised = 1; 60 61 #ifdef DEBUG 62 Serial.println("drop target raised"); 63 #endif 64 65 Wire.beginTransmission(MSG_LIGHTS_ADDR); 66 Wire.write(SPOT_L); 67 Wire.endTransmission(); 68 69 Wire.beginTransmission(MSG_SOUNDBOARD_ADDR); 70 //Wire.write(YOUR MESSAGE HERE); 71 Wire.endTransmission(); 72 73 #ifdef DEBUG 74 Serial.println("drop target lights on"); 75 #endif 76 } 77 } 78 79 //loop through all the switches 80 for(int x = 0; x < TOTAL_SWITCHES; x++){ 81 /*if the coil has already been activated check to see if 82 * it needs to be de-activated*/ 83 if(switchPB[x].coilState){ // \\ 84 //check the time it was activated agains the system time // | Coil is activated - 85 if(millis() > (switchPB[x].timeStampHigh + COIL_MILLIS)){ // | Switches off the 86 // | coil after the 87 digitalWrite(switchPB[x].coilPin, LOW); // | time defined in 88 //reset the values to show the coil is now de-activated // | COIL_MILLIS 89 switchPB[x].coilState = 0; // | 90 } // / 91 } 92 93 if(!switchPB[x].coilState) { // \\ 94 //check to see if we're still waiting to debounce the switch // | Coil is not activated - 95 if(millis() > (switchPB[x].timeStampHigh + switchPB[x].debounceMs)){ // | Checks to see if the 96 switchPB[x].timeStampHigh = 0; // | switch has debounced 97 } // | 98 99 if(suppressUntilMillis){ // Ignore switches if a global 100 if(suppressUntilMillis < millis()){ // debounce has not yet expired 101 suppressUntilMillis = 0; // (see variable declaration for more info) 102 } 103 }else{ 104 //only check for a new trigger if timeHigh is 0 // | if the switch has debounced 105 int switchState = digitalRead(switchPB[x].switchPin); 106 107 if (switchState){switchPB[x].lastState = 1;} // if the switch is open update last state. 108 109 if (!switchPB[x].timeStampHigh && !switchState && switchPB[x].lastState){ // | then takes action 110 111 switchPB[x].lastState = 0; //record that the switch is now closed 112 //************************** 113 //***Game control actions*** 114 //************************** 115 //Delay for ball eject 116 if(switchPB[x].switchPin == BALL_EJECT_S){ 117 Wire.beginTransmission(MSG_SOUNDBOARD_ADDR); 118 Wire.write(FAIRGROUND_SND); 119 Wire.endTransmission(); 120 delay(BALL_EJECT_DELAY_MILLIS); 121 } 122 123 //Action for Drain 124 if(switchPB[x].switchPin == DRAIN_S){ 125 126 if(lives){ 127 lives--; 128 } 129 #ifdef DEBUG 130 Serial.println("ball in drain"); 131 Serial.print(lives); 132 Serial.print(" lives remaining"); 133 #endif 134 if(lives > 0){ 135 //need to wait for the ball to reach ball return mechanism 136 delay(DRAIN_TO_BALL_RETURN_MILLIS); 137 activateCoil(newBallIndex); 138 } else { 139 //GAME OVER 140 gameInProgress = 0; 141 #ifdef DEBUG 142 Serial.println("game over!"); 143 #endif 144 //send messages to scoreboard, sound etc. 145 } 146 } 147 148 //Action for NEW_BALL [game] 149 if(switchPB[x].switchPin == NEW_BALL_S){ 150 #ifdef DEBUG 151 Serial.println("new ball button pressed"); 152 Serial.print(lives); 153 Serial.println(" lives remaining in current game"); 154 #endif 155 if(!gameInProgress){ 156 #ifdef DEBUG 157 Serial.println("New Game - score reset"); 158 #endif 159 lives = LIVES; 160 score = 0; 161 sendScore(); 162 activateCoil(newBallIndex); 163 gameInProgress = 1; 164 } 165 } 166 //Action for entry lane 167 if(switchPB[x].switchPin == ENTRY_LANE_S){ 168 lifeStart = millis(); 169 #ifdef DEBUG 170 Serial.println("New Life"); 171 #endif 172 173 } 174 if(switchPB[x].switchPin == DROP_TARGET_S){ 175 dropTargetRaised = 0; 176 lifeStart = millis(); 177 } 178 //********************************* 179 //***end of game control actions*** 180 //********************************* 181 182 if(switchPB[x].coilPin){ 183 activateCoil(x); 184 } 185 if(switchPB[x].soundMsg){ 186 Wire.beginTransmission(MSG_SOUNDBOARD_ADDR); 187 Wire.write(switchPB[x].soundMsg); 188 Wire.endTransmission(); 189 #ifdef DEBUG 190 Serial.print("Message sent to soundboard: "); 191 Serial.println(switchPB[x].soundMsg); 192 #endif 193 } 194 if(switchPB[x].lightsMsg){ 195 196 Wire.beginTransmission(MSG_LIGHTS_ADDR); 197 Wire.write(switchPB[x].lightsMsg); 198 Wire.endTransmission(); 199 #ifdef DEBUG 200 Serial.print("Message sent to lights: "); 201 Serial.println(switchPB[x].lightsMsg); 202 #endif 203 } 204 if(switchPB[x].rouletteMsg){ 205 206 Wire.beginTransmission(MSG_ROULETTE_ADDR); 207 Wire.write(switchPB[x].rouletteMsg); 208 Wire.endTransmission(); 209 #ifdef DEBUG 210 Serial.print("Message sent to roulette: "); 211 Serial.println(switchPB[x].rouletteMsg); 212 #endif 213 } 214 if(switchPB[x].scoreIncrement){ 215 updateScore(switchPB[x].scoreIncrement); 216 } 217 } 218 } 219 } 220 } 221} 222
PinballAudio_01.ino
arduino
This is from Spark Fun Electronics 2011 and written by Nathan Seidle
1 2 3/* 4 4-28-2011 5 Spark Fun Electronics 2011 6 Nathan Seidle 7 8 This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license). 9 10 This example shows how to play a given track (track001.mp3 for example) from the SD card when a GPIO pin goes low. 11 This code monitors A0 through A5. If A0 is pulled low then track 1 plays. If A5 is pulled low then the track # simply advances. 12 13 This code is heavily based on the "MP3 PlayTrack" example code. Please be sure to review it for more comments and gotchas. 14 15 While track is playing, Arduino scans to see if pins change. If pins change, stop playing track, check pins again to get 16 trigger # and then decide what new track to play. 17 18 */ 19 20#include <SPI.h> 21#include<Wire.h> 22#include "names.h" 23#include "mp3_config.h" 24#include <PBWire.h> 25 26 27#define TRUE 0 28#define FALSE 1 29 30//Add the SdFat Libraries 31#include <SdFat.h> 32#include <SdFatUtil.h> 33 34//Create the variables to be used by SdFat Library 35Sd2Card card; 36SdVolume volume; 37SdFile root; 38SdFile track; 39 40//This is the name of the file on the microSD card you would like to play 41//Stick with normal 8.3 nomeclature. All lower-case works well. 42//Note: you must name the tracks on the SD card with 001, 002, 003, etc. 43//For example, the code is expecting to play 'track002.mp3', not track2.mp3. 44char trackName[] = "track001.mp3"; 45int trackNumber = 0; 46int previousTrigger = 1; //This indicates that we've already triggered on 1 47 48long lastCheck; //This stores the last millisecond since we had a trigger 49 50char errorMsg[100]; //This is a generic array used for sprintf of error messages 51 52//MP3 Player Shield pin mapping. See the schematic 53#define MP3_XCS 6 //Control Chip Select Pin (for accessing SPI Control/Status registers) 54#define MP3_XDCS 7 //Data Chip Select / BSYNC Pin 55#define MP3_DREQ 2 //Data Request Pin: Player asks for more data 56#define MP3_RESET 8 //Reset is active low 57//Remember you have to edit the Sd2PinMap.h of the sdfatlib library to correct control the SD card. 58 59//VS10xx SCI Registers 60#define SCI_MODE 0x00 61#define SCI_STATUS 0x01 62#define SCI_BASS 0x02 63#define SCI_CLOCKF 0x03 64#define SCI_DECODE_TIME 0x04 65#define SCI_AUDATA 0x05 66#define SCI_WRAM 0x06 67#define SCI_WRAMADDR 0x07 68#define SCI_HDAT0 0x08 69#define SCI_HDAT1 0x09 70#define SCI_AIADDR 0x0A 71#define SCI_VOL 0x0B 72#define SCI_AICTRL0 0x0C 73#define SCI_AICTRL1 0x0D 74#define SCI_AICTRL2 0x0E 75#define SCI_AICTRL3 0x0F 76 77void setup() { 78 79 Wire.begin(MSG_SOUNDBOARD_ADDR); 80 Wire.onReceive(wireUpdate); 81 82 pinMode(MP3_DREQ, INPUT); 83 pinMode(MP3_XCS, OUTPUT); 84 pinMode(MP3_XDCS, OUTPUT); 85 pinMode(MP3_RESET, OUTPUT); 86 87 digitalWrite(MP3_XCS, HIGH); //Deselect Control 88 digitalWrite(MP3_XDCS, HIGH); //Deselect Data 89 digitalWrite(MP3_RESET, LOW); //Put VS1053 into hardware reset 90 91 Serial.begin(9600); //Use serial for debugging 92 Serial.println("MP3 Player Example using Control"); 93 94 //Setup SD card interface 95 pinMode(10, OUTPUT); //Pin 10 must be set as an output for the SD communication to work. 96 if (!card.init(SPI_FULL_SPEED, 9)) Serial.println("Error: Card init"); //Initialize the SD card, shield uses pin 9 for SD CS 97 if (!volume.init(&card)) Serial.println("Error: Volume ini"); //Initialize a volume on the SD card. 98 if (!root.openRoot(&volume)) Serial.println("Error: Opening root"); //Open the root directory in the volume. 99 100 //We have no need to setup SPI for VS1053 because this has already been done by the SDfatlib 101 102 //From page 12 of datasheet, max SCI reads are CLKI/7. Input clock is 12.288MHz. 103 //Internal clock multiplier is 1.0x after power up. 104 //Therefore, max SPI speed is 1.75MHz. We will use 1MHz to be safe. 105 SPI.setClockDivider(SPI_CLOCK_DIV16); //Set SPI bus speed to 1MHz (16MHz / 16 = 1MHz) 106 SPI.transfer(0xFF); //Throw a dummy byte at the bus 107 108 //Initialize VS1053 chip 109 delay(10); 110 digitalWrite(MP3_RESET, HIGH); //Bring up VS1053 111 112 Mp3SetVolume(20, 20); //Set initial volume (20 = -10dB) LOUD 113 //Mp3SetVolume(40, 40); //Set initial volume (20 = -10dB) Manageable 114 //Mp3SetVolume(80, 80); //Set initial volume (20 = -10dB) More quiet 115 116 //Now that we have the VS1053 up and running, increase the internal clock multiplier and up our SPI rate 117 Mp3WriteRegister(SCI_CLOCKF, 0x60, 0x00); //Set multiplier to 3.0x 118 119 //From page 12 of datasheet, max SCI reads are CLKI/7. Input clock is 12.288MHz. 120 //Internal clock multiplier is now 3x. 121 //Therefore, max SPI speed is 5MHz. 4MHz will be safe. 122 SPI.setClockDivider(SPI_CLOCK_DIV4); //Set SPI bus speed to 4MHz (16MHz / 4 = 4MHz) 123 124 //MP3 IC setup complete 125 trackNumber = 1; 126 127} 128 129void loop(){ 130 131 132 //Let's play a track of a given number 133 if(trackNumber){ 134 sprintf(trackName, "track%03d.mp3", trackNumber); //Splice the new file number into this file name 135 playMP3(trackName); //Go play trackXXX.mp3 136 trackNumber = 0; 137 } 138} 139 140 141//PlayMP3 plays a given file name 142//It pulls 32 byte chunks from the SD card and throws them at the VS1053 143//We monitor the DREQ (data request pin). If it goes low then we determine if 144//we need new data or not. If yes, pull new from SD card. Then throw the data 145//at the VS1053 until it is full. 146void playMP3(char* fileName) { 147 148 if (!track.open(&root, fileName, O_READ)) { //Open the file in read mode. 149 sprintf(errorMsg, "Failed to open %s", fileName); 150 Serial.println(errorMsg); 151 return; 152 } 153 154 sprintf(errorMsg, "Playing track %s", fileName); 155 Serial.println(errorMsg); 156 157 uint8_t mp3DataBuffer[32]; //Buffer of 32 bytes. VS1053 can take 32 bytes at a go. 158 int need_data = TRUE; 159 160 while(1) { 161 while(!digitalRead(MP3_DREQ)) { 162 //DREQ is low while the receive buffer is full 163 //You can do something else here, the buffer of the MP3 is full and happy. 164 //Maybe set the volume or test to see how much we can delay before we hear audible glitches 165 166 //If the MP3 IC is happy, but we need to read new data from the SD, now is a great time to do so 167 if(need_data == TRUE) { 168 if(!track.read(mp3DataBuffer, sizeof(mp3DataBuffer))) { //Try reading 32 new bytes of the song 169 //Oh no! There is no data left to read! 170 //Time to exit 171 break; 172 } 173 need_data = FALSE; 174 } 175 } 176 177 if(need_data == TRUE){ //This is here in case we haven't had any free time to load new data 178 if(!track.read(mp3DataBuffer, sizeof(mp3DataBuffer))) { //Go out to SD card and try reading 32 new bytes of the song 179 //Oh no! There is no data left to read! 180 //Time to exit 181 break; 182 } 183 need_data = FALSE; 184 } 185 186 //Once DREQ is released (high) we now feed 32 bytes of data to the VS1053 from our SD read buffer 187 digitalWrite(MP3_XDCS, LOW); //Select Data 188 for(int y = 0 ; y < sizeof(mp3DataBuffer) ; y++) 189 SPI.transfer(mp3DataBuffer[y]); // Send SPI byte 190 191 digitalWrite(MP3_XDCS, HIGH); //Deselect Data 192 need_data = TRUE; //We've just dumped 32 bytes into VS1053 so our SD read buffer is empty. Set flag so we go get more data 193 } 194 195 while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating transfer is complete 196 digitalWrite(MP3_XDCS, HIGH); //Deselect Data 197 198 track.close(); //Close out this track 199 200 sprintf(errorMsg, "Track %s done!", fileName); 201 Serial.println(errorMsg); 202} 203 204 205//Write to VS10xx register 206//SCI: Data transfers are always 16bit. When a new SCI operation comes in 207//DREQ goes low. We then have to wait for DREQ to go high again. 208//XCS should be low for the full duration of operation. 209void Mp3WriteRegister(unsigned char addressbyte, unsigned char highbyte, unsigned char lowbyte){ 210 while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating IC is available 211 digitalWrite(MP3_XCS, LOW); //Select control 212 213 //SCI consists of instruction byte, address byte, and 16-bit data word. 214 SPI.transfer(0x02); //Write instruction 215 SPI.transfer(addressbyte); 216 SPI.transfer(highbyte); 217 SPI.transfer(lowbyte); 218 while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete 219 digitalWrite(MP3_XCS, HIGH); //Deselect Control 220} 221 222//Read the 16-bit value of a VS10xx register 223unsigned int Mp3ReadRegister (unsigned char addressbyte){ 224 while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating IC is available 225 digitalWrite(MP3_XCS, LOW); //Select control 226 227 //SCI consists of instruction byte, address byte, and 16-bit data word. 228 SPI.transfer(0x03); //Read instruction 229 SPI.transfer(addressbyte); 230 231 char response1 = SPI.transfer(0xFF); //Read the first byte 232 while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete 233 char response2 = SPI.transfer(0xFF); //Read the second byte 234 while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete 235 236 digitalWrite(MP3_XCS, HIGH); //Deselect Control 237 238 int resultvalue = response1 << 8; 239 resultvalue |= response2; 240 return resultvalue; 241} 242 243//Set VS10xx Volume Register 244void Mp3SetVolume(unsigned char leftchannel, unsigned char rightchannel){ 245 Mp3WriteRegister(SCI_VOL, leftchannel, rightchannel); 246} 247void wireUpdate(int numBytes){ 248 Serial.println("got message"); 249 if(numBytes = 1){ 250 trackNumber = Wire.read(); 251 } 252} 253
Addscore_03.ino
arduino
This takes receives a number over I2C and displays it on the pinScore 7 digit display
1#include <PBWire.h> 2 3#include <Wire.h> 4/* 5 * The arduino takes receives a number over I2C and displays 6 * it on the pinScore 7 digit display. 7 * 8 * Wiring connections 9 * ------------------ 10 * With the pinscore display facing you and '20' on the left 11 * with 'J1' on the right, pin 1 is the leftmost pin 12 * 13 * Pin Arduino Connection 14 * 1 +5V 15 * 2 8 | A 4 bit message is sent via this 16 * 3 9 | connection so need to use port 17 * 4 10 | manipulation to set the output 18 * 5 11 | simultaneously 19 * 6 12 20 * 7 No Pin 21 * 8 Gnd 22 * 9 Not Connected 23 * 10 A0 24 * 11 Not Connected 25 * 12 2 26 * 13 3 27 * 14 4 28 * 15 5 29 * 16 6 30 * 17 7 31 * 18 Not Connected 32 * 19 Not Connected 33 * 20 Not Connected 34 * 35 * I2C - arduino needs A4 & A5 connected to the same pins 36 * on master arduino 37 * 38 */ 39 40//structure to map a pin to a digit for display 41struct pinScore { 42 int8_t pin; 43 int8_t digit; 44}; 45 46//array to hold the 7 pins and digits 47pinScore scoreMap[9]; 48 49void setup() { 50 51 Wire.begin(MSG_SCOREBOARD_ADDR); 52 Wire.onReceive(wireUpdate); 53 54 Serial.begin(9600); 55 56 //Map the pins to the digits on the pinscore 57 scoreMap[0].pin = 7;//Analogue pin for highest order digit 58 scoreMap[1].pin = 6; 59 scoreMap[2].pin = 5; 60 scoreMap[3].pin = 4; 61 scoreMap[4].pin = 3; 62 scoreMap[5].pin = 2; 63 scoreMap[6].pin = 14; 64 65 //set the pins to output 66 for(int x = 0; x <7; x++){ 67 pinMode(scoreMap[x].pin, OUTPUT); 68 scoreMap[x].digit = 1;//initialise the score to 1234567 69 } 70 71 //set port B pins to output 72 DDRB = B00111111 | DDRB; //set to output apart from osc pins 73} 74 75void loop() { 76 /*cycles to constantly refresh the pinscore using the current 77 score saved in scoreMap*/ 78 79 for(int x = 0; x < 7; x++){ 80 //set all the digit id pins to low 81 for(int y = 0; y < 7; y++){ 82 digitalWrite(scoreMap[y].pin,LOW); 83 } 84 //set the pin for the current digit to high 85 digitalWrite(scoreMap[x].pin,HIGH); 86 //send the 4 bit integer to pinscore 87 PORTB = scoreMap[x].digit; 88 //write high to set the message 89 digitalWrite(12,HIGH); 90 delay(1); 91 92 } 93 94 /*if(counter == 9999999L){ 95 counter = 0; 96 }else{ 97 counter++; 98 } 99 mapScore(counter); 100 */ 101} 102 103//takes in an unsigned long over I2C and populates the scoring array 104//with the new number. 105void mapScore(unsigned long score){ 106 unsigned long sumScore=0; 107 //extract the digits from the number and populate the scoring array 108 for(int x = 0; x < 7; x++){ 109 if(score>0){ 110 int mod; 111 mod = score % 10; // take the lowest order digit 112 scoreMap[x].digit = mod; //copy it to the map 113 score = (score-mod)/10; //move all the digits 1 to the left 114 }else{ 115 scoreMap[x].digit = 15;//will display nothing 116 } 117 } 118} 119 120void wireUpdate(int numBytes){ 121 unsigned long newScore; 122 /* 123 * The score is received over I2C as 4 individual bytes 124 * they need to be put back together into the long by 125 * copying a byte, bit shifting it accross 8 bits and 126 * reading in the next byte. 127 */ 128 if(numBytes == 4){ 129 newScore = Wire.read(); 130 for(int x = 0; x < 3; x++){ 131 newScore = newScore << 8; 132 newScore = newScore + Wire.read(); 133 } 134 } 135 //replace the old score in the scoreMap with the new one 136 Serial.println(newScore); 137 mapScore(newScore); 138} 139 140 141 142
names.h
arduino
This is a “tab” file for PinballAudio_01.ino
1/* Board I2C Addresses (except for controller [master]) 2 * and position in 10 byte array, bytes 6-9 carry the score 3 */ 4#define CONTROLLER_ID 0 5#define MSG_LIGHTS_ID 1 6#define MSG_SOUND_ID 2 7#define MSG_IGUANA_ID 3 8#define MSG_SCOREBOARD_ID 4 9 10#define DEBOUNCE_TIME 50 11 12#define TOTAL_SWITCHES 17 13//Pin IDs for switches 14#define NEW_BALL 18 15#define ENTRY_LANE 19 16#define LEFT_GATE 22 17#define CENTRE_GATE 23 18#define RIGHT_GATE 24 19#define LEFT_POP 25 20#define RIGHT_POP 26 21#define VOLCANO_SWITCH 27 22#define LEFT_TARGET 28 23#define CENTRE_TARGET 29 24#define RIGHT_TARGET 30 25#define DROP_TARGET_SWITCH 31 26#define LEFT_SLINGSHOT 32 27#define RIGHT_SLINGSHOT 33 28#define LEFT_EXIT 34 29#define RIGHT_EXIT 35 30#define DRAIN 36 31 32//I2C actions 33#define AUDIO_1 1 34#define IGUANA_FLASH_LONG 1 35#define IGUANA_FLASH_SHORT 2 36 37#define MSG_LIGHTS 1 38#define MSG_SOUND 2 39#define MSG_IGUANA 3 40 41 42
PinballLights_08.ino
arduino
This defines a lighting sequence for each of the sets of lights on the machine
1#include <PBWire.h> 2#include <Wire.h> 3 4//define pins 5 6#define TORCH_PIN 12 7#define GATES_PIN 6 8#define CURVE_PIN 4 9#define POP_BUMPER_PIN 13 10#define ORGAN_PIN 6 11#define SPOT_PIN 7 12#define SPINNER_PIN 8 13#define DRUGS_PIN 9 14#define SEX_PIN 10 15#define HELL_PIN 11 16#define DRINK_PIN 2 17#define SMOKE_PIN 5 18 19 20//buffer for incoming I2C message 21byte wireMsg = 0; 22 23void setup(){ 24 25 //set up pins as outputs: 26 27 pinMode(TORCH_PIN, OUTPUT); 28 pinMode(GATES_PIN, OUTPUT); 29 pinMode(CURVE_PIN, OUTPUT); 30 pinMode(POP_BUMPER_PIN, OUTPUT); 31 pinMode(ORGAN_PIN, OUTPUT); 32 pinMode(SPOT_PIN, OUTPUT); 33 pinMode(SPINNER_PIN, OUTPUT); 34 pinMode(DRUGS_PIN, OUTPUT); 35 pinMode(SEX_PIN, OUTPUT); 36 pinMode(HELL_PIN, OUTPUT); 37 pinMode(DRINK_PIN, OUTPUT); 38 pinMode(SMOKE_PIN, OUTPUT); 39 40 41 //start I2C with relevant slave address 42 Wire.begin(MSG_LIGHTS_ADDR); 43 Serial.begin(9600); 44 Serial.println("Starting PinballLights"); 45 Wire.onReceive(wireUpdate); 46 47} 48 49void loop() { 50 if(wireMsg== NEW_BALL_L){ 51 Serial.println("flashing LED"); 52 for(int c= 0; c<4; c++){ 53 digitalWrite(CURVE_PIN,HIGH); 54 digitalWrite(GATES_PIN,HIGH); 55 delay(200); 56 digitalWrite(CURVE_PIN,LOW); 57 digitalWrite(GATES_PIN,LOW); 58 delay(200); 59 wireMsg = 0; 60 } 61 } 62 if(wireMsg== CURVE_L){ 63 Serial.println("flashing LED"); 64 for(int c= 0; c<10; c++){ 65 digitalWrite(CURVE_PIN,HIGH); 66 delay(100); 67 digitalWrite(CURVE_PIN,LOW); 68 69 delay(100); 70 wireMsg = 0; 71 } 72 } 73 if(wireMsg== SPINNER_L){ 74 Serial.println("flashing LED"); 75 { 76 digitalWrite(SPINNER_PIN,HIGH); 77 delay(4000); 78 digitalWrite(SPINNER_PIN,LOW); 79 wireMsg = 0; 80 } 81 } 82 if(wireMsg== SPOT_L){ 83 Serial.println("flashing LED"); 84 { 85 digitalWrite(SPOT_PIN,HIGH); 86 delay(4000); 87 digitalWrite(SPOT_PIN,LOW); 88 wireMsg = 0; 89 } 90 } 91 if(wireMsg== DRINK_L){ 92 Serial.println("flashing LED"); 93 for(int c= 0; c<12; c++){ 94 digitalWrite(DRINK_PIN,HIGH); 95 digitalWrite(GATES_PIN,HIGH); 96 delay(200); 97 digitalWrite(DRINK_PIN,LOW); 98 digitalWrite(GATES_PIN,LOW); 99 delay(200); 100 101 wireMsg = 0; 102 } 103 } 104 if(wireMsg== SEX_L){ 105 Serial.println("flashing LED"); 106 for(int c= 0; c<12; c++){ 107 digitalWrite(SEX_PIN,HIGH); 108 digitalWrite(GATES_PIN,HIGH); 109 delay(200); 110 digitalWrite(SEX_PIN,LOW); 111 digitalWrite(GATES_PIN,LOW); 112 delay(200); 113 wireMsg = 0; 114 } 115 } 116 if(wireMsg== SMOKE_L){ 117 Serial.println("flashing LED"); 118 for(int c= 0; c<4; c++){ 119 digitalWrite(SMOKE_PIN,HIGH); 120 digitalWrite(GATES_PIN,HIGH); 121 delay(200); 122 digitalWrite(SMOKE_PIN,LOW); 123 digitalWrite(GATES_PIN,LOW); 124 delay(200); 125 wireMsg = 0; 126 } 127 } 128 if(wireMsg== GATES_L){ 129 Serial.println("flashing LED"); 130 for(int c= 0; c<8; c++){ 131 digitalWrite(GATES_PIN,HIGH); 132 delay(200); 133 digitalWrite(GATES_PIN,LOW); 134 delay(200); 135 wireMsg = 0; 136 } 137 } 138 if(wireMsg == DRUGS_L){ 139 for(int c=0;c<4;c++){ 140 digitalWrite(DRUGS_PIN, HIGH); 141 delay(500); 142 digitalWrite(DRUGS_PIN, LOW); 143 delay(500); 144 wireMsg = 0; 145 } 146 } 147 if(wireMsg == DRINK_L){ 148 for(int c=0;c<4;c++){ 149 digitalWrite(DRINK_PIN, HIGH); 150 delay(500); 151 digitalWrite(DRINK_PIN, LOW); 152 delay(500); 153 wireMsg = 0; 154 } 155 } 156 157 if(wireMsg == HELL_L){ 158 for(int c=0;c<40;c++){ 159 digitalWrite(HELL_PIN, HIGH); 160 delay(50); 161 digitalWrite(HELL_PIN, LOW); 162 delay(50); 163 wireMsg = 0; 164 } 165 } 166 } 167 168//handler for incoming I2C message 169void wireUpdate(int numBytes){ 170 Serial.print("got message: "); 171 if(numBytes = 1){ 172 wireMsg = Wire.read(); 173 } 174 Serial.println(wireMsg); 175} 176
Types.ino
arduino
this is a “tab”file for PinballControl_14.ino.
1struct switchPB { 2 int8_t switchPin; //the pin connected to a switch 3 int8_t coilPin; //the associated coil 4 int8_t coilState; //1 when activated, 0 when not 5 unsigned long timeStampHigh; //the time in millis when coil was activated 6 int debounceMs; /* time in millis to debounce the switch 7 this is only useful if the debounce needs to be longer than the coil 8 is charged (defined as COIL_MILLIS in names.h)*/ 9 int8_t soundMsg; 10 int8_t rouletteMsg; 11 int8_t lightsMsg; 12 long scoreIncrement; 13 int globalDebounceMs; /*Stops all switches being sensed for the number of 14 milliseconds defined (used to set suppressUntilMillis with a time to start listening 15 to the switches again).*/ 16 int8_t lastState; 17 18}; 19switchPB switchPB[TOTAL_SWITCHES]; 20
functions.ino
arduino
This is a “tab” file for PinballControl_14.ino. It maps each of the switches to the set of functions that it has to initiate.
1void switchPbSetup(){ 2 3 /*Set up the mapping of switches, coils, 4 varaibles for timing and I2S messages. 5 *The structure 'switchPB' and array 6 are declared in types.h. 7 *Elements are in the following order 8 * 1 - 9 switchPin - the pin to which the switch is connected 10 * 2 - coilPin - the 11 pin to which the coil is connected (0 if none connected) 12 * 3 - coilState 13 - *not set here - used to record whether a coil is currently activated 14 * 4 15 - timeStampHigh - *not set here - used to record the point in time (ms) that a coil 16 was activated 17 * 5 - debounceMs - time in milliseconds to debounce the switch 18 19 * 6 - soundMsg - I2C message to be sent to the sound board when switch activates 20 21 * 7 - rouletteMsg - I2C message to be sent to the roulette board 22 * 8 23 - lightsMsg - I2C message to be sent to the sound board 24 * 9 - scoreIncrement 25 - I2C message to send the default score for the switch 26 * 10 - globalDebounceMs 27 - Number of milliseconds to stop reacting to all switches 28 * *Note* - score 29 may be changed by multipliers in main code. 30 * 11 - lastState - used to record 31 the last state of a switch to ensure a switch held closed 32 * is not repeatedly 33 acted upon. 1 --> Open 0--> Closed 34 */ 35 switchPB[0] = {ENTRY_LANE_S, 36 NOTHING, 0, 0, 0, ORGAN_LOUD_SND, NO_ROULETTE, CURVE_L, 0, 0,1}; 37 38 switchPB[1] 39 = {GATE_LEFT_S, NOTHING, 0, 0, 0, HAMMOND_SND, NO_ROULETTE, DRINK_L, 1000, 0,1}; 40 41 42 switchPB[2] = {GATE_CENTRE_S, NOTHING, 0, 0, 0, HAMMOND_SND, NO_ROULETTE, 43 SEX_L, 5000, 0,1}; 44 45 switchPB[3] = {GATE_RIGHT_S, NOTHING, 0, 0, 0, HAMMOND_SND, 46 NO_ROULETTE, SMOKE_L, 1000, 0,1}; 47 48 switchPB[4] = {POP_BUMPER_LEFT_S, POP_BUMPER_LEFT_C, 49 0, 0, 200, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 3000, 200,1}; 50 51 switchPB[5] 52 = {POP_BUMPER_CENTRE_S, POP_BUMPER_CENTRE_C, 0, 0, 200, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 53 3000, 200,1}; 54 55 switchPB[6] = {POP_BUPER_RIGHT_S, POP_BUMPER_RIGHT_C, 0, 56 0, 2, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 500, 3000,1}; 57 58 switchPB[7] = {DROP_TARGET_S, 59 NOTHING, 0, 0, 200, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 100000, 0,0}; 60 61 switchPB[8] 62 = {SPINNER_S,NOTHING, 0, 0, 0, NO_SOUND, NO_ROULETTE, SPINNER_L, 3000, 0}; 63 64 65 switchPB[9] = {BALL_EJECT_S, BALL_EJECT_C, 0, 0, 0, 0, NO_ROULETTE, CURVE_L, 200000, 66 0,1}; 67 68 switchPB[10] = {TARGET_LEFT_S, NOTHING, 0, 0, 0, ROULETTE_SND, 69 SPIN_CCW, NO_LIGHTS, 30000, 0,1}; 70 71 switchPB[11] = {TARGET_RIGHT_S, NOTHING, 72 0, 0, 0, ROULETTE_SND, SPIN_CW, NO_LIGHTS, 30000, 0,1}; 73 74 switchPB[12] 75 = {EXIT_LANE_LEFT_S, NOTHING, 0, 0, 0, HAMMOND_SND, NO_ROULETTE, HELL_L, 500, 0,1}; 76 77 78 switchPB[13] = {EXIT_LANE_RIGHT_S, NOTHING, 0, 0, 0, NO_SOUND, NO_ROULETTE, 79 HELL_L, 500, 0,1}; 80 81 switchPB[14] = {SLINGSHOT_LEFT_S, SLINGSHOT_LEFT_C, 82 0, 0, 500, NO_SOUND, NO_ROULETTE, DRUGS_L, 1000, 0,1}; 83 84 switchPB[15] = 85 {SLINGSHOT_RIGHT_S, SLINGSHOT_RIGHT_C, 0, 0, 500, NO_SOUND, NO_ROULETTE, DRINK_L, 86 1000, 0,1}; 87 88 switchPB[16] = {DRAIN_S, NOTHING, 0, 0, 0, NO_SOUND, NO_ROULETTE, 89 HELL_L, 0, 0,1}; 90 91 switchPB[17] = {NEW_BALL_S,NOTHING, 0, 0, 0, NO_SOUND, 92 NO_ROULETTE, NO_LIGHTS, 0, 0,1}; 93 94 switchPB[18] = {NOTHING,NEW_BALL_C, 95 0, 0, 0, NO_SOUND, NO_ROULETTE, NO_LIGHTS, 0, 0,1}; //special case as no switch 96 assigned 97 98 switchPB[19] = {NOTHING,DROP_TARGET_C, 0, 0, 0, NO_SOUND, NO_ROULETTE, 99 NO_LIGHTS, 0, 0,1}; 100 101 102 103 for (int x = 0; x < TOTAL_SWITCHES; x++){ 104 105 #ifdef DEBUG 106 Serial.println ("Setting up pins..."); 107 Serial.print("Pin 108 "); 109 Serial.print(switchPB[x].switchPin); 110 #endif 111 if (switchPB[x].switchPin){ 112 113 #ifdef DEBUG 114 Serial.println( " Input Pullup"); 115 #endif 116 117 pinMode(switchPB[x].switchPin, INPUT_PULLUP); 118 } else { 119 #ifdef 120 DEBUG 121 Serial.println (" Not Set!"); 122 #endif 123 } 124 125 126 if (switchPB[x].coilPin) { 127 pinMode(switchPB[x].coilPin, OUTPUT); 128 129 digitalWrite(switchPB[x].coilPin, LOW); 130 } 131 } 132} 133 134void updateScore(long 135 scoreIncrement){ 136 //Serial.println("sending"); 137 score = score + scoreIncrement; 138 139 sendScore(); 140} 141 142void sendScore(){ 143 byte longAsBytes[4]; 144 longAsBytes[0] 145 = score >>24; 146 longAsBytes[1] = score >>16; 147 longAsBytes[2] = score >>8; 148 149 longAsBytes[3] = score; 150 151 Wire.beginTransmission(MSG_SCOREBOARD_ADDR); 152 153 for (int x = 0; x < 4; x++){ 154 Wire.write(longAsBytes[x]); 155 } 156 Wire.endTransmission(); 157} 158 159void 160 activateCoil(byte index){ 161 //takes in the index for the switchPB array and activates 162 the associate coil 163 #ifdef DEBUG 164 Serial.print("firing coil connected 165 to pin "); 166 Serial.println(switchPB[index].coilPin); 167 #endif 168 digitalWrite(switchPB[index].coilPin, 169 HIGH); 170 switchPB[index].timeStampHigh = millis(); 171 172 switchPB[index].coilState = 1; 173 if(switchPB[index].globalDebounceMs){ 174 175 suppressUntilMillis = millis() + switchPB[index].globalDebounceMs; 176 } 177} 178 179
PinballAudio_01.ino
arduino
This is from Spark Fun Electronics 2011 and written by Nathan Seidle
1 2 3/* 4 4-28-2011 5 Spark Fun Electronics 2011 6 Nathan Seidle 7 8 9 This code is public domain but you buy me a beer if you use this and we meet 10 someday (Beerware license). 11 12 This example shows how to play a given track 13 (track001.mp3 for example) from the SD card when a GPIO pin goes low. 14 This 15 code monitors A0 through A5. If A0 is pulled low then track 1 plays. If A5 is pulled 16 low then the track # simply advances. 17 18 This code is heavily based on the 19 "MP3 PlayTrack" example code. Please be sure to review it for more comments and 20 gotchas. 21 22 While track is playing, Arduino scans to see if pins change. If 23 pins change, stop playing track, check pins again to get 24 trigger # and then 25 decide what new track to play. 26 27 */ 28 29#include <SPI.h> 30#include<Wire.h> 31#include 32 "names.h" 33#include "mp3_config.h" 34#include <PBWire.h> 35 36 37#define 38 TRUE 0 39#define FALSE 1 40 41//Add the SdFat Libraries 42#include <SdFat.h> 43#include 44 <SdFatUtil.h> 45 46//Create the variables to be used by SdFat Library 47Sd2Card 48 card; 49SdVolume volume; 50SdFile root; 51SdFile track; 52 53//This is the 54 name of the file on the microSD card you would like to play 55//Stick with normal 56 8.3 nomeclature. All lower-case works well. 57//Note: you must name the tracks 58 on the SD card with 001, 002, 003, etc. 59//For example, the code is expecting 60 to play 'track002.mp3', not track2.mp3. 61char trackName[] = "track001.mp3"; 62int 63 trackNumber = 0; 64int previousTrigger = 1; //This indicates that we've already 65 triggered on 1 66 67long lastCheck; //This stores the last millisecond since we 68 had a trigger 69 70char errorMsg[100]; //This is a generic array used for sprintf 71 of error messages 72 73//MP3 Player Shield pin mapping. See the schematic 74#define 75 MP3_XCS 6 //Control Chip Select Pin (for accessing SPI Control/Status registers) 76#define 77 MP3_XDCS 7 //Data Chip Select / BSYNC Pin 78#define MP3_DREQ 2 //Data Request Pin: 79 Player asks for more data 80#define MP3_RESET 8 //Reset is active low 81//Remember 82 you have to edit the Sd2PinMap.h of the sdfatlib library to correct control the 83 SD card. 84 85//VS10xx SCI Registers 86#define SCI_MODE 0x00 87#define SCI_STATUS 88 0x01 89#define SCI_BASS 0x02 90#define SCI_CLOCKF 0x03 91#define SCI_DECODE_TIME 92 0x04 93#define SCI_AUDATA 0x05 94#define SCI_WRAM 0x06 95#define SCI_WRAMADDR 96 0x07 97#define SCI_HDAT0 0x08 98#define SCI_HDAT1 0x09 99#define SCI_AIADDR 0x0A 100#define 101 SCI_VOL 0x0B 102#define SCI_AICTRL0 0x0C 103#define SCI_AICTRL1 0x0D 104#define 105 SCI_AICTRL2 0x0E 106#define SCI_AICTRL3 0x0F 107 108void setup() { 109 110 Wire.begin(MSG_SOUNDBOARD_ADDR); 111 112 Wire.onReceive(wireUpdate); 113 114 pinMode(MP3_DREQ, INPUT); 115 pinMode(MP3_XCS, 116 OUTPUT); 117 pinMode(MP3_XDCS, OUTPUT); 118 pinMode(MP3_RESET, OUTPUT); 119 120 121 digitalWrite(MP3_XCS, HIGH); //Deselect Control 122 digitalWrite(MP3_XDCS, HIGH); 123 //Deselect Data 124 digitalWrite(MP3_RESET, LOW); //Put VS1053 into hardware reset 125 126 127 Serial.begin(9600); //Use serial for debugging 128 Serial.println("MP3 Player 129 Example using Control"); 130 131 //Setup SD card interface 132 pinMode(10, OUTPUT); 133 //Pin 10 must be set as an output for the SD communication to work. 134 if 135 (!card.init(SPI_FULL_SPEED, 9)) Serial.println("Error: Card init"); //Initialize 136 the SD card, shield uses pin 9 for SD CS 137 if (!volume.init(&card)) Serial.println("Error: 138 Volume ini"); //Initialize a volume on the SD card. 139 if (!root.openRoot(&volume)) 140 Serial.println("Error: Opening root"); //Open the root directory in the volume. 141 142 143 //We have no need to setup SPI for VS1053 because this has already been 144 done by the SDfatlib 145 146 //From page 12 of datasheet, max SCI reads are CLKI/7. 147 Input clock is 12.288MHz. 148 //Internal clock multiplier is 1.0x after power 149 up. 150 //Therefore, max SPI speed is 1.75MHz. We will use 1MHz to be safe. 151 152 SPI.setClockDivider(SPI_CLOCK_DIV16); //Set SPI bus speed to 1MHz (16MHz / 16 153 = 1MHz) 154 SPI.transfer(0xFF); //Throw a dummy byte at the bus 155 156 //Initialize 157 VS1053 chip 158 delay(10); 159 digitalWrite(MP3_RESET, HIGH); //Bring up VS1053 160 161 162 Mp3SetVolume(20, 20); //Set initial volume (20 = -10dB) LOUD 163 //Mp3SetVolume(40, 164 40); //Set initial volume (20 = -10dB) Manageable 165 //Mp3SetVolume(80, 80); //Set 166 initial volume (20 = -10dB) More quiet 167 168 //Now that we have the VS1053 up 169 and running, increase the internal clock multiplier and up our SPI rate 170 Mp3WriteRegister(SCI_CLOCKF, 171 0x60, 0x00); //Set multiplier to 3.0x 172 173 //From page 12 of datasheet, max 174 SCI reads are CLKI/7. Input clock is 12.288MHz. 175 //Internal clock multiplier 176 is now 3x. 177 //Therefore, max SPI speed is 5MHz. 4MHz will be safe. 178 SPI.setClockDivider(SPI_CLOCK_DIV4); 179 //Set SPI bus speed to 4MHz (16MHz / 4 = 4MHz) 180 181 //MP3 IC setup complete 182 183 trackNumber = 1; 184 185} 186 187void loop(){ 188 189 190 //Let's play a track 191 of a given number 192 if(trackNumber){ 193 sprintf(trackName, "track%03d.mp3", 194 trackNumber); //Splice the new file number into this file name 195 playMP3(trackName); 196 //Go play trackXXX.mp3 197 trackNumber = 0; 198 } 199} 200 201 202//PlayMP3 203 plays a given file name 204//It pulls 32 byte chunks from the SD card and throws 205 them at the VS1053 206//We monitor the DREQ (data request pin). If it goes low then 207 we determine if 208//we need new data or not. If yes, pull new from SD card. Then 209 throw the data 210//at the VS1053 until it is full. 211void playMP3(char* fileName) 212 { 213 214 if (!track.open(&root, fileName, O_READ)) { //Open the file in read mode. 215 216 sprintf(errorMsg, "Failed to open %s", fileName); 217 Serial.println(errorMsg); 218 219 return; 220 } 221 222 sprintf(errorMsg, "Playing track %s", fileName); 223 224 Serial.println(errorMsg); 225 226 uint8_t mp3DataBuffer[32]; //Buffer of 32 bytes. 227 VS1053 can take 32 bytes at a go. 228 int need_data = TRUE; 229 230 while(1) 231 { 232 while(!digitalRead(MP3_DREQ)) { 233 //DREQ is low while the receive 234 buffer is full 235 //You can do something else here, the buffer of the MP3 236 is full and happy. 237 //Maybe set the volume or test to see how much we can 238 delay before we hear audible glitches 239 240 //If the MP3 IC is happy, but 241 we need to read new data from the SD, now is a great time to do so 242 if(need_data 243 == TRUE) { 244 if(!track.read(mp3DataBuffer, sizeof(mp3DataBuffer))) { //Try 245 reading 32 new bytes of the song 246 //Oh no! There is no data left to 247 read! 248 //Time to exit 249 break; 250 } 251 need_data 252 = FALSE; 253 } 254 } 255 256 if(need_data == TRUE){ //This is here in 257 case we haven't had any free time to load new data 258 if(!track.read(mp3DataBuffer, 259 sizeof(mp3DataBuffer))) { //Go out to SD card and try reading 32 new bytes of the 260 song 261 //Oh no! There is no data left to read! 262 //Time to exit 263 264 break; 265 } 266 need_data = FALSE; 267 } 268 269 //Once 270 DREQ is released (high) we now feed 32 bytes of data to the VS1053 from our SD read 271 buffer 272 digitalWrite(MP3_XDCS, LOW); //Select Data 273 for(int y = 0 ; 274 y < sizeof(mp3DataBuffer) ; y++) 275 SPI.transfer(mp3DataBuffer[y]); // Send 276 SPI byte 277 278 digitalWrite(MP3_XDCS, HIGH); //Deselect Data 279 need_data 280 = TRUE; //We've just dumped 32 bytes into VS1053 so our SD read buffer is empty. 281 Set flag so we go get more data 282 } 283 284 while(!digitalRead(MP3_DREQ)) ; 285 //Wait for DREQ to go high indicating transfer is complete 286 digitalWrite(MP3_XDCS, 287 HIGH); //Deselect Data 288 289 track.close(); //Close out this track 290 291 sprintf(errorMsg, 292 "Track %s done!", fileName); 293 Serial.println(errorMsg); 294} 295 296 297//Write 298 to VS10xx register 299//SCI: Data transfers are always 16bit. When a new SCI operation 300 comes in 301//DREQ goes low. We then have to wait for DREQ to go high again. 302//XCS 303 should be low for the full duration of operation. 304void Mp3WriteRegister(unsigned 305 char addressbyte, unsigned char highbyte, unsigned char lowbyte){ 306 while(!digitalRead(MP3_DREQ)) 307 ; //Wait for DREQ to go high indicating IC is available 308 digitalWrite(MP3_XCS, 309 LOW); //Select control 310 311 //SCI consists of instruction byte, address byte, 312 and 16-bit data word. 313 SPI.transfer(0x02); //Write instruction 314 SPI.transfer(addressbyte); 315 316 SPI.transfer(highbyte); 317 SPI.transfer(lowbyte); 318 while(!digitalRead(MP3_DREQ)) 319 ; //Wait for DREQ to go high indicating command is complete 320 digitalWrite(MP3_XCS, 321 HIGH); //Deselect Control 322} 323 324//Read the 16-bit value of a VS10xx register 325unsigned 326 int Mp3ReadRegister (unsigned char addressbyte){ 327 while(!digitalRead(MP3_DREQ)) 328 ; //Wait for DREQ to go high indicating IC is available 329 digitalWrite(MP3_XCS, 330 LOW); //Select control 331 332 //SCI consists of instruction byte, address byte, 333 and 16-bit data word. 334 SPI.transfer(0x03); //Read instruction 335 SPI.transfer(addressbyte); 336 337 338 char response1 = SPI.transfer(0xFF); //Read the first byte 339 while(!digitalRead(MP3_DREQ)) 340 ; //Wait for DREQ to go high indicating command is complete 341 char response2 342 = SPI.transfer(0xFF); //Read the second byte 343 while(!digitalRead(MP3_DREQ)) 344 ; //Wait for DREQ to go high indicating command is complete 345 346 digitalWrite(MP3_XCS, 347 HIGH); //Deselect Control 348 349 int resultvalue = response1 << 8; 350 resultvalue 351 |= response2; 352 return resultvalue; 353} 354 355//Set VS10xx Volume Register 356void 357 Mp3SetVolume(unsigned char leftchannel, unsigned char rightchannel){ 358 Mp3WriteRegister(SCI_VOL, 359 leftchannel, rightchannel); 360} 361void wireUpdate(int numBytes){ 362 Serial.println("got 363 message"); 364 if(numBytes = 1){ 365 trackNumber = Wire.read(); 366 } 367} 368
Types.ino
arduino
this is a “tab”file for PinballControl_14.ino.
1struct switchPB { 2 int8_t switchPin; //the pin connected to a switch 3 int8_t coilPin; //the associated coil 4 int8_t coilState; //1 when activated, 0 when not 5 unsigned long timeStampHigh; //the time in millis when coil was activated 6 int debounceMs; /* time in millis to debounce the switch 7 this is only useful if the debounce needs to be longer than the coil 8 is charged (defined as COIL_MILLIS in names.h)*/ 9 int8_t soundMsg; 10 int8_t rouletteMsg; 11 int8_t lightsMsg; 12 long scoreIncrement; 13 int globalDebounceMs; /*Stops all switches being sensed for the number of 14 milliseconds defined (used to set suppressUntilMillis with a time to start listening 15 to the switches again).*/ 16 int8_t lastState; 17 18}; 19switchPB switchPB[TOTAL_SWITCHES]; 20
PinballControl_14.ino
arduino
This controls the game. Its triggers are the rollover switches on the playfield and the switches in the devices such as pop bumpers, targets etc. It causes coils within the devices to be energized and also triggers, via 12C comms, actions such as light and sound sequences which are controlled by the slave Arduinos.
1#include <arduino.h> 2#include <Wire.h> 3#include "names.h" 4#include "types.h" 5#include <PBWire.h> 6 7unsigned long suppressUntilMillis = 0; 8/* May be used to ignore all switches for period 9 * defined by the attribute globalDebounceMs as 10 * part of the switchPB structure defined in types.h 11 */ 12 13//Game control variables 14int lives = 0; 15long hiScore = 0; 16unsigned long lifeStart = 0; 17byte gameInProgress = 0; 18byte newBallIndex = 0; 19byte dropTargetIndex = 0; 20byte dropTargetRaised = 0; 21 22long score = 0; 23 24void setup() { 25 26 Serial.begin(9600); 27 Wire.begin(); 28 Serial.println("Starting Lost & Found"); 29 Serial.print("Version: "); 30 Serial.println(VERSION); 31 32 33 switchPbSetup(); 34 //identify the array elements for drop target and new ball coils 35 for(int x = 0; x < TOTAL_SWITCHES; x++){ 36 if(switchPB[x].coilPin == NEW_BALL_C){ 37 newBallIndex = x; 38 } 39 if(switchPB[x].coilPin == DROP_TARGET_C){ 40 dropTargetIndex = x; 41 } 42 } 43 44 //!!Start up lights messages here 45 Wire.beginTransmission(MSG_LIGHTS_ADDR); 46 //Wire.write(YOUR_MESSAGE_HERE); 47 Wire.endTransmission(); 48 #ifdef DEBUG 49 Serial.println("Startup message sent to lights: "); 50 #endif 51} 52 53 54void loop() { 55 //is it time to raise the drop target? 56 if(!dropTargetRaised){ 57 if(millis() > lifeStart + DROP_TARGET_RAISE_MILLIS){ 58 activateCoil(dropTargetIndex); 59 dropTargetRaised = 1; 60 61 #ifdef DEBUG 62 Serial.println("drop target raised"); 63 #endif 64 65 Wire.beginTransmission(MSG_LIGHTS_ADDR); 66 Wire.write(SPOT_L); 67 Wire.endTransmission(); 68 69 Wire.beginTransmission(MSG_SOUNDBOARD_ADDR); 70 //Wire.write(YOUR MESSAGE HERE); 71 Wire.endTransmission(); 72 73 #ifdef DEBUG 74 Serial.println("drop target lights on"); 75 #endif 76 } 77 } 78 79 //loop through all the switches 80 for(int x = 0; x < TOTAL_SWITCHES; x++){ 81 /*if the coil has already been activated check to see if 82 * it needs to be de-activated*/ 83 if(switchPB[x].coilState){ // \\ 84 //check the time it was activated agains the system time // | Coil is activated - 85 if(millis() > (switchPB[x].timeStampHigh + COIL_MILLIS)){ // | Switches off the 86 // | coil after the 87 digitalWrite(switchPB[x].coilPin, LOW); // | time defined in 88 //reset the values to show the coil is now de-activated // | COIL_MILLIS 89 switchPB[x].coilState = 0; // | 90 } // / 91 } 92 93 if(!switchPB[x].coilState) { // \\ 94 //check to see if we're still waiting to debounce the switch // | Coil is not activated - 95 if(millis() > (switchPB[x].timeStampHigh + switchPB[x].debounceMs)){ // | Checks to see if the 96 switchPB[x].timeStampHigh = 0; // | switch has debounced 97 } // | 98 99 if(suppressUntilMillis){ // Ignore switches if a global 100 if(suppressUntilMillis < millis()){ // debounce has not yet expired 101 suppressUntilMillis = 0; // (see variable declaration for more info) 102 } 103 }else{ 104 //only check for a new trigger if timeHigh is 0 // | if the switch has debounced 105 int switchState = digitalRead(switchPB[x].switchPin); 106 107 if (switchState){switchPB[x].lastState = 1;} // if the switch is open update last state. 108 109 if (!switchPB[x].timeStampHigh && !switchState && switchPB[x].lastState){ // | then takes action 110 111 switchPB[x].lastState = 0; //record that the switch is now closed 112 //************************** 113 //***Game control actions*** 114 //************************** 115 //Delay for ball eject 116 if(switchPB[x].switchPin == BALL_EJECT_S){ 117 Wire.beginTransmission(MSG_SOUNDBOARD_ADDR); 118 Wire.write(FAIRGROUND_SND); 119 Wire.endTransmission(); 120 delay(BALL_EJECT_DELAY_MILLIS); 121 } 122 123 //Action for Drain 124 if(switchPB[x].switchPin == DRAIN_S){ 125 126 if(lives){ 127 lives--; 128 } 129 #ifdef DEBUG 130 Serial.println("ball in drain"); 131 Serial.print(lives); 132 Serial.print(" lives remaining"); 133 #endif 134 if(lives > 0){ 135 //need to wait for the ball to reach ball return mechanism 136 delay(DRAIN_TO_BALL_RETURN_MILLIS); 137 activateCoil(newBallIndex); 138 } else { 139 //GAME OVER 140 gameInProgress = 0; 141 #ifdef DEBUG 142 Serial.println("game over!"); 143 #endif 144 //send messages to scoreboard, sound etc. 145 } 146 } 147 148 //Action for NEW_BALL [game] 149 if(switchPB[x].switchPin == NEW_BALL_S){ 150 #ifdef DEBUG 151 Serial.println("new ball button pressed"); 152 Serial.print(lives); 153 Serial.println(" lives remaining in current game"); 154 #endif 155 if(!gameInProgress){ 156 #ifdef DEBUG 157 Serial.println("New Game - score reset"); 158 #endif 159 lives = LIVES; 160 score = 0; 161 sendScore(); 162 activateCoil(newBallIndex); 163 gameInProgress = 1; 164 } 165 } 166 //Action for entry lane 167 if(switchPB[x].switchPin == ENTRY_LANE_S){ 168 lifeStart = millis(); 169 #ifdef DEBUG 170 Serial.println("New Life"); 171 #endif 172 173 } 174 if(switchPB[x].switchPin == DROP_TARGET_S){ 175 dropTargetRaised = 0; 176 lifeStart = millis(); 177 } 178 //********************************* 179 //***end of game control actions*** 180 //********************************* 181 182 if(switchPB[x].coilPin){ 183 activateCoil(x); 184 } 185 if(switchPB[x].soundMsg){ 186 Wire.beginTransmission(MSG_SOUNDBOARD_ADDR); 187 Wire.write(switchPB[x].soundMsg); 188 Wire.endTransmission(); 189 #ifdef DEBUG 190 Serial.print("Message sent to soundboard: "); 191 Serial.println(switchPB[x].soundMsg); 192 #endif 193 } 194 if(switchPB[x].lightsMsg){ 195 196 Wire.beginTransmission(MSG_LIGHTS_ADDR); 197 Wire.write(switchPB[x].lightsMsg); 198 Wire.endTransmission(); 199 #ifdef DEBUG 200 Serial.print("Message sent to lights: "); 201 Serial.println(switchPB[x].lightsMsg); 202 #endif 203 } 204 if(switchPB[x].rouletteMsg){ 205 206 Wire.beginTransmission(MSG_ROULETTE_ADDR); 207 Wire.write(switchPB[x].rouletteMsg); 208 Wire.endTransmission(); 209 #ifdef DEBUG 210 Serial.print("Message sent to roulette: "); 211 Serial.println(switchPB[x].rouletteMsg); 212 #endif 213 } 214 if(switchPB[x].scoreIncrement){ 215 updateScore(switchPB[x].scoreIncrement); 216 } 217 } 218 } 219 } 220 } 221} 222
names.h
arduino
This is a “tab” file for PinballAudio_01.ino
1/* Board I2C Addresses (except for controller [master]) 2 * and position in 10 byte array, bytes 6-9 carry the score 3 */ 4#define CONTROLLER_ID 0 5#define MSG_LIGHTS_ID 1 6#define MSG_SOUND_ID 2 7#define MSG_IGUANA_ID 3 8#define MSG_SCOREBOARD_ID 4 9 10#define DEBOUNCE_TIME 50 11 12#define TOTAL_SWITCHES 17 13//Pin IDs for switches 14#define NEW_BALL 18 15#define ENTRY_LANE 19 16#define LEFT_GATE 22 17#define CENTRE_GATE 23 18#define RIGHT_GATE 24 19#define LEFT_POP 25 20#define RIGHT_POP 26 21#define VOLCANO_SWITCH 27 22#define LEFT_TARGET 28 23#define CENTRE_TARGET 29 24#define RIGHT_TARGET 30 25#define DROP_TARGET_SWITCH 31 26#define LEFT_SLINGSHOT 32 27#define RIGHT_SLINGSHOT 33 28#define LEFT_EXIT 34 29#define RIGHT_EXIT 35 30#define DRAIN 36 31 32//I2C actions 33#define AUDIO_1 1 34#define IGUANA_FLASH_LONG 1 35#define IGUANA_FLASH_SHORT 2 36 37#define MSG_LIGHTS 1 38#define MSG_SOUND 2 39#define MSG_IGUANA 3 40 41 42
mp3_config.h
arduino
This is a “tab” file for PinballAudio_01.ino
1 2
names.ino
arduino
this is a “tab” file for PinballControl_14.ino. It defines: the pins allocated to each of the switches; pins that are outputs to device coils; game control constants.
1 2 3#define VERSION "0.5" 4//#define DEBUG //comment out when 5 deploying full version 6/* V0.2 - Added score, suppressUntilMillis and switchPB.globalDebounceMs 7 variables 8 * Included I2C for scoreboard 9 * ability to ignore all switches 10 for a period of time added to fix rf problems with coils 11 * 12 * V0.3 - corrected 13 message to scoreboard to separate long into 4 bytes 14 * 15 * V0.4 16 * 17 18 * 19 * Delays have been included in the code for the ball eject and ball return 20 actions. It is 21 * assumed that due to the time taken for the ball to travel that 22 all activated coils e.g. 23 * pop bumpers will have been deactivated before the 24 delays occur. If this is not the case 25 * then the coils will remain activated 26 for the duration of the delay. 27 * !!!END WARNING!!! 28 * 29 * Added delay 30 [BALL_EJECT_MILLIS] for ball eject 31 * 32 * New function [activateCoil(byte 33 index)] created to allow the ball eject and drop target 34 * coils to be activated 35 independently of the switchPB loop. 36 * 37 * New code in setup identifies the 38 elements in the switchPB array that reference the 39 * ball return [newBallIndex] 40 and drop target [dropTargetIndex] coils. 41 * 42 * Game control: 43 * 44 * 45 Lives are counted - when ball goes down drain, a life is deducted until no lives 46 47 * are left and the ball is automatically returned after a defined period 48 * [BALL_EJECT_DELAY_MILLIS]. 49 50 * 51 * New ball button is now being used as new game - it is only available if 52 a game is not 53 * in progress [gameInProgress]. When pressed, a new game is initiated 54 with lives [LIVES] 55 * and the score is reset. 56 * 57 * When the entry lane 58 switch is closed a timer now starts that is used to raise the 59 * drop target 60 after a defined period [DROP_TARGET_RAISE_MILLIS] 61 * 62 * V0.5 - added new 63 lastState value to switchPB to allow checking that the swich has closed 64 * before 65 re-triggering. 66 * 67 */ 68 69 70 71/************************** 72 * the 73 following are used to 74 * tune the performance of the machine 75 * 76 *Duration 77 to hold coils high*/ 78#define COIL_MILLIS 100 79//Debounce values 80#define 81 MICROSWITCH_DB 30 82#define PLATE_SWITCH_DB 60 83/************************** 84 85 */ 86 87//Game control constant 88#define LIVES 3 89 90#define BALL_EJECT_DELAY_MILLIS 91 3000 92#define DROP_TARGET_RAISE_MILLIS 20000L 93 94 95/* The delay to account 96 for the time between 97 * the drain switch closing and the ball reaching 98 99 * the ball return mechanism - tune as appropriate 100 */ 101#define DRAIN_TO_BALL_RETURN_MILLIS 102 1000 103 104#define NOTHING 0 105#define NO_SOUND 0 106#define NO_SCORE 0 107#define 108 NO_LIGHTS 0 109#define NO_ROULETTE 0 110 111 112#define TOTAL_SWITCHES 20 113//Pins 114 allocated to switches 115#define ENTRY_LANE_S 31 116#define GATE_LEFT_S 30 117#define 118 GATE_CENTRE_S 29 119#define GATE_RIGHT_S 28 120#define POP_BUMPER_LEFT_S 27 121#define 122 POP_BUMPER_CENTRE_S 26 123#define POP_BUPER_RIGHT_S 25 124#define DROP_TARGET_S 125 22 126#define SPINNER_S 34 127#define BALL_EJECT_S 35 128#define TARGET_LEFT_S 36 129#define 130 TARGET_RIGHT_S 37 131#define EXIT_LANE_LEFT_S 38 132#define EXIT_LANE_RIGHT_S 39 133#define 134 SLINGSHOT_LEFT_S 24 135#define SLINGSHOT_RIGHT_S 23 136#define DRAIN_S 40 137#define 138 NEW_BALL_S 42 139 140//Output coil pins 141#define POP_BUMPER_LEFT_C 46 142#define 143 POP_BUMPER_RIGHT_C 53 144#define POP_BUMPER_CENTRE_C 47 145#define SLINGSHOT_LEFT_C 146 49 147#define SLINGSHOT_RIGHT_C 50 148#define DROP_TARGET_C 52 149#define BALL_EJECT_C 150 51 151#define NEW_BALL_C 48 152 153 154 155 156
PinballLights_08.ino
arduino
This defines a lighting sequence for each of the sets of lights on the machine
1#include <PBWire.h> 2#include <Wire.h> 3 4//define pins 5 6#define TORCH_PIN 12 7#define GATES_PIN 6 8#define CURVE_PIN 4 9#define POP_BUMPER_PIN 13 10#define ORGAN_PIN 6 11#define SPOT_PIN 7 12#define SPINNER_PIN 8 13#define DRUGS_PIN 9 14#define SEX_PIN 10 15#define HELL_PIN 11 16#define DRINK_PIN 2 17#define SMOKE_PIN 5 18 19 20//buffer for incoming I2C message 21byte wireMsg = 0; 22 23void setup(){ 24 25 //set up pins as outputs: 26 27 pinMode(TORCH_PIN, OUTPUT); 28 pinMode(GATES_PIN, OUTPUT); 29 pinMode(CURVE_PIN, OUTPUT); 30 pinMode(POP_BUMPER_PIN, OUTPUT); 31 pinMode(ORGAN_PIN, OUTPUT); 32 pinMode(SPOT_PIN, OUTPUT); 33 pinMode(SPINNER_PIN, OUTPUT); 34 pinMode(DRUGS_PIN, OUTPUT); 35 pinMode(SEX_PIN, OUTPUT); 36 pinMode(HELL_PIN, OUTPUT); 37 pinMode(DRINK_PIN, OUTPUT); 38 pinMode(SMOKE_PIN, OUTPUT); 39 40 41 //start I2C with relevant slave address 42 Wire.begin(MSG_LIGHTS_ADDR); 43 Serial.begin(9600); 44 Serial.println("Starting PinballLights"); 45 Wire.onReceive(wireUpdate); 46 47} 48 49void loop() { 50 if(wireMsg== NEW_BALL_L){ 51 Serial.println("flashing LED"); 52 for(int c= 0; c<4; c++){ 53 digitalWrite(CURVE_PIN,HIGH); 54 digitalWrite(GATES_PIN,HIGH); 55 delay(200); 56 digitalWrite(CURVE_PIN,LOW); 57 digitalWrite(GATES_PIN,LOW); 58 delay(200); 59 wireMsg = 0; 60 } 61 } 62 if(wireMsg== CURVE_L){ 63 Serial.println("flashing LED"); 64 for(int c= 0; c<10; c++){ 65 digitalWrite(CURVE_PIN,HIGH); 66 delay(100); 67 digitalWrite(CURVE_PIN,LOW); 68 69 delay(100); 70 wireMsg = 0; 71 } 72 } 73 if(wireMsg== SPINNER_L){ 74 Serial.println("flashing LED"); 75 { 76 digitalWrite(SPINNER_PIN,HIGH); 77 delay(4000); 78 digitalWrite(SPINNER_PIN,LOW); 79 wireMsg = 0; 80 } 81 } 82 if(wireMsg== SPOT_L){ 83 Serial.println("flashing LED"); 84 { 85 digitalWrite(SPOT_PIN,HIGH); 86 delay(4000); 87 digitalWrite(SPOT_PIN,LOW); 88 wireMsg = 0; 89 } 90 } 91 if(wireMsg== DRINK_L){ 92 Serial.println("flashing LED"); 93 for(int c= 0; c<12; c++){ 94 digitalWrite(DRINK_PIN,HIGH); 95 digitalWrite(GATES_PIN,HIGH); 96 delay(200); 97 digitalWrite(DRINK_PIN,LOW); 98 digitalWrite(GATES_PIN,LOW); 99 delay(200); 100 101 wireMsg = 0; 102 } 103 } 104 if(wireMsg== SEX_L){ 105 Serial.println("flashing LED"); 106 for(int c= 0; c<12; c++){ 107 digitalWrite(SEX_PIN,HIGH); 108 digitalWrite(GATES_PIN,HIGH); 109 delay(200); 110 digitalWrite(SEX_PIN,LOW); 111 digitalWrite(GATES_PIN,LOW); 112 delay(200); 113 wireMsg = 0; 114 } 115 } 116 if(wireMsg== SMOKE_L){ 117 Serial.println("flashing LED"); 118 for(int c= 0; c<4; c++){ 119 digitalWrite(SMOKE_PIN,HIGH); 120 digitalWrite(GATES_PIN,HIGH); 121 delay(200); 122 digitalWrite(SMOKE_PIN,LOW); 123 digitalWrite(GATES_PIN,LOW); 124 delay(200); 125 wireMsg = 0; 126 } 127 } 128 if(wireMsg== GATES_L){ 129 Serial.println("flashing LED"); 130 for(int c= 0; c<8; c++){ 131 digitalWrite(GATES_PIN,HIGH); 132 delay(200); 133 digitalWrite(GATES_PIN,LOW); 134 delay(200); 135 wireMsg = 0; 136 } 137 } 138 if(wireMsg == DRUGS_L){ 139 for(int c=0;c<4;c++){ 140 digitalWrite(DRUGS_PIN, HIGH); 141 delay(500); 142 digitalWrite(DRUGS_PIN, LOW); 143 delay(500); 144 wireMsg = 0; 145 } 146 } 147 if(wireMsg == DRINK_L){ 148 for(int c=0;c<4;c++){ 149 digitalWrite(DRINK_PIN, HIGH); 150 delay(500); 151 digitalWrite(DRINK_PIN, LOW); 152 delay(500); 153 wireMsg = 0; 154 } 155 } 156 157 if(wireMsg == HELL_L){ 158 for(int c=0;c<40;c++){ 159 digitalWrite(HELL_PIN, HIGH); 160 delay(50); 161 digitalWrite(HELL_PIN, LOW); 162 delay(50); 163 wireMsg = 0; 164 } 165 } 166 } 167 168//handler for incoming I2C message 169void wireUpdate(int numBytes){ 170 Serial.print("got message: "); 171 if(numBytes = 1){ 172 wireMsg = Wire.read(); 173 } 174 Serial.println(wireMsg); 175} 176
Addscore_03.ino
arduino
This takes receives a number over I2C and displays it on the pinScore 7 digit display
1#include <PBWire.h> 2 3#include <Wire.h> 4/* 5 * The arduino takes receives a number over I2C and displays 6 * it on the pinScore 7 digit display. 7 * 8 * Wiring connections 9 * ------------------ 10 * With the pinscore display facing you and '20' on the left 11 * with 'J1' on the right, pin 1 is the leftmost pin 12 * 13 * Pin Arduino Connection 14 * 1 +5V 15 * 2 8 | A 4 bit message is sent via this 16 * 3 9 | connection so need to use port 17 * 4 10 | manipulation to set the output 18 * 5 11 | simultaneously 19 * 6 12 20 * 7 No Pin 21 * 8 Gnd 22 * 9 Not Connected 23 * 10 A0 24 * 11 Not Connected 25 * 12 2 26 * 13 3 27 * 14 4 28 * 15 5 29 * 16 6 30 * 17 7 31 * 18 Not Connected 32 * 19 Not Connected 33 * 20 Not Connected 34 * 35 * I2C - arduino needs A4 & A5 connected to the same pins 36 * on master arduino 37 * 38 */ 39 40//structure to map a pin to a digit for display 41struct pinScore { 42 int8_t pin; 43 int8_t digit; 44}; 45 46//array to hold the 7 pins and digits 47pinScore scoreMap[9]; 48 49void setup() { 50 51 Wire.begin(MSG_SCOREBOARD_ADDR); 52 Wire.onReceive(wireUpdate); 53 54 Serial.begin(9600); 55 56 //Map the pins to the digits on the pinscore 57 scoreMap[0].pin = 7;//Analogue pin for highest order digit 58 scoreMap[1].pin = 6; 59 scoreMap[2].pin = 5; 60 scoreMap[3].pin = 4; 61 scoreMap[4].pin = 3; 62 scoreMap[5].pin = 2; 63 scoreMap[6].pin = 14; 64 65 //set the pins to output 66 for(int x = 0; x <7; x++){ 67 pinMode(scoreMap[x].pin, OUTPUT); 68 scoreMap[x].digit = 1;//initialise the score to 1234567 69 } 70 71 //set port B pins to output 72 DDRB = B00111111 | DDRB; //set to output apart from osc pins 73} 74 75void loop() { 76 /*cycles to constantly refresh the pinscore using the current 77 score saved in scoreMap*/ 78 79 for(int x = 0; x < 7; x++){ 80 //set all the digit id pins to low 81 for(int y = 0; y < 7; y++){ 82 digitalWrite(scoreMap[y].pin,LOW); 83 } 84 //set the pin for the current digit to high 85 digitalWrite(scoreMap[x].pin,HIGH); 86 //send the 4 bit integer to pinscore 87 PORTB = scoreMap[x].digit; 88 //write high to set the message 89 digitalWrite(12,HIGH); 90 delay(1); 91 92 } 93 94 /*if(counter == 9999999L){ 95 counter = 0; 96 }else{ 97 counter++; 98 } 99 mapScore(counter); 100 */ 101} 102 103//takes in an unsigned long over I2C and populates the scoring array 104//with the new number. 105void mapScore(unsigned long score){ 106 unsigned long sumScore=0; 107 //extract the digits from the number and populate the scoring array 108 for(int x = 0; x < 7; x++){ 109 if(score>0){ 110 int mod; 111 mod = score % 10; // take the lowest order digit 112 scoreMap[x].digit = mod; //copy it to the map 113 score = (score-mod)/10; //move all the digits 1 to the left 114 }else{ 115 scoreMap[x].digit = 15;//will display nothing 116 } 117 } 118} 119 120void wireUpdate(int numBytes){ 121 unsigned long newScore; 122 /* 123 * The score is received over I2C as 4 individual bytes 124 * they need to be put back together into the long by 125 * copying a byte, bit shifting it accross 8 bits and 126 * reading in the next byte. 127 */ 128 if(numBytes == 4){ 129 newScore = Wire.read(); 130 for(int x = 0; x < 3; x++){ 131 newScore = newScore << 8; 132 newScore = newScore + Wire.read(); 133 } 134 } 135 //replace the old score in the scoreMap with the new one 136 Serial.println(newScore); 137 mapScore(newScore); 138} 139 140 141 142
Comments
Only logged in users can leave comments