Arduino: Multiple Buttons on One Pin

Voltage dividers can be used to control 3-way latched switches. They work in a quite simple way: depending on the position of the switch, the analogue pin connected reads a different value, therefore the status of the switch is identifiable and an appropriate Joystick Output can be sent to the PC.
After having implemented such latched switched I had a simple idea: using a series of resistors connected to an analogue pin to control a number of buttons, each of them wired after a resistor. Being a series, the total resistance value of each button (when considered in sequence)
adds up and is greater than the button before it. This can be used to identify (hence control) multiple buttons by means of a single analogue pin.
Unfortunately, this also mean that only one button can be pressed at the same time (per pin), although this is rarely an issue, at least for flight sims, Star Citizen and similar games.

Here comes reality

The idea is simple: press a button, read the analogue value, compare it to a reference value, activate the corresponding HID Joystick Output. However, due to a number of factors, if we read the value on the analogue pin as we press a button, we may have a dodgy reading. This is due to a number of facts. Extremely simply put, the current doesn’t change from zero to a certain level right away, the board’s circuitry requires a brief amount of time to process the reading and so on. In other words, the whole process needs a bit of time to digest the variation and stabilize.

Escaping real life

There are many ways and techniques to resolve the issue but in this article I will consider only a simplified version of the algorithm I use for my firmware. It uses a timer for each analogue pin and, as a button is pressed, the relative timer starts. When the timer expires, if the reading matches a specific reference value, then a HID “Button Pressed” Joystick output signal is sent to the PC.
When the button is released, the reading doesn’t match the reference any more and a “Button Released” signal is sent to the PC.
Up to here, everything sounds very easy.

Reality strikes back

In theory each button can be identified by one of the 1023 (0 is the open circuit) values used by the board to represent what the analogue pin reads, but unfortunately there are a number of limitations that must be taken into account.

Resistors are not perfect. If you are familiar with the Resistors Colour Code, you know that one of the bands is dedicated to represent the tolerance.
To better understand what this entails, let’s consider a 100 Ω resistor. Its colour code can be: Brown, Black, Brown, Gold. Gold = ±5% tolerance therefore such resistors may present an actual resistance value between 95 Ω and 105 Ω. This means that an arbitrary reference value cannot be used.
Moreover, the read value can change over time due to external factors, such as the temperature (as it gets higher, the conductivity decreases) and therefore, the firmware must take into account all the effects that can affect the process of identification of a button.
A simple solution can be having a wide tolerance but, depending on the resistors chosen and the amount of inputs on the series, the intervals may overlap, making the button identification incorrect.

Wiring

Compared to a button matrix, the wiring diagram is very simple.
The following is a WIP sketch of my first implementation of this method to a Control Panel. I actually never finished the diagram and since then I didn’t draw any new sketch because, simply put, they are all the same, no matter the number of analogue pins or buttons used.
resistors-series-wiring
Once you get the idea behind it, the wiring is extremely easy.
The following picture is a phase of the wiring and soldering of one of my Panels. You can see the breadboards filled with resistors. That’s the series of resistors that allow this algorithm to work. I usually use one breadboard per analogue pin.

aux-box-wip1

Algorithm

The following is a flow chart that should make more clear how the algorithm works. I removed most of the controls, checks and watchdogs to make it is as easy to understand as possible.
For simplicity’s sake, let’s assume that only one analogue pin is being used.

resistors-series-algorithm
I divided the chart into four logical blocks.
Part I
The first part is about the detection of a pressed button. This is accomplished by controlling the status of the analogue pin. There are may different ways to implement this passage: for instance, the reading can be confronted vs the open-circuit reference reading or to the value read in the previous run of the loop. The means you choose may influence the following steps of the program and the additional features the firmware can provide (which are beyond the scope of this article).

Part II
If the condition defined in Part I is met, the timer is started. When the timer expires (or reaches a defined threshold, if it’s incremental rather than decremental), the reading on the analogue pin is checked again.

Part III
If the reading matches the reference value ± tolerance, a Joystick Output is sent to the PC. Depending on your preferences, the signal can be timed or “toggled on” and released when the condition above is not met any more. I suggest you to avoid sending Joystick commands if the button’s status is not changed. For instance, my firmware sends the command only once.

Part IV
The last part starts as the condition above is not met any more, therefore the button has been released. If you have opted for a non-timed Joystick output, this is the moment to send a “toggle off” Joystick command to the PC.

Working Example

After discussing for so long, what is the result of this different implementation? This is an example, the first Panel to use this new algorithm rather than the button matrix: the “Auxiliary Panel”. We have seen part of its wiring diagram above.

I use it mostly for secondary functions, such as Lights, Oxygen, additional LANTIRN controls and so on.
The 3-way latched Master Switch was originally intended for the Mi-8: by toggling the switch, the corresponding seat is selected and the controls operate on a different set of buttons. In other words, it’s like having a different Panel for the pilot, co-pilot and engineer seat.

No Firmware Download?

I’m sorry to disappoint you but I’m now going to share this firmware. I’d love at some point to open a small company (or join one) to build and sell my own, custom-made (and accessible!) Control Panels and the latest, improved and expanded versions of firmware may be the starting point.
Unfortunately the Flight Simulation hobby in a niche, and hardware producers sometimes take advantage of this fact and inflate the costs of their devices (or, I guess, they don’t produce them altogether due to a potentially low ROI). A Control Panels such as the ones I build, with 7 encoders and ~130 logical buttons should be on the market for no more than the cost of an Elgato Stream Deck, which provides 15 buttons for 120£ (of course it’s fancy and has lots of great functions and has amazing features, justifying its cost, but imo a Flight Simmer should have a more ad hoc peripheral).
Not to mention the fact that a custom solution can be adopted to take advantage of the muscle memory and your tactile sense by choosing different shapes of buttons and layouts. This is fundamental for VR users.


I hope you have found this article useful. As usual, my goal is giving you ideas, feel free to use and improve my suggestions 🙂

3 comments

  1. Aside from novelty’s sake, I don’t see why anyone would practically use this design. It’s really just a poor man’s port multiplier, but you can do the same thing with shift registers, multiplexers, or port expanders. They don’t cost all that much either, you can get a pack of 10 shift registers for $4:

    https://www.ebay.com/sch/i.html?_from=R40&_trksid=p2380057.m570.l1313.TR1.TRC0.A0.H0.Xshift+register.TRS0&_nkw=shift+register&_sacat=0

    Or if you’re feeling a bit richer, a 16-port I2C expander is only $6:

    https://www.sparkfun.com/products/13601

    Both have open source libraries, don’t have issues with temperature, don’t have to deal with tolerances, use significantly less power, take up much less space, and supports multiple simultaneous button presses.

    Your design also has the problem that it’s not voltage tolerant (as your Vcc changes, the measured voltage will also change), and is incredibly inefficient; if you’re pressing the last button in the circuit so that it only goes through one 100mA resistor, with a 5V power source you’re drawing 50mA (enough to power 2 red LEDs) just to detect if a switch is opened or closed.

    Like

    1. Hi mate, thanks for your comment. I have to correct you though: this is *the poorest* attempt to a port multiplier. On the other hand is very easy to assemble and works very well. If you have noticed the general scope of this site, it is not academic: it’s about flight sims enthusiasts that sometimes need some more buttons, starting from a zero-knowledge position (I myself come from coding, not hardware!). Therefore, saying that my solution draws a lot of current just to check a switch it’s definitely correct, but checking the switch it’s all that the board has to do.
      This doesn’t mean that I won’t get to more complex designs (although “complex” is a very relative concept). In fact, in my drafts, I have a few articles about arduino, covering diodes for the button matrix to expand the button matrix original set of articles. I also have something similar to what you mentioned, using an i2c port expander (MCP23017 maybe?). Other examples are the usage of a NPN transistor to drive a LED (rather than wiring it directly) or controlling a 7-segment display and its next slightly more complex implementation by means of a multiplexer.
      The fact is that I want to build such things myself before discussing them and sharing the sketch, so I can add advices and suggestions but I’m really short on time in this period and I have no ETA for such articles.

      Nevertheless, I thank you for your comment, feel free to add more. Constructive criticism is always more than welcome, mate. If you have a blog or something else discussing matters contextual with these pages, send them to me, I’ll happily link them here 🙂

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.