Jump to content
Due to a large amount of spamers, accounts will now have to be approved by the Admins so please be patient. ×
IGNORED

Generic Programming ideas for Homebrew pinball


Recommended Posts

If your code is all set up to do its work with polling, doing a rising (or falling) edge trigger can be messy because now you have to deal with interrupts (and there are only two on an Arduino Uno).

 

Instead, keep the polling approach and notice when a switch toggles state from open to closed (or vice versa). Record the time at which the transition happens. If you have multiple switches you want to monitor, keep the time in an array indexed by the switch ID (the pending array). In the event loop, scan the pending array for non-zero entries and react to entries that occurred more than x milliseconds in the past. Make x the length of your debounce interval. When you find a switch that changed state, clear the entry in the array, decrement the count of pending entries, and fire whatever event handler you want to associate with the switch transition.

 

There are many more ways to skin this particular cat. For example, instead of using an array of timestamps, you can schedule a timer when a switch changes state. The timer runs x milliseconds later and calls the event handler for the switch. The point is that it is possible to do all of this without additional hardware.

 

Michi.

  • Thanks 1
Link to comment
Share on other sites

I just made a note for myself - When debouncing many inputs - Use a max6818.

 

Regarding the code to detect the rising edge,

Wouldn't code that just looks for change of state to trigger an event be easier than detecting rising edge?

ie. The max6818 should remove those multiple rising edge triggers (bouce) while the switch is closing/closed.

 

** I probably used the wrong terminology for what I've done but it sounded impressive 😉 **

 

Yes it just checks for a low to high but disables the input until it goes from high to low again. All the debouncing is handled by the chip.

 

I started thinking about the delay used in a debounce circuit and didn't want the switch to trigger twice during this delay especially if the switch reading code is in a fast loop where it continually checks the switch. So no matter how long the delay and the signal is HIGH it'll only record one press.

 

This is also handy if you get a stuck switch on a pop bumper or another coil, if the switch stays closed the coil will only fire once until the switch is released well that's the theory anyway.😁

 

For example

 

Start loop

// Read Switch

If Switch is HIGH (switch closed)

// Fire bumper

Bumper on; small delay; bumper off

// Do it again

Go to the start of the loop

 

If the switch gets stuck the coil will practically be on permanently.

 

But if you do this......

 

 

OldVariable = LOW

 

Start Loop

 

NewSwitch = Switch value

(HIGH closed LOW open)

 

If ( OldVariable = LOW and NewSwitch = HIGH) then // Fire the bumper

{Bumper on; small delay; bumper off and make OldVariable = HIGH }

 

NewSwitch = Switch input

 

If (OldVariable = HIGH and NewSwitch = LOW) then {OldVariable = LOW}

 

Go to the start of the Loop

 

The bumper will fire once only and won't fire again until the switch has been opened.

Cheers Trev

 

Sent from my SM-G930F using Tapatalk

Link to comment
Share on other sites

The problem you have with this code is that it stops the event loop for the delay period. If lots of stuff is going on, the code will quickly fall behind actual events, resulting in pops or slings firing late. I would strongly recommend to not use delay() to implement wait times. Instead, use a timer library or record the time at which something has happened and then, some time later, check the time and, if the debounce interval has elapsed, reacted to the state transition.

 

That way, you don't slow down your event loop and you get loads more things done per iteration.

 

Michi.

Link to comment
Share on other sites

So if you get any switch bouncing you can just increase the delay slightly.

 

You could simplify that loop by removing the second switch read.

OldState = LOW

Start Loop
 NewState = [READ Switch]

 if ( OldState = LOW and NewState = HIGH) then //Fire the bumper
   {Bumper on; small delay; bumper off; make OldState = HIGH }
   
 If ( OldState = HIGH and NewState = LOW) then  //Reset the bumper
   {OldState = LOW}
Goto Start

Link to comment
Share on other sites

If your code is all set up to do its work with polling, doing a rising (or falling) edge trigger can be messy because now you have to deal with interrupts (and there are only two on an Arduino Uno).

 

Instead, keep the polling approach and notice when a switch toggles state from open to closed (or vice versa). Record the time at which the transition happens. If you have multiple switches you want to monitor, keep the time in an array indexed by the switch ID (the pending array). In the event loop, scan the pending array for non-zero entries and react to entries that occurred more than x milliseconds in the past. Make x the length of your debounce interval. When you find a switch that changed state, clear the entry in the array, decrement the count of pending entries, and fire whatever event handler you want to associate with the switch transition.

 

There are many more ways to skin this particular cat. For example, instead of using an array of timestamps, you can schedule a timer when a switch changes state. The timer runs x milliseconds later and calls the event handler for the switch. The point is that it is possible to do all of this without additional hardware.

 

Michi.

I'm going to need a drink after reading that.[emoji106]

 

I think I get what you're saying but the debounce is handled by the chip which just puts out a steady high pulse for something like 20 to 40 milli seconds so I'm just checking for low to high and high to low rather than duration.

Cheers Trev

 

Sent from my SM-G930F using Tapatalk

Link to comment
Share on other sites

The problem you have with this code is that it stops the event loop for the delay period. If lots of stuff is going on, the code will quickly fall behind actual events, resulting in pops or slings firing late. I would strongly recommend to not use delay() to implement wait times. Instead, use a timer library or record the time at which something has happened and then, some time later, check the time and, if the debounce interval has elapsed, reacted to the state transition.

 

That way, you don't slow down your event loop and you get loads more things done per iteration.

 

Michi.

 

But arn't we talking about delay being ~40 milliseconds? (Enough to debounce the switch).

 

I'd imagine each switch loop would need to be running on it's own CPU thread for this to work though.

 

- - - Updated - - -

 

I'm going to need a drink after reading that.[emoji106]

 

I think I get what you're saying but the debounce is handled by the chip which just puts out a steady high pulse for something like 20 to 40 milli seconds so I'm just checking for low to high and high to low rather than duration.

Cheers Trev

 

Sent from my SM-G930F using Tapatalk

 

You'd have one or the other.

 

~40ms Delay in the switch loop (Each loop on it's own thread).

 

OR

 

Hardware debounce for each switch (max6818 or alternative).

 

 

 

ADDED: You can still keep the state transition code to protect against permanent switch on conditions.

Link to comment
Share on other sites

The problem with delay() is that it effectively stops the CPU dead. Nothing can happen during the delay() time other than interrupts.

 

Typical debounce time is 40 ms, meaning that, with the delay() approach, at most 25 things can be done per second (assuming that they take zero time to do). So, as things get busy, the machine won't keep up with the state of reality and weird stuff happens. I'd strongly recommend to use a timer library to run event handlers asynchronously or to at least busy-wait and check timestamps with the event loop running at full speed. It's not quite as easy to program, but it'll save you lots of trouble in the long run.

 

Michi.

 

- - - Updated - - -

 

You'd have one or the other.

Right. The extra hardware simplifies the code because it won't post a state transition until after the hardware has decided that you don't have a false positive due to a switch bounce.

 

~40ms Delay in the switch loop (Each loop on it's own thread).

Depending on a hardware. You'll have a hard time running threads on an Arduino Uno. Even on a Mega, only 8 kB of RAM are available…

 

Michi.

Link to comment
Share on other sites

Using delay() in this type of code is a deadset recipe for trouble - as Michi says it's a blocking function so nothing else happens whilst the delay is timing.

 

For all the debounce issue I reckon you can solve most of them in a "who cares" way.

 

- say it's a scoring shot - then you might get a few bonus points from a bounce - call it a player bonus

- say it's something like hit this switch to turn on this lamp - then the lamp comes on and will be reset by some other event maybe completing a few switches to light all lamps in a series - who cares if it bounces, lamp is already on

- the ones where it may matter are firing a solenoid. But you are going to fire (the output control) for a duration that is longer than any likely switch bounce duration so in effect the fire pulse will mask the switch bounce. Maybe use a simple RC series network in parallel with the switch to try and get a clean closed pulse.

 

For any that I was really worried about - and there may well be none - I'd use a schmidt trigger on the input.

 

 

Your led string looks pretty long too - maybe split into two? and while bother with connectors rather than mount on playfiled and solder in situ?

Link to comment
Share on other sites

So if you get any switch bouncing you can just increase the delay slightly.

 

You could simplify that loop by removing the second switch read.

OldState = LOW

Start Loop
 NewState = [READ Switch]

 if ( OldState = LOW and NewState = HIGH) then //Fire the bumper
   {Bumper on; small delay; bumper off make OldState = HIGH }
   
 If ( OldState = HIGH and NewState = LOW) then  //Reset the bumper
   {OldState = LOW}

Goto Start

That's actually what I've done but I added the second switch read for clarity.

 

The delay in this code may not be necessary but I've read that you may need a bit of ON time for the solenoid to travel the full distance.

 

I'm nowhere near being a high level programmer but I'm taking on board what you guys have posted. It's a learning process for me.

Cheers Trev

 

Sent from my SM-G930F using Tapatalk

Link to comment
Share on other sites

Using delay() in this type of code is a deadset recipe for trouble - as Michi says it's a blocking function so nothing else happens whilst the delay is timing.

 

For all the debounce issue I reckon you can solve most of them in a "who cares" way.

 

- say it's a scoring shot - then you might get a few bonus points from a bounce - call it a player bonus

- say it's something like hit this switch to turn on this lamp - then the lamp comes on and will be reset by some other event maybe completing a few switches to light all lamps in a series - who cares if it bounces, lamp is already on

- the ones where it may matter are firing a solenoid. But you are going to fire (the output control) for a duration that is longer than any likely switch bounce duration so in effect the fire pulse will mask the switch bounce. Maybe use a simple RC series network in parallel with the switch to try and get a clean closed pulse.

 

For any that I was really worried about - and there may well be none - I'd use a schmidt trigger on the input.

 

Yep I agree mostly.

Using delay is a no go (Especially if they must be handled sequential because of thread limitations).

 

Though you don't quite want a who cares approach - Because it can make troubleshooting difficult down the track. And some triggers may be important.

 

Either way keep the bumper state protection method (Only fire after an OFF state). This is a good idea.

 

The only thing to choose is how to debouce.

 

Either with a timing array as Michi explained.

OR

Hardware debounce with max6818 or alternative.

 

 

Just as an extra note: A schmidt trigger by itself wont protect against switch bouncing - You also need the supporting resistor & capacitor to smooth out the spike.

 

- - - Updated - - -

 

That's actually what I've done but I added the second switch read for clarity.

 

The delay in this code may not be necessary but I've read that you may need a bit of ON time for the solenoid to travel the full distance.

 

I'm nowhere near being a high level programmer but I'm taking on board what you guys have posted. It's a learning process for me.

Cheers Trev

 

Sent from my SM-G930F using Tapatalk

 

This is where your code snippet falls over (Despite the fact that we have established that using delay is a bad approach).

 

The delay can be set to a value that suits the input (~40ms to debouce).

OR

The delay can be set to a value that suits the solenoid firing (??? 100-200ms, I'm not knowledgeable on solenoid firing times).

 

This is where timing arrays (Same as Michi explained for software handling input debouce) can be used to give solenoids individual fire times.

This way you can time firing events. For example give some solenoids quicker firing times than others.

And it's not just solenoids you can time the firing of, You can light up lamps or turn on motors for set times (ie. seconds) after trigger events.

 

You'd probably want to look at how to integrate flashing lamps and other game driven events into the system as well.

 

The other thing you want to do correctly is flipper control.

eg. Push hard for the initial swing, Then hold at a lower current to avoid burning out the flipper solenoid.

Edited by ozfalcon
Link to comment
Share on other sites

I understand where you're coming from now.

I think the simplest idea for me is to implement a hardware based delay.

Something like a timer board that has 4 inputs and 4 adjustable stretched outputs. (Monostable Multivibrator) I can fit this inbetween the Arduino and the mosfet board. That will allow me to remove the delay from the code and speed up the loop.

 

Now where's that ETI 555 timer cookbook? Just one 558 should do it.

 

http://www.electronics-tutorials.ws/waveforms/555_timer.html

 

The flippers are sorted, I'll have three terminal flipper coils and end of stroke switches installed. I just haven't got around to fitting them yet.

Cheers Trev

 

 

Sent from my SM-G930F using Tapatalk

Link to comment
Share on other sites

Interesting solutions that I guess only in a real time practice will you see exactly what works.

 

Don't rule out putting a cap over the switches as a additive to cleaning the bounce. It won't do it on it's own but may help simply by adding a slight stretch to the input signals.

Link to comment
Share on other sites

- the ones where it may matter are firing a solenoid. But you are going to fire (the output control) for a duration that is longer than any likely switch bounce duration so in effect the fire pulse will mask the switch bounce. Maybe use a simple RC series network in parallel with the switch to try and get a clean closed pulse.

 

Or, even simpler, schedule a timer that runs, say, 1/10th of second later and calls a function that turns the solenoid off again. No need for extra hardware that way.

 

Your led string looks pretty long too - maybe split into two? and while bother with connectors rather than mount on playfiled and solder in situ?

 

That should be OK. It takes 31 µs to send the 24 bits for a single LED, plus a 50 µs reset pulse at the end. So, if you have 100 LEDs, you can refresh all 100 of them around 317 times per second. That's plenty fast enough.

 

People have used Arduinos to drive well over 1000 LEDs at 30 frames per second.

 

 

You can't use a colour map for the LEDs in that case because there isn't enough memory, but it's possible to generate the colour values procedurally and clock them out as they are being computed, so there is essentially no memory overhead. Not recommended for a pinball machine though—it's the wrong approach for that.

 

Michi.

Link to comment
Share on other sites

In general there are lots of ways to skin cats, which is a good thing and we all have opinions so will have many differing ideas, so I'm not going to argue too much but a few I feel I need to comment on.

 

@ozfalcon thinks I'm being too casual when I say you can largely ignore bounce issues.

 

Now do you remember when George Negus was interviewing Margaret Thatcher and he said "... some people don't like you" and she said "Who, you tell me their names" (check it out it's pretty funny and bound to be on youtube) well in the same way when ozfalcon says

 

...And some triggers may be important.

 

To me that's a bit of hand waving maybeish generalisisation so I say name them. I gave the very typical example of behaviours in pinball and reckon that covers most. Especially for the job BigTrev is trying to achieve.

 

 

@mitchi on the subject of leds

When I do calculation* I end up with about 100 refreshes per second for your hundred string led which whilst not the 300 your suggesting is fine for refresh rate but that's not my issue.

 

The issues to me are firstly current - every led is potentially 60 mA, so over half an Amp for each 10 leds. I wouldn't want more than that in those wires and secondly just for design elegance.

 

 

* Calc is 1.3 microsec per bit times 24 bits plus 50 microsec reset per led = 81.2 microsec per led

so thats 8120 microsecs or 0.00812 secs for 100 leds, which is 123 leds / sec

Link to comment
Share on other sites

@mitchi on the subject of leds

When I do calculation* I end up with about 100 refreshes per second for your hundred string led which whilst not the 300 your suggesting is fine for refresh rate but that's not my issue.

 

The issues to me are firstly current - every led is potentially 60 mA, so over half an Amp for each 10 leds. I wouldn't want more than that in those wires and secondly just for design elegance.

 

 

* Calc is 1.3 microsec per bit times 24 bits plus 50 microsec reset per led = 81.2 microsec per led

so thats 8120 microsecs or 0.00812 secs for 100 leds, which is 123 leds / sec

 

I think there might be a misunderstanding here. From the data sheet, the typical time for a 0 bit is 1.15 µs, and for a 1 bit is 1.3 µs. Call it 1.3 µs worst case, assuming all bits are 1, so that's 32 µs per LED. Now, the reset does not apply to each individual LED. Instead, you blast out the bits without any stop in between. The reset applies only once, to indicate the end of the frame. (The micro controller in each LED uses the reset interval to latch the value it read last into memory.)

 

So, for 100 LEDs, we get 32 µs * 100 + 50 µs = 3250 µs. That works out to around 307 frames per second. (The 317 frame figure I gave earlier assumes that half the bits are 1 and half are 0; the timings are slightly different.)

 

In terms of current, I take your point. I guess it depends on how thick the wire is and what current those little three-pin connectors are rated for. The 144 LED strip I have uses the same connectors though and eats something like 8.5 A at full brightness, so I guess the connectors are up to it. If we assume 5 A maximum per string (83 LEDs), AWG 18 wire will be fine.

 

Michi.

Link to comment
Share on other sites

In general there are lots of ways to skin cats, which is a good thing and we all have opinions so will have many differing ideas, so I'm not going to argue too much but a few I feel I need to comment on.

 

@ozfalcon thinks I'm being too casual when I say you can largely ignore bounce issues.

 

To me that's a bit of hand waving maybeish generalisisation so I say name them.

 

The main thing I had in mind is if you cycle though targets.

 

ie. If your trying to light up one particular target - And bounce causes it to cycle 2-3 at a time.

An example might be the skill shot on your initial ball release.

Or some sort of combo shot where each target needs to be hit once in sequence.

 

I'd probably be able to list more if I was more knowledgeable on the workings of pinball.

Link to comment
Share on other sites

I think there might be a misunderstanding here. ....

 

Yep agree on a misunderstanding - not sure who's right? I was doing worst case for FFFFFF colour data - but that changes little. I think I saw some data when I first looked at these that showed some padding bytes around the frame too, but not significant. The way I read the datasheet - each led has a micro and I think they use the reset time after their colour packet to extract their parcel and pass the residual string on down the line, so the reset acts to allow the individual controller to drive the local three internal leds and syncs the led position in the string.

 

I guess a single reset at the end of the entire sting could do this but the individual module / led micro would not known of the reset signal because it just passes it thru. Maybe it relies on absence of signal activity to display?

 

I honestly don't know and in a way it doesn't matter - thank goodness for libraries and smart blokes who write them!

 

The main thing I had in mind is if you cycle though targets.

 

ie. If your trying to light up one particular target - And bounce causes it to cycle 2-3 at a time.

An example might be the skill shot on your initial ball release.

Or some sort of combo shot where each target needs to be hit once in sequence.

 

I'd probably be able to list more if I was more knowledgeable on the workings of pinball.

 

I reckon these can all be handled by the way you process the initial read, but lots of options / opinions.

Link to comment
Share on other sites

The way I read the datasheet - each led has a micro and I think they use the reset time after their colour packet to extract their parcel and pass the residual string on down the line, so the reset acts to allow the individual controller to drive the local three internal leds and syncs the led position in the string.

 

I guess a single reset at the end of the entire sting could do this but the individual module / led micro would not known of the reset signal because it just passes it thru. Maybe it relies on absence of signal activity to display?

 

The micro controller in each LED is essentially a shift register. It clocks in the first 24 bits. Once that's done, any bits that follow are simply passed downstream. So, as I send a long string of 24-bit colour values, each LED strips off the first 24 bits it sees and then "switches through", passing anything else that comes along unchanged. When the reset pulse comes along, all the LEDs latch whatever 24 bits they've read into memory and display the corresponding colours. Then the game starts again.

 

So, this isn't a matter of the reset pulse telling an individual LED what to do. Instead, each LED is told to remember what it's supposed to do once the reset pulse comes along. Then, when the reset pulse does arrive, all LEDs do what they were told to remember all at once.

 

Michi.

Link to comment
Share on other sites

Maybe there should be a programming thread like the one @Autosteve has started for parts?

 

I agree. We are definitely deeply into nerd territory here…

 

I'm not terribly strong on the hardware side, but I know about software. If anyone wants to discuss coding issues, I'll be happy to help. If someone who knows hardware needs help with coding, I'll do a trade: I'll teach you software if you teach me hardware :)

 

Michi.

Link to comment
Share on other sites

 

I reckon these can all be handled by the way you process the initial read, but lots of options / opinions.

 

Actually it can be a bit more complicated than that, And I'll try to explain why.

 

Switches bounce when they close as we well know.

But switches also bounce when they open - Something often overlooked.

 

A hardware debouce will take care of both by the nature of it's operation.

 

So, Take for example code that debounces by not re-triggering for 40ms (Using runtime clock).

If for any reason that switch is closed for greater >40ms, Then when it opens again it is likely to bounce and give a false trigger.

 

Either way is ok for a home project though.

Link to comment
Share on other sites

If for any reason that switch is closed for greater >40ms, Then when it opens again it is likely to bounce and give a false trigger.

Not really. The software can track the current state of the switch. If it was open and then was closed, 40 ms after it was closed, the software decides that it detected a switch closure. Now the switch is closed and, when the software detects the switch open, 40 ms after it was opened, the software decides that the switch is now open.

 

It's essentially the exact same thing that a debounce circuit does, except that it's realised in software instead of hardware. Either way works, and there is no "better" or "worse" here. It simply is a design trade-off. If I have lots of logic circuits already, adding one more chip isn't going to make or break it, so a hardware solution is fine. If I don't want to muck around with additional TTL logic, or really need to minimise component costs, a software solution will do the job, too.

 

Many ways to skin a cat :)

 

Michi.

Link to comment
Share on other sites

Not really. The software can track the current state of the switch. If it was open and then was closed, 40 ms after it was closed, the software decides that it detected a switch closure. Now the switch is closed and, when the software detects the switch open, 40 ms after it was opened, the software decides that the switch is now open.

 

It's essentially the exact same thing that a debounce circuit does, except that it's realised in software instead of hardware. Either way works, and there is no "better" or "worse" here. It simply is a design trade-off. If I have lots of logic circuits already, adding one more chip isn't going to make or break it, so a hardware solution is fine. If I don't want to muck around with additional TTL logic, or really need to minimise component costs, a software solution will do the job, too.

 

Many ways to skin a cat :)

 

Michi.

 

Agreed, Both work equally well (Software & Hardware debounce).

 

Though I wanted to point out that on the software side, Open debounce is often overlooked (Until there is a problem ;) )

  • Like 1
Link to comment
Share on other sites

I understand where you're coming from now.

I think the simplest idea for me is to implement a hardware based delay.

Something like a timer board that has 4 inputs and 4 adjustable stretched outputs. (Monostable Multivibrator) I can fit this inbetween the Arduino and the mosfet board. That will allow me to remove the delay from the code and speed up the loop.

 

Now where's that ETI 555 timer cookbook? Just one 558 should do it.

 

http://www.electronics-tutorials.ws/waveforms/555_timer.html

 

The flippers are sorted, I'll have three terminal flipper coils and end of stroke switches installed. I just haven't got around to fitting them yet.

Cheers Trev

 

 

Sent from my SM-G930F using Tapatalk

 

This post has been nagging me ;)

 

I think you are now understanding the need to split input triggers and output events.

 

Though I think you will find it easier to design the system the other way round.

Doing so will provide much more flexibility to rework your design down the track.

 

This is the way I would approach it.

 

Use a max6818 for your input conditioning. When you need more than 8 input lines, Add another max6818 "module" to expand your inputs.

This will server as a long term protection mechanism for your processing io lines - limiting the need to replace the main IO because one or two lines are fried.

Note: The reason I'd choose HW debounce is because your pinball software is going to be complicated enough AND it physically protects your CPU unit.

 

Use software arrays for your output timing. It's easier and much more flexible to implement a timed output event in software than in hardware.

You really don't want 555 timers spread throughout the machine doing different things (Solenoids, Lamps, Motors etc) when you can have one simple software solution.

Imagine you need to adjust the timing of one device,

Would you want to open open the machine desolder resistors & capacitors put new ones in - Repeat a few times untill you got the timing right.

OR

Just Adjust one number in a timing table in your program.

 

Extra note: For a low number of inputs (ie.8,16 upto 32) I'd probably use the max6818.

If the number of inputs was pushing 64-128+ I'd probably look at other solutions.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...