Lesson 1: Using buttons
Table of Contents
- Switches
- Making an initial button circuit
- Intro to digital input
- Hooking up digital input with microcontrollers
- Let’s make stuff!
- Pull-down resistor configuration
- Pull-up resistor configuration
- Internal pull-up resistor configuration
- Putting it all together
- More resources
- Next Lesson
This is the first lesson in the Intro to Arduino Input lesson series. We assume you’ve already completed the Intro to Arduino Output series. If not, please complete that first!
In this lesson, we’ll finally get to build something interactive: turning on an LED with a push button. We’ll cover buttons (aka momentary switches), how to use digital input with the digitalRead
function, and pull-up and pull-down resistors.
Switches themselves are conceptually easy to understand—they are either “closed” or “open”. We use switches everyday when we turn on and off our light circuits in our home. However, when using switches with microcontrollers, we’ve found that students often struggle to understand why pull-up or pull-down resistors are necessary. So, take your time with this lesson. Try to understand the why and how of these resistors in your circuits.
For example, in the animation below, we show a button circuit with a pull-down resistor hooked up to Pin 2. Importantly, notice where the current flows when the button is pressed—perhaps surprisingly, it does not flow into Pin 2. In fact, (virtually) no current flows into Pin 2! Why not? We’ll talk about this and more in this lesson!
Animation shows the Arduino’s built-in LED illuminating when the button on Pin 2 is pressed. When the button is pressed, current flows from \(V_{CC}\) through the pull-down resistor to GND. We’ll learn more about this circuit in this lesson.
Switches
As noted above, switches are simple components: they’re typically either “open” (disconnected) or “closed” (connected). There are lots of different types of switches from momentary switches (aka buttons) to toggle or slide switches (which maintain their state) to switches that activate based on environmental conditions like a tilt switch or a reed switch.
Prices and pictures are from Sparkfun.com, Jan 2020; parts can be cheaper in bulk from suppliers like Digi-Key or Mouser Electronics.
In this lesson, we are going to use a four-legged push button (momentary switch), which look like:
If you want to learn more about switches in general, see these articles by Sparkfun and ITP NYU.
Making an initial button circuit
We’ll first learn how to use a button without a microcontroller. This will strengthen our understanding of buttons, in general, before switching over to digital input.
Materials
We’ll need the following materials:
Breadboard | Arduino | LED | Resistor | Button |
---|---|---|---|---|
Breadboard | Arduino Uno, Leonardo, or similar | Red LED | 220Ω Resistor | 12x12mm “Tactile Switch Buttons” |
Four-legged tactile buttons
The four-leg push button is one of the most common button types for breadboarding circuits; however, it’s also a bit funky and non-intuitive at first. You might be wondering: why four legs instead of two? How does this button work? See the diagram below.
The key thing to remember is that the two legs closest together (on the same side) are, somewhat unintuitively, not connected until you press the button. Upon button press, all four legs become connected (i.e., the same node). We created the following animation to help explain further. Observe the orientation of the button and how the legs are connected.
Video. Animation shows which two sides of a four-legged tactile button are disconnected until the button is pressed, creating a connection between all four legs.
And, of course, the best way to learn it is to try it yourself (and hopefully the animation will help). In general, if you’re confused about how to use a component, it’s a good idea to consult the datasheet. You can also use a multimeter, if you have one, to check for continuity between the four legs.
Using buttons without a microcontroller
We’ll make a simple button-based circuit that turns on an LED when the button is pressed. This will give you experience with how the button connections work before hooking it up to an Arduino.
Below, we’ve included two wiring diagrams: one using an external power source like a 9V battery with a snap connector (you could use alligator clips!) and the other using Arduino’s 5V pin for power, just like we did in the LED on lesson. We suggest the 9V battery approach just to avoid confusion—remember, this circuit is completely independent of Arduino!
With 9V Battery | With Arduino 5V Pin |
---|---|
Circuit powered by 9V battery | Circuit powered by 5V Arduino pin |
If you build the 9V battery circuit, then we suggest a 680Ω or 1kΩ resistor rather than a 220Ω resistor.
That’s it! Once you’ve created the circuit, give it a try by pushing the button and the LED should turn on. See the animation below.
Video. Animation showing two different voltage sources for a basic LED-based circuit with a four-legged button to control “on” and “off.” When no button is pressed, there is no current flow and the LED is off.
Now that we understand how this button works, let’s move on to using switches/buttons with a microcontroller.
Intro to digital input
In our Blink lesson, we introduced digital I/O with a specific focus on digital output. Here, we are going to talk about digital input, which is a bit more complicated.
Digital I/O refresher
Recall that the Arduino Uno and Leonardo have 20 general-purpose input/output (GPIO) pins that can be used for digital input/output (I/O) using digitalRead()
and digitalWrite()
, respectively.
As noted in our Intro to Digital Output lesson, you can control any of these 20 digital I/O pins with three functions:
pinMode(int pin, int mode)
configures a specified pin as either anINPUT
orOUTPUT
. For our buttons, we’ll be usingINPUT
—because a button is an input and not an output—and a variant ofINPUT
calledINPUT_PULLUP
.digitalRead(int pin)
reads digital input from the specified pin, eitherHIGH
orLOW
. This is what we need to read the button’s state (either pressed or not pressed)digitalWrite(int pin, int value)
writes digital output to the specified pin, eitherHIGH
orLOW
. This is not relevant to us here because we don’t write out data to a button.
What is digital input?
Digital input is any input that can be considered either on (typically, HIGH
or 5V) or off (typically, LOW
or 0V). Simple, right? However, using digital input with microcontrollers can be confusing, at least at first.
The most critical concept to understand is that microcontrollers read voltage, not current. This directly (and dramatically) affects how we setup our input circuits.
Indeed, the Arduino documentation states that pins configured as digital input “are in a high-impedance state” equivalent to a 100,000,000Ω (100MΩ) resistor added to the front of the input pin. This means that once you configure a microcontroller pin as input, very little current will “seep” into the pin. More specifically, Section 26.2 (entitled DC Characteristics) of the ATMega328 datasheet states that the input “leakage” current is 1 microamp (1 µA).
Is it LOW or is it HIGH?
You might be wondering: what’s the precise voltage-related definition of HIGH
and LOW
from the microcontroller’s perspective? And what does digitalRead
read if our voltage signal is not 0V and not 5V? Great questions!
As Lee describes in his Arduino lecture notes, “the value returned from digitalRead()
is only well-defined when the input pin voltage is close to \(V_{CC}\) or \(0V\). The precise meaning of “close” varies between microcontrollers”
For the ATmega328, the input voltage needs to be at least \(0.6\cdot V_{CC}\to 0.6\cdot5 V=3V\) to qualify as HIGH
and between \(0\) and \(0.3\cdot V_{CC}\to 0.3\cdot 5V=1.5V\) to qualify as LOW
. For the middle range \(0.3\cdot V_{CC}\) to \(0.6\cdot V_{CC}\), the behavior of the pin is undefined.
In general, this is unlikely to affect how you wire your digital input circuits with switches, buttons, or binary sensors (like reed switches)—because your two states will be 5V and 0V—but it may affect whether and how you hook up other sensors to a microcontroller, if you want to interpret them as digital input.
Hooking up digital input with microcontrollers
Let’s walk through how one might try to hook up a button to a microcontroller. In doing so, we’ll learn about what not to do and why as well as what to do and the role of pull-down resistors.
The floating pin problem
Given how we built button-based circuits above—without a microcontroller—you might initially think to hook up your button like the following:
Figure. Button circuit for microcontroller hooked up similarly to our non-microcontroller version above. This circuit will “sort of” work but has a problem related to a “floating pin” when the switch is open.
However, if you do this, what will the digital input pin read when the switch is open (that is, when the button is not pressed)? Well, this is called a “floating pin” and it’s not good. A floating pin is susceptible to randomly oscillating between HIGH
and LOW
. See the animation below.
Figure. Animation of what happens when you press a button with the simple circuit configuration.
In fact, try wiring up this configuration yourself and running the following program with the Serial Monitor open. What happens when you press the button? Try touching the button legs with your fingers but not actually pressing the button, what happens to the digitalRead
value? Are you reliably tracking the button state?
Here’s a quick video demonstration of what happens—the floating pin problem! Note: we are using a slightly modified version of this code where an LED is turned on if the button is pressed (i.e., if buttonVal == 1
). This just makes it easier to see the fluctuating button state.
Video. Floating pins are digital input pins that are not tied to a specific input voltage (either 0V or 5V) and thus, are subject to electromagnetic interference. Here, the button state is oscillating between HIGH
and LOW
simply due to the electromagnetic interference from my body. Makes me feel like Dumbledore! The source code is here.
In the video above, the button state is noisy and vulnerable to electromagnetic interference, which could come from the human body (as it does here), cross-talk between wires, stray capacitance, etc. To fix this, we need to force the input pin into a known state. But how?
An (incorrect) attempt to fix the floating pin
To solve the floating pin problem, we need to bias the digital input pin to a known voltage state when the circuit is open (the button is not pressed).
You might try to do this by adding GND
to the other leg of the button like this:
Figure. Warning: Do not do this. When the switch closes, a short circuit occurs, which could damage your microcontroller or Arduino.
And you’re on the right track. Now, when the switch is open, the digital input pin is in a known voltage state—it reads 0V. But what happens when we actually press the button? Oh no, a short circuit occurs! This could damage your microcontroller and/or Arduino!
Figure. Animation showing the effect of connecting GND
without a resistor. A short circuit!
So, what to do? Pull-down resistors to the rescue!
Pull-down resistors
To solve this problem, we can add in what’s called a pull-down resistor before the GND connection, which prevents short circuits when the switch is closed while still biasing the pin to 0V when the switch is open.
Typically, this pull-down resistor value is 10kΩ, which is also what the official Arduino docs recommends. A small resistor is called a strong pull-down and a large resistor is called a weak pull-down. In a bit, we’ll talk about what factors influence the pull-down resistor value (hint: use a 10kΩ) but the primary tradeoff is in power efficiency (low resistor values “waste” more current) and function (a large resistor may not always work properly as a pull-down).
The pull-down resistor is quite large: 10,000Ω (10kΩ)
Here’s an animation showing how a pull-down resistor configuration works. Notice how (almost) none of the current goes into Pin 2. This is because, as stated above, the digital input pins “are in a high-impedance state” equivalent to a 100,000,000Ω (100MΩ) resistor (see Arduino docs). And remember, microcontrollers read voltage, not current (so we don’t need current into our input pin)!
And here’s a video demonstrating the floating pin problem and fix:
Pull-up resistors
So, what’s a pull-up resistor then? With a pull-down resistor configuration, the input pin is biased to GND when the circuit is in an open state. With a pull-up resistor configuration, the resistor moves from the GND side of the circuit to the 5V side and logic is flipped: the input pin is “pulled up” to \(V_{CC}\) when the switch is open and goes to GND when the switch is closed.
Pull-up resistor configurations can be confusing because the HIGH
and LOW
values are flipped. Now, when the switch is open, the digital input pin is HIGH
. When the switch closes, the input pin goes LOW
.
Switch State | Input pin with pull-down | Input pin with pull-up |
---|---|---|
Open | LOW | HIGH |
Closed | HIGH | LOW |
For convenience, here are side-by-side diagrams of a pull-down vs. pull-up resistor configuration:
Internal pull-up resistors
Finally, many microcontrollers include an internal pull-up resistor that can be activated with software. On the Arduino, we can configure an input pin to use its internal pull-up resistor with: pinMode(<pin>, INPUT_PULLUP);
. This eliminates the need for any external resistors (thus simplifying your circuit).
Some microcontrollers have both internal pull-up and pull-down resistors. The popular ESP32 chip, for example, used in the Adafruit Huzzah32 has built-in pull-up and pull-down resistors on all GPIO pins except for GPIO34-GPIO39 (see link). These can be enabled with either pinMode(<pin>, INPUT_PULLUP);
or pinMode(<pin>, INPUT_PULLDOWN);
What value should I use for my pull-down or pull-up resistors?
The short answer: use a 10kΩ resistor.
As mentioned above, the official Arduino docs recommend a 10kΩ pull-down or pull-up resistor for digital input pins. On the ATmega microcontrollers (those on the Arduino Uno and Leonardo), the internal pull-up resistor is 20kΩ. On the Arduino Due, the internal pull-up is between 50kΩ and 150kΩ.
The following section detailing tradeoffs in selecting pull-up and pull-down resistor values is optional. You can skip to Let’s make stuff.
Tradeoffs in selecting a pull-up resistor
The longer answer: there are multiple factors to consider, but the primary tradeoff is in selecting a resistor that is small enough to “pull-up” the voltage to HIGH
when the switch is open but large enough to not “waste” power due to too much current through the resistor when the switch is closed. Remember, a “strong” pull-up is a low-resistance value while a “weak” pull-up is a high-resistance value. Why? Let’s read more below.
Figure. On the ATmega328, the digital input pin voltage (\(V_{pin}\)) needs to be greater than 3V to quality as HIGH
and less than 1.5V to qualify as LOW
. The in-between voltage range (1.5-3V) is undefined.
Above, we show two diagrams: one for when the switch is closed and the other for when the switch is open. We’ll walk through each scenario and their relevance to selecting a pull-up resistor.
Scenario 1: Switch is open
When the switch is open (not pressed), we have a leakage current \(I_{IH}\) into our input pin of \(0.000001A\) or (\(1µA\))—as specified in the ATmega328 datasheet (Section 26.2). We can thus calculate the voltage on the input pin (\(V_{pin}\)) using Ohm’s Law: \(V_{pin} = V_{in} - I_{IH} * R\) where \(V_{in}=5V\), \(I_{IH}=1µA\), and \(R\) is our pull-up resistor value. Recall that on the ATmega328, the input voltage needs to be at least \(0.6\cdot V_{CC}\to 0.6\cdot5 V=3V\) to qualify as HIGH
. So, we must ensure that our selection of \(R\) is not so high as to drop below this threshold for \(V_{pin}\). So, what’s the theoretical maximum pull-up resistor value to meet this limit?
Using this formula alone to drive our decision, we can determine that \(R\) should not exceed 2MΩ—which would be a very large (weak) pull-up resistor.
Scenario 2: Switch is closed
When the switch is closed (button pressed), the leakage current of (\(1µA\)) can be ignored as the current is dominated by the \(V_{in}\) to \(GND\) branch. And here, the key factor is how much current is flowing when the switch is closed: from Ohm’s Law (\(I=\frac{V_{in}}{R}\)), we know that a small resistor will result in more current. We can characterize this as how much power is being dissipated by the resistor—ideally, we want to minimize this. The formula for power is \(P = I \cdot V\) (in watts), which, using Ohm’s Law to replace \(I\) with \(\frac{V}{R}\), can be rewritten as \(P = \frac{V^2}{R}\). Or, in this case, \(P = \frac{V_{in}^2}{R}\). Given the exponential, when \(R\) is small, power dissipation is quite large.
Calculating tradeoffs in selecting a resistance value
We can use the two formulas from above to determine the the tradeoff in selecting a resistance value for the pull-up resistor \(R\) and the effect on \(V_{pin}\) and power dissipation. Given that we are using a 5V microcontroller, we can set \(V_{in}=5V\).
\[V_{pin} = V_{in} - I_{IH} * R = 5V - 0.000001A * R \\ P = \frac{V_{in}^2}{R} = \frac{25}{R}\]Below, we are graphing these two equations together for varying values of the pull-up resistor \(R\). For convenience, we’ve marked the ATmega328 HIGH
threshold for \(V_{pin}\) and the 10kΩ \(R\) value.
Figure. Graphing the input voltage \(V_{pin}\) and the power dissipation \(P\) as a function of pull-up resistor value. Both graphs are the same but differ in the x-axis: the right graph zooms in so that you can better see the 10kΩ pull-up value.
When the switch is closed (button is pressed), with a pull-up resistor of \(R=100Ω\) (an unnecessarily strong pull-up), we are drawing 50mA (and consuming 250 milliwatts of power)—a non-trivial amount for a battery-powered circuit (e.g., for a mobile or wearable). In contrast, with a 10kΩ pull-up—the recommended value for the ATmega328 microcontroller—we would draw a more reasonable \(I=0.5mA\) and consume 2.5mW of power.
OTHER FACTORS
Though beyond the scope of this class, there are other factors to consider as well—for example, including line capacitance and capacitive coupling. For the former, the input line will have some “stray capacitance” to ground, which creates an “RC circuit” that has associated rise and fall times. Larger resistors can slow down the responsiveness of the circuit. But these factors are beyond the scope of our class. See these forum posts for more details: AVR Freaks and (EE StackExchange).
Tradeoffs in selecting a pull-down resistor
What about pull-down resistors? The same tradeoffs and factors apply here. But instead of \(I_{IH}\), we need to know the leakage current \(I_{IL}\) from an input pin to ground. The ATmega328 datasheet specifies the same leakage current for \(I_{IH}\) and \(I_{IL}\) as \(1µA\).
The above sub-sections were strongly informed by Section 12.6.9 entitled “Pullup and Pulldown Resistors” of Scherz and Monk’s Practical Electronics for Inventors.
Let’s make stuff!
Whew, OK. We’ve now explained how to use four-legged tactile buttons, how to use pull-down, pull-up, and internal pull-up resistors and their purpose, and provided a general overview of digital input.
It’s time to make stuff. We’re going to start with a button in a pull-down configuration before making circuits with external pull-up and internal pull-up configurations. Then, in the next lesson, we’ll make a simple “piano” synthesizer that puts our skills to the test!
Figure: In Tinkercad, we created interactive demos of external pull-down resistors, external pull-up resistors, and internal pull-up resistors. For simplicity, we built these without breadboards to allow you to really focus on the circuit and wiring. Click on the links to try them yourself!
Pull-down resistor configuration
Let’s begin with a pull-down resistor configuration.
Pull-down resistor wiring diagram
As with any circuit, there are many ways to wire up a button with a pull-down resistor configuration. Here are some examples—all are functionally equivalent. I tend to use the wiring shown on the far left, which is the same one shown above.
To zoom in on this image, right-click and select ‘Open image in a new tab.’
Code to turn on LED with button press
Tinkercad version with no breadboard
As some of you may still find breadboards a bit confusing, here’s a pull-down resistor version without a breadboard made in Tinkercad. We’ve also hooked up an external LED with a current limiting resistor to Pin 13 (LED_BUILTIN
on the Uno and Leonardo).
You can try this out on Tinkercad.
Pull-up resistor configuration
Here’s the wiring for a pull-up resistor configuration. Modify the code above to turn on the LED when the button is pressed.
Tinkercad external pull-up resistor example
Here’s the interactive external pull-up resistor Tinkercad version (sans breadboard).
Internal pull-up resistor configuration
Now try it with the internal pull-up resistor.
The schematic is for illustrative purposes. The internal software controlled switch is a transistor so the actual circuit looks something like this (from Lee, Input/output pins on Arduino, Stanford ENGR40M)
Tinkercad internal pull-up resistor example
Here’s the interactive internal pull-up resistor Tinkercad version.
Putting it all together
For your prototyping journals, make a circuit that has three buttons wired to digital input and three corresponding (external) LEDs wired to digital output. Use a different pull-down or pull-up configuration for each button. You must use your breadboards. Then write code to respond accordingly.
To get you started: initial version in Tinkercad
To help you get started, here’s an initial version in Tinkercad (but without a breadboard) and it controls only one LED rather than three separate LEDs.
Figure. An example Tinkercad circuit+code that shows how to wire up and use an external pull-down resistor, external pull-up resistor, and internal pull-up resistor. When you click on any of the three buttons, the LED turns on.
More resources
Still feeling confused or want to learn more about pull-up and pull-down resistors? Try watching this video by AddOhms:
Video by AddOhms demonstrating floating pins and why pull-up resistors are necessary for digital input with microcontrollers.
Or this video by NYU ITP’s Jeff Feddersen:
Video by NYU ITP’s Jeff Feddersen on pull-up and pull-down resistors.
See also:
- Sparkfun’s tutorial on Pull-up Resistors
- Arduino’s official
INPUT_PULLUP
tutorial - Electronics Tutorials’ Pull-up Resistors
Next Lesson
In the next lesson, we’ll get to apply our newfound digital input skills to build a simple interactive piano with tactile buttons and a piezo buzzer.