Fun Project: Using only one analog pin for a 5 keys kepad - Page 1
Forum section: electronics / general

author: Rick Law
author_rank: Super Contributor
timestamp: August 07, 2016, 03:10:53 am
content: Many of us at time wish we have more IO pins for the Arduino. I got used to a 5-key keypad I made early on. Butusing up 5 digital pins on the ATMEGA328 is using up a lot. So, I got into this experiment to see if I canuse one single analog pin to support 5 tact-switcheswith a voltage divider alone.The design is simple:just a voltage dividersetup with each tact-switch shorting out the specific resistor of specific value for that specific key.Net-net is, it works!The 10bit ADC has just good enough resolution to do the job for 5-keys. It can discern all 32 combinations 00000 to 11111 easily. Easily meansresults are not ambiguous and with a reasonable margin for safety. The center key is ENTER (1K), then UP (2K), RIGHT (4K), DOWN (8K), LEFT (16K).EDIT: before you ask... I use 3K+1K for the 31K, 2K+2K for the 4K, so on. That is why there are so many resistors.I had some misconceptions before I actually thought this thought. So experience to share:- Before I thought it through, with the USB power going through diode drop and deltas with different 5V regulator, I though that is a show stopper. If that isn't enough, high current draw causing a drop is another show stoppers. In actually doing the feasibility analysis, these kinds of voltage drop turns out to be irrelevant - as long as it is stable at the time of key press, that theboard is running a good bit below 5V doesn't matter. The ADC count is thefractionsof supplied voltage, and giving fractions of supplied voltage is exactly what voltage dividers do. So,there is even no need to convert counts to actual volts. The ADC counts alone suffice.- The logic of choosing resistor values as 2^n was obvious, so I use 1K, 2K, 4K, 8K, 16K. What wasless obvious to me, a novice, is the choice of R-Reference. The minimum change in voltage is when Key0 is pressed (ie: 1K resistor is shorted). I need the minimum change to be as big as possible so the ADC can easily see the change. When I actually do the math for the best R-Reference, I realizedfunction has a max: 31K. Even when maximized, thesmallest theoretical change is just 8.4 countson the ADC. Per datasheet, ATMEGA328 ADC is rated +/- 2 counts, so it can comfortably discern an 8-counts change.- Using 2^n resistor value makes the math easier. But,8-count is 8/1023=0.78%. With 1% parts, I need to use the measured value instead of the nominal value - which means I need to store the measured values.Net-net: using a data-table of results actually use less flash-space, faster, and easier to code. I ended up juststoring the 32actual ADC valuesin an array intrather than calculating it on-the-fly. For the compare, I use:for (idx=0;idx<31;idx++) { // if nothing matched, assume it is 31 - no key pressedif (stored-4 <= adcValue && adcValue <= stored+4) break;}// now idx has the key combination- To see if the actual ADC counts varies against environment, I switched between different Arduino boards, different power source, different power drain, so on. Thus far, I have not seen the counts change more than 2 counts. So a tolerance of 4 works very nicely even when I switch the keypad and between different setups.- Draw back is, for every such keypad (with its resistors), one needs to get the 32 actual ADC values specific to that keypad.

author: dannyf
author_rank: Super Contributor
timestamp: August 07, 2016, 03:35:38 am
content: The arduino screen plus keypad uses analog buttons too. And others before that. No need for 2-based resistors either.Try to make your code recognize multiple key presses and add denounce too.

author: Rick Law
author_rank: Super Contributor
timestamp: August 07, 2016, 03:39:29 am
content: Quote from: dannyf on August 07, 2016, 03:35:38 amWhat's the fun in just buying!This setup recognizes multiple key presses. 5 keys = 32 combinations from 00000 to 11111.(Sorry, can't resist) Not only do I denounce the keys, I denounce the whole machine when it doesn't work - with language too harsh to repeat.

author: boffin
author_rank: Unknown
timestamp: August 07, 2016, 04:40:00 am
content: You're wasting half the resolution of the A/D by using 31K reference. Drop that to 1K and you'll get a much bigger range across the AD, and probably a little more consistency.

author: voltsandjolts
author_rank: Unknown
timestamp: August 07, 2016, 10:47:06 am
content: QuoteYou could have a calibration routine, get user to press each key in turn.

author: b_force
author_rank: Super Contributor
timestamp: August 07, 2016, 11:13:28 am
content: If you're thinking smart, you could even use a potentiometer AND switches at the same time.

author: Rick Law
author_rank: Super Contributor
timestamp: August 07, 2016, 06:28:30 pm
content: Quote from: b_force on August 07, 2016, 11:13:28 amGood thought - use the VR like an encoder. Would be fun to think that through next time around.Quote from: voltsandjolts on August 07, 2016, 10:47:06 amYup,I did that the first go round...But first, it is not the calibration trouble but thelost of portability. Pressing 32 key-combos and type in the ADC count is easy. But needing keypad-dependent calibration data means codes I would bind the Arduino to the keypad. I have two 5keys keypads (5 digital pins kind) "at large" that I plug into different Arduinos each playing with different things. Needing pad-individual data in the program or the eeProm defeats that.So I have very tentative plans to try anATTINY85-driven I2C keypadjust for the fun of it. Ideally ATTINY13a, but I doubt I2C could be done with 1K flash.Back to storing individual resistor values:I needed only int to store the resistor values but theevaluation needs floatto do the math. That math endedup using a lot more flash (code) space. So I changed it to minimal code and store the entire int.I have plans to test a bit-field array and how much code space that would take. The theoretical max is 511 counts, so 9 bit per element would be just right, but I plan to do full 10 bits for future full adc resolution "enhancement" to this silly little thing. uint9_t would take 36 bytes. uint10_t would take 40.Messing around with that is the fun part of it.

author: Rick Law
author_rank: Super Contributor
timestamp: August 07, 2016, 07:36:37 pm
content: Quote from: boffin on August 07, 2016, 04:40:00 amGood spot but wrong!I had the exact samemisconceptionat the start.With such a simple little contraption, I started with using just my gut feel alone and I too selected 1K as R-reference at the get-go. But the thing doesn't work.It forced me to sit down and do the mathand rethink what I thought I knew. That is a reason I wrote the OP, and talked about misconceptions.The change in counts is what tells if a key is pressed. So, what is important here is not the range but the delta. What one gained by having a larger range are lost many times over by having a much smaller delta.Here is the math:First the method of translating into counts:Using Rs for R(switches, adding the 1K to the 16K)Using Rr for R(reference)Therefore, total divider resistance = Rs+RrThe proportion that feed the ADC = Rs/(Rs+Rr)Converting that into counts = 1023*(Rs/(Rs+Rr))With 1K Refrence:Rs = 31K with 1K switch NOT pressed,Rs = 30K with 1K switch pressed thus shorting it.Rf = 1KSwitch NOT pressed: 1023*(Rs/(Rs+Rr))=1023*(31/(31+1)) = 1023*31/32=991.031250Switch IS pressed: 1023*(Rs/(Rs+Rr))=1023*(30/(30+1)) = 1023*30/31=990.000000This is a 1.03 counts change when the 1K key is pressed and 1 count is what I saw in the first implementation. With merely a 1 count delta, it can hardly tell whether the 1K is pressed at all.With 31K Refrence:Rs = 31K with 1K switch NOT pressed,Rs = 30K with 1K switch pressed thus shorting it.Rf = 31KSwitch NOT pressed: 1023*(Rs/(Rs+Rr))=1023*(31/(31+31)) = 1023*31/62=511.500000Switch IS pressed: 1023*(Rs/(Rs+Rr))=1023*(30/(30+31)) = 1023*30/61=503.114754This is an 8.38 counts change when the 1K key is pressed. An 8 counts change is what I saw after I replaced the 1K reference with the mathematically derived 31K resistor.---If implementation permits, a better way is to use another voltage divider for AREF. Say 31K+31K and use the center to feed the AREF. So, ADC full count is the same as the full count for 31K(ref)+31K(switch).That is how I plan to do it if/when I do a ATTINY85-I2C-keypad. With the entire ATTINY85 for the job, I can change the AREF without affecting the host-Ardunino - whichever Arduino I plan to use the keypad on.[EDIT: The first write-up with verbose explanation was awful and way too hard to read. Reworded.]

author: boffin
author_rank: Unknown
timestamp: August 09, 2016, 10:45:09 pm
content: Quote from: Rick Law on August 07, 2016, 07:36:37 pmHow wonderfully un-obvious. You're quite right in that by wasting half the range of the AD you get a little more consistency between button presses, my brain says it's wrong initially, but pumping the numbers in you're quire right. However, even 8 counts is still < 1% so I'm not sure it's a great solution for 5 buttons. Drop it to 3 or 4....., or situations where there are distinct buttons (not multiple presses) and reliability will go way up.

author: bitseeker
author_rank: Super Contributor
timestamp: August 10, 2016, 12:03:54 am
content: Thanks, Rick. This project has more to it than initially meets the eye.

author: ludzinc
author_rank: Unknown
timestamp: August 10, 2016, 01:14:21 am
content: It's interesting to see where people's exploration takes them.So to expand on this idea, why not use an R2R ladder?https://en.wikipedia.org/wiki/Resistor_ladderMade a 5 bit D2A, feed your 1024 bit A2D and just like that you have a 32 count minimum difference between all possible button states.With 1% resistors and 5 bits of resolution, totally do-able.

author: bobaruni
author_rank: Regular Contributor
timestamp: August 10, 2016, 03:07:08 am
content: Another way....Make the switches common ground for safety and usability, and using only 4 resistors, it's possible to have 5 switch inputs by enabling the internal pull up on the ADC input.

author: LabSpokane
author_rank: Super Contributor
timestamp: August 10, 2016, 03:46:56 am
content: This is an OK way of multiplexing keys to a single pin, but get a dirty switch contact and you'll start getting erroneous inputs.

author: ludzinc
author_rank: Unknown
timestamp: August 10, 2016, 03:53:27 am
content: Quote from: LabSpokane on August 10, 2016, 03:46:56 amJust filter / fix it in software

author: LabSpokane
author_rank: Super Contributor
timestamp: August 10, 2016, 06:28:44 am
content: Quote from: ludzinc on August 10, 2016, 03:53:27 amThis issue is different than a simple debounce fix. Even crappy eBay microswitches put out sweet square waves, but get a bad switch and it's easy for the micro to read a different, but legitimate value.

author: Rick Law
author_rank: Super Contributor
timestamp: August 10, 2016, 06:52:10 am
content: Quote from: boffin on August 09, 2016, 10:45:09 pm"re:my brain says it's wrong initially"Yeah... I had a shock too. I was mind-locked on accuracy. After doing the math about 3 times to make sure I was right, I had to rethink and I realize it was the deltas/derivatives I should be looking at.That 8 counts <1% really gives me the chill in "deploying" it as a solution. If I can put a voltage divider and drop the AREF to 2.5V, that would make it easier to swallow.The result shows doing a 3 key keypad with a single analog line should be (for lack of a better term) a slam dunk! No sweat, easy as pie...But - rather than the "easy as pie" solution, I am going to throw an ATTINY85 at this 5 keys keypad. The TINY's only job will be just an I2C analog keypad so I can modify AREF (maintaining portability between Arduinos). See what other lessons I can learn.

author: Rick Law
author_rank: Super Contributor
timestamp: August 10, 2016, 07:41:23 am
content: Quote from: LabSpokane on August 10, 2016, 06:28:44 amYup. Besides a bad switch, a real key press change during sampling can occur.I sample the ADC 8 times to stretch the resolution. It takes 1 to 2 ms. It is entirely possible that a real key press change occurs. If the first 4 ADC-read was the 4K switch and the second 4 ADC-read was the 2K switch, I could easily have a false match for the 1K+2K switch. (Not a perfect match,. Only numerator changed by perfect average value but the denomintors changed differently.)With each of the 8 samples, I start over if abs(thisRead - lastRead)>4. 4 is my selected tolerance since the smallest delta caused by a key change should be 8.4. That gives me some measure of safety, but I have not yet done the analysis to determine how close to the cliff I am.But for the fun... else this analog keypad is a suitable only for ones suffering a serious case of digital pin famine.

author: dannyf
author_rank: Super Contributor
timestamp: August 10, 2016, 10:51:52 am
content: "So to expand on this idea, why not use an R2R ladder? "Woukd definitively work - have used it before.A minor tweak: switch's are often used active low. Flipping your schematic will make it so.

author: wraper
author_rank: Unknown
timestamp: August 10, 2016, 11:19:58 am
content: Quote from: ludzinc on August 10, 2016, 03:53:27 amSeen a lot of devices fail because of this approach, especially in TVs, monitors where this approach is used quiet often. Current is too low for proper contact wetting and switch no longer can make a good contact. Also because of ion migration, tact switches often become a little bit conductive in off state and weird stuff start to happen as this approach is not resistant to a little bit of leakage.

author: GK
author_rank: Super Contributor
timestamp: August 10, 2016, 12:38:06 pm
content: Quote from: ludzinc on August 10, 2016, 01:14:21 amThat won't work as a proper R2R DAC unless the switches/buttons are SPDT and wired such that each respective resistor is grounded when its switch off (logic 0).

author: plazma
author_rank: Frequent Contributor
timestamp: August 10, 2016, 01:01:09 pm
content: Quote from: bobaruni on August 10, 2016, 03:07:08 amPush SW5 and nothing else is recognised.

author: bobaruni
author_rank: Regular Contributor
timestamp: August 10, 2016, 02:06:25 pm
content: Of course SW5 will block all others, but I was showing a way to save components (make it smaller if required, the op could easily put another resistor in series with SW5 (4K7) if simultaneous button presses are required) although this is not the best way to decode 5 simultaneous button actions!

author: rrinker
author_rank: Super Contributor
timestamp: August 10, 2016, 02:52:55 pm
content: Just saw one in a 2001 issue of Elektor that uses the voltage divider technique for a full 12 key keypad on one of their projects. Based on the values shown on the schematic, it looks like it would have easily handled a 16 key keypad. Not sure what micro they were connecting it to, an 80C52 variant I believe.

author: helius
author_rank: Super Contributor
timestamp: August 10, 2016, 04:24:42 pm
content: A variant of the R2R approach is used by the Alesis LRC and the Fostex 8312.http://www.dnd.utwente.nl/~mrjb/electro/lrcdecoder/They decode 17 different button combinations with just two wires (and know when the device is connected or not, because there is no open circuit when it's plugged in).

author: Paul Price
author_rank: Super Contributor
timestamp: August 10, 2016, 05:04:07 pm
content: Easy to debounce and reliable switch selection, fewest parts using common 5% resistor values. Even with 9-switches there is ~.5V between each switch for high noise immunity. Good to go with at least 10 or more switches.Debounce code simply oversamples analog voltage measured, a sw-press is valid if the voltage for two debounce time separated readings is stable. To qualify as valid the 1st and 2nd readings for a sw. press voltage must be within the calculated voltage +/- error limits for each sw.

author: LabSpokane
author_rank: Super Contributor
timestamp: August 10, 2016, 05:31:44 pm
content: Model the circuit with a switch and a randomly variable resistor in place of an ideal switch and suddenly, the results are less deterministic.This pin count reducing method is completely fine for non-critical uses, but not for defibrillators and the like. Horsies for coursies...

author: Paul Price
author_rank: Super Contributor
timestamp: August 10, 2016, 05:44:10 pm
content: The robustness of reliability of a switch reading is directly proportional and all to do with the quality of debounce code, not the hardware,A push-button switch is no more like a random resistor in this application that in a mission-critical defilibrator application.If your choice of push-button MC switches are so poor in operation that they are best modeled by a random valued resistor, then you should consider better quality switches for any use or application you have in mind.

author: LabSpokane
author_rank: Super Contributor
timestamp: August 10, 2016, 06:42:50 pm
content: Quote from: Paul Price on August 10, 2016, 05:44:10 pmNot all switches are tactile microswitches and contact resistance can vary considerably due to age, user pressure, location the user presses, etc.

author: Rick Law
author_rank: Super Contributor
timestamp: August 10, 2016, 06:44:04 pm
content: Quote from: Paul Price on August 10, 2016, 05:04:07 pmThis is basically same as my circuit drawn vertically and with 6 switches. The resistor values are different but the same math applies. Written in the attached JPG is"10 bit ADC." That has to be a typo.With the written resistor values:No key press:Rf=10; Rs =(56+18+10+5.6+3.9+2.7+1.8+1.5)= 99.51.5K pressed:Rf=10; Rs =(56+18+10+5.6+3.9+2.7+1.8)= 98.0No keypress: 1023*(Rs/(Rs+Rf)) = 1023*(99.5/(99.5+10)) = 1023*(99.5/109.5) = 929.6 counts1.5K preseds: 1023*(Rs/(Rs+Rf)) = 1023*(98.0/(98.0+10)) = 1023*(98.0/108.0) = 928.3 countsSo, to determine if the 1.5K switch is ON or OFF, the average ADC count difference is1.3 counts. This 6-switch one would work with a hell of a good ADC - butif the last LSB is off by 1, you are toasted.This would work nicely with an ADS1115 (15 bit ADC) for sure, but I am very apprehensive that an MCU's 10 bit ADC can do better than +-0.5LSB.

author: Rick Law
author_rank: Super Contributor
timestamp: August 10, 2016, 07:57:35 pm
content: Quote from: LabSpokane on August 10, 2016, 06:42:50 pmIn the context of fun, it may be fun (and enlightening) to use an optical resistor or optical transistor in parallel with the individual Rs'es. The tact switches will drive the 5 optical transistors or 5 LEDs to flood the 5 optical resistors.Then again, I am mind-locked now on using a ATTINY. With the tiny85, I have enough pins to do I2C and 4 pins left to do 5 digital-keys. Alternatively, I can use analog keys and I have something left over in the tiny85 to do other fun things: a Piezo-buzzer for audio key-clicks, temperature of the room (rather, of the keypad), even meaningless statistics like "time between key presses", so on...If I can make I2C (slave) works on the USI-less tiny13a, that would be better. I have 1/2 dozen of tiny13a's in the drawer and they do a miserable job of being paper-weights. This could be a good use for them if I can fit the I2C (slave) and the digital pins scanning into its really tiny space.More options for fun than an amusement park and without the risk of people falling from a roller coaster hitting you on the head.

author: metrologist
author_rank: Super Contributor
timestamp: August 10, 2016, 08:05:15 pm
content: Quote from: Rick Law on August 10, 2016, 06:44:04 pmRick:If top button is pressed, he has 930 counts (1023*99.5/109.5).If bottom button is pressed, he has 1023 countsIf 2nd from bottom is pressed, he has 133 counts (1023*1.5/11.5).9308327356225093842541331023

author: dannyf
author_rank: Super Contributor
timestamp: August 10, 2016, 08:10:42 pm
content: "Then again, I am mind-locked now on using a ATTINY. "Somewhere here, I posted a software UART that goes to 38400bps on a 1mips attiny. All it takes is a timer isr plus a pin. It is one of those fire-and-forget types where you point a transmission pointer to your string and the isr does it for the rest while your execution moves on.You can easily adapt that to pretty much any MCU with minimum changes.

author: Rick Law
author_rank: Super Contributor
timestamp: August 10, 2016, 08:27:31 pm
content: Quote from: metrologist on August 10, 2016, 08:05:15 pmYou are right,I was very careless. No ice cream for me today so I learn a lesson.He is connecting them inparallel! Not like myserialone at all. At least his bottom key is not individual - ie: cannot be combined with other keys.I have to analyze if the other 5 are "combinable". If individual keys can be combined, may be 5 in parallel is better than 5 in serial.

author: Rick Law
author_rank: Super Contributor
timestamp: August 10, 2016, 08:29:08 pm
content: Quote from: dannyf on August 10, 2016, 08:10:42 pmThanks! I will look into that for sure.