If you have wired a button to an Arduino or an ESP32 and the input turned on and off by itself, the code was probably fine. The pin was floating.
A pull-up resistor fixes that. It ties the pin to the supply through a resistor, so the pin sits at a known HIGH while the button is open. Press the button and the pin connects straight to ground, which wins, and the pin reads LOW.
What is a pull-up resistor?
A pull-up resistor is a resistor between a signal line and the positive supply, either 3.3 V or 5 V. It holds the line HIGH whenever nothing else is driving it.
Without it, an input pin with an open switch on it is connected to neither supply nor ground, so its voltage is undefined. The board still reports HIGH or LOW, but the answer comes from noise rather than from your circuit.
Why a resistor and not a wire? A wire to the supply would also hold the pin HIGH, but pressing the button would then short the supply to ground. A 10 kΩ resistor limits that current to half a milliamp, so the button can safely overrule it.
What is a floating pin?
A floating pin has no voltage reference, so it responds to anything nearby: mains hum, a hand near the breadboard, a motor starting elsewhere in the project. In code it looks like a fault in your logic. What you see is:
- An LED that turns itself on and off.
- A button press registering when nobody touched the button.
- A counter that climbs on its own.
- Serial output flipping between 0 and 1 with the circuit sitting still.
If your project does any of that, check the input wiring before you touch the code.
How does a pull-up resistor work?
- Button open. A small current runs from the supply through the resistor into the pin. An input pin draws almost nothing, so there is almost no voltage drop across the resistor and the pin sits at nearly the supply voltage. The board reads HIGH.
- Button closed. The pin is wired directly to ground through the switch contacts. That path has near zero resistance, the 10 kΩ resistor cannot compete, and the pin is dragged to 0 V. The board reads LOW.
So the logic reads backwards, which catches everyone out the first time. Not pressed is HIGH, pressed is LOW, and your code tests for LOW to detect a press.
Pull-up resistor vs pull-down resistor
| Resistor | Wired between | Not pressed | Pressed | Typical use |
|---|---|---|---|---|
| Pull-up | Pin and the positive supply, with the switch to ground | HIGH | LOW | Buttons, micro switches, I2C, anything using a built-in pull-up |
| Pull-down | Pin and ground, with the switch to the positive supply | LOW | HIGH | Circuits where a press should read HIGH, some sensor and logic inputs |
Neither is better. Pull-ups are more common because microcontrollers have them built in and pull-downs have to be added by hand.
Pull-up resistor with Arduino
Arduino boards have a pull-up inside the chip on every digital pin, 20 kΩ to 50 kΩ on an ATmega328, so most button projects need no resistor on the breadboard. Enable it with INPUT_PULLUP instead of INPUT, and wire the button between the input pin and GND with nothing else.
const int buttonPin = 2;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {
if (digitalRead(buttonPin) == LOW) {
Serial.println("Button pressed");
} else {
Serial.println("Button not pressed");
}
delay(200);
}
Two mistakes cause most button problems. Leaving the pin on plain INPUT is the floating case above. Wiring the button to the supply instead of to ground leaves the pin HIGH in both states. If you want the rails explained hole by hole, see our guide on what a breadboard is and how to use it.
Pull-up resistor with ESP32
ESP32 chips have internal pull-ups too, around 45 kΩ, enabled with the same INPUT_PULLUP. Two things differ from an Uno. The logic is 3.3 V, so feeding a 5 V rail into a GPIO pin damages it. And pin choice matters: some GPIOs are strapping pins that decide how the chip boots, some are input only with no internal pull-up, and on the classic ESP32 the pins on the second ADC stop working once Wi-Fi runs.
The reason to use an ESP32 for a switch is what happens after the press, when a contact becomes a Home Assistant entity over Wi-Fi. Our ESPHome and MQTT guide covers the software, and ESP32 projects for beginners has working examples.
On battery, note that an internal pull-up with the switch closed draws current continuously, around 70 µA at 3.3 V through 45 kΩ. That is nothing beside an active radio and a lot beside deep sleep, so wire the switch to idle open.
Pull-up resistor with a button or micro switch
To a microcontroller a push button and a micro switch are the same part: a pair of contacts that either touch or do not. Neither generates a voltage, which is why both need a pull-up or a pull-down to be readable at all.
A micro switch gives you a choice of contact. Wire COM to GND and NO to the input pin and it behaves like a push button, reading LOW while the lever is pressed. Move that wire from NO to NC and the states swap, which is what a door sensor wants. Our guide to what a micro switch is covers the terminals in detail.
What a pull-up does not fix is contact bounce. The contacts chatter for a few milliseconds as they close and a fast loop reads that as several presses, so debounce it in code by ignoring further changes for 20 ms to 50 ms.
Pull-up resistors and I2C
Devices on an I2C bus never drive SDA or SCL high. They pull a line to ground or let go of it, which is what makes it safe for a dozen chips to share two wires. The pull-ups are the only thing returning each line to HIGH, so they are part of the signalling rather than a precaution. Values are 4.7 kΩ or 2.2 kΩ, lower than a button pull-up because the bus has to charge the cable capacitance quickly.
Almost every breakout has its pull-ups fitted, so a first project works with nothing added. The trouble starts at the fourth or fifth module, because those resistors sit in parallel: five 4.7 kΩ pull-ups behave like one 940 Ω resistor, and eventually no device can pull a line low enough to register. Boards that expect this put the pull-ups on solder jumpers so you can cut them. Our guide on how I2C works covers the addressing side.
Internal vs external pull-up resistors
| Aspect | Internal pull-up | External pull-up |
|---|---|---|
| Value | Fixed by the chip, 20 kΩ to 50 kΩ | Whatever you fit |
| Wiring | None, one line of code | One resistor per input |
| Active | Only after pinMode() runs |
The moment power is applied |
| Suits | Buttons and switches on short wires | Long cables, noise, I2C, anything defined at boot |
The timing row is the one that bites. An internal pull-up is off while the chip boots, so the pin floats for those first milliseconds. Fine for a button, not for a reset or enable line that has to be HIGH from the instant power arrives.
Cable length is the other reason to go external. A metre of wire behaves like an antenna, and a 47 kΩ internal pull-up is weak enough that the noise it picks up can register as presses. Past a few metres on I2C the cable capacitance itself becomes the limit, and a bus extender fixes that properly.
What value should a pull-up resistor be?
For a button, 10 kΩ. Too high and the resistor cannot hold the line against noise and leakage. Too low and you waste current whenever the switch closes: at 5 V a 10 kΩ pull-up costs 0.5 mA while the button is held, a 220 Ω one would cost 23 mA.
- Buttons and switches: 10 kΩ, or the internal pull-up and no resistor at all.
- I2C, one to three modules: 4.7 kΩ, usually already on the breakout.
- I2C at 400 kHz or on a long cable: 2.2 kΩ.
- A switch on a metre or more of cable: 4.7 kΩ external, in place of the internal one.
Beginner project ideas using pull-up resistors
1. Button-controlled LED
One button, one LED and INPUT_PULLUP. The shortest circuit in which you can watch the reversed logic happen: the serial monitor prints HIGH until your finger lands.
2. Micro switch door detector
Mount a micro switch so a drawer or lid presses the lever when it closes, COM to GND and NO to the input. Add a buzzer and it is an alarm; log the changes and it is a usage counter.
3. ESP32 mailbox notifier
The same switch on a battery-powered ESP32. It sleeps until the contact changes, sends one message and sleeps again, which is where pull-up current actually matters.
4. Physical Home Assistant button
A button on an ESP32 running ESPHome becomes a switch entity for any automation. Two wires and no resistor.
5. I2C sensor display
A temperature sensor and a 16x2 I2C LCD on the same two pins. Nothing here needs a resistor from you, which is the clearest way to see that the breakouts already carry the pull-ups.
Why Soldered is a good place to start learning electronics basics
A pull-up resistor teaches the rule that most beginner faults break: every input has to be connected to something definite. Once that lands, buttons, micro switches, sensor inputs, reset lines and I2C buses stop failing mysteriously, and you check the wiring instead of rewriting the loop.
Soldered sells the boards, kits, breakouts and displays for building that up in order, from a button and an LED on a breadboard to a battery-powered sensor reporting to Home Assistant. Every breakout has a Qwiic connector and a maintained Arduino library.
Frequently asked questions
What is a pull-up resistor?
A resistor between a signal line and the positive supply. It holds an input pin HIGH when nothing else is driving it.
Why do buttons need pull-up resistors?
An unpressed button leaves the pin connected to nothing, so it reads noise and flips between HIGH and LOW on its own.
What is a floating pin?
An input pin with no definite connection to HIGH or LOW. With no voltage reference it responds to electrical noise and the readings are random.
How does a pull-up resistor work?
With the switch open, a tiny current through the resistor holds the pin at the supply voltage: HIGH. With the switch closed, the pin is wired straight to ground, which overrules the resistor: LOW.
What is the difference between a pull-up and a pull-down resistor?
A pull-up goes to the positive supply and idles the pin HIGH. A pull-down goes to ground and idles it LOW. Both stop the pin floating.
Does Arduino have internal pull-up resistors?
Yes, on every digital pin. Enable one with pinMode(pin, INPUT_PULLUP). On an ATmega328 it is 20 kΩ to 50 kΩ.
Does ESP32 have internal pull-up resistors?
Yes, around 45 kΩ on most GPIO pins. Input-only pins have none, and some pins affect boot, so check your board pinout.
Why does my button read LOW when pressed?
That is correct for pull-up wiring. Test for LOW to detect a press.
What resistor value should I use for a pull-up resistor?
10 kΩ for buttons and switches, 4.7 kΩ for I2C. For a simple button, the internal pull-up removes the need for one.
Do I2C devices need pull-up resistors?
Yes. Devices only ever pull SDA and SCL low, so without pull-ups nothing returns them HIGH. Most breakouts have them fitted.
Can I use a pull-up resistor with a micro switch?
Yes. Wire COM to GND and NO to the input pin, and it reads LOW while the lever is pressed. Use NC to invert it.
Should beginners use internal or external pull-up resistors?
Start with internal. Go external when the wire to the switch gets long, the environment is noisy, or the line must be HIGH before your code runs.
Can a pull-up resistor stop a button from double-triggering?
No. That is contact bounce, and it needs debouncing in code or a small capacitor.