Go Down

Topic: pinball project (space cadet) (Read 1 time) previous topic - next topic

aka_daz

Feb 02, 2018, 06:59 pm Last Edit: Feb 02, 2018, 07:36 pm by aka_daz
so... had a good 2.5 yr break from electronics, i'm quite experienced in a self taught sense. mostly my experience lies in audio and valves. i tried a bandit build back in 2015 and i got majorly hung up on the coin slot... fast forward to now and i think i might be better to get something "simpler" under my belt, i used to love playing the old pinball game on windows so you can probs guess my current project.

here is where i am so far:

Code: [Select]



#include <Wire.h> //initiailise the script by accessing needed libraries
#include <LiquidCrystal_I2C.h> // adds the iIIc  lcd library

#define wiresda 20  //this pin is for iIIc data
#define wirescl 21  //this pin is for iIIc clock
#define launchdtct 2 //this pin is for detecting launch tube entry into play area
#define missionCon 3 //this pin is connected to launch pad rollover
#define ballDrain 8  //this pin is for the ball drain opto

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // assigns the pins for lcd use

int ballsRem = 3; //this is the value where balls remaining are stored
int currentscore = 0; // this is the value used to diplay the score
int gamestate = 0; // this bit will indicate wether the game is still running
int currentLevel = 0; // this int is used to store current level tag
int sel1 = 0;
int sel2 = 0;
int sel3 = 0;
int levelsel = 0;  //a variable to be used for the level selection
int scoring = 0; // this will be used as a score modifier when each level advances
int score = 0; // this is where the current score will be stored
int highScore = 0;

void lightUp() {// a subroutine to flash lights on certain event triggers
}

void gameover (){// a subroutine to handle lives lost
}

void setup() {
  pinMode (wiresda, OUTPUT);
  pinMode (wirescl, OUTPUT);
  pinMode (launchdtct, INPUT);
  pinMode (missionCon, INPUT);
  pinMode (ballDrain, INPUT);

  digitalWrite(launchdtct, HIGH);
  digitalWrite(missionCon, HIGH);
  digitalWrite(ballDrain, HIGH);
}

void loop() {

  if (launchdtct == LOW) {
    gamestate++ ; //this will detct the ball and activate the game
  }

  if (gamestate >= 1) {
    currentLevel = 1 ; //after detecting the ball lauch this puts the game into level selection 1
    gamestate = 0; //reset the game status ready for next ball release
    lightUp();
  }

  do {
   
if(ballDrain == LOW){
 
  ballsRem --;
 
  if(ballsRem >0){
    lightUp();
    currentLevel = 0;
    scoring = 0;
    if(score>highScore){
      score = highScore;
      }
    }
   else{
     lightUp();
     currentLevel --;
     }
  }   
   
    if (sel1 == LOW && sel2 == HIGH && sel3 == HIGH) {
      levelsel = 2; // this detects level 2 being selected
    }
    if (sel1 == HIGH && sel2 == LOW && sel3 == HIGH) {
      levelsel = 3; // this detects level 3 being selected
    }
    if (sel1 == HIGH && sel2 == HIGH && sel3 == LOW) {
      levelsel = 4; // this detects level 4 being selected
    }
    if (sel1 == LOW && sel2 == LOW && sel3 == LOW) {
      levelsel = 5; // this detects level 5 being selected
    }
    if (levelsel != 1 && missionCon == LOW) {
      currentLevel = levelsel; // this confirms level by moving the selected number into the register
    }
  }                  while (currentLevel == 1); // this statement tells the do staement when to run
}



i'm still yet to map the layout for lights/outputs from the table, that is actually my next task so i can work out something for the scoring

in the mean time i had two major questions? firstly, is there any glaring mistakes in my level select code?
second, the game space cadet has a lot of input/outputs connecting to the table and the arduino mega (my board of choice for this) has a finite amount of storage. would i be right in assuming the best method would be to use two lots of two shift registers both in a matrix to control the lighting and a keyboard style matrix for my inputs?

thank you in advance ;)

johnwasser

firstly, is there any glaring mistakes in my level select code?
Well...  sel1, sel2, and sel3 are never given values so it's hard to see if the code that sets those values is correct.  With three values that can be LOW or HIGH you have 8 possible patterns but you only seem to be using 4 of them.


second, the game space cadet has a lot of input/outputs connecting to the table and the arduino mega (my board of choice for this) has a finite amount of storage. would i be right in assuming the best method would be to use two lots of two shift registers both in a matrix to control the lighting and a keyboard style matrix for my inputs?
The MEGA has 54 digital pins and 16 analog inputs.  The first step is to actually COUNT the inputs and outputs you need.  The phrase 'a lot of' is too vague.  If your I/O needs exceed the capability of the processor, THEN you start considering options like shift registers or I2C port expanders.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

aka_daz

The MEGA has 54 digital pins and 16 analog inputs.  The first step is to actually COUNT the inputs and outputs you need.  The phrase 'a lot of' is too vague.  If your I/O needs exceed the capability of the processor, THEN you start considering options like shift registers or I2C port expanders.


i actually did go to do a quick count right after posting, 75+ inputs which would equate to 100+ outputs i appreciate this is vague but at the moment i was trying to source some tactile switches and some cheap indicators ready for my proto board.  i was only so vague because i realise i will need more then the mega can provide, also pin count aside, i was working under the assumption digital write uses more memory than shifting bits?
another reason, i don't plan on including everything right away. i will be starting with around 20 inputs and 30 outputs and expanding as i can e bothered to add them in code. i'm predicting a lot of copy and paste ;)

 
Well...  sel1, sel2, and sel3 are never given values so it's hard to see if the code that sets those values is correct.  With three values that can be LOW or HIGH you have 8 possible patterns but you only seem to be using 4 of them.

sel1 2 3 each represent three drop down targets to select a level, i want the level chosen either to be the last drop down activated ie, 1, 2 or 3 (in game name) or to be level four if all 3 have been dropped, in the context of the game the other potential combinations aren't needed, they are never given values as i haven't yet added the #define sel1 2; to give them an input number... thanks for pointing it out :)

i hope i've made sense of this for you, thank you for the reply :)

aka_daz

Code: [Select]


#include <Wire.h> //initiailise the script by accessing needed libraries
#include <LiquidCrystal_I2C.h> // adds the iIIc  lcd library

#define wiresda 20  //this pin is for iIIc data
#define wirescl 21  //this pin is for iIIc clock
#define launchdtct 2 //this pin is for detecting launch tube entry into play area
#define missionCon 3 //this pin is connected to launch pad rollover
#define ballDrain 8  //this pin is for the ball drain opto
#define sel1 22 // this pin is drop target 1a
#define sel2 23 // this pin is drop target 2a
#define sel3 24 // this pin is drop target 3a

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // assigns the pins for lcd use


int ballsRem = 3; //this is the value where balls remaining are stored
int currentscore = 0; // this is the value used to diplay the score
int gamestate = 0; // this bit will indicate wether the game is still running
int currentLevel = 0; // this int is used to store current level tag
int levelSel = 0;  //a variable to be used for the level selection
int scoring = 0; // this will be used as a score modifier when each level advances
int score = 0; // this is where the current score will be stored
int highScore = 0;

void lightUp() {// a subroutine to flash lights on certain event triggers
}

void gameover () { // a subroutine to handle lives lost
}



void setup() {
  pinMode (wiresda, OUTPUT);
  pinMode (wirescl, OUTPUT);
  pinMode (launchdtct, INPUT);
  pinMode (missionCon, INPUT);
  pinMode (ballDrain, INPUT);
  pinMode (sel1, INPUT);
  pinMode (sel2, INPUT);
  pinMode (sel3, INPUT);

  digitalWrite(launchdtct, HIGH);
  digitalWrite(missionCon, HIGH);
  digitalWrite(ballDrain, HIGH);
  digitalWrite(sel1, HIGH);
  digitalWrite(sel2, HIGH);
  digitalWrite(sel3, HIGH);




}

void loop() {

  if (launchdtct == LOW) {
    gamestate++ ; //this will detct the ball and activate the game
  }

  if (gamestate >= 1) {
    currentLevel = 1 ; //after detecting the ball lauch this puts the game into level selection 1
    gamestate --; //reset the game status ready for next ball release. decrement incase of replays
    lightUp();
  }

  do {

    if (ballDrain == LOW) {

      ballsRem --;
      delay(5);

      if (ballsRem == 0) {
        lightUp();
        currentLevel = 0;
        scoring = 0;
        if (score > highScore) {
          score = highScore;
        }
      }
      else {
        lightUp();
        currentLevel --;
      }
    }


    if (sel1 == LOW && sel2 == HIGH && sel3 == HIGH) {
      levelSel = 2; // this detects level 2 being selected
      lcd.begin (1,2);
      lcd.print ("confirmlevel1");
    }
    if (sel1 == HIGH && sel2 == LOW && sel3 == HIGH) {
      levelSel = 3; // this detects level 3 being selected
      lcd.begin (1,2);
      lcd.print ("confirmlevel2");
    }
    if (sel1 == HIGH && sel2 == HIGH && sel3 == LOW) {
      levelSel = 4; // this detects level 4 being selected
      lcd.begin (1,2);
      lcd.print ("confirmlevel3");
    }
    if (sel1 == LOW && sel2 == LOW && sel3 == LOW) {
      levelSel = 5; // this detects level 5 being selected
      lcd.begin (1,2);
      lcd.print ("confirmlevel4");
    }
    if (levelSel != 1 && missionCon == LOW) {
     
     
      lcd.begin (1,2);
      lcd.print ("level");
      lcd.print (levelSel-1);
      lcd.print ("accepted");
      currentLevel = levelSel; // this confirms level by moving the selected number into the register
    }
 
  }  while (currentLevel == 1); // this statement tells the do staement when to run
 
  switch (currentLevel){
    case 2:;
    //level 1 here
   
    case 3:;
    //level 2 here
   
    case 4:;
    //level 3 here
   
    case 5:;
    //level 4 here
  }
  }



made some tweaks, my next step is to open a new sketch and copy across the defines, ints etc and the void setup. from there i plan on creating level 1 in the void loop and then copying it back across into case 2 when i've tested it on my protoboard (really hope that makes sense) in the final version, after some serious consideration i'm thinking 64 inputs, each input requiring at least one output, so i was going to double up to 128 with 2 shift register matrixes. (deliberately giving myself too many for later expansion)

with so many in/outputs, the matrix out seems like a no brainer, however what would be my best option for in? i deliberately chose 64 as at most it should be 16 inputs using an 8*8 k/board style grid?? (i think) is there maybe a better method?

my main concern, i have no idea if i'm approaching this in the right way. i'm more than happy to make mistakes, best way to learn but i don't want to invest a lot of time into something that turns out to be completely off the mark. so.. do the switch case statements work how i'm assuming or do i need another way?

again, many thanks in advance :)

wvmarle

For both LED outputs and button inputs, a matrix will work, but for any detailed advice you'll have to show the circuit diagram of the machine first.

Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

DrDiettrich

An input matrix most probably will be too slow for a pinball game. Port expanders instead offer an interrupt output, which signals a change in the input lines, so that polling  at least  can be reduced. In a real ball game the contacts are closed for a short time only, so that hardware support is required for those many inputs.

aka_daz

but for any detailed advice you'll have to show the circuit diagram of the machine first.


i'm hesitant to post any schemes till i know it works, don't want to spread bad info for others, plus im still sorting through parts. however i plan on having an assembled dev board for it by next week. i'll be happy to post that.
i'm not too concerned with replicating the targets on board as i'm willing to tweak the the actual input signal to be comparable using hardware if needs be (pulse extenders, buffers or inverters, etc(i have ample components to do this))

An input matrix most probably will be too slow for a pinball game. Port expanders instead offer an interrupt output, which signals a change in the input lines, so that polling  at least  can be reduced. In a real ball game the contacts are closed for a short time only, so that hardware support is required for those many inputs.
i didn't think the matrix would be to slow to keep up but i shall definitely bear that in mind!
port expanders? are they basically iIIc accessed i/o pins?
 funny you should mention the interrupt. i was going to ask in my previous post, but didn't. if there was any practicality to making it so every time any button(in game target) is pressed it triggers an interrupt on the rising edge. then have the interrupt "scan" the keys  for the falling edge and adjust my ints, scores etc. i was thinking if i could do it this way then i could use my case "loops" for level progression and bonus modifiers and let the interrupt handle scoring?

all criticism is welcome.
I really appreciate these answers guys, thank you!

wvmarle

Port expanders are e.g. the MCP23008/23017 and PCF8574/8575 (8/16 pin). They indeed have a pin change interrupt output available, which is to connect to a separate pin on the Arduino to trigger an interrupt there. The interrupt is for a block of 8 pins (so the MCP23017 and PCF8575 have two interrupt outputs), you then have to check which of the eight triggered the interrupt. You can read all pins of one bank in one go and get a single byte for the status of all 8.

Presses by a bouncing ball are indeed much shorter than a human pressing a button, so indeed may be too slow. A port expander with interrupt should definitely work for that.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

johnwasser

sel1 2 3 each represent three drop down targets to select a level, i want the level chosen either to be the last drop down activated ie, 1, 2 or 3 (in game name) or to be level four if all 3 have been dropped, in the context of the game the other potential combinations aren't needed,
That may be what you want but what you code says is:
"Set the level to 1, 2, or 3 if only one is down. Do nothing if two are down. Set the level to 4 if all three are down."
That will give the effect of setting the level to the FIRST one dropped, ignoring the second one dropped, and switching to level 4 when the third one drops.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

aka_daz

That may be what you want but what you code says is:
"Set the level to 1, 2, or 3 if only one is down. Do nothing if two are down. Set the level to 4 if all three are down."
That will give the effect of setting the level to the FIRST one dropped, ignoring the second one dropped, and switching to level 4 when the third one drops.

nicely spotted, this is down to the custom hardware i'll be implementing essentially each drop will trigger a pulse, latching will only occur when all 3 drop, i hope i'm correct in my assumption that pulses will suffice for the first 3 conditions?

(next part i'm still designing) when all 3 drop i plan to use the latch to send some sort of reset signal to a servo/solenid/generic actuator to reset targets while ignoring any pulses but keep the state in the code, this being analogue and not interfacing with arduino. the code only shows 3 drops at the moment in total there will be 12 split into 4 banks of three. given my extremely high i/o count i figured i best to try getting some things done automatically...

to be honest i actually stopped working out the switching for the time being, i'm currently trying to wrap my head round the port expanders... confused.com comes to mind, trying to find a library that makes use of the interrupt function but having no luck as of yet.

again, thanks guys! :)

wvmarle

The libraries for those expanders don't use the interrupt themselves. You are to set the interrupt in your own code, then when an interrupt was received go ahead and read the relevant register.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

DrDiettrich

The described level management is different from the space cadet game, it may deserve some more thoughts.

The port expander interrupt pins do not necessarily force controller interrupts, they also can be treated as sequentially readable "hit" flags for one of their inputs.

Eventually the targets can permanently break a line when hit, which is closed again when the target is restored later. Then it's only required that every hit (state change) is counted, and a single actuator can raise all targets of a group.


aka_daz

The described level management is different from the space cadet game, it may deserve some more thoughts.


i don't understand?

Eventually the targets can permanently break a line when hit, which is closed again when the target is restored later. Then it's only required that every hit (state change) is counted, and a single actuator can raise all targets of a group.


thats kinda what i was trying to say except in my version, they pulse as activated individually, then latch once all 3 fall which then triggers the reset. though i must admit i like the sound of your way. if i have this correct? each "bank" would only require one output which i could instead use to increment a value?

DrDiettrich

I didn't play the game since a couple of years. Perhaps we talk about different levels, I meant the circle in the center reflecting the player's rank.

The target handling depends on the mechanical implementation. How do you drop and raise your targets?

aka_daz

oh no you are correct! i was going to worry about that aspect later, keeping that side in the code for now, my main objectives at the moment where to get the four cadet levels sorted as individual programs: with that done i'll have targets and basic scoring sorted from there i was going to add the interface you mention, plus other "illuminations" and then merge programs...  this is my 3rd day on the project in total i've only spent around 10 hours on this so far but as it stands i've accounted for the following...

1) added value to represent rank multiplier "scoring" at default it will be 5 as rank progresses this int gets doubled and upon wipeout is reset to 0 then bumped back up to 5 on ball launch
2) offset irl level numbers with incode to account for the different ranks 1 = cadet 2 = cadet level 1 so on until i reach 6 which is the next ranks selection stage
3)in  furtherance to my previous i realised and am about to account in code for "experience" being what takes us to level 6 (in code tag) by having victory increment an as of yet un made int to be checked when the case breaks back in to the do for level 1 (i really hope that makes sense)
4) added measures in place to add replay and reflex shots later
5)on my drawing i have grouped inputs into "banks" and labelled each with it's in code name

there is more. in the mood to code not explain atm ;) but if u wanna know more by all means ask and i'll reply :)

targets... the plate would be spring held on a ledge, ball knocks them off and they drop down, the reset was going to be handled like normal drop targets but i was thinking servo instead of solenoid... not yet worked out how i plan to take the inputs. maybe a limit switch or something...
the "latching" i reffered to was probs bad wording, it would be better to say the reset would of pulsed all 3 on the way up

i was hoping to ask a question, i don't like the look of these external I/O things.. well out of my comfort zone as i've never dabbled before. so i nipped out and got my self a pickit, was thinking i might be better having pics interfacing with my switches and sending the data out to be used, have them handle some of the simpler out puts for me aswell ? i assume the mega having extra rx tx lines is more than happy with this?

either way for now i'm using the mega pins to test and limiting my self to 20in 20 out plus my iIIc for lcd debugging

Go Up