Tutorial: Using analog input for multiple buttons

The purpose of this article is demonstrate how you can read many push buttons (used for user-input) using only one analog input pin. This will allow you to save digital I/O pins for other uses such as LCD modules and so on.

Hopefully you recall how we used analogRead() in chapter one, and how we used a potentiometer to control menu options in exercise 10.1. For this article, we will be looking at reading individual presses, not simultaneous (i.e. detecting multiple button presses).

To recap, an analog input pin is connected to an analog to digital (ADC) converter in our Arduino’s microcontroller. It has a ten bit resolution, and can return a numerical value between 0 and 1023 which relates to an analog voltage being read of between 0 and 5 volts DC. With the following sketch:

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 2, 3);
int a=0;

void setup()
{
  lcd.begin(20, 4);
  pinMode(A5, INPUT_PULLUP);
}

void loop()
{
  a = analogRead(5);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("  analogRead() ");
  lcd.setCursor(0,1);
  lcd.print("  value is :");
  lcd.print(a);
  delay(250);
}

and in the following short video, we have demonstrated the possible values returned by measuring the voltage from the centre pin of a 10k ohm potentiometer, which is connected between 5V and GND:

As the potentiometer’s resistance decreases, the value returned by analogRead() increases. Therefore at certain resistance values, analogRead() will return certain numerical values.

So, if we created a circuit with (for example) five buttons that allowed various voltages to be read by an analog pin, each voltage read would cause analogRead() to return a particular value.

And thus we can read the status of a number of buttons using one analog pin. The following circuit is an example of using five buttons on one analog input:

serial-buttons

… and here it is in action:

Where is the current coming from? Using pinMode(A5, INPUT_PULLUP); turns on the internal pull-up resistor in the microcontroller, which gives us ~4.8V to use.

Some of you may have notice that when the right-most button is pressed, there is a direct short between A5 and GND. When that button is depressed, the current flow is less than one milliamp due to the pull-up resistor protecting us from a short circuit. Also note that you don’t have to use A5, any analog pin is fine.

As shown in the previous video clip, the values returned by analogRead() were:

  • 1023 for nothing pressed (default state)
  • 454 for button one
  • 382 for button two
  • 291 for button three
  • 168 for button four
  • 0 for button five

So for our sketches to react to the various button presses, they need to make decisions based on the value returned by analogRead(). Keeping all the resistors at the same value gives us a pretty fair spread between values, however the values can change slightly due to the tolerance of resistors and parasitic resistance in the circuit.

So after making a prototype circuit, you should determine the values for each button, and then have your sketch look at a range of values when reading the analog pin.

Doing so becomes more important if you are producing more than one of your project, as resistors of the same value from the same batch can still vary slightly. Using the circuit from above, we will use a function to read the buttons and return the button number for the sketch to act upon:

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 2, 3);
int a=0;

void setup()
{
  lcd.begin(20, 4);
  pinMode(A5, INPUT_PULLUP); // sets analog pin for input 

} 

int readButtons(int pin)
// returns the button number pressed, or zero for none pressed 
// int pin is the analog pin number to read 
{
  int b,c = 0;
  c=analogRead(pin); // get the analog value  
  if (c>1000)
  {
    b=0; // buttons have not been pressed
  }   
else
  if (c>440 && c<470)
  {
    b=1; // button 1 pressed
  }     
  else
    if (c<400 && c>370)
    {
      b=2; // button 2 pressed
    }       
    else
      if (c>280 && c<310)
      {
        b=3; // button 3 pressed
      }         
      else
        if (c>150 && c<180)
        {
          b=4; // button 4 pressed
        }           
        else
          if (c<20)
          {
            b=5; // button 5 pressed
          }
return b;
}

void loop()
{
  a=readButtons(5);
  lcd.clear();
  if (a==0) // no buttons pressed
  {
    lcd.setCursor(0,1);
    lcd.print("Press a button");
  }   
  else
    if (a>0) // someone pressed a button!
    {
      lcd.setCursor(0,2);
      lcd.print("Pressed button ");
      lcd.print(a);
    }
  delay(1000); // give the human time to read LCD
}

And now our video demonstration:

So now you have a useful method for receiving input via buttons without wasting many digital input pins.

This post brought to you by pmdway.com – everything for makers and electronics enthusiasts, with free delivery worldwide.

To keep up to date with new posts at tronixstuff.com, please subscribe to the mailing list in the box on the right, or follow us on twitter @tronixstuff.