Go Down

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

DrDiettrich

Argh, killed my essay by pressing a wrong key :-(

Perhaps you are ahead of me WRT levels, at least you seem to have a plan :-)

For the drop targets I'd use light barriers or the like, instead of ordinary switches, so that no debouncing is required.
If all targets of a group go into the same port or port extender byte, a simple pattern match is sufficient to find out when all targets of a group are down.

DrAzzy

A matrix for the input switches is fine. That's how all real pinball machines do it. They put a diode in series with each switch to prevent phantom presses when multiple switches are closed, pull the rows up to +12v, then drive the columns low (via a ULN2803) in turn and looking at the rows to see if any are pressed. The rows get monitored by LM339's comparing against 6v reference voltage. (this is how it works on 90's era williams bally ones at least)

I skimmed reading above (not enough time to read essays). Are you making a full sized pinball table with it? If so, I suggest using the mechanisms (like for drop targets and the like) from a real pin, not trying to make your own. The pinball machine environment is incredibly harsh - even these commercial parts, designed by experts in pinball mechanics and that sort of mechanical engineering, routinely get beaten up. In general, use the same parts that are used in real pins whenever possible, rather than reinventing the wheel (unless you like mechanical-wheel-reinvention).

(also, as an aside - if you happen to be located in newengland, and need a pin that you could gut and fill with your new game, I've got a non-working 70's era EM pin that I could cut you a really good deal on cause my roommates are on my ass to either make it work or move it out)
ATtiny core for 841+1634+828 and x313/x4/x5/x61/x7/x8 series Board Manager:
http://drazzy.com/package_drazzy.com_index.json
ATtiny breakouts (some assembled), mosfets and awesome prototyping board in my store http://tindie.com/stores/DrAzzy

aka_daz

(also, as an aside - if you happen to be located in newengland, and need a pin that you could gut and fill with your new game, I've got a non-working 70's era EM pin that I could cut you a really good deal on cause my roommates are on my ass to either make it work or move it out)
unfortunately, no... i'm old England ;) i really appreciate the thought though!

I skimmed reading above (not enough time to read essays).
i was trying to avoid that..lol! got a lot of thinking/planning to do and didn't want to bore everyone! ;)

@the rest of DrAzzy, yeh i realise the harshness of the pinball machine. however, i am determined to make my own.  realistically i'll be doing it in steps. for now... a piece of a3 wood with various controllers and switches with an lcd. "debug mode" then a cheap machine. then upgrade... at least that be the plan ;) it's the challenge. reinventing the wheel always looked fun!

i shall certainly look into the ULN2803 and LM339 after i have got my basic "level progression" with 20in/out running, thank you!




Argh, killed my essay by pressing a wrong key :-(

rubbish when that happens

Argh, killed my essay by pressing a wrong key :-(

Perhaps you are ahead of me WRT levels, at least you seem to have a plan :-)

For the drop targets I'd use light barriers or the like, instead of ordinary switches, so that no debouncing is required.
If all targets of a group go into the same port or port extender byte, a simple pattern match is sufficient to find out when all targets of a group are down.
i have no idea what WRT level is lol. i actually was thinking i'd either use opto or rollover limit tbh. i have ample quad op amps for debouncing if required, it's going to come down to cost at this stage, not really priced either up yet tbh



thanks for your replies guys!! i always come away thinking, appreciated!

if any ones curious i can upload my code samples later? (after i've tested and merged them)

aka_daz

sorry to be a nag, been splicing my codes together and started thinking about lights. originally i was thinking 16F690s but now i'm veering to nanos. if i was to use TX/RX1 off the mega would i be able to send words ie, "bank1_hit" "bank1_reset" which would then activate "void bank1_hit(){blah blah}" on the other side?

in furtherence to that i was thinking of adding a 3rd line just a logic out from the mega going to an input pin on nano, the purpose to signal a priority message(the signal will also be sent to sound once i worked it out) so that it knows to play the latest lighting cycle over what its currently doing(basically i'll have my entire code twice in two while statements) is this possible?



while i'm here, my first attempt at merging buggy as out, actually had to start again from my 4 source files, currently my display is bugging out on the first level reset. i'm sure i'll figure it out ;)

Warning, code is long lol

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


//targets start here


#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

#define jkpt1 25 // this pin is the jackpot drop target
#define jkpt2 26 // this pin is the jackpot drop target
#define jkpt3 27 // this pin is the jackpot drop target


//variables for scoring start here


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;
int experience = 0;

int cadet1 = 5; // the first cadet level, ramp entry four times




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



void setup() {
 
 
 
  pinMode (launchdtct, INPUT);
  pinMode (missionCon, INPUT);
  pinMode (ballDrain, INPUT);
 
  pinMode (sel1, INPUT);
  pinMode (sel2, INPUT);
  pinMode (sel3, INPUT);
  pinMode (jkpt1, INPUT);
  pinMode (jkpt2, INPUT);
  pinMode (jkpt3, INPUT);

  digitalWrite(launchdtct, HIGH);
  digitalWrite(missionCon, HIGH);
  digitalWrite(ballDrain, HIGH);
 
  digitalWrite(sel1, HIGH);
  digitalWrite(sel2, HIGH);
  digitalWrite(sel3, HIGH);
 
  digitalWrite(jkpt1, HIGH);
  digitalWrite(jkpt2, HIGH);
  digitalWrite(jkpt3, HIGH);
 
 
  lcd.begin(16,2);
}

void loop() {

 // if (digitalRead(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();
  //}
 currentLevel=1;
  while (currentLevel == 1) {        // this statement tells the do statement when to run

    if (digitalRead(ballDrain) == LOW) { //code here to handle ball drain
     
      lcd.setCursor(1,2);
      lcd.clear();
  ballsRem --; //removes one life from counter
      delay(5);

      if (ballsRem == 0) { // code here to handle no live remaining
        lightUp();
        currentLevel = 0;
        scoring = 0;
        if (score > highScore) {
          score = highScore;
        }
      }
      else { // code to handle lives remaining
        lightUp();
        currentLevel --;
      }
    }
    lcd.setCursor(0,0);
    lcd.print("choose level");

    if (digitalRead(sel1) == LOW) { //select level cadet1
      levelSel = 2; // this detects level 2 being selected
      lcd.setCursor(1,2);
      lcd.clear();
      lcd.print ("confirmlevel 1");
    }
    if (digitalRead(sel2) == LOW) { //select level cadet2 (doesn't yet exist, glitches code if confirmed)
      levelSel = 3; // this detects level 3 being selected
      lcd.setCursor(1,2);
      lcd.clear();
      lcd.print ("confirmlevel 2");
    }
    if (digitalRead(sel3) == LOW) { //select level cadet3, see above
      levelSel = 4; // this detects level 4 being selected
      lcd.setCursor(1,2);
      lcd.clear();
      lcd.print ("confirmlevel 3");
    }
    if (sel1 == LOW && sel2 == LOW && sel3 == LOW) { //select level cadet4, see above
      levelSel = 5; // this detects level 5 being selected
      lcd.setCursor(1,2);
      lcd.clear();
      lcd.print ("confirmlevel 4");
    }
    if (levelSel != 1 && digitalRead(missionCon) == LOW) {
     
     
      lcd.setCursor(1,2);
      lcd.clear();
      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 ==2){
       scoring=5;
if(digitalRead(sel1) == LOW){
score=score+(2*scoring);
                delay(75);
}
if(digitalRead(sel2) == LOW){
score=score+(2*scoring);
                delay(75);
}
if(digitalRead(sel3) == LOW){
score=score+(2*scoring);
                delay(75);
}



if(digitalRead(jkpt1) == LOW){
score=score+(2*scoring);
                delay(75);
}
if(digitalRead(jkpt2) == LOW){
score=score+(2*scoring);
                delay(75);
}
if(digitalRead(jkpt3) == LOW){
score=score+(2*scoring);
                delay(75);
}



if(digitalRead(missionCon) == LOW){ //this detects mission objective being triggered
cadet1 --;
score = score+(scoring*2);
delay(75);
}
       
        if(cadet1 == 0){ //this detects victory condition
        experience= 5+experience;
                cadet1=cadet1+4;
                currentLevel = 1;
lcd.clear();

if(experience == 16){ //if 16 or more experience has been gained then rank will be promoted
currentLevel = 6;
            }
               
}
       
   
delay(75);
         lcd.clear();
        lcd.setCursor(7,0);
lcd.print(score);
        lcd.setCursor(0,0);
        lcd.print("Score:");
        lcd.setCursor(0,1);
        lcd.print("Ramp Remain:");
        lcd.setCursor(13,1);
        lcd.print(cadet1);
        levelSel = 1;
  }
   
   

    //level 2 here
   

    //level 3 here
   

    //level 4 here
  }


wildbill

sorry to be a nag, been splicing my codes together and started thinking about lights. originally i was thinking 16F690s but now i'm veering to nanos. if i was to use TX/RX1 off the mega would i be able to send words ie, "bank1_hit" "bank1_reset" which would then activate "void bank1_hit(){blah blah}" on the other side?

You could, but perhaps not as simply as you are hoping. You can send commands over serial, parse them at the recipient's end and then call functions based on what was received. You can't send "bank1_hit" and automagically call the function of that name. You could however have an array of structs containing a string received and a corresponding pointer to a function to call when you get that string.

aka_daz

oh no, i realised it would be complicated. it seemed to good to be to true. my overall design hinges on it though (probs find another way in a pinch), i'll be copy and pasting my sound board from the light and just changing the "events" while keeping the names, so hoping to share the data control to save mega pins

An array of structs you say? i think i should get reading lol

Thank you! :)

wvmarle

I would solve that with a long switch/case statement.

Also keep your Serial communications short, one or two values, to keep it fast. Use constants or #define statements to replace it with human readable commands in your code.

If you have less than 255 commands you can do it in a single character on the Serial. You may even be able to do away with start/stop bytes, even faster. Just a single value, every one value has a meaning. If more than 255 you need two bytes, and start/stop bytes (use e.g. 0 or 255 for that).

So you'd get code like this in the slave:

Code: [Select]

#define BANK1_HIT 1
#define BANK1_RESET 2
#define BANK2_HIT 3
#define BANK2_RESET 4

void loop() {

  // bank 1 hit! Send notification to host.
  Serial.write(BANK1_HIT);

}


And on the receiving end:
Code: [Select]

#define BANK1_HIT 1
#define BANK1_RESET 2
#define BANK2_HIT 3
#define BANK2_RESET 4

void loop() {

  command = Serial.read();
  switch (command) {
    case 1:
      bank1_hit();
      break;

    case 2:
      bank1_reset();
      break;
  }
}


You can use a #include to keep your list of #defines in sync between the various sketches.
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.

aka_daz

I would solve that with a long switch/case statement.

Also keep your Serial communications short, one or two values, to keep it fast. Use constants or #define statements to replace it with human readable commands in your code.

If you have less than 255 commands you can do it in a single character on the Serial. You may even be able to do away with start/stop bytes, even faster. Just a single value, every one value has a meaning. If more than 255 you need two bytes, and start/stop bytes (use e.g. 0 or 255 for that).

So you'd get code like this in the slave:

my original thoughts where using while statements pretty much the way your describing. but in the context of your example ending each while statement with a "command = 0;" is there any reasons i'd use the case over the while? or vica versa? and thank you. I think i'm gonna go have a play with that idea :)

You can use a #include to keep your list of #defines in sync between the various sketches.
i have a dedicated file in notepad with all my header and void setups, everytime i add more features or start something new i take what a copy to my new sketc. then at the end go back update the file. in truth i wouldn't be sure how to include other sketches variables, is it the same as adding a library?

wvmarle


is there any reasons i'd use the case over the while? or vica versa?
The while statement is a loop. The switch/case and if/else statements are selectors. Very different use case.

Quote
i have a dedicated file in notepad with all my header and void setups, everytime i add more features or start something new i take what a copy to my new sketc. then at the end go back update the file. in truth i wouldn't be sure how to include other sketches variables, is it the same as adding a library?
Pretty much like adding a library indeed - save your headers in a .h file in your libraries folder, then include that in the top of your sketch and it'll be read at compile time.
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.

aka_daz

so i been making a lot of various boards and what not for lighting control and decided to have a go at the serial control:

Code: [Select]

#include <Shifty.h>


#define GAME_START 1
#define BALL_LOST 2
#define GAME_RESET 3
#define BANK2_RESET 4

Shifty shift;


// Declare pins for CLK, DTA, LTCH
int CLK = 11;
int DTA = 12;
int LTCH = 8;
int delayTime = 250;
int command = 256;

void gameStart (){
  shift.writeBit(1, HIGH);
  delay(delayTime);
  shift.writeBit(1, LOW);
  delay(delayTime);
}
void ballLost (){
  shift.writeBit(2, HIGH);
  delay(delayTime);
  shift.writeBit(2, LOW);
  delay(delayTime);
}
void setup(){
Serial.begin(9600);
shift.setBitCount(16);
shift.setPins(CLK, DTA, LTCH);
}
void loop() {

  command = Serial.read();
  switch (command) {
    case 1:
      gameStart();
      break;

    case 2:
      ballLost();
      break;
  }
}


i cant get it to work.. i send the command and nothing... i know how i have this set up (wiring etc) that its all working , i can start a new sketch, insert "shifty" library with the same variables and control any led. my error has come from the serial, i think i'm missing something glaringly obvious but can't place it... the fact i'm using the serial monitor to send commands should affect it should it? (same baud rate)

wildbill

You're looking for a command of character 1, which is hard to send from serial. Try 49 instead, or:
Code: [Select]

    case '1':
      gameStart();
      break;


Note single quotes.

aka_daz

single quotes worked, thank you!!! thats been irritating me for ages lol

wvmarle

#27
Feb 10, 2018, 12:15 am Last Edit: Feb 10, 2018, 04:09 pm by wvmarle
Code: [Select]

int command = 256;

This should probably be a single byte type, so one of:
Code: [Select]

byte command;
char command;
uint8_t command;
unsigned short int command;


256 does not fit in a single byte, by the way. 255 is the highest number allowed.

(edit: fixed bbcode)
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.

aka_daz

it was deliberate. it was part of my debugging to make sure it wasn't in a case on startup. it will all be changed by the time i get to revision 1. nicely spotted though! thanks for the warning :)

my work for today, i am having a new issue
Code: [Select]

#define sel1 0
int sel1s = LOW;

#define sel2 1
int sel2s = LOW;

int row1[] {sel1, sel2, sel3, entry1, entry2, entry3, 6, 7};
int row1s[] {sel1s, sel2s, sel3s, entry1s, entry2s, entry3s, LOW, LOW};


void SEL1() {
  if(sel1s == LOW){
  sel1s = HIGH;
  }
  else(){
  sel1s = HIGH;
  }
}


void ballLost () {
  shift.writeBit(1, HIGH);
  delay(delayTime);
  shift.writeBit(1, LOW);
  delay(delayTime);
}

void setup() {
  Serial.begin(9600);
  shift.setBitCount(16);
  shift.setPins(CLK, DTA, LTCH);

  for (thisPin = 0; thisPin <= 15; thisPin ++) {
    shift.writeBit(thisPin, LOW);
  }

}
void loop() {

  command = Serial.read();
 // for (int x = 0; x <= 2; x++) {
       // thisPin=0;
        for (thisPin = 0; thisPin <= 6; thisPin++) {
          shift.writeBit(row1[thisPin], row1s[thisPin]);
          delay(5);
        }
        delay(5);
        if(thisPin=6){
          thisPin = 0;
        }
   //   }
  switch (command) {

    case '0':

     
      break;
     
     
    case '1':
      SEL1();
      command = 256;
      break;

    case '2':
      ballLost();
      break;

  }
}





that isn't all of it so anything that looks "undefined" is actually in my code, just copied where i think the problem lies...

when i parse in a 2 sel2 (number 1 on my bitwrite) lights up and goes off as it should, when i parse in a 1 nothing happens.. if i was to put the "row1s=row1s[] {sel1s, sel2s, sel3s, entry1s, entry2s, entry3s, LOW, LOW};" inside my for loop just before the writeBit part would that sort it? or do i require another way?

my ultimate aim is to have the incoming data modify these states so the main program is essentially just displaying everything.

wvmarle

You're going to need lots of states, but: are you sure about adding delay() calls there? I thought this code is to be as responsive as possible!
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.

Go Up