Chapter 4: Digital Input - Pushbuttons


We often see LEDs used by a computer to tell us something and just as often we use a pushbutton to tell the computer something. The alarm clock buzzes and LEDs tell us what time it is. We may then push a button to tell the alarm clock that we want to sleep another ten minutes (snooze button) or we may be ready to get up so we push the alarm off button. These two buttons tell the computer two different things. The snooze button tells it to turn off the alarm and set a new alarm for 10 minutes in the future. The alarm off button tells the computer to turn the alarm off and reset the new alarm time for 24 hours in the future. The alarm clock has a built in microcontroller (not unlike the one on the Arduino) that turns the LEDs on and off to show you the time and reads the buttons to learn what you want it to do next. In this chapter you will learn how to design circuits using pushbuttons and how to use them to get user input with Arduino software that will let your system take actions when a button is pressed.

But before we get into that, let's learn another couple of Arduino C programming concepts that we will use when testing pushbuttons. We will look at decision-making using the if-else conditional flow control construct. Then we will learn to use do-while, which is similar to the 'while' flow control construct we saw last month. Next we will learn how to use the Arduino function millis() to do some event timing. And finally we will learn about '=' and '==' two C operators that surprisingly aren't equal. We will apply this knowledge in our labs where we will learn how to get the Arduino to detect a button push, use that information to control LED states. And for our last exercise we will bring it all together along with the millis() function to create an Arduino based reaction timer. How quick can you get your finger off a pushbutton after an LED turns on? Well, by the end of this chapter you will know!

 

Chapter 4: Digital Input - Pushbuttons


We often see LEDs used by a computer to tell us something and just as often we use a pushbutton to tell the computer something. The alarm clock buzzes and LEDs tell us what time it is. We may then push a button to tell the alarm clock that we want to sleep another ten minutes (snooze button) or we may be ready to get up so we push the alarm off button. These two buttons tell the computer two different things. The snooze button tells it to turn off the alarm and set a new alarm for 10 minutes in the future. The alarm off button tells the computer to turn the alarm off and reset the new alarm time for 24 hours in the future. The alarm clock has a built in microcontroller (not unlike the one on the Arduino) that turns the LEDs on and off to show you the time and reads the buttons to learn what you want it to do next. In this chapter you will learn how to design circuits using pushbuttons and how to use them to get user input with Arduino software that will let your system take actions when a button is pressed.

But before we get into that, let's learn another couple of Arduino C programming concepts that we will use when testing pushbuttons. We will look at decision-making using the if-else conditional flow control construct. Then we will learn to use do-while, which is similar to the 'while' flow control construct we saw last month. Next we will learn how to use the Arduino function millis() to do some event timing. And finally we will learn about '=' and '==' two C operators that surprisingly aren't equal. We will apply this knowledge in our labs where we will learn how to get the Arduino to detect a button push, use that information to control LED states. And for our last exercise we will bring it all together along with the millis() function to create an Arduino based reaction timer. How quick can you get your finger off a pushbutton after an LED turns on? Well, by the end of this chapter you will know!

More decisions if-else


The Arduino provides several ways for a program to make decisions. One of these is to pose the question "if this is true do this, else do that". It examines a statement to see if it is true and if that statement is true then it does one thing and if that statement is not true, then it does something else. The question is posed in code as follows:

     if (statement is true)
     {
          // do this;
     }
     else
     {
          // do that;
     }

In this chapter's lab's pushbutton LED examples, we ask the question: "Is the button pushed?" which we can determine by looking at the Arduino pin the pushbutton is connected to and seeing if it is HIGH. If it is true that the pin is HIGH, then we turn the LED on. If it is not true, then we turn the LED off as follows:

First get the pushbutton state HIGH or LOW by using the digitalRead function.

     pushButtonState = digitalRead(pushButtonPin);

Next we use the if/else statements to turn the LED on if it the pushbutton state is HIGH and turn it off if it is LOW. [Note that this uses the '==' operator to determine if the item on the left is equal to the item on the right - we will discuss this operation in detail in the next section.]

     if (pushButtonState == HIGH) {
          // turn LED on:
          digitalWrite(ledPin, HIGH);
     }
     else {
          // turn LED off:
          digitalWrite(ledPin, LOW);
     }

We will later see many other examples for using if-else for making decisions based on the statement within the 'if' parentheses. For instance we might ask if one variable is larger than another using the '<' less than operator as follows:

     if(firstVar < secondVar)
     {
          doThis();
     }
     else
     {
          doThat();
     }

And we can combine if-else to ask several questions about the operator:

      if(firstVar < secondVar)
     {
          doThis();
     else if(firstVar > secondVar)
     {
          doThat();
     else
     {
          doTheOther();
     }


Another Control loop: do while()

In Chapter 3 we learned about the while() control structure that runs the block of code that follows the while(condition) each time it checks the condition and finds it true. A variant on this is the do while() control structure which is similar to while() except that it - at least once - runs the code in the block that follows the 'do' regardless of the condition in the while. In a regular while(condition) loop, the following block will not be run first time through if the condition is false, but in do while() the block runs once regardless. In the lab exercises we will see a situation where this makes sense:

     do{
          // get the state of the pushbutton
          buttonState = digitalRead(buttonPin);
     }while(buttonState == HIGH);

This makes sense if the buttonState variable is initialized to 0, but may have been pressed at sometime after initialization, but before this bit of code runs. If we had used while(buttonState == HIGH) but hadn't yet checked the button state then it would never run the subsequent block of code.

One way to time events

The Arduino keeps track of the time since it starts running (for up to about 50 days when the number rolls over.) If you get the number of milliseconds when an event starts and then after the event ends you get the number of milliseconds - you can then subtract the start milliseconds from the end milliseconds to get the elapsed time. We will see this used in the reaction time tester lab at the end of this chapter where we will use the do while() discussed above and the Arduino millis() function to get a start and end millisecond value to report the reaction time:

     // get the start time as soon as the LED goes off
     startTime = millis();

     // wait until the subjects gets his finger off the button
     // read the button state until it is equal to HIGH
     do{
          // get the state of the pushbutton
          buttonState = digitalRead(buttonPin);
     }while(buttonState == HIGH);

     // get the end time as soon as the finger is off the switch
     endTime = millis();

     // Tell the world your reaction time
     Serial.print("You took: ");
     Serial.print(endTime-startTime,DEC);
     Serial.println(" milliseconds.");

Some equals are more equal than others


We learned that operators in C are used for arithmetic-like operations and include such things as '+', '-', and '<'. Of all the operators we will see, the '=' and '==' seem to give most folks trouble. The '=' operator is the arithmetic assignment operator and it will assign the value from the right side of the = sign to the variable on the left side. The '==' operator is the comparison operator and it is used to compare the values on either side of the operator. If they are actually equal, then the comparison is said to be true and if they aren't equal then the comparison is said to be false (giving the operation a value of 0). So if we want to set one variable to equal the value contained by another variable, we use the '=' assignment operator as in this example:

     // First assignment
     char firstVar = 5;
     char secondVar = 10;

     //Second assignment
     firstVar = secondVar;

We see that we originally set the firstVar to contain the value 5 and the secondVar to contain the value 10. Then write a statement that assigns the value of secondVar to firstVar. After this assignment, firstVar contains the value 10.

But what if we want to know if one variable contains the same value as another? In the example above, when the variables are defined they contain different values but after the assignment statement they contain the same value. Let's consider the situation where we want to do one thing in the program if those two values are the same and we want to do something different if they are different. We would use the comparison operator for to make that decision as for example:

     //First assignment
     char firstVar = 5;
     char secondVar = 10;

     // First test
     if(firstVar == secondVar) doThis();
     else doThat();

     // Second assignment
     firstVar = secondVar;

     // Second test
     if(firstVar == secondVar) doThis();
     else doThat();

In the above code, the first test if/else statements will call doThat(); since the variables being compared by the '==' operator are not equal. After they become equal in the second assignment, then comparing them results in calling doThis(). And this is a contrived example to help clarify how = and == differ, in a real program we are unlikely to know the values of the variables being compared - that is why we are asking the if/else questions.

Oh, but the problems these guys cause!

What is wrong with the following statement?

     //BAD CODE:
     if(ledState = true) // set LED state to true - bad idea
     {
          // do something
     }

This 'if' evaluation will always be true because you just set the ledState to equal true. You really meant to ask a question "is ledState equal to true?":

     //GOOD CODE:
     if(ledState == true) // if ledState is equal to true
     {
          // do something
     }

The confusion involved between these two operators is very common and even experienced programmers make the mistake of using = when they meant ==. Just remember that if you write a program where you want to see if some value is equal to another value and the program has a bug, look for the = and == operators.

Before we move on, see if you can avert World War III -

     //The World's Last C Bug
     while (1)
     {
          status = GetRadarInfo();
          if (status = 1)
          LaunchMissles();
     }

[Many thanks to 'theusch' on www.avrfreaks.net for this example that he thinks came originally from Jack Ganssle.]

So we can fix this by changing the if(status = 1) to if(status == 1) as follows:

     // NOT The World's Last C Bug
     while (1)
     {
          status = GetRadarInfo();
          if (status == 1)
          LaunchMissles();
     }

But what if we accidentally introduce another bug? Can you find the reason this 'fixed' version will also start WWIII?

     //The World's Last C Bug
     while (1)
     {
          status = GetRadarInfo();
          if (status == 1);
          LaunchMissles();
     }

Yep, you got it - I stuck a semicolon after the if(status == 1); making it a standalone statement. The C compiler assumes I know what I'm doing and does nothing with the if statement and then moves to the next statement which is an unqualified LaunchMissles();. I made this very sort of error recently and fortunately nobody yet trusts me to program nuclear systems. So, yeah software bugs can be tricky - now let's mess with some hardware.

Input versus Output of Higher and Lower Voltages


You learned how to output higher and lower voltages to control an LED in chapter 2 where you used the Arduino digital I/O pins in the output mode. You will now learn how to use those pins in the input mode to tell if the pin is exposed to either the higher voltage (in our case 5 volts) or lower voltage (in our case 0 volts). You will learn to read the pin state (HIGH or LOW) in Arduino software to indicate that a button is pushed or not pushed and to use that information to control actions of your system.

ASIDE: There are many other terms we may see that express the concept of what a pin reads. Where we say that the pin state is "HIGH or LOW", others may say "TRUE or FALSE", "1 or 0" or "Vcc or GND". We are referring both to an analog concept for the voltage on the pin and a digital concept describing the pin state.

What is a Pushbutton?


There are many kinds of pushbuttons, but all serve the same purpose and that is to let a user connect or break an electrical circuit. Our pushbuttons are designed to break the circuit unless they are pressed and to make the connection only when pressed. There are other buttons that are designed to keep the circuit connected unless pushed and then to break it while pushed. Another type will toggle between connecting and un-connecting the circuit on each press.

How Does a Pushbutton Work?


In Chapter 2 we learned that a circuit is simply a complete path of a circle of conductors through which electricity can flow. If you break the path of the circle by cutting one of the conductors, the electricity will no longer flow. Figure 1 shows a circuit with a 9-volt battery, a resistor, an LED and a push button with the pushbutton open so that no current flows and the LED is not lit. Figure 2 shows the same circuit with the pushbutton pressed making the connection and allowing current to flow and light up the LED.


Figure 1: Pushbutton Open Circuit - No Current

 

Figure 2: Pushbutton Closed Circuit - Current Flows

The Arduino 101 Projects Kit Pushbutton

 

Figures 1 and 2 show how a pushbutton works. Our pushbutton is a little different and instead of having one connection on each side of the switch, it has two connections on each side as shown in Figure 3.

 

Figure 3: Pushbutton Breadboard and Schematic Symbols


This can be a little confusing. In Figure 4 we see a pushbutton on our Arduino Proto Shield with the connections highlighted in yellow and purple. As you can see the left side of the pushbutton connects (shown in yellow), both the top and bottom 5-pin columns and the right side shown in purple connects those upper and lower rows.

Figure 4: Connections when not pushed


To make this even clearer Figures 5 and 6 show the pushbutton schematic symbol superimposed on the breadboard symbol with the pushbutton open and closed. As you can see, when the pushbutton is closed (pushed) you now have both the left and right columns connected (shown in red).

Figure 5: Open Pushbutton Schematic Symbol Superimposed


Be sure and remember when you use a pushbutton that the pins come out on only two sides of the pushbutton, the top and bottom as shown in the illustration and that the pins have the switch between them on the side they come out of, but are connected to the pin on the other side. If this is still a bit confusing, things should get clearer as you get to use the pushbutton in the lab exercises.

Figure 6: Closed Pushbutton Schematic Symbol Superimposed

 

Lab 1: Pushbutton with LED - Analog


Before we learn to use pushbuttons with the computer (digital), let's first look at how they work electrically (analog). In chapter 2 we tested an LED with an analog circuit, this time let's add a pushbutton.

Parts Required: 1 Pushbutton
1 Red LED
1 1000 ohm Resistors (brown black red)
2 jumper wires

Figure 7: Pushbutton LED Normally Off Analog Circuit

 

Figure 8: Open pushbutton leaves LED off

 

Figure 9: Closed pushbutton turns LED on


Notice that Figures 8 and 9 correspond exactly to Figures 1 and 2 that show the current flow.

Turn the LED on with the pushbutton



 Check off when complete: Make sure the power is off before building the circuit.
Build the circuit as shown in Figure 7
Apply power to the circuit.

As you can see from Figure 8 the pushbutton is open and does not make a connection so the circuit is broken and the LED does not light up. When you press the button the LED will light up.


Is the LED on or off?
Press the button and see if the LED comes on and stays on while the button is pressed.
Take a moment to think about how the pins on the breadboard are connected by the pushbutton when it is open (un-pressed) and closed (pressed). Figures 8 and 9 show these two states

Turn the LED off with a pushbutton - Analog


Let's rebuild the circuit so that the LED is normally on and goes off only if the button is pushed.

Figure 10: Pushbutton LED Normally On Analog Circuit


Note from the schematic in Figure 10 that the current will flow from 5V through the resistor and the LED to ground, but in Figure 11 the current will flow from 5V through the switch to ground. The reason it flows through the switch and not the LED is that the switch has near 0 resistance while the LED has a relatively higher resistance and the current (like water) flows down the path of least resistance.

Figure 11: Pushbutton LED Normally On Analog Circuit - button pushed



 Check off when complete:
Make sure the power is off before building the circuit.
Build the circuit as shown in Figure 10
Apply power to the circuit.
Is the LED on or off when the button is not pushed?
Push the button to verify that the LED turns off while the button remains pushed as shown in Figure 11.
Briefly explain why the LED is on when the button is not pressed and why it turns off when the button is pressed.

Lab 2: Reading a Pushbutton - Digital


When an Arduino pin is set to the input mode, it may be used to determine if the pin is exposed to a higher or lower voltage. In the design shown in Figure 12, pin 12 is connected to a 10kO resistor that is connected to ground. The pin is also connected to a pushbutton that is connected to 5-volts. When the button is not pushed the pin is exposed to 0 volts and the Arduino software will read this as a LOW. If the button is pushed then the pin is connected both to the 10kO resistor to ground and to the 5-volt supply. Now the pin detects that the current is running from the 5 volt through the 10kO resistor to ground so it 'sees' the 5 volts at the positive side of the 10kO resistor. Figure 13 shows what happens in both cases. Let's build and test this circuit to help make these concepts clearer.

Parts Required: 1 Arduino
1 Arduino Proto Shield
1 Pushbutton switch
1 10k ohm Resistor (brown black orange)
2 jumper wires

Pushbutton Circuit

 

Figure 12: Pushbutton to Digital Pin 12

 

Figure 13: Pushbutton Off and On Schematic




 Check off when complete:
Make sure the power is off before building the circuit.
Build the circuit as shown in Figure 12.
Apply power to the circuit.
Open the Arduino IDE and load the 'C4_Pushbutton_Serial' program.
Verify and upload the program to your Arduino.
Open on the Tools menu item and click on the Serial Monitor as shown in Figure 14.
Push the button and release it several times to verify that you get serial output showing the button state as shown in Figure 15.

Pushbutton Program:

 

  // C4_Pushbutton_Serial
// Pushbutton program reports when a button is pushed and released // Constants used to set pin numbers > //(constants can't change while the program runs) const int buttonPin = 12; // the number of the pushbutton pin // variables //(Variables may change while the program runs) int buttonState = 0; // variable the pushbutton status void setup() { // initialize Serial communications Serial.begin(9600); // set the buttonPin mode to INPUT pinMode(buttonPin, INPUT); } void loop(){ // get the state of the pushbutton buttonState = digitalRead(buttonPin); // is the button pressed? // if it is, the buttonState is HIGH: if (buttonState == HIGH) { // Tell the world Serial.println("Button pushed."); } else { Serial.println("Button not pushed."); } delay(500); // pause for 1/2 a second }

 

Figure 14: Open the Serial Monitor

 

Figure 15: Serial Monitor showing button pushes

 

Lab 3: Using a Pushbutton for Digital Control of an LED


In lab 1 we saw how to use a pushbutton to turn an LED on and off by making or breaking an analog circuit. In this lab we will see how to use the Arduino to read the pushbutton state and then decide whether to turn the LED on or off.

Parts Required: 1 Arduino
1 Arduino Proto Shield
1 Pushbutton
1 LED
1 1k ohm Resistor (brown black red)
1 10k ohm Resistor (brown black orange)
5 Jumper Wires

Pushbutton LED Circuit

 

Figure 16: Breadboard Pushbutton LED Digital Control

 

Figure 17: Schematic Pushbutton LED Digital Control

 

Figure 18: Pushbutton and LED photo


ASIDE: Figure 18 shows a photo of a physical realization of the circuit shown in Figures 16 and 17. Note that the jumper wires are not the same color as in the diagram and that they obscure the view of the board. You'll need to be extra careful when building the boards to make sure that you've connected the wires shown in the diagrams and schematics.

Use a pushbutton to turn an LED on



 Check off when complete:
Make sure the power is off before building the circuit.
Build the circuit shown in Figures 16 and 17.
Plug the USB cable into the Arduino.
Apply power to the circuit.
Open the Arduino IDE and load the 'C4_Pushbutton_LED' program.
Verify and upload the program to your Arduino.

 
// C4_Pushbutton_LED
// Program turns LED on or off depending on pushbutton state

// Constants used to set pin numbers 
const int buttonPin = 12; // the number of the pushbutton pin
const int ledPin = 11; // the number of the LED pin

// variables 
int buttonState = 0; // variable the pushbutton status

void setup() { 
  // set the buttonPin mode to INPUT
  pinMode(buttonPin, INPUT); 
  // set the ledPin mode to OUTPUT
  pinMode(ledPin, OUTPUT);       
}

void loop(){

  // get the state of the pushbutton
  buttonState = digitalRead(buttonPin);

  // is the button pressed?
  // if it is, the buttonState is HIGH:
  if (buttonState == HIGH) {     
    // turn LED on:    
    digitalWrite(ledPin, HIGH);  
  } 
  else {
    // turn LED off:
    digitalWrite(ledPin, LOW); 
  }
  
  delay(500); // pause for 1/2 a second
}

 

Use a pushbutton to turn an LED off


We can easily reverse the way this system works so that instead of turning the LED on when the button is pressed, it can turn the LED off - by changing only the two lines highlighted in the loop() function as shown below. [The comments are also changed to match the functional change, but only the source needs to be changed to reverse the way the button works.]

My friend Jay Flanders pointed out here that this is the beauty of programmable devices - you can change the software instead of the hardware.

Check off when complete:

Make sure the power is off before building the circuit.
Build the circuit shown in Figures 16 and 17.
Plug the USB cable into the Arduino.
Apply power to the circuit.
Change the 'C4_Pushbutton_LED' program to the two highlighted lines shown in the loop() function below.
Verify and upload the program to your Arduino.

void loop(){
     // get the state of the pushbutton
    buttonState = digitalRead(buttonPin);

    // is the button pressed?
    // if it is, the buttonState is HIGH:
    if (buttonState == HIGH) {
    // turn LED off:
           digitalWrite(ledPin, LOW);
    }
    else {
    // turn LED on:
          digitalWrite(ledPin, HIGH);
    }

    delay(500); // pause for 1/2 a second
}


Notice that the only difference in the code is that we reversed the HIGH and LOW in the digitalWrite() function which is shown in the source by highlighting the lines to change.

Use a pushbutton to control a pulsing LED


In last example the LED stays on or off only while the pushbutton is pressed or released. This might be inconvenient if we have an LED that is normally off and only comes on when we hold down the pushbutton. What if somebody knocks on the door? We release the pushbutton and the LED goes off while we are answering that door. What we probably really want is a design where we can just press a button to turn on the LED and then come back later and press the button to turn it off. Let's write a program so that if the LED is off and the button is pressed and released, the LED will come on. And if the LED is on and the button is pressed and released, the LED will turn off. Think about how you might do this. The microcontroller will be running through the loop() function and each time through it will need to decide to turn the LED on or off as with the earlier programs, but in them it checked the state of the pushbutton to make the on/off decision. What we want is a variable that the loop() can examine to see if the LED should be on or off. For instance we may create a global variable ledState and set it to 0, then when we are in the loop() function we will check the pushbutton and if it is pressed we will then check the ledState and if that state is 0 we will change it to 1 and if it is 1 we will change it to 0. This action is called toggling and we are said to toggle the state. We will also use a delay so we can get our finger off the pushbutton before the loop() goes around and checks it again.

 Check off when complete:
Make sure the power is off before building the circuit.
Build the circuit shown in Figures 16 and 17.
Plug the USB cable into the Arduino.
Apply power to the circuit.
Open the Arduino IDE and load the 'C4_Pushbutton_LED_state_change' program.
Verify and upload the program to your Arduino.

// C4_Pushbutton_LED_state_change
// Changes the LED state each time the pushbutton is pressed.

// Constants used to set pin numbers 
const int buttonPin = 12; // the number of the pushbutton pin
const int ledPin = 11; // the number of the LED pin

// variables 
int buttonState = 0; // variable the pushbutton status
int ledState = 0; // variable the LED 0 is off, 1 is on 

void setup() { 
  // set the buttonPin mode to INPUT
  pinMode(buttonPin, INPUT); 
  // set the ledPin mode to OUTPUT
  pinMode(ledPin, OUTPUT);         
}

void loop(){

  // get the state of the pushbutton
  buttonState = digitalRead(buttonPin);
  
  // set the LED state based on the pushbutton state
  if(buttonState)
  {
      if(ledState)
      {
         ledState = 0;
      }
      else ledState = 1;
  }
delay(500); // Give time to release the button

  // is the button pressed?
  // if it is, the buttonState is HIGH:
  if (ledState == 1) {     
    // turn LED on:    
    digitalWrite(ledPin, HIGH);  
  } 
  else {
    // turn LED off:
    digitalWrite(ledPin, LOW); 
  }
  
}