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
- Exercises
- More resources
- Lesson summary
- 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 every day 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!
In this lesson, you will learn:
- How momentary switches (buttons) work, including four-legged tactile buttons
- What the “floating pin” problem is and why it occurs
- How pull-down, pull-up, and internal pull-up resistors solve the floating pin problem
- How to use
digitalRead()andpinMode()for digital input- How to select appropriate pull-up/pull-down resistor values
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 looks like this:
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 anINPUTorOUTPUT. For our buttons, we’ll be usingINPUT—because a button is an input and not an output—and a variant ofINPUTcalledINPUT_PULLUP.digitalRead(int pin)reads digital input from the specified pin, eitherHIGHorLOW. 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, eitherHIGHorLOW. 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 set up 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}\) (that is, \(0.6 \times 5 = 3\)V) to qualify as HIGH and at most \(0.3 \cdot V_{CC}\) (that is, \(0.3 \times 5 = 1.5\)V) to qualify as LOW. The middle range (1.5V to 3V) 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. Because the input pin has extremely high impedance, it effectively acts like a tiny antenna—even minuscule charge transfers from nearby electric fields, your body’s capacitance, or crosstalk between wires can swing the voltage. A floating pin is susceptible to picking up this stray electromagnetic interference, causing it to randomly oscillate 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?
const int INPUT_BUTTON_PIN = 2;
void setup()
{
pinMode(INPUT_BUTTON_PIN, INPUT);
Serial.begin(9600); // for printing values to console
}
void loop()
{
int buttonVal = digitalRead(INPUT_BUTTON_PIN); // returns 0 (LOW) or 1 (HIGH)
Serial.println(buttonVal); // print value to Serial
delay(5); // small delay
}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 qualify 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), a tiny leakage current \(I_{IH}\) flows into the input pin—just 1µA, as specified in the ATmega328 datasheet (Section 26.2). Using Ohm’s Law, we can calculate the voltage on the input pin: \(V_{pin} = V_{in} - I_{IH} \cdot 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 must be at least \(0.6 \cdot V_{CC}\) (3V) to qualify as HIGH. So we must ensure our choice of \(R\) doesn’t drop \(V_{pin}\) below this threshold. What’s the theoretical maximum pull-up resistor value? Substituting \(V_{pin} = 3\) (the minimum for HIGH), \(V_{in} = 5\), and \(I_{IH} = 0.000001\) (all in volts and amps):
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}\). Because power is inversely proportional to resistance, 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 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. Substituting:
\[V_{pin} = V_{in} - (I_{IH} \cdot R) = 5 - (0.000001 \cdot 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. Beyond the scope of this class, there are additional factors to consider, such as line capacitance and capacitive coupling. The input line will have some “stray capacitance” to ground, which creates an RC circuit with associated rise and fall times. Larger resistors can slow down the responsiveness of the circuit. See these forum posts for 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
const int INPUT_BUTTON_PIN = 2;
const int OUTPUT_LED_PIN = LED_BUILTIN;
void setup()
{
pinMode(INPUT_BUTTON_PIN, INPUT);
pinMode(OUTPUT_LED_PIN, OUTPUT);
}
void loop()
{
// read the state of the pushbutton value
int buttonState = digitalRead(INPUT_BUTTON_PIN);
digitalWrite(OUTPUT_LED_PIN, buttonState);
delay(30);
}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.
Try it yourself: With a pull-up resistor, the button reads HIGH when not pressed and LOW when pressed—the opposite of pull-down! How would you modify the code to turn the LED on when the button is pressed? (Hint: you only need to change one line!)

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.

Exercises
These exercises will help solidify your understanding of digital input and pull-up/pull-down resistors.
Exercise 1: Three-button challenge
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.
Exercise 2: Toggle behavior
Can you modify your code so that the LED toggles on and off with each button press (rather than only being on while the button is held down)? Think about what state you need to track. You’ll likely run into a “contact bouncing” issue—we’ll cover that in Lesson 3!
Exercise 3: Button-controlled blink rate
Use two buttons to control the blink rate of an LED: one button increases the rate, the other decreases it. Use the millis()-based approach from Multi-rate Blinking to avoid delay().
Exercise 4: Serial detective
Wire up three buttons — one with a pull-down resistor, one with an external pull-up, and one with INPUT_PULLUP. Use Serial.println to print the digitalRead value of each button on a single line (separated by commas or tabs). Open the Serial Monitor and press each button one at a time. Which buttons read HIGH when pressed? Which read LOW? Can you explain why? This exercise builds your Serial debugging skills, which you’ll rely on throughout the course.
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_PULLUPtutorial - Electronics Tutorials’ Pull-up Resistors
Lesson summary
In this lesson, you learned how to use buttons for digital input with Arduino. The key concepts were:
- Buttons (momentary switches) are either open or closed. Four-legged tactile buttons have two internally-connected pairs that join when pressed.
- Digital input pins on the Arduino read voltage, not current. Pins configured as
INPUThave extremely high impedance (~100MΩ), so virtually no current flows into them. - A floating pin occurs when an input pin is not connected to a defined voltage level, causing it to oscillate unpredictably between
HIGHandLOW. - Pull-down resistors bias the input pin to
LOW(0V) when the switch is open; the pin readsHIGHwhen pressed. - Pull-up resistors bias the input pin to
HIGH(\(V_{CC}\)) when the switch is open; the pin readsLOWwhen pressed. - The Arduino supports internal pull-up resistors via
pinMode(pin, INPUT_PULLUP), which simplifies your circuit by eliminating the need for an external resistor. - A 10kΩ resistor is the standard recommendation for external pull-up or pull-down 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.






