loading

This project came about because I was trying to think of the best way to get input from a large number of buttons, while requiring the minimum number of Arduino pins. I started with the wonderful tutorial at the Arduino site demonstrating how to get input from one button (see: https://www.arduino.cc/en/Tutorial/Button). There are also a number of execllent Instructables which demonstrate clever ways to combine multiple buttons with resistors so you only need one Arduino pin.


However, my goal was to be able to detect button presses from dozens buttons, and most of the tutorials above were restricted in the number of buttons they could address. Also, I noticed that many of them were not able to detect multiple, simultaneous button presses, which was another feature I wanted to support.

Step 1: Thinking It Through

So, let's think this through.

If you look at the tutorial given above, you can see that the brute-force solution is that for every button we need one power pin, one drain through a 10 KOhm resistor and one input pin. The power pin and drain can be shared across all buttons, but, in the circuit shown there, we cannot get around the fact that we need a separate input pin for each individual button. If our application requires 10 buttons, we need 10 Arduino pins. Ouch.

As I gave the matter some thought, though, it occurred to me that this was similar to a problem I faced in a different project I had worked on, namely addressing multiple LED display digits, something we see everywhere from microwave ovens to alarm clocks. So please have patience with me while I digress into LED land for a moment.

In most circuits which include multiple LED digits, each digit is not activated with a constant current. Rather, the controller sets up the segment-pattern for a particular target digit (say, the pattern which makes up the number "5"), and outputs that pattern to all the digits in the display simultaneously. It then sets up the pattern for the next digit, outputs that, etc., cycling through all the digits and repeating forever.

But, you may ask, how is it possible, then, to see different numbers on each digit? This is possible because the controller uses transistors to turn on the target digit and turn off all the others before outputting the pattern.

For example, let's say we have a 4 digit LED display (each digit named A, B, C and D), and we want to show the numbers "5678" on this display. Here's what happens:

  1. the controller turns A on and turns digits B, C and D off
  2. it outputs the pattern for the number "5" to all the digits
  3. it turns B on and A, C, D off
  4. outputs pattern for number "6"
  5. turns C on and A, B, D off
  6. outputs "7"
  7. turns D on and A, B, C off
  8. outputs "8"
  9. repeats from step 1

This allows the electronics involved to be much simpler and cheaper. And it only works because the controller and the transistors can turn digits on and off so unbelievably fast that each individual segment on each digit doesn't actually have time to fade before it is refreshed again. This process is known as "multiplexing" or "strobing", and it allows you to drive, theoretically, any number of LED digits. (In actuality, however, there is a limit: as you add more and more digits, you will notice that they begin to flicker, because the controller can't refresh them fast enough. Eventually, this flicker becomes so bad that you are no longer able to recognize the numbers.)

OK, back to button land.

I saw a connection between the LED multiplexing above and this multiple buttons problem: what if we could multiplex the buttons in some way? Instead of multiplexing the output, we would multiplex the input: this means that we would turn off all but one button, detect that button's up/down state, store it, and then do the same for the next button, etc., cycling through all the buttons very, very quickly. Since only one button is active at any given instant, and the controller knows which one it is, we could use the same, single input pin for any number of buttons.

Sounds good, but hold on: we still need to activate every button, one at a time. So, haven't we just turned the problem around, since now we need X output pins for X buttons?

Well, this is where we bring in shift registers. The shift register is an IC which receives a pattern of 8 bits one after another (i.e., serially), and, when commanded, will drive that pattern to 8 output pins simultaneously (i.e., in parallel). Just search Instructables for "shift register" and you'll find many tutorials. Shift registers are fast, easy to use, very cheap and tremendously powerful. But, we do still need to command the shift register, and it takes 3 Arduino pins to do so. However, the wonderful part is that you can connect multiple shift registers together in a chain, and still drive the whole chain with the same 3 Arduino pins!

OK, so where does all this get us? If we can figure out how to multiplex the button inputs, we can use one input pin, and if we can leverage shift registers, we only need 3 output pins. Hey! We've brought the problem down to requiring only 4 pins total to drive a (theoretically) limitless number of buttons. The breaking point is 4: if you need 4 or less buttons, just brute-force it: use 4 input pins, since this approach is unnecessarily complex and doesn't gain you anything. If, however, you need 5 or, especially many more, you win if you use the following circuit and code.

Step 2: Parts

For this circuit, you'll need the following parts:

  • Arduino Uno
  • bread board and patch wires
  • 3 standard, momentary buttons
  • 1 10 KOhm resistor
  • 1 74HC595 shift register (DigiKey, Amazon, etc.)

This is only a proof-of-concept, so I'm only using 3 buttons here. The circuit and code can be extended easily to drive many, many more buttons.

Step 3: The Circuit

The circuit is given above.

In order to see it do anything, you'll have to use the Serial output window: when you press any button you'll see "Button X Down", and when you let go you'll see "Button X Up".

Step 4: The Code

Attached is the code.

The real crux of the script happens in a function which must be called every loop. This function checks exactly one button. It sends a pattern of bits to the shift register which turns off all the buttons except the one target button for this loop. It then checks the state of the input pin at this instant: if it's HIGH, this button is pressed, if LOW the button isn't. It then compares this state against the previous state for this button to decide if the user just did something new or not. It stores state for this button and advances an index so the next button in order will be checked in the next loop. I also included a timer so you can tune the script for CPU usage vs. button sensitivity.

Let me know if you find any bugs.

I hope this helps anyone who is faced with the problem of needing to handle input from a large number of buttons using the least number of Arduino pins. I'm very interested in hearing from anyone who has solved this problem in other ways.

<p>Hi Mike</p><p>Thanks for your input. I will look at your ideas and try to get going. Will build the circuit as above to see how the multiplexer works etc. I am not the fastest in getting projects done so it could be a while to I get finished.</p><p>Thanks</p><p>Charles</p>
<p>HI</p><p>I have a project almost opposite the above. I have an old 1980s keyboard on a model train controller which I am having a parallel connection. I want to enter data (single digits) into arduino monitor and output from Nano or Uno to 20 different opto isolators (leds will be used for testing) which will momentarily turn on and send command to train controller via optos. If I change pins to output and change buttons to leds what else would I need to do? swchuck att gmail dot com</p><p>Thanks</p><p>Charles</p>
<p>Charles,</p><p>If I understand correctly, you can just modify and expand on the circuit above. Instead of driving buttons, you'll be driving LEDs (disclaimer: I don't know what an &quot;opto&quot; is, so I'm only going to talk about LEDs here).</p><p>You're going to need to chain together a total of 3 shift registers (8x3=24 pins, which leaves you with 4 free shift register pins you could use for something else). There are explanations on how to chain shift registers in other Instructables, as well as on the datasheet (<a href="http://www.nxp.com/documents/data_sheet/74HC_HCT595.pdf" rel="nofollow">http://www.nxp.com/documents/data_sheet/74HC_HCT595.pdf</a>). Connect each of the 20 shift-register data pins to one of the LEDs (NB: unless you like the sound of popping LEDs, DO NOT FORGET TO PUT a 440 Ohm resistor in series between the shift register pin and the LED!). </p><p>On the output side, in callEveryLoop(), instead of one shiftOut(), you'll be doing 3, using binary arithmetic to turn bits on and off on each byte, corresponding to the LEDs' states, as you push it out. The first byte you shiftOut goes all the way to the last shift register in the chain, etc.</p><p>If the LEDs need to be turned off after a set amount of time, you're going to need to keep state for each pin, but that's just an array of 20 unsigned longs timers, each one holding a millis value for when that pin should be turned off. Check one timer each time through callEveryLoop() and set flags accordingly.</p><p>On the input side, if I understood you correctly, I'd avoid trying to parse the Ard's Serial input into a number. Instead, you could just set up 20 buttons by expanding the exact circuit I have above. I'd probably use two different sets of 3 Ard pins in order to keep the two chains of shift registers (button input vs. LED output) separate. Trying to do it all with one chain would save you one shift register, but make things unnecessarily complex, IMHO.</p><p>Does that help? Let me know how it goes. Post a picture when you're done.</p><p>--Mike</p>
<p>Although others may have used this approach, It's always nice to see a new lesson.</p><p>Thanks.</p>
<p>Thanks, Acheide.</p><p>I was pretty sure I wasn't the first in the history of electronics to come <br>up with this approach. I did actually search for a similar Instructable, though, and was unable to find one.</p>

About This Instructable

858views

31favorites

License:

More by fototrip:Driving Dozens of Buttons Pimp My Zoomobil 
Add instructable to: