Skip to content
Study Hall
Lesson 2 of 4Computer Interfacing
Unit 6 · Lesson 2 of 4

Digital Input and Output

Switching things on and off, reading a button reliably, and why a pin needs a pull-up or pull-down resistor.

4 min read9 practice questionsAsk AI about this →

Key things to remember

  1. 1An unconnected input floats and reads random values — every button needs a pull-up or pull-down resistor.
  2. 2Pull-down: resistor to ground, button to 5 V, resting LOW. Pull-up: resistor to 5 V, button to ground, resting HIGH.
  3. 3INPUT_PULLUP uses the chip's internal resistor, so pressed reads LOW.
  4. 410 kΩ is the standard pull resistor value: defines the level firmly, wastes only 0.5 mA.
  5. 5Switch bounce produces multiple transitions from one press — debounce in software or hardware.
  6. 6delay() blocks everything; millis() lets you time without blocking.

Digital I/O is the simplest thing a microcontroller does and the foundation of everything else. An output pin is either at 5 V or at 0 V. An input pin reports whether the voltage on it is nearer 5 V or nearer 0 V. Both directions have one non-obvious trap, and both traps catch nearly every beginner.

Arduino CWhat it does
pinMode(pin, OUTPUT);Configure the pin to drive a voltage
digitalWrite(pin, HIGH);Drive the pin to 5 V
digitalWrite(pin, LOW);Drive the pin to 0 V
pinMode(pin, INPUT);Configure the pin to read — needs an external pull resistor
pinMode(pin, INPUT_PULLUP);Read, using the chip's built-in pull-up resistor
int state = digitalRead(pin);Returns HIGH (1) or LOW (0)

The floating input problem

Connect a button between a pin and 5 V, and press it: the pin reads HIGH, exactly as expected. Release it, and the pin is connected to nothing at all. A microcontroller input is extremely high impedance, so with nothing driving it the pin picks up stray electromagnetic noise from the mains, from your hand, from the wiring — and reports HIGH and LOW essentially at random. This is a floating input, and it is why a button circuit needs a third component.

An input pin with nothing connected floats — it reads random noise. A resistor fixes it.PULL-DOWN — rests LOW, pressed = HIGH5 Vbuttonto pin10 kΩPULL-UP — rests HIGH, pressed = LOW5 V10 kΩto pinpinMode(2, INPUT_PULLUP) uses the chip's built-in resistor, so pressed reads LOW — invert your test.
Fig 1A resistor gives the pin a defined resting state.
APull-down resistor
  • 10 kΩ from the pin to GROUND
  • Button connects the pin to 5 V
  • Resting state: LOW. Pressed: HIGH
  • Reads the way you intuitively expect
BPull-up resistor
  • 10 kΩ from the pin to 5 V
  • Button connects the pin to GROUND
  • Resting state: HIGH. Pressed: LOW
  • Available inside the chip — no external part needed
Use INPUT_PULLUP
pinMode(2, INPUT_PULLUP); switches on a resistor built into the chip, so you wire the button between the pin and ground and need no external components at all. The price is inverted logic: pressed reads LOW. Write your test as if (digitalRead(2) == LOW) and remember why — this catches people every single time.

Why 10 kΩ specifically? It has to be small enough that the stray currents in a high-impedance input cannot pull the pin away from its resting level, and large enough that when the button is pressed the current through it is negligible. At 5 V across 10 kΩ the current is 0.5 mA — firm enough to define the level, small enough to ignore. A 100 Ω resistor would work electrically but would waste 50 mA whenever the button is held.

Switch bounce

A mechanical switch does not close cleanly. Its contacts physically bounce apart and together for a few milliseconds, so a single press produces a burst of HIGHs and LOWs. A human sees one press; a microcontroller sampling millions of times per second sees five or ten. A counter incremented on every press jumps by seven, and a toggle ends up in a random state.

Arduino CWhat it does
if (digitalRead(BTN) == LOW) {Button is pressed (with INPUT_PULLUP)
delay(50);Wait past the bouncing window
if (digitalRead(BTN) == LOW) {Still pressed — so it was real, not a bounce
count = count + 1;Act on the press
while (digitalRead(BTN) == LOW) { }Wait for release, so holding it does not count repeatedly
}
}
The simplest reliable debounce: confirm after a delay, then wait for release.
delay() stops everything
delay(1000) blocks the processor completely — no button is read, no sensor is checked, nothing else happens for that whole second. It is fine in a simple sketch and fatal in anything that must stay responsive. The alternative is millis(), which returns the milliseconds since power-up so you can check whether enough time has passed without ever stopping.
Arduino CWhat it does
unsigned long previous = 0;Remember when the last action happened
const long interval = 1000;How long to wait between actions
if (millis() - previous >= interval) {Has enough time passed? Never blocks
previous = millis();Reset the timer for the next interval
// do the timed thing hereMeanwhile loop() keeps running and reading inputs
}
Non-blocking timing — this pattern is worth memorizing.
Sourcing and sinking
A pin can source current (drive HIGH and push current out through an LED to ground) or sink it (sit LOW while current flows in from 5 V through the LED). Both work and both are limited to 40 mA. Sinking inverts the logic — digitalWrite(pin, LOW) turns the LED on — which surprises people reading the code later.

Worked examples

Reveal one step at a time and try to beat it to the next line.

Example 1
Write a sketch that toggles an LED on pin 9 each time a button on pin 2 is pressed, using the internal pull-up and debouncing correctly.
Example 2
A counter increments by about 6 each time a button is pressed once. Diagnose the cause and give two fixes.
Example 3
A sketch must blink an LED every second while still responding instantly to a button. Explain why delay(1000) fails and write the alternative.

Where marks get lost

  • Wiring a button with no pull resistor, producing an input that reads randomly.
  • Forgetting that INPUT_PULLUP inverts the logic — pressed reads LOW, not HIGH.
  • Not debouncing, so one press registers as several.
  • Using delay() in a sketch that must stay responsive to inputs.
  • Connecting a button between 5 V and ground with no resistor at all — a dead short when pressed.
  • Forgetting pinMode() in setup, leaving the pin in its default input state so digitalWrite seems to do nothing.

Flashcards 9

What is a floating input?
Pull-down resistor arrangement
Pull-up resistor arrangement
What does INPUT_PULLUP do?
+ 5 more

Practice

1
A button is wired between a pin and 5 V with no other components. What does the pin read when the button is NOT pressed?
2
With pinMode(2, INPUT_PULLUP);, what does digitalRead(2) return while the button is pressed?
3
What is the standard resistance value for a pull-up or pull-down resistor?
4
A counter increments several times from one button press. What is happening?
5
Why is millis() preferred over delay() in a sketch that reads a sensor continuously?
6
How much current flows through a 10 kΩ pull-down resistor when the pin is pulled to 5 V? Answer in milliamps.
mA
7
Order the steps of a software debounce routine.
  1. 1Wait about 50 ms
  2. 2Act once, then wait for the button to be released
  3. 3Re-read the pin to confirm it is still LOW
  4. 4Detect the pin reading LOW
8
What happens if you wire a button directly between 5 V and GND with no resistor?
9
Match each function to what it does.
pinMode(p, OUTPUT)
digitalWrite(p, HIGH)
digitalRead(p)
millis()

Spot a mistake on this page? Let us know