Pages: [1] 2   Go Down
Author Topic: example of using one analog pin to read 5 switches  (Read 19535 times)
0 Members and 2 Guests are viewing this topic.
0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Before I found that I could use the analog pins as digital I/O using pin numbers 14-19 and digitalRead(num), I figured I'd use a series resistor circuit and one analog input.  Figuring it might be of interest to someone, I'm posting the code here:

Code:
// OneAnalogPin5switches
// Doug LaRue
// November 2008
//
// In some cases when you are running out of digial I/O pins for switches
// and want a simple solution for adding many switches with a small amount
// of added software. This example shows how 5 switches can be identified
// using 5 .5K ohm resistors and one analog pin. It's mostly only important
// that the resistor values are large enough so that there isn't too much
// current through them but enough to have a reliable and stable voltage
// divider circuit. I used .5K resistors because I had them close by but
// 1K, 2.2K, and 4.7K are very common and should work fine.
// If DEBUG_ON is defined, this code will set a pulse width value on the
// ouput digial/PWM pin to dim an attached LED based on the switch position.
// It also puts out strings on the serial port based on what switch is set.
//
// All code released under
// Creative Commons Attribution-Noncommercial-Share Alike 3.0


#define ERROR_WINDOW 50  // +/- this value
#define BUTTONDELAY 20
#define DEBUG_ON

int ledPin = 9;      // LED connected to digital pin 9
int analogPin = 3;   // switch circuit input connected to analog pin 3
long buttonLastChecked = 0; // variable to limit the button getting checked every cycle

void setup()
{
  pinMode(ledPin, OUTPUT);   // sets the pin as output on a PWM capable pin
  Serial.begin(115200);
}

void loop()
{
  if( buttonLastChecked == 0 ) // see if this is the first time checking the buttons
    buttonLastChecked = millis()+BUTTONDELAY;  // force a check this cycle
  if( millis() - buttonLastChecked > BUTTONDELAY ) { // make sure a reasonable delay passed
    if( int buttNum = buttonPushed(analogPin) ) {
      Serial.print("Button "); Serial.print(buttNum); Serial.println(" was pushed.");  
    }
    buttonLastChecked = millis(); // reset the lastChecked value
  }
}

/*
 * Read 5( or more or less ) buttons using one analog pin and an equal number of
 * resistors configured in series across +5 and ground.
 * The concept is to create a voltage divider and the switches connect to one
 * analog intput pin the voltage at some fixed position in the series circuit.
 * The software just checks within a wide range of values and returns a button
 * number if a button was determined to have been pushed.
 *
 * The internal 20K resistor is enabled so we can't just divide the 1023 by the
 * number of resistors to get the range for each switch. With the 20K int R, the
 * circuit looks like intR is in parallel with the switch resistors above the
 * switch pushed. For example, SW2 would be 20K in parallel with 1K resulting in
 * an equivalent R of .566K ohms. This then looks almost like there is only 4
 * resistors in series and 1023 / 4 = ~255 and 1023-255=~767. Someone could probably
 * spend the time to calculate this but I just used imperical measurements and a
 * +/- 50 step range.
 *
 *
 *   analogPin                 +5 V
 *      |                         |
 *      |                         \
 *      ----------------          /  
 *                     |          \    .5K
 *                     |          /
 *                     |          \
 *                     |____ \____|
 *                     |   SW1    |
 *                     |          \
 *                     |          /  
 *                     |          \    .5K
 *                     |          /
 *                     |          \
 *                     |____ \____|
 *                     |   SW2    |
 *                     |          |
 *                     |          \
 *                     |          /  
 *                     |          \    .5K
 *                     |          /
 *                     |          \
 *                     |____ \____|
 *                     |   SW3    |
 *                     |          |
 *                     |          \
 *                     |          /  
 *                     |          \    .5K
 *                     |          /
 *                     |          \
 *                     |____ \____|
 *                     |   SW4    |
 *                     |          |
 *                     |          \
 *                     |          /  
 *                     |          \    .5K
 *                     |          /
 *                     |          \
 *                     |          |
 *                     |____ \____|
 *                         SW5    |
 *                                |
 *                                |
 *                              _____  
 *                               ___     ground
 *                                _
 *
 */

int buttonPushed(int pinNum) {
  int val = 0;         // variable to store the read value
    digitalWrite((14+pinNum), HIGH); // enable the 20k internal pullup
    val = analogRead(pinNum);   // read the input pin
    
    #ifdef DEBUG_ON
      Serial.println(val);
      analogWrite(ledPin, val/4); // analog input 0-1023 while analogWrite 0-255
    #endif
    // we don't use the upper position because that is the same as the
    // all-open switch value when the internal 20K ohm pullup is enabled.
    //if( val >= 923 and val <= 1023 )
    //  Serial.println("switch 0 pressed/triggered");
    if( val >= 780 and val <= 880 ) {  // 830
      #ifdef DEBUG_ON
      Serial.println("switch 1 pressed/triggered");
      #endif
      return 1;
    }
    else if ( val >= (630-ERROR_WINDOW) and val <= (630+ERROR_WINDOW) ) { // 630
      #ifdef DEBUG_ON
      Serial.println("switch 2 pressed/triggered");
      #endif
      return 2;
    }
    else if ( val >= (430-ERROR_WINDOW) and val <= (430+ERROR_WINDOW) ) { // 430
      #ifdef DEBUG_ON
      Serial.println("switch 3 pressed/triggered");
      #endif
      return 3;
    }
    else if ( val >= (230-ERROR_WINDOW) and val <= (230+ERROR_WINDOW) ) { // 230
      #ifdef DEBUG_ON
      Serial.println("switch 4 pressed/triggered");
      #endif
      return 4;
    }
    else if( val >= 0 and val <= (20+ERROR_WINDOW) )  {
      #ifdef DEBUG_ON
      Serial.println("switch 5 pressed/triggered");    
      #endif
      return 5;
    }
    else
      return 0;  // no button found to have been pushed
}

Doug
Logged

San Francisco
Offline Offline
Jr. Member
**
Karma: 0
Posts: 92
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the details-- just when I needed two extra inputs!  Quite clever...smiley
Logged

My Arduino blog: http://jmsarduino.blogspot.com
Comprehensive (?) Arduino-compatible board list: http://tinyurl.com/allarduinos

San Francisco
Offline Offline
Jr. Member
**
Karma: 0
Posts: 92
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Played around with this for a little while, and while I got consistent values when buttons were pressed, the value varied wildly when none was pressed.

Connecting the analog pin to ground via another resistor solved the problem-- the analog pin reads 0 when no button is down, and only varies by +/- 2 when a button is pressed!  I'll use this for the four inputs for my current project, which give me /just/ enough pins not to have to resort to a shift register....smiley-wink

So, it seems this could be used for more than five buttons, though it would get tricky figuring out the values when multiple buttons are pressed.

In my circuit, these are the readings I get:
No buttons:  0
button 1:  693
button 2:  485
button 3:  326
button 4:  175


Schematic:

 /*
 *
 *   analogPin                 +5 V
 *      |                         |
 *      |                         \
 *      ----------------          /  
 *                     |          \  1K
 *                     |          /
 *                     |          \
 *                     |____ \____|
 *                     |   SW1    |
 *                     |          \
 *                     |          /  
 *                     |          \  1K
 *                     |          /
 *                     |          \
 *                     |____ \____|
 *                     |   SW2    |
 *                     |          |
 *                     |          \
 *                     |          /  
 *                     |          \  1K
 *                     |          /
 *                     |          \
 *                     |____ \____|
 *                     |   SW3    |
 *                     |          |
 *                     |          \
 *                     |          /  
 *                     |          \  1K
 *                     |          /
 *                     |          \
 *                     |____ \____|
 *                     |   SW4    |
 *                     |          |
 *                     |          \
 *                     |          /  
 *                     |          \  1K
 *                     |          /
 *                     |          \
 *                     |          |
 *                     |__/\/\/\__|
 *                         4.7K   |
 *                                |
 *                                |
 *                              _____  
 *                               ___     ground
 *                                _
 *
 */
Logged

My Arduino blog: http://jmsarduino.blogspot.com
Comprehensive (?) Arduino-compatible board list: http://tinyurl.com/allarduinos

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

you shouldn't need that pulldown if you turn on the internal pull up resistor.

Make sure you have this line in your code as I did in the buttonPushed() method. With this, you should read full scale(1023) with no switches pushed.

Code:
digitalWrite((14+pinNum), HIGH); // enable the 20k internal pullup

Note: if this pull up resistor has been blown or failed, you'd have to do a pull down or pull up to keep it from floating.
Logged

San Francisco
Offline Offline
Jr. Member
**
Karma: 0
Posts: 92
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Excellent-- will do!  Thanks!!
Logged

My Arduino blog: http://jmsarduino.blogspot.com
Comprehensive (?) Arduino-compatible board list: http://tinyurl.com/allarduinos

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for the info. I'm trying to get a rotary input for a project im working on and managed to get hold of a 4 wire (16 position, 0000 to 1111) rotary switch.

Obviously with this I will need to be able to read all 16 states. I have tried playing around with everything but I cannot get it to come out with 16 significantly unique values (there are 2 replications using 1k dividers and 4.7k down), but as I'm using a rotary switch i will only need to check the values to the left and right of the last known value.

Thanks for the help!
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 90
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

if you can't resolve the switch voltages over 16 positions and 5V, then one option might be to use 2 analog inputs instead of one. You'll have to test both pins but atleast there should be enough range between each switch position to get positive readings.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The problem with the switch i was using is that it has a single common pole for all four switches (in one package). Of cause I overlooked the obvious, in that I only really needed to use the two least significant bits of the input to do what I was trying to do (it's a rotary switch that loops over from 1111 to 0000, so I can get everything I need from 0000 to 0011).
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 619
Posts: 33961
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You need a different type of D/A converter than the one in the diagram above.
You can use a resistor off each switch output with the values R 2R 4R & 8R where R can be anything (say 1K). You can't actually get these exact values so you will have to make some of them up with two resistors. For example 4K can be two 2Ks in series and 8 K could be a 4K7 and a 3K3 all using 1% resistors.
Connect the common of the switch up to 5V and have a 1K pull down resistor.
Logged

Weymouth UK
Offline Offline
Newbie
*
Karma: 0
Posts: 38
Sailing rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I understand how this works BUT if 2 or more switches were pressed then the software would not operate correctly.

Microchip had a Design guide on this subject. If the resistor values are different then it is possible to detect the combination of keys pressed.

I have just arrived at the point in time when I need to do this (run out of digital I/P and can't afford a Mega) so I will try to get this working.

Matt ;D
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 56
(Insert nerdy joke here)
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So simple yet so useful, I love it!
I will definetly be using this for my next project that involves buttons. No need for multiplexing or any shift registers and stuff like that.

EDIT:
One noob question:
What resistor values should i use for 9 buttons?
« Last Edit: August 02, 2009, 04:47:56 pm by jamieriddles » Logged

London
Offline Offline
Newbie
*
Karma: 0
Posts: 46
I love my 'duinos
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Nicely presented, cheers - it helped me to see how you had done what I wanted to do.

I just built a 14 step ladder using 1k resistors connected to a stylophone-like keyboard. Decoding the input is dead easy and with 13 'keys' (one octave) there's still plenty of margin.
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 619
Posts: 33961
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Decoding the input is dead easy and with 13 'keys' (one octave) there's still plenty of margin.

Remember that you will only be able to detect if one button is pressed with a ladder set up. If two or more are pressed then the results will be ambiguous.
Logged

London
Offline Offline
Newbie
*
Karma: 0
Posts: 46
I love my 'duinos
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That's certainly true, multiple presses won't work with same-valued resistors. This is not a problem with a stylophone where you can only 'press' one key at a time.

The solution should otherwise be to use power-of-two values, right, such as:
500R, 1k, 2k, 4k, 8k...
so that they can be added up in any combination -
is that right, and does it work? I guess with a bit of breadboarding I could find out...
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 619
Posts: 33961
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
is that right, and does it work?

Yes that will work but you can't do as many bits. There is only a 10 bit A/D on the arduino so you might think you could do it for 10 switches, but resistor tolerances and noise will limit you to about five or six switches only.
Logged

Pages: [1] 2   Go Up
Jump to: