Electronics Software Development

How To Use Buttons With Your Arduino

Arduino Buttons Graphic
Written by John Woolsey

Skill Level: Beginner

Table Of Contents

Introduction

This tutorial will show you how to connect and use a button with your Arduino development board. It is meant as a follow-on beginner article to the Blink: Making An LED Blink On An Arduino Uno tutorial. In this tutorial, we will continue the journey by adding a button to control the LED used in that tutorial. I will first describe what a button is and how to connect it to your Arduino. Then I will provide two examples.

  1. Turning on the LED only when the button is pressed, and
  2. Changing the LED’s blink rate on successive button presses.

If you haven’t already, please review that tutorial before proceeding with this one as we will be applying what we learned in that tutorial.

A basic understanding of electronics and programming is expected along with some familiarity with the Arduino platform. In addition, this tutorial uses a solderless breadboard to build a circuit from a schematic diagram. The All About Circuit’s Understanding Schematics, SparkFun’s How to Read a Schematic, Core Electronics’ How to Use Breadboards, and Science Buddies’ How to Use a Breadboard guides are good resources for learning how to translate a schematic to a breadboard.

The resources created for this tutorial are available on GitHub for your reference.

What Is Needed

Background Information

A typical switch, or button, is a mechanical device that conducts electricity through metal contacts when activated. There are many different types of switches (e.g. toggle, pushbutton, relay, rotary, etc.) used for various applications. They can be either normally-open (NO) or normally-closed (NC), meaning that their contacts are normally disconnected (does not conduct electricity) or connected (does conduct electricity) respectively. Multiple distinct switches can be combined into a single device that has multiple poles (separate switches) and/or multiple throws (separate conducting paths). For instance, a single-pole double-throw (SPDT) switch will conduct electricity in two distinct paths when the single switch is activated. Switches can also be constant (flip style) or momentary (only “active” while being depressed). Switches are considered “on” when they are conducting electricity and “off” when their contacts are disconnected. This tutorial will use a momentary normally-open single-pole single-throw pushbutton type of switch.

The contacts of mechanical switches “bounce” for a short time period after initial activation. This means that when the electrical contacts of a switch are first connected (or disconnected), they rapidly alternate, or bounce, between their “on” and “off” states until the switch settles down into its new stable state. Typical switches have bounce times of 5-20 milliseconds, but some can take longer. Bouncing can cause problems with switch sensing circuitry or microcontrollers that are trying to read the switch’s state. The sensing device can erroneously detect that a switch is in a different state than what was expected. A fast device can even detect multiple switch flips during the time a switch is bouncing. The process of eliminating this effect is called switch debouncing. Various methods can be used to implement debouncing. They can be implemented in hardware (e.g. digital logic gates or analog filters) or software (e.g. delayed readings or digital filters). Most microcontroller based projects use software based debouncing to conserve the cost of additional electrical components and is what we will use in this tutorial. Please see Switch on Wikipedia for more information about switches.

When reading a button, we are interested in knowing what state, “on” or “off”, the button is in so that we can apply its selection in our application. Generally, the “on” state is represented by a high voltage (power) level and the “off” state is represented by a low voltage (ground) level, but it can be the other way around. To implement the button state selection, circuit designers use a large pull-up (or pull-down) resistor to pull up (or down) the sensing terminal of a button to power (or ground) while tying the other button’s terminal directly to ground (or power). You know what they say, “a picture is worth a thousand words”, so let’s see how this works by looking at the typical pull-up and pull-down momentary pushbutton configurations shown below.

Schematic Diagram Of Typical Pull-Up And Pull-Down Pushbutton Configurations
Schematic Diagram Of Typical Pull-Up And Pull-Down Pushbutton Configurations

Here, we are looking at two types of momentary pushbuttons (normally-open and normally-closed) each used with both pull-up and pull-down resistors. The A) and D) circuits are both active low, meaning they present a low value to the GPIO pin while the button is being pressed. Specifically, A) is pulled high with resistor R1 while inactive and driven low when the SW1 momentary normally-open pushbutton is pressed. Conversely, D) is driven high while inactive and pulled low with resistor R4 when the SW4 momentary normally-closed pushbutton is pressed. The opposite is true for configurations B) and C).

While logically it would make sense to use active high buttons in our design, most microcontrollers incorporate pull-up resistors on their GPIO pins, but not necessarily pull-down resistors, and most designers use normally-open pushbuttons, so the active low A) configuration is the most typically used. Most Arduino boards have only pull-up resistors, of approximately 20-50 KΩ each, on their GPIO pins, so we will use the typical A) choice in this tutorial using the integrated pull-up resistor within the Arduino’s microcontroller.

In addition, most pushbuttons utilize 4 pins, one on each corner of the button. This may sound strange since only two connections are required for switches, but they are intended to provide mechanical stability when the buttons are mounted to printed circuit boards or breadboards. Most buttons connect two pins together internally for each side of the integrated switch. The terminals protruding from one side of a button are typically both sides of the switch, meaning they are connected when the button is activated. Likewise, the terminals on the opposite side of the button also constitute a switch connection. This can sometimes be confusing and not all buttons are built the same, so it is usually good practice to consult the datasheet for the button or use a multimeter to check the connections for the button before incorporating it into your circuit.

I am using the Arduino Uno WiFi Rev2 development board connected to a macOS based computer running the desktop Arduino IDE for this tutorial. If you are using a different Arduino board or computer setup, the vast majority of this tutorial should still apply, however, some minor changes may be necessary. If you need assistance with your particular setup, post a question in the comments section below and I, or someone else, can try to help you.

Building The Circuit

Before connecting any circuitry to your Arduino board, connect your board to your computer, open the Arduino IDE, upload the BareMinimum (Main Menu > File > Examples > 01.Basics > BareMinimum) sketch to the board, and then disconnect your board from your computer. This avoids accidental damage during wiring and power-up operations.

Construct the button and LED circuit on a breadboard and connect it to your Arduino’s GPIO pins according to the schematic shown below. The LED connection is the same as what we used in the Blink: Making An LED Blink On An Arduino Uno tutorial. You just need to add the button, which is connected to pin D3. Reminder, the anode (positive terminal) of an LED is longer than the cathode (negative terminal) if you are reconnecting the LED.

Schematic Diagram Of The Button And LED Circuit Connected To An Arduino Uno
Schematic Diagram Of The Button And LED Circuit Connected To An Arduino Uno

The circuit should look similar to the one shown below once completed.

Completed Button And LED Circuit Connected To An Arduino Uno
Completed Button And LED Circuit Connected To An Arduino Uno

Once the circuit is built, connect your Arduino to your computer with the USB cable and power it up.

Detecting Button Presses And Releases

Now, let’s move on to the programming phase. Open the Arduino IDE and create (Main Menu > File > New Sketch) and save (Main Menu > File > Save As…) a new sketch named ButtonBasic with the code shown below.

const uint8_t Button = 3;
const uint8_t RedLED = 2;

void setup() {
   Serial.begin(9600);
   while (!Serial);
   pinMode(Button, INPUT_PULLUP);
   pinMode(RedLED, OUTPUT);
   digitalWrite(RedLED, LOW);
}

void loop() {
   readButton();
}

void readButton() {
   static uint8_t previousButtonState = HIGH;
   uint8_t currentButtonState = digitalRead(Button);
   static unsigned long previousTimeButtonChanged = 0;
   unsigned long currentTime = millis();
   if (currentTime - previousTimeButtonChanged > 10
      && currentButtonState != previousButtonState) {
      if (currentButtonState == LOW) {
         Serial.println("Button pressed.");
         digitalWrite(RedLED, HIGH);
      } else {
         Serial.println("Button released.");
         digitalWrite(RedLED, LOW);
      }
      previousTimeButtonChanged = currentTime;
      previousButtonState = currentButtonState;
   }
}

This example will turn on the LED when you press and hold the button and turn off the LED when you release the button.

We first define our pin connections to the button and LED (lines 1 and 2).

The setup() function begins by establishing a connection to the serial port in order to view printed messages in the Serial Monitor. Line 5 initializes the connection at 9600 baud and line 6 waits until the connection is established before moving on.

Lines 7 and 8 set the pin modes for our button and LED. The INPUT_PULLUP mode is utilized, instead of simply INPUT, for the button in order to take advantage of the internal pull-up resistor of the Arduino board’s microcontroller.

Line 9 sets the RedLED pin low thereby turning off the LED so that we have a known initial state when starting our program.

The readButton() function (beginning on line 16) performs all of the necessary operations for reading the button. This routine is called from within the loop() function to repeatedly check for and act upon button transitions.

In order to properly determine whether a button was pressed or released, we need to keep track of the button’s state and time of activation. This is accomplished with the previousButtonState and previousTimeButtonChanged variables defined on lines 17 and 19. The static keyword is utilized so that we can save the values of those variables in between invocations of the readButton() function.

We also retrieve the current time and state of the button (lines 18 and 20) in order to compare those current values against the previous values as is performed on lines 21 and 22. That comparison first checks if the appropriate 10 ms of button debounce time has elapsed and then makes sure that the button state has indeed changed.

Once the comparison criteria are met, we then proceed to print a message to the Serial Monitor and update the state of the LED depending on whether the button was pressed or released as shown in lines 23-29.

We end the comparison, and the routine, by saving the current activation time (line 30) and button state (line 31) values for use in the next button check.

Verify (Main Menu > Sketch > Verify/Compile) your code and fix any errors that are reported.

Save (Main Menu > File > Save) your program when you are done editing.

Upload (Main Menu > Sketch > Upload) the sketch to the Arduino and open the Serial Monitor (Main Menu > Tools > Serial Monitor).

You should see the LED turn on and off as you press and release the button along with button transitions being displayed in the Serial Monitor.

Debouncing buttons is not an exact science. A software based debounce time of 10 milliseconds usually works well for most buttons and applications. However, If you find that you are getting some extra button pressed or released events, you may want to increase the bounce time to 50 ms or more. If the bounce time becomes too large, in the range of 200 ms or so, you may end up missing some events.

If there is something that needs further explanation, please let me know in the comment section and I will try to answer your question.

Using A Button To Change Operations

Now let’s create a second example that will change the blink rate for the LED each time you press the button. Create and save a sketch named ButtonMultifunction in the Arduino IDE with the code shown below.

const uint8_t Button = 3;
const uint8_t RedLED = 2;

bool quickBlink = false;

void setup() {
   Serial.begin(9600);
   while (!Serial);
   pinMode(Button, INPUT_PULLUP);
   pinMode(RedLED, OUTPUT);
   digitalWrite(RedLED, LOW);
}

void loop() {
   readButton();
   blinkLED();
}

void blinkLED() {
   static unsigned long previousBlinkTime = 0;
   unsigned long currentTime = millis();
   unsigned long delay = (quickBlink) ? 500 : 1000;
   if (currentTime - previousBlinkTime >= delay) {
      digitalWrite(RedLED, !digitalRead(RedLED));
      previousBlinkTime = currentTime;
   }
}

void readButton() {
   static uint8_t previousButtonState = HIGH;
   uint8_t currentButtonState = digitalRead(Button);
   static unsigned long previousTimeButtonChanged = 0;
   unsigned long currentTime = millis();
   if (currentTime - previousTimeButtonChanged > 10
      && currentButtonState != previousButtonState) {
      if (currentButtonState == LOW) {
         Serial.println("Button pressed.");
         quickBlink = !quickBlink;
      }
      previousTimeButtonChanged = currentTime;
      previousButtonState = currentButtonState;
   }
}

This example follows the same process for configuring and initializing the pins and serial port as we did in the previous example.

To enable the blink rate functionality, we first add the quickBlink boolean global variable (defined on line 4) and update the readButton() function to toggle the blink rate (quickBlink) only when the button is pressed (line 38).

We then add a blinkLED() function, beginning on line 19, that utilizes the same code as that used in the BetterBlink sketch of the Blink: Making An LED Blink On An Arduino Uno tutorial, but this time, the blink rate (delay) is determined by the value of the quickBlink variable instead of the BlinkPeriod constant. The blinkLED() function is then added to the loop() function to get called after the button state is determined from the readButton() function.

Verify, save, and upload your sketch. The LED should begin blinking and then blink faster when you press the button. Each successive button press should alternate between fast and slow blinking. The button presses will also be shown within the Serial Monitor.

Now that you know how to incorporate a button into your design, play around with the code and try creating a new sketch that can perform different operations. For instance, try changing the blink rate only while the button is being pressed or providing three distinct operations on each button press, like 1) turning on the LED, 2) turning off the LED, and 3) blinking the LED.

Before we end, now would be a good time to upload the BareMinimum (Main Menu > File > Examples > 01.Basics > BareMinimum) sketch in order to reset all of the board’s GPIO pins back to their default states. This ensures no outputs are being driven that may damage your board or connected electronics when plugging in your board for your next project.

Summary

In this tutorial, we learned how to connect and read a button to control an LED on an Arduino.

Specifically, we learned

  • about the different types of switches and buttons available,
  • how their electrical contacts bounce,
  • how to implement debouncing methods to reduce reading errors,
  • how to connect a button to your Arduino,
  • how to detect button presses and releases, and
  • how to use a button to change program operations.

The final source code used for this tutorial is available on GitHub. The GitHub versions of the code are fully commented to include additional information, such as the program’s description, circuit connections, and code clarifications.

Thank you for joining me on this journey and I hope you enjoyed the experience. Please feel free to share your thoughts or questions in the comments section below.

This tutorial is provided as a free service to our valued readers. Please help us continue this endeavor by considering a donation.

About the author

John Woolsey

John is an electrical engineer who loves science, math, and technology and teaching it to others even more.
 
He knew he wanted to work with electronics from an early age, building his first robot when he was in 8th grade. His first computer was a Timex/Sinclair 2068 followed by the Tandy 1000 TL (aka really old stuff).
 
He put himself through college (The University of Texas at Austin) by working at Motorola where he worked for many years afterward in the Semiconductor Products Sector in Research and Development.
 
John started developing mobile app software in 2010 for himself and for other companies. He has also taught programming to kids for summer school and enjoyed years of judging kids science projects at the Austin Energy Regional Science Festival.
 
Electronics, software, and teaching all culminate in his new venture to learn, make, and teach others via the Woolsey Workshop website.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.