Introduction: Accessing 5 Buttons Through 1 Arduino Pin - Revisited
I made the original Instructible that this is based on over a year ago (You can see it here to see some of the theory around how this works:https://www.instructables.com/id/How-to-access-5-buttons-through-1-Arduino-input ). To make a long story short, I moved to a different country a few weeks after that and have not played with any electronics since.
I'm starting a project to build a clock that will display multiple timezones (And a bunch of other stuff too), and I need some inputs to set the time and manage the menu options. Since I'd already done the research (Albeit a year ago..) I figured I'll use the same setup to create a 4 or 5 button panel to navigate the settings and change the relevant values.
What I found though is that the original Instructable was a good proof of concept, but it wasn't good enough to put in practice. After working on this the whole night I now have a more practical solution.
Step 1: Parts List
1 x Arduino (Or Arduino compatible board. I use the BBB from moderndevice.com)
1 x 100K Resistor (Brown Black Yellow)
1 x 1K Resistor (Brown Black Red)
1 x 10K Resistor (Brown Black Orange)
1 x 22K Resistor (Red Red Orange)
1 x 33K Resistor (Orange Orange Orange)
1 x 68K Resistor (Blue Gray Orange)
5 x Push button switches
Some wires to connect it all
Step 2: The Theory - a Quick Recap
I suggest that you read step 2 and step 3 of the previous Instructable for the details, but if you're lazy (Uhmm, busy I mean), I'll give you a quick recap.
The usual way to read a button is to hook it up to a digital input pin and 5V. When you press the button, the input pin is connected to 5V and you can read a logical HIGH on the pin.
For the Arduino's analog pins, you read a value between 0 and 1023 based on the voltage you have on the pin. If there's 0V, you read a 0, and if there's 5V, you read 1023. Every voltage in between will return a proportional value based on the actual voltage.
From this, it follows that it is possible to connect multiple buttons to analog pin if you can get each of them to connect a different voltage to the analog pin.
As it turns out, this is very simple. You just need to set up a different voltage divider for each button. I'll show you how.
If you look at the circuit, R1 acts as a pulldown resistor when the buttons are open, but when (for example) the S4 button closes, the current flows from 5V, through R5, through the button, through R1 and to ground. The IO Pin will measure the voltage between S4 (or R5) and R1 - AKA a voltage divider is formed by R1 and R5. By using Ohm's law, we can calculate that we'll read about 2.75V on the IO pin or about 562.
PS: If any of this is unclear, you should really read the previous Instructable - I go into a lot more detail. Also have a look at the above link to the Wikipedia page on voltage dividers.
Step 3: Breadboard It
You can now build the previous circuit on your breadboard. If you look at the previous circuit and my breadboard, You'll notice that I used different resistors. The resistors are really not critical as long as you keep a few things in mind.
The first thing is the bigger the difference between the two resistors in a voltage divider, the closer the values are going to be to either ground or the input voltage.
The second thing is that the closer the values of the resistors linked to buttons are, the smaller the differences between them, which might make it difficult to read unique values.
So, the main thing I would change in the circuit is to make R1 a 47K resistor. By having the pulldown resistor the same as the value in the middle of the range, you'll get the biggest spread of readings.
In the previous Instructable, I mentioned that I had some bad buttons and that some readings were all over the place. I speculated about emf and bad buttons, etc... As it turns out, it's a bad breadboard. I'm using two breadboards that I've had for longer than 10 years, and it seems that some places aren't making good contact anymore. I've ordered new breadboards, but I'm still waiting. If you have inconsistent readings, try moving the button somewhere else on the breadboard.
Step 4: Getting the Neccessary Readings
The next step is to get the values that you'll need to check which buttons was pressed. Download the attached sketch and run it with the circuit set up as in the previous step. Open the serial monitor when the sketch is uploaded to the board and then hold down the buttons (1 at a time) for about 2 seconds each.
For each button, when you press and release it, you'll see that the values might be a bit inconsistent for a very small period of time, ignore those erratic values and write down the biggest and smallest values that remain.
This is what I got for my 5 buttons:
1 (1K) -> 930 - 935
2 (10K) -> 510 - 520
3 (22K) -> 315 - 326
4 (47K) -> 175 - 179
5 (68K) -> 127 - 135
As you can see, with solid connections, the range for each button is very small. The biggest range I had was 11 points which translates to a fluctuation of about 0.054V. Not bad at all.. With a range that small there's no reason why you couldn't scale this up to twelve buttons using the E12 range of resistor values between 10 and 100K (10k, 12k, 15k, 18k, 22k, 27k, 33k, 39k, 47k, 56k, 68k, 82k and 100k) with a pulldown resistor of 27K - 33K.
Step 5: Coding It
This is the main area where I made improvements. Even though the previous hardware worked and the code could figure out which button was pressed, you would get multiple button presses reported for as long as you help down the button.
What I need now is to have the Arduino read a single button press for each time you press and release a button. The attached code does this. I also just put a delay of 50ms between checking the button state, which seems to be good enough to debounce it.
The other thing I changed was to clean up the code. The previous code used 3 constants for each button. This version uses arrays which makes the code a lot easier to read.
To get it working for your values, replace your values (That you got in the previous step) for each button in the buttonLowRange and buttonHighRange arrays.
Once you've done that, you just need to set up the actions you want to link the buttons to in the dealWithButtonPress method. Right now it just prints a message to the Serial output to indicate which button was pressed.
Step 6: Extending It
As I mention in step 4, this could probably be extended to 12 buttons with minimal effort.
With the analog inputs having a 10 bit resolution, that gives us 1023 values between 0V and the reference voltage. If you divide that by 12 buttons, you can see that each button would have to be within a unique range of 85 points. I would call it no more than 40 - 50 to be safe.
If you do this, you would have to use fairly accurate resistors (Or measure them and use the ones that are close to the E12 vaues) and you would quite possibly need a better way to debounce the buttons.
From the fact that all my values are within a maximum range of 11 points, this seems completely doable.
Step 7: Final Thoughts
This version is a lot better than the previous version. The one thing I don't like is relying on the delay for debouncing. I'll have a look at interrupts to rather read the buttons that way, but for now it works well enough.
As I mention in Step 4, with solid connections to the switches, the range for each button is very small. The biggest range I had was 11 points which means that there's no reason why you couldn't scale this up to twelve buttons using the E12 range of resistor values between 10 and 100K (10k, 12k, 15k, 18k, 22k, 27k, 33k, 39k, 47k, 56k, 68k, 82k and 100k) with a pulldown resistor of about 27K - 33K.
If anything is unclear, feel free to ask and I'll answer and update the Instructable as necessary.
Thanks for taking the time to read this! I hope you find this version more useful than the first one. Any feedback or ratings (wink, wink) would be appreciated. Also, Please take the time to vote for me in the Microcontroller contest if you found this helpful.
This is the first part of a pretty big project. Hopefully this time I'll get to finish it (I'm not planning on moving to a different continent in the next couple of weeks). Look out for my next instructables on creating a clock that displays multiple timezones, an RGB Orb, 7 segment displays and interfacing this all with an online app that will update statuses (If at all possible on a single Arduino).