Learn

Arduino digitalRead(), on a button and an LED

Start building free

Or see every lesson.

  • 455parts on the bench
  • 43boards running now
  • 1.00xreal time, on every board
Arduino Uno: a button on pin 2 lights the LED on pin 13live0.000 s 0.00x
Press Run, then hold the button
INPUT_PULLUP holds pin 2 at 5 V; the button pulls it to ground, and digitalRead sees LOW.

digitalRead(pin) looks at the voltage on a pin and returns HIGH (1) or LOW (0). Set the pin up first with pinMode(pin, INPUT_PULLUP) and wire a button from the pin to ground, and it reads HIGH until the button is pressed and LOW while it is held. That is the whole of reading a button.

The circuit above is exactly that: a button on pin 2 and an LED on pin 13 of an Uno. Press Run, then hold the button. Pin 2 sits at 5 V until the button pulls it down to 0 V, digitalRead() sees LOW, and the LED lights. Let go and it goes out.

Below is why the pull-up matters, what a pin with nothing on it reads, the voltage where LOW turns into HIGH, and why one press can be read thousands of times, each tried on an Uno in Mokxi with what it printed.

Want the step-by-step version? The lesson "Reading a pin, and the pull-up" walks through this with checkpoints.

Open the lesson

The sketch: pinMode, then digitalRead

Here is the sketch running above. pinMode(BUTTON_PIN, INPUT_PULLUP) makes pin 2 an input and switches on a resistor inside the chip that pulls it up to 5 V. In loop(), digitalRead(BUTTON_PIN) == LOW is true while the button is held, and the LED follows it.

That is why pressed reads LOW, which surprises everyone the first time. The button connects the pin to ground; the pull-up is what makes it read HIGH the rest of the time. If you would rather think in "pressed is true", write bool pressed = digitalRead(2) == LOW; once and use pressed from then on.

firmware/uno/examples/button (the sketch running above)
const int BUTTON_PIN = 2;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  bool pressed = digitalRead(BUTTON_PIN) == LOW;
  digitalWrite(LED_BUILTIN, pressed ? HIGH : LOW);
  delay(5);
}

A pin with nothing on it reads noise

With plain pinMode(2, INPUT) and nothing pulling the pin either way, it floats, and whatever stray charge is nearby decides what it reads. Twenty reads, ten milliseconds apart, printed 00111111010100110100. A real board does the same, and a button wired with no resistor at all behaves like this whenever it is not pressed: random presses that nobody made.

Switch the pull-up on and the same twenty reads printed 11111111111111111111. Use INPUT_PULLUP with the button to ground, as above, or INPUT with your own resistor, 10 k from the pin to 5 V or to ground. The pull-up resistor page draws both.

Floating, then pulled up
void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT);
  for (int i = 0; i < 20; i++) {
    Serial.print(digitalRead(2));
    delay(10);
  }
  Serial.println();
  pinMode(2, INPUT_PULLUP);
  for (int i = 0; i < 20; i++) {
    Serial.print(digitalRead(2));
    delay(10);
  }
  Serial.println();
}

void loop() {
}

What counts as HIGH

A digital pin answers yes or no, but the voltage on it can be anything from 0 to 5 V. The ATmega328P datasheet promises LOW below 1.5 V and HIGH above 3 V on a 5 V Uno, and leaves the space between to the chip. We put a potentiometer on pin 2 and A0 and turned it slowly down and back up. Going down, the pin turned LOW at 2184 mV; coming back up it only turned HIGH again at 2619 mV.

That gap is on purpose. A pin that switched at one exact voltage would chatter back and forth on a slowly changing signal; with a gap, it changes its mind once. It also tells you that a 3.3 V sensor’s HIGH, well above 3 V, reads as HIGH on a 5 V Uno, and that a weak 2 V signal is a coin toss. For anything in between, read it with analogRead() instead.

Where the pin changes its mind
void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT);
}

void loop() {
  static int last = -1;
  int v = digitalRead(2);
  if (v != last) {
    last = v;
    long mv = analogRead(A0) * 5000L / 1023;
    Serial.print(mv);
    Serial.print(" mV -> ");
    Serial.println(v);
  }
  delay(2);
}

One press, read thousands of times

loop() runs far faster than a finger. We held the button for half a second and counted: about 47,800 passes of loop() saw it LOW. A sketch that adds to a score whenever the pin is LOW adds about 47,800 points for one press. To count presses, remember the last reading and act only when it changes from HIGH to LOW.

Even that counted 4 presses for our one. A pushbutton’s contacts bounce for a few milliseconds when they close and open, and Mokxi’s button bounces the way a real one does, so the sketch saw the pin go LOW four separate times. The button debounce page shows how to wait out the bounce with millis().

Levels against presses
const int BUTTON = 2;
long held = 0;
int presses = 0;
int last = HIGH;

void setup() {
  Serial.begin(9600);
  pinMode(BUTTON, INPUT_PULLUP);
}

void loop() {
  int now = digitalRead(BUTTON);
  if (now == LOW) held++;
  if (now == LOW && last == HIGH) presses++;
  last = now;
  if (millis() >= 1500) {
    Serial.print("loops that saw LOW: ");
    Serial.println(held);
    Serial.print("presses: ");
    Serial.println(presses);
    while (true) {
    }
  }
}

Output pins, analog pins and one wrong =

digitalRead() works on any pin. On a pin set to OUTPUT, it reads back what you last wrote: after digitalWrite(13, HIGH) it printed 1, after LOW it printed 0. That is a quick way to toggle a pin, digitalWrite(13, !digitalRead(13));. The analog pins of an Uno are digital pins as well: A0 tied to 5 V printed 1 and A1 tied to ground printed 0.

HIGH and LOW are just 1 and 0, and Serial.println(digitalRead(2)) prints the number. Compare with two equals signs. if (digitalRead(2) = HIGH) stops the build: Mokxi’s clang says expression is not assignable, and the Arduino IDE’s GCC says lvalue required as left operand of assignment.

Questions

Why does digitalRead return LOW when the button is pressed?

Because the button connects the pin to ground, and INPUT_PULLUP holds it at 5 V the rest of the time. Pressed is LOW, released is HIGH. Compare with LOW, or write bool pressed = digitalRead(pin) == LOW;.

Why does my digitalRead change randomly?

The pin is floating: nothing holds it HIGH or LOW. Use pinMode(pin, INPUT_PULLUP) with the button wired to ground, or add a 10 k resistor from the pin to 5 V or ground.

Can I use digitalRead on an analog pin?

Yes. On an Uno, A0 to A5 are digital pins as well, and digitalRead(A0) returns HIGH or LOW. (On a Nano, A6 and A7 are analog only.)

What voltage does digitalRead see as HIGH?

On a 5 V Uno, anything above 3 V is guaranteed HIGH and anything below 1.5 V is guaranteed LOW. In between, the pin keeps its last answer for a while: in our test it fell to LOW at about 2.2 V and rose to HIGH again at about 2.6 V.

Do I need pinMode before digitalRead?

Pins start as inputs, so digitalRead works without it, but the pin floats. Call pinMode(pin, INPUT_PULLUP) or pinMode(pin, INPUT) in setup() so it is clear what the pin is for and how it is held.

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.