Efficient way dealing with ~80 input states

Hi,

I'm in the process of building a pinball machine, which happens to have ~80 input switches. I currently have these connected via multiple chained shift-in registers, so that I can read the state of all inputs nearly simultaneously.

Now I'm wondering what an efficient way would be to 1) debounce, 2) keep track of state and 3) only act if a state changed. A temporary piece of code that I wrote had too much (global) variables to keep the state, and waaaay to many if-statements to check for changes. I'm convident that this could be done way easier, but I'm quite new to Arduino, so this is some sort of bump for me.

Performance (speed) is a must-have in my project, not only do I need to handle ~80 inputs, there are hundreds of outputs to handle as well! If you know an efficient way, please let me know!

Kind regards,
Raymond

Hello Raymond175

Take a view here to get some basic ideas about port expander.

Have a nice day and enjoy programming in C++ and learning.

@paulpaulson, I don't need a port expander, as I don't have an I/O issue, I have a programming issue (hence the location of this thread in "Programming Questions").

Hello Raymond175

Did you made a HW selection already?
How do you integrate approx. 80 inputs on which hardware?
This information is needed to start with coding.

I'd guess that arrays would make your life a lot easier.

Traditional pinball machines have much less sensors and outputs. What you want to do is a proof of concept which may work or fail.

At the input side there should be little activity with only one ball in play. A debouncing struct may hold the active pin number or port and bit mask, so that only one debounce time has to be stored and handled for just that pin. Action can be taken as soon as the input triggers, then the pin is locked for the remaining debounce time.

You also may bypass many problems by use of Hall effect sensors with built-in hysteresis and no debouncing required at all.

@DrDiettrich, I'm not sure if you actually know pinball machines, but I do. Normally they have a 8x8 switch matrix which results in 64 input switches. I have designed my machine with additional EOS-switches to automatically test the functionality of solenoids.

But this is all not important... I have my cabinet, I have my hardware, I have my wires, all is working! Except that my code is a mess and I want to make this more efficient.

@wildbill, honestly I did not think of that, I have absolutely no idea why not. I'm currently checking how this affects memory as well. Thanks for this!

Now how would I or anyone else have been able to look at this so-called messy code that has not been posted in code tags to be able to give any feedback?

Why are you not doing that, then? Except, an 8x10 matrix.

Lots of pinballs are hard wired from the bumper switch directly to the associated kicker solenoid. Only an input is needed if you're going to score the hit since the hardware does the kick. Tilt relay kills V+ so you don't need an enable. This should cut the code size AND by more responsive.

Debounce isnt really needed if you block an input until anoth input trips. The ball can't be in two places at once, eh?
Most of what the Arduino outputs will be things like gates, traps, etc. Plus, of course, lots of blinkey lights and noises. You gotta have a free game "cork!" solenoid too!
Real pins have test sequences built in that fire each output in turn about a second apart.

Get a bottle of pinball playfield cleaner & lube. It won't take the paint off and it insures the ball glides instead of rolling

You can gain some efficiency with bit packed key buffers. Because, you can compare 8 key states at at one time when you compare bytes. That's very common when programming QWERTY keyboards.

Frankly, you should be able to use a data structure that loops through all of the switches and jumps to an appropriate routine via a "pointer to function" vector without running into performance issues. 16MHz is very fast compared to 80 switchs.
You could optimize, if you still have a switch matrix, but only going through the switches in the activated row. Or indexing directly off of the row/column numbers.

I wouldn't think that many of the switches actually need debouncing. If they were hit, they've been hit, and your next switch scan will probably be more than a debounce-time away. (ye olde "scan switches every 10ms, and you don't need to debounce!" algorithm.) That'll go with the whole "lots of outputs to manage as well."

You'd probably gain more performance by avoiding digitalRead() than you would with avoiding an 80-switch loop.

please show a schematic at least for the first 8 inputs how you have wired them and show a full working stripped down version of the sketch to read this 8 inputs as an example.

again show a schematic - what load needs do be driven.

just as an example, two HT16K33 on I2C could drive 256 LEDs if you can wire them in matrix. That is no problem but we need to know WHAT and HOW you want to drive.

and by the way, which microcontroller are you using currently?

@Idahowalker, my question is about how to efficiently handle a large amount of inputs/outputs. How my code currently is, doesn't matter, as my question is a general one.

@anon57585045, I'm already kind of doing that, except not 8x10, but 10x8 bits. Only my code did not reflect any of that, and I'm still not sure if I find that easier than "just" 80 bits. I'm in currently in the process of finding that out.

@madmark2150, bumpers (not flippers) directly to the switches, but still inputs are also connected. Internally the pinball machines gathers all kinds of information about how often certain switches are triggered. Probably for finding out how "fair" a certain game is. Still, in my version I want everything to be controlled by the CPU. Call me stupid, but that is what I want :slight_smile:

@anon57585045, actually my first version did exactly that... store the separate bytes in a separate variable and compare these with the previous state. If nothing changed, I skipped comparing any of the bits. This worked well, but as @westfw mentioned, it's true that "just" comparing ~80 bits on a 16 Mhz CPU is peanuts, so I bitshifted all bytes into a single variable to test the difference. Only the Arduino Editor just lost my complete sketch after renaming it, so I have to start over :frowning:

@westfw, unfortunately I get an average of 2 to 3 bounces on switches with capacitors in ~50 μs, and an average of 4 to 5 bounces on switches without capacitors in ~100 μs. But today I thought of a different kind of debouncing, which in theory should work without any delay by just flipping the logic: instead of acknowledging the new state after the state being stable for a certain time (which introduces a delay), directly acknowledge the new state if the previous state was stable for a certain time (and thus ignore any state changes after that moment for a certain time). I have to rewrite my sketch after losing it, so I hope to be able to test this out tonight.

@noiasca, my question is about how to efficiently handle a large amount of inputs/outputs. I already mentioned that I used chained shift-registers, meaning that I "just" read bytes and have to compare bits. How my schema currently is doesn't matter, as my question is a general one. I'm currently working on the inputs, so the outputs are mostly out of scope for now. I'm using the Arduino Mega.

I already did receive some helpfull comments, and I'm trying to implement this in my sketch to see if this cleans up some of the mess. Thank you all so far!

It's not possible to answer a general question like, "what is the most efficient way to handle large numbers of I/O's?". It completely depends on what you mean by "handling".

The simple question I have for you, is "what is different about your application vs. all the other extremely common similar applications like QWERTY keyboard etc.". Because, if there is almost no difference in the requirements, you should look to the huge base of existing code for those other applications. Probably, all the answers are there, and you need only to adapt them slightly to your specific hardware.

Also, if you have some efficiency problem, people here would find it 100x easier to help you with concrete code, than with an abstract question. So please post the code that you have. If you haven't written any then please do so.

One of the advantages of posting your code here is that it would have been easy to recover :wink:

5 Likes

Check out the Toggle Library, can debounce 1 byte at a time (8 buttons), so instantiate 10 objects to handle 10 bytes (80 buttons). Very fast and robust debounce algorithm.

1 Like

@wildbill :cry:

@dlloyd, thanks, that is very helpful! I'll definitely have a look at it...

How should one tell you an efficient way if he doesn't know what's behind the scene?

No schematic, no code, no mercy.