You write the code, everything compiles, the button should do something — but nothing happens. Or worse: the button triggers on its own without being touched. Variables jump randomly between 0 and 1. The code seems wrong, but the code isn't the problem.

A floating input is the problem. And you fix it with two cents' worth of resistors.

What is a floating input?

A digital input reads high or low. But if it isn't firmly connected to anything — not to supply voltage, not to ground — it floats. It randomly picks up interference from the environment: electromagnetic noise, capacitive coupling with other signals, even your hand getting close. The result is unpredictable behaviour that has nothing to do with your code.

Pull-up: default high, goes low on action

A pull-up resistor connects the input through a resistor to the supply voltage. Without any action, the input reads high. Press a button that pulls the input to ground and the signal goes low.

This is the most common circuit for buttons on microcontrollers. The logic is inverted — pressed is LOW, released is HIGH — but you get used to it quickly and it's the standard pattern in Arduino code with INPUT_PULLUP.

Pull-down: default low, goes high on action

A pull-down resistor connects the input through a resistor to ground. Without any action, the input reads low. Connect the input to the supply voltage — via a button or sensor — and the signal goes high. The logic is straightforward: active is HIGH.

Quiet desk with open notebook showing pull-up circuit and breadboard with pushbutton

Choosing a value

10 kΩ is a common starting point for a simple pushbutton, but it is not a universal value. Check the input datasheet and consider leakage current, valid HIGH and LOW thresholds, power use, wiring, electrical noise and the required transition speed. A lower resistance gives a stronger pull but draws more current when the signal is active. A higher resistance draws less current but can be more sensitive to leakage and interference.

I2C requires pull-ups on SDA and SCL. Do not choose them from bus speed alone: logic voltage, total bus capacitance, permitted rise time, device sink current and pull-ups already fitted to modules together determine the useful range. Follow the I2C specification and the datasheets of the devices on the bus.

Do I need a pull-up, and is one already fitted to my module?

Use a pull-up or pull-down when a digital input could otherwise float during normal operation, start-up or reset. If another output always drives the input both HIGH and LOW, an extra pull is not automatically required; follow the datasheet and check the start-up state.

On I2C, SDA and SCL must be able to return HIGH through pull-ups. Some breakout modules already include them and others do not. Pull-ups on multiple modules are connected in parallel, which can lower the total resistance. Check the schematic or measure the module, include all fitted pull-ups, then choose any external value from supply voltage, sink current, bus capacitance and permitted rise time. 4.7 kΩ and 10 kΩ are starting points, not universal answers.

Internal pull-ups

Many microcontrollers can enable an internal pull-up. Whether it is suitable depends on the exact board, pin, reset/start-up state, wiring and application. Check its value and behaviour in the datasheet. I2C, longer wiring or a noisy environment may require a calculated external pull-up.

Check what drives the input

A digital input needs a valid level whenever nothing else actively drives it. That may come from an internal or external pull-up/pull-down, but sometimes from an active logic output or a dedicated bus-hold feature. Do not add 10 kΩ everywhere by default: check the function, datasheet, wiring and the state during start-up and reset.

View resistors, then check value, package, tolerance and power rating for your circuit.