Uses Greedy-Best-First-Search algorithm to determine viable solutions to complex voltage-divider resistance combinations that will allow a microcontroller to control variously sized button matrices using only 1 analog input pin. Cut-n-Paste app-generated Arduino code to quickly and easily include button-matrix into your project. Sweep-And-Prune system of objects create interactive bitmaps user-interface.
Watch a short video about this app
Introduction
Weary of Covid-19? Worried termites may eat the foundations of your house? Wary of the gophers in your backyard? If you are, then The All-New Voltage-Divider Microcontroller Button Matrix is just for you!
Are you still using 8 of your Arduino's 13 digital pins to control a simple 4x4 button matrix during a global pandemic?!? If you are, then The All-New Voltage-Divider Microcontroller Button Matrix is just for you!
Yes, you'll still need to wear a mask. And you really should have an exterminator look into your termite problem. But at least the gophers in your backyard will learn to appreciate what an inciteful and resourceful Arduino-ite you are once they hear you've discovered this little known trade-secret. Control up to dozens of buttons on a matrix using only 1 analog input microcontroller pin and you too can walk with a grin behind that handsome new fashion trend on your face.
Disadvantages of Traditional Methods
If you've ever had to work with a 4x4 button controller like the one in the image below, then you know that they make pretty multi-colored lines that cloud your judgment and make you lose all hope of adding greater and better things to your project. Yes, the flashy rainbow colored wires may look good but sometimes an engineer, maker or tinkerer needs to ask himself "do these wires make me look fat?" You look great without all those flashy colors to hide that droopy chin or dimply cheeks which your loved ones would truly admire if you only got rid of all those tangled wires that hide your inner beauty.
Sure, you may like the exercise of polling individual columns one at a time and testing the rows of buttons one-by-one so much you do it twice just to add delays to your project's execution time. Or maybe you feel name-dropping a KeyPad.h library reference during your lunch-break helps you make friends over tuna-sandwiches and chips. You can slurp your soup with ease knowing your peers at work share the anxieties you have been hiding for years. But no longer will you have to suffer in silence the iniquities of failure for now, you too can achieve better, greater and more liberating lunch-breaks with the help of the All-New Voltage-Divider Microcontroller Button Matrix which frees you from the tangled wires that have shackled your work in the past to let you reign supreme over your button matrix with only One Pin to Rule them All!
Why Can't a Button Matrix Detect More than One Button?
With One Pin to Rule them All, you need not worry about losing out on the ability to detect more than one button at a time since some farsighted engineer's cost-saving measures neglected the diodes necessary to protect your test voltage from grounding to zero by a confused user's clumsy multi-button pressing ways. So, be not disheartened to learn that a voltage divider suffers from the same multi-button conflict of distorted voltages that result when the aberrant paths which are created by any ill-advised user's careless button pressing habits conducts current in ways that distort the voltages measured by your microcontroller which can do ought but fail.
Fear of Resistance?
Do you feel anxious around carbon metal oxide bullies in your work place? Are you afraid of wiry resistors making your life difficult? With the All-New Voltage-Divider Microcontroller Button Matrix interface to help you will no longer feel intimidated by these spindly two-legged resistors that have done nothing but mock you and your baseless fears for far too long. The All-New Voltage-Divider Microcontroller Button Matrix App is in your corner. Soon, you'll have your resistors in order and obeying your commands. Controlling these riotous colored bands running amok about your work-station is easy so long as you have the help of this great new tool which you will no longer want to be without. The easy to use intuitive interface allows you to put a powerful engine at work to help you find a solution to any voltage divider matrix button controller you devise.
Set the size of matrix you need to solve and tell it where to report its results. The app will perform a Greedy-Best-First Search algorithm to find valid solutions for you to easily implement One-Pin button control.
Greedy Best-First Search Algorithm
This project uses what I'm told is a 'Greedy Best-First Search' Algorithm to find valid resistor combinations that result in a workable button controller. To do this, it needs to try various combinations of resistors, measure the voltage levels each button press produces at the measured node and find a combination that produces a unique voltage 'signature' for each button. The image below depicts a 4x4 button matrix. There are 9 resistors. 4 resistors tie Vcc to the row buttons and 4 resistors tie the column buttons to a common node. The common node joins the columns resistors with the common resistor which is tied to ground. When a button is pressed, the current flows through that button's column/row resistor combination before reaching the common resistor and produces a unique voltage which can be measured by your micro controller. The 'Greedy Best-First-Search' algorithm tries a range of 3 values for each resistor. Measures the result of all the possible combinations (in the case of a 4x4 matrix there are 93 combinations) and chooses the ~best~ result to Seed the next iteration where it will try a range of 3 possibilities that are +1/-1 above and below the resistor values of the previous iteration's search result. This app multiplies the seed value by a Growth-Factor before the seed value is used as the Middle-Range of the next iteration's search.
To determine whether or not a given combination of resistors is better than another, it measures the voltages for each button press. Takes those voltages and reorders them in ascending order. Since a valid solution requires each button-voltage level to be unique, the best solution is the one that produces the greatest-least voltage difference between two adjacent voltages. If Vcc were set to be equal to the number of buttons plus 1 volt, then the 'greatest-least voltage' difference between each button-voltage would be 1Volt. Given that in reality microcontrollers work on specific voltage levels (generally 5V), the greatest-least difference is equal to 5Volts/(number of buttons +1). Since at the beginning of the search it will only produce results that have two or more button-voltages of equal value then the 'greatest-least' difference is equal to zero for those early resistor combinations. This can sometimes happen for several iterations of the search depending on how large your matrix is. In order to continue searching before it has found a single valid solution (with unique voltage values for each button pressed), it sets Vcc equal to (NumberOfButtons+1
) in its calculations and measures the difference between each voltage in ascending order and the integral numeric values between 1 and the total number of buttons. The Sum of these differences is then used as a measure of which resistor-combination is best by choosing the sum of differences which is least. It chooses the resistor combinations that most-closely approximates the integral values 1, 2, 3, 4 ... NumberOfButtons
until it finds its first resistor combination that produces a unique voltage for each button and continues to improve on that result.
When the best results of the previous search iteration are seeded into the next iteration, those seed-values are multiplied by the 'Growth-Rate' before being used in the next iteration. Setting this value to 1
will keep the results small but may also produce an infinite loop where the results of one iteration are the same as the next. You can get out of this infinite-loop and continue your search by increasing the growth facter for as many iterations as needed. The growth-rate value can be changed at any time during a search and is only referenced by the search-algorithm at the end of an iteration when that growth-rate is used against the previous iteration's best solution resistor values to seed the next iteration.
How to Use the Calculator Interface
If you haven't already, have a look at this 20 minute video I recorded about this app.
The video explains it all, or most of it. If I remember correctly, I didn't talk about the 'Seed' button. As mentioned in the explanation of the search-algorithm in the previous section, each iteration of that search Seeds itself with the results of the previous search. Using the Seed button in the Calculator form, you can seed the search algorithm with the matrix-size and resistor values you choose using the mouse-wheel over the numeric-up-down interface on the left side of the screen. Be sure to set the intended working-directory using the 'dir' button in the Search-Form's Controls button if you want it to save its search results to a specific directory. With this interface, you can load a previous result using the 'Load' button here and then seeding those resistor values (or their altered forms if you intend to tailor the search) into the Search-Engine and continue a previously interrupted search. The app, when terminated using the 'Close' form button (top-right most X on all Windows forms) will save its search settings and continue from where it was interrupted the next time the app is loaded.
Sweep and Prune Objects
I was working on another project (Animation Editor) when I added many many control objects for some new options I'm still developing and saw my computer's response time slip into a coma. It was probably those incessant 'System Interrupts' but the incident got me thinking "There's a LOT of overhead involved in putting so many Buttons, Group-, List- and Text-boxes on the screen... maybe I can improve the Memory cost of my User-Interface?" and I came up with a plan to use only one picture box and a Sweep-And-Prune algorithm to implement a cheaper copy of what Microsoft has been building on for over forty years. Ambitious, right, but I got it working and the SPObjects
library was born. As I said, it uses a Sweep-And-Prune algorithm to re-create a simplified collection of back-to-basics interactive objects like Labels, Buttons, Textboxes and Numeric-Up-Down. The above GIF with the interacting buttons and voltage information is one PictureBox
that interacts with the user using the mouse. I'm still developing the library and have only a few objects(those that I immediately needed to improve my Animation Editor app) and I'm still working on it.
SPObjects
is similar to the ...
CKObjects.classMultiButtonPic
...which I talked about in my earlier article Creative Writer's Word Processor. The Sweep-And-Prune algorithm needed some improvements in the kinds of searches it can serve such as Search for elements that contain a point or search for elements whose regions touch or are contained in another search rectangle. Also, there was an issue when I tried to add new objects that weren't yet sized properly and negative widths/heights jammed up the Search-And-Prune algorithm and needed to be excised from the search to avoid those problems. Since some objects like GroupBox
es and Panel
s contain other objects, a draw sequence needed to be implemented and the Search results needed to consider the draw sequence when reporting which object is directly under the mouse as there is often more than one. The library is stable and working fine though I can't really say how much overhead savings there are.
What's Next?
This project just sort of happened when I pulled the 4x4 matrix out of my widgets drawer and saw that they needed 8 wires to work. It's been a distraction from my Animation Editor (which I still have many plans for) but I'm quite pleased with the results and it let me test out my SPObjects
library a little more. I'm thinking that eventually I may decide to allow a user to enter their resistor inventory into the app and let the app tell them which resistors can be used with which solution in order to create a One-Pin-To-Rule-Them-All button controller... but I have so many other projects planned that that is probably going to take a while.
History
- 15th October, 2020 - Originally published
- 15th October, 2020 - there were a few last minute issues that I had to fix - Search & Seed
- 16th October, 2020 - fixed a few more issues - calculate_VoltageVariance() & GrowthRate
- 19th October, 2020 - GrowthRate improved - filename added Sum value
- 5th December, 2020 - fixed Seed & restart issues