Sunday, November 3, 2013

Sensing Switches on an Analog Input, Including Multiple Key Presses

Many microcontroller projects quickly run out of pins when adding functionality.  The methodology used to continue to add functionality is to use expansion chips or share pins.  Many methods implement an approach that will register individual switches.  This article shows a method detecting single or multiple key presses.

One method recently written about at http://tronixstuff.com/2011/01/11/tutorial-using-analog-input-for-multiple-buttons/ uses a well known method of using one analog pin to register different values for different key presses.

This method is also used on various "LCD Shields" used to add buttons to an LCD display on Arduinos.

While these methods work well, they will not correctly register two buttons pressed at the same time.  For the first picture, if the far right button is pressed, the line is grounded and no further buttons will register.  Same for the second picture with the "RIGTH" button.  The reason is the button shorts out parts of the resistor "ladder".  Again, these work, but only give the lowest value to the analog pin.

For my next project, an alarm, I need to be able to differentiate between pins including multiple "buttons" at the same time.  The following is a schematic of what I will use to differentiate between 3 inputs:

The R4 pullup resistor can be the same as the internal pull-up on microcontrollers like the Arduino, enabled via 
pinMode(analogpin, INPUT_PULLUP); 
or 
pinMode(analogpin, INPUT); 
digitalWrite(analogpin, HIGH);  // set pullup on analog pin while the pin is an input.

On Arduinos like Uno, analogpin can be A0 through A5.  For ATtiny based boards like Adafruit Trinket and Gemma, you must use numbers 1 to 3 for the analog pin (GPIO #2 is A1, GPIO #3 is A3, GPIO #4 is A2 only on Trinket and not Gemma).  Note the pullup does change the range of the AnalogRead.  See http://arduino.cc/en/Tutorial/AnalogInputPins for more information.

My application uses an Adafruit 5 volt Trinket.  In the schematic of the Trinket board, pin GPIO #3 (Analog 3), already has a 1.5 K ohm pullup to use USB, so no additional pullup resistor is required for use, a bonus.

For mapping values, you can read the analog measurement as various switch states are changed (your values may differ depending on resistor changes, pull-up values, etc.  You should take measurements on your own setup):

Switches   Value
Nothing    647
1 closed   397
2 closed   537
3 closed   576
1 and 2    320
1 and 3    342
2 and 3    457
All closed 283 

This works by using changing resistors in parallel along with R4 in series.  So if Switches 1 and 2 are closed, the resistance is (R1*R2)/(R1+R2) + R4.  See Wikipedia for a detailed explanation of resistor combinations.  This is also why the resistor values on the switches must be different - you are looking for unique resistances for each combination.  If you use the same values on the switches, the same analog reading will register for each key press.

The values do not have to be the same as the ones I used.  Avoid too small values to limit current to the analog pin.  Avoid values in the Megohm range as being too close to an open.  Values should not be too close or too far from each other.  I suggest you wire the circuit up and measure the values. You want probably 10 or more analog value steps between each registered combination.  

Sometimes due to conditions, the same read value will not always come up but be a close value.  This is called jitter and for my project it was about 2, so when you compare values you want to check like this:

const int jitter = 3;
int readvalue;
int switchone = 397; // from table above for my circuit

readvalue=analogRead(analogpin);  // use your pin number

if(readvalue >= (switchone-jitter) && readvalue <= (switchone+jitter) ) {
    Serial.println("Switch one is closed");
}

Replace the if statement with a switch case statement for multiple buttons.

So for a project where you may have multiple key presses, you can sense them on one analog input by placing switches in parallel with differing resistors.