Pinball Project

Thank you guys for the input. Wow, you guys are fast!

How are the switches (to be) wired to the Arduino? Since you are not using the internal pullup resistors, you'll need external resistors. Using the internal ones is so much easier.

50 milliseconds is a loooong time for a switch to bounce. 10 milliseconds is usually long enough for bouncing to die out.

In most of the cases, there will be TTL AND or NAND gates between the switches and the Arduino. But not in the case of the All Drop Targets Down Switch, my guess is that if it makes any difference at all, I'd go for pulling the switch up towards the Arduino's 5 volt level, rather than the pinball game's.

Changed the debounce delay to 10 ms. Thanks. The other delays will most likely be subject to change, too. I just have to hear it to know if it produces the right "feel".

In the long run you have no chance at all of getting code like this to work as it uses delay(). Look at the "blink without delay" example , and then look at FSM's (Finite State Machines) in the playground.

Mark

I will look at it, right after I've finished writing this post. I think I've already forseen that "blink without delay" issue if I got it right, I'm not sure.
I think there's no chance of using only one Adrino for all the tasks, even if the ports and the memory are sufficient. So this unit will handle three of the four instances where there just have to be delays. (The forth is the two Bumpers, but I will use a dual copy of Gottlieb's System 80 Bumper Driver board there, and the switch signal for 100 points score will go to the other Arduino unit, the one without or with very short delays.)
This one will only control the two pits and the drop target bank. While it works with its delays, the ball will hit the slingshots and other targets That would have posed big troubles if the same unit was supposed to take care of those signals, too. But they will not be sent to this busy unit. And while the ball is in one of the two pits, of course no other inputs are made from the rest of the playfield. (Sorry, my English skills is not 100%, so sometimes I need ten words to express something that should take two words...)

I should also have pointer you at the use of shift registers (and other chips) the extend the number of I/O ports you have.

Mark

Shift registers are currently beyond my knowledge. I think I got the idea in general, but no details.
Don't know if I have the brain capacity to figure that out. But for now, I think the I/O ports of the Uno will be sufficient for this part of the project.

SimLego:
I think there's no chance of using only one Adrino for all the tasks, even if the ports and the memory are sufficient. So this unit will handle three of the four instances where there just have to be delays.

I'll take you up on that challenge. Without knowing much about this particular pinball or driver board, it is much less demanding than - say - drive a 3D printer (3 steppermotors, LCD display, Serial I/O, monitoring switches, heatbeds etc.)

there just have to be delays

There is the Two LED without delay which shows you can have several, totally independent "delays" running, that is wait a short time, without using delay()

I'll take you up on that challenge. Without knowing much about this particular pinball or driver board, it is much less demanding than - say - drive a 3D printer (3 steppermotors, LCD display, Serial I/O, monitoring switches, heatbeds etc.)

Now I see that you're most likely right, so I can't accept your challenge. :slight_smile: It is probably possible with an Uno and some workarounds (multiplexing, shift registers...), but it's going to be quite easy and straightforward with a Mega. I thought that this "blinking without delay()" was some low-level special coding, but now I see it's uncomplicated with the standard function millis().

The next trick is polling basically your code should look something like

void loop(){
     check each input and get there current state;
     if an input changed (the ball hit something) then do something about it.
}

You need to look at "debouncing" - basic when you turn on a switch it does not just go to the on state. It "bounces" doing a few on - off- on - off before it ends up in the on state.

Mark

Just for fun - how about a photo of this machine.

Mark

Just for fun - how about a photo of this machine.

I will take some photos along the work, meanwhile you can have a look at a few quite low-res ones at ipdb:

(Not my game, but same model.)

My Uno arrived today from HK, much earlier than expected. :smiley:
Installation and writing/uploading 'Hello World' was totally hassle free, guess it took about ten minutes. I'm already in love! Discovering the non-standard gap between digital pins 7 and 8 was the only unpleasant surprise so far. Makes it much harder to make DIY shields on prototype boards.

Now I have to study the Multiplexer/Driver Board once more to see how much is still useful. This will surely take some time...

SimLego:
My Uno arrived today from HK, much earlier than expected. :smiley:
Installation and writing/uploading 'Hello World' was totally hassle free, guess it took about ten minutes. I'm already in love! Discovering the non-standard gap between digital pins 7 and 8 was the only unpleasant surprise so far. Makes it much harder to make DIY shields on prototype boards.

Now I have to study the Multiplexer/Driver Board once more to see how much is still useful. This will surely take some time...

Yes the non-standard gap can be a hassle. It was just a simple rush to market last min error that didn't get caught when they released their very first version of the arduino board. And once release into the wild they didn't want to correct it as it would make useless any shield made prior if they corrected the spacing.

However there is a pretty simple fix, use a bent 'offset header' that will align up with standard .1" spaced proto-boards:

https://www.sparkfun.com/products/9374?

Lefty

Great idea, Lefty. Only problem is their international shipping costs are not of this world.

SimLego:
Great idea, Lefty. Only problem is their international shipping costs are not of this world.

Well you are free to bend your own up from standard long pin headers, now that you know the trick. Many E-bay sellers have free shipping. Perhaps setting up your location in your profile settings would help others know from where you are posting and others can tell of possible local suppliers?

Bad news. Seems like two out of four functions on the Multiplexer/Driver Board are dead. The solenoid drivers are ok, but neither the multiplexer to the switch matrix nor the multiplexer/driver for the Bonus lamps work; some of the inputs are shorted. Remains to see if the other lamp drivers apart from the bonus array work.

Well well, I'll begin with making a new Multiplexer board just for the switch matrix. It's very simple and straightforward. Just a 7445 and some 1K2 pullup resistors for the switch lines in use. I don't have a 7445, but I can't see why one of my 7442's shouldn't do the same job.

Here's some code I have in mind for scanning the switch matrix. I'll use the same addresses, just to make it easier to refer to the manual. No debounching or any response to input yet.

// scan_switches.ino

const int bit0pin = 2;  //  MA41 (Black)
const int bit1pin = 3;  //  MA43 (Blue)
const int bit2pin = 4;  //  MA45 (Green)
// const int bit3pin = 5;  //  MA47 (Yellow) // Not needed?
const int Apin = 6;     //  JC09 Slate-White   or MA48
const int Bpin = 7;     //  JC10 Yellow-White  or MA46
const int Cpin = 8;     //  JC11 Red-White     or MA44
const int Dpin = 9;     //  JC12 Brown-White   or MA42

int ret;

void setup()
{
  pinMode(bit0pin, OUTPUT);
  pinMode(bit1pin, OUTPUT);
  pinMode(bit2pin, OUTPUT);
 //  pinMode(bit3pin, OUTPUT);  // Not needed?
  pinMode(Apin, INPUT);
  pinMode(Bpin, INPUT);
  pinMode(Cpin, INPUT);
  pinMode(Dpin, INPUT);
}

void loop()
{
  while(1)
  {
    ret = scansw();
    if(ret == 0) continue;
    if(ret == 1)  // Drain Pit Switch closed
    {
       // code...
    }
    // if(ret == ...) and so on...
  }
}


int scansw()
{
  // Scan Line 0
  digitalWrite(bit0pin, LOW);
  digitalWrite(bit1pin, LOW);
  digitalWrite(bit2pin, LOW);
  //  digitalWrite(bit3pin, LOW);  // Not needed?
  if(digitalRead(Apin) == LOW); return(1); // Drain Pit
  if(digitalRead(Bpin) == LOW); return(2); // Top Target
  if(digitalRead(Cpin) == LOW); return(3); // 50k Target
  if(digitalRead(Dpin) == LOW); return(4); // Top Pit

  // Scan Line 3
  digitalWrite(bit0pin, HIGH);
  digitalWrite(bit1pin, HIGH);
  if(digitalRead(Bpin) == LOW); return(32); // 500p Rollovers
  if(digitalRead(Cpin) == LOW); return(33); // 30p Contacts

  // Scan Line 4
  digitalWrite(bit0pin, LOW);
  digitalWrite(bit1pin, LOW);
  digitalWrite(bit2pin, HIGH);
  if(digitalRead(Bpin) == LOW); return(42); // 5k Rollovers, Bonus
  if(digitalRead(Cpin) == LOW); return(43); // Rollovers x2

  // Scan Line 5
  digitalWrite(bit0pin, HIGH);
  if(digitalRead(Apin) == LOW); return(51); // Left Drop Target
  if(digitalRead(Bpin) == LOW); return(52); // Center Drop Target
  if(digitalRead(Cpin) == LOW); return(53); // Right Drop Target

  // Scan Line 6
  digitalWrite(bit0pin, LOW);
  digitalWrite(bit1pin, HIGH);
  if(digitalRead(Apin) == LOW); return(61); // Left Bumper
  if(digitalRead(Bpin) == LOW); return(62); // Right Bumper
  if(digitalRead(Cpin) == LOW); return(63); // Left Slingshot
  if(digitalRead(Dpin) == LOW); return(64); // Right Slingshot

  // Scan Line 7
  digitalWrite(bit0pin, HIGH);
  if(digitalRead(Cpin) == LOW); return(73); // 30k Rollover
  if(digitalRead(Dpin) == LOW); return(74); // Top Slingshot
}

Seems to be OK. A few "cosmetic" changes
loop() gets called continously. Thus the while(1) { ... } construct is totally superflous.
The brackets around the return value are not needed. http://arduino.cc/en/Reference/Return

In your main loop, instead of lots of if ( ret == n ) do something ; use the switch. Same functionality, code more clearly states what is happening (code readability).

void loop()
{
  switch( scansw() ) {  // omitted the "ret" variable, too. (Optional)
    case 0: continue; break ;
    case 1: DrainPitSwitchClose() ; break ;   // Drain Pit Switch closed
    case 2: Othercall() ; break ; 
    case 52: digitalWrite(13,HIGH) ; if (Counter>10) FlashBonus() ; break ; 
    case 21: somethingelse() ; break ;
   default: Serial.println("Something wrong - impossible value") ;
  }  // end switch
}

Note that case do not have to be in order, the default case is optional (but usefull); you can just call a procedure, but any sequence of statements is OK in a case (including a nested switch) - and most importantly : remember to end each case with a break

Thank you, Msquare.

loop() gets called continously. Thus the while(1) { ... } construct is totally superflous.

I used while(1) to be able to use break/continue.

I used if statements instead of switch in order to get a new switch - electric switch, that is... - scan as quickly as possible if no swicth was activated in the last scan. Not that I know if it makes any difference since I don't know how long it takes for each instruction to be executed. A switch statement may even be faster than 20 if statements, I don't know.

The brackets around the return value are not needed.

I didn't know that. Thanks again!


The Multiplexer board for scanning the switch matrix is ready now. Just have to restore some connections to the switches on the playfield, some disconnected by previous owner, some hacks made by me when I didn't realize how easy it (hopefully) is to scan a matrix, especially using an Arduino! Then it's time to test if it rellay works, not just in theory. Exciting!

Allright then. Time to find out if the theories work in practice.
To the left: my Arduino Uno board.
In the middle: my homemade mulitiplexer for the playfield switch matrix.
To the right: the original multiplexer/driver board. I'll probably use only the solenoid drivers there.

I have a feeling it won't work. First, there were separate grounds for the playfield and the logic. Maybe that have to be kept apart or the electronics may be interfered? Also, that are many chances to make mistakes, even though I think I've doublechecked every connection.

I'll give you a report in a couple of hours.

/SimLego

What are you worried about - noise on signal lines (causing the wrong signal to be read/sent) or a voltage spike (all those heavy solenoids) ruining you electronics or a fatal short (like getting 24V on a 5V line)?

The ground has to be common - else there is no meaning to voltage levels. This is true also with several power supplies.

Unless you choose to galvanically seperate the two, in which case all signals have to go through optocouplers, and of course you have seperate power supplies.

The spikes should already been absorbed by diodes (the driver board contains electronics I see). Spurious signal on the input side can be compensated for in the software, it is akin to debounce a switch. To guard against a fatal short you need galvanic isolation, but if you have nothing more than 24V then you "only" need to replace the AVR microchip (or some driver chip you may have) in "the unlikely event" of disaster

Total failure. Nothing works, and I have no idea why. This means debugging all night...

I hate it. Now I stripped down everything to pin11=output, pin12=input(pullup), and pin13 as LED output (using only the built in LED).
On pin11 I sent a 1Hz signal; 500ms low and 500ms high. Pin13 displays the reading from pin12. Everything else is completely unplugged.

Now, with nothing conneted to the board, the LED stays lit all the time. Just as it should.
With a diode connected from pin12 to pin11, the LED flashes at 1Hz. Just like it should.
But with pin12 and pin11 connected to an open circuit on the playfield - totally open, not even several megaohms - the LED flashed occationally. I don't even touch the machine, and like I said, nothing else is plugged in.

What can it be? The only thing I can think of, is that the quite long parallell wires become some kind of capacitor that discharges or something. Or the unshielded wires make some kind of hum like the one you can hear from a microphone line that's lost its ground shield.

Any suggestions I may try? Some kind of software or hardware filter? Some more puul-up? Or pull-down?

I found a potential solution. In my desperation, I thought that maybe the little transience when wires connected pin11 goes from high to low could effect the wires connected to pin12. So, as much as you all discourage delays, I added a one ms delay between writing to pin11 and reading pin12. And yes, it seems like it made the trick. At least in this very stripped off test.

  digitalWrite(11, HIGH);
  delay(1);  
  if(digitalRead(12) == LOW)  
  {
    digitalWrite(13, LOW);
  } 
  else
  {
    digitalWrite(13, HIGH);
  }

This time, the LED only flashes when the switch is closed, just as I excpected.

Well, that's all for tonight. Tomorrow I hopefully will get the time to test this scanning the whole switch matrix.

EDIT: The downside of this solution (if it even works) is that it adds 6 milliseconds to every complete switch matrix scan performed.

Post the whole sketch - the one you had where it "triggered" even though the switch was open. The fragment you showed gives no clue why it should flash, even though that is what you wanted/expected. So there may be other things "lurking".

An Arduino input pin is High Impedance, meaning that a long attached wire (and no defined voltages) results in "random input". So you must have a pullup or down, and the closing of the switch pulls it in the opposite direction.

Ok, here's the erratic code:

void setup()
{
  pinMode(11, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(12, INPUT_PULLUP);
}

void loop()
{
  digitalWrite(11, HIGH);
  if(digitalRead(12) == LOW)  
  {
    digitalWrite(13, LOW);
  } 
  else
  {
    digitalWrite(13, HIGH);
  } 

  delay(500);  

  digitalWrite(11, LOW);
  if(digitalRead(12) == LOW)  
  {
    digitalWrite(13, LOW);
  } 
  else
  {
    digitalWrite(13, HIGH);
  } 

  delay(500);
}

When pin11 is LOW, that "line" of switches is scanned. When HIGH, another line of switches is scanned. (In this test sketch, I have stripped off all other lines and use just one switch to isolate the problem.) And the problem was that even though pin12 wasn't connected to a switch closed to LOW and it was pulled up, it still returned LOW occationally.

But here's the working code:

void setup()
{
  pinMode(11, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(12, INPUT_PULLUP);
}

void loop()
{
  digitalWrite(11, HIGH);
  delay(1);  
  if(digitalRead(12) == LOW)  
  {
    digitalWrite(13, LOW);
  } 
  else
  {
    digitalWrite(13, HIGH);
  } 

  delay(500);  

  digitalWrite(11, LOW);
  delay(1);  
  if(digitalRead(12) == LOW)  
  {
    digitalWrite(13, LOW);
  } 
  else
  {
    digitalWrite(13, HIGH);
  } 

  delay(500);
}

The only difference is the two "delay(1);" lines. It seems one millisecond is enough to settle down the induction or whatever that pin11 may have caused while going LOW.

Next step is to see if it still works when I remove the "delay(500);" lines. But I can't do that tonight, I'm busy with other stuff.