Solution


Atekon Technology Co., Ltd.

Digital Input Module Interfacing with Arduino


Release date:

2026-10-02

Image Source: statics.mylandingpages.co The Arduino Uno board has 14 digital I/O pins. A digital input module reads a switch. A push button supplies input. Pull-up resistor, pull-down resistor, or internal pull-up prevents false readings. The switch connects to ground. The code reads the switch, driving an LED. Another switch tests wiring. A

Digital
Image Source: statics.mylandingpages.co

The Arduino Uno board has 14 digital I/O pins. A digital input module reads a switch. A push button supplies input. Pull-up resistor, pull-down resistor, or internal pull-up prevents false readings. The switch connects to ground. The code reads the switch, driving an LED. Another switch tests wiring. A final switch checks stability. Each switch needs setup.

Key Takeaways

  • Choose a pull-up or pull-down resistor to keep Arduino input pins stable. This prevents false switch readings.
  • Enable INPUT_PULLUP in pinMode() to read buttons without extra parts. Pressed buttons read LOW, released buttons read HIGH.
  • Add software debouncing to ignore switch bounce. This makes one button press produce one clean action.

Understanding the Digital Input Module

Understanding
Image Source: unsplash

What "Digital Input" Means on Arduino

A digital input module reads one of two voltage conditions on a digital I/O pin. The Arduino Uno board interprets these voltages as logic HIGH or logic LOW. The ATmega328P microcontroller defines the threshold voltages for each logic level.

DeviceVIH (Min)VIL (Max)Reference
ATmega328P in Arduino Uno and Nano0.6VCC, 3V @ VCC = 5V0.3VCC, 1.5V @ VCC = 5VATmega328P Datasheet (Section 28.2 Electrical Characteristics)

SparkFun's Arduino logic-level tutorial states that the invalid voltage region is 1.5V to 3.0V. On a 5V Arduino Uno, voltages up to 1.5V are interpreted as logic LOW, voltages from 3.0V upward are interpreted as logic HIGH, and voltages between 1.5V and 3.0V are indeterminate. Tech Explorations, citing the ATmega328P datasheet, explains that an Arduino Uno input interprets any voltage above 0.6 × Vcc as logic HIGH. At the Uno's nominal 5V supply, this means voltages above 3.0V are read as HIGH; if Vcc is lower, the HIGH threshold scales down accordingly.

Common Push Button and Switch Types

Common Arduino-style digital input setups use momentary tactile pushbuttons, which make a click when pressed and are designed to fit across a breadboard. A single-pole double throw switch is a maintained type: it stays in the last position it was pushed into and has no internal spring, unlike a momentary switch. Micro limit or lever switches are also used and are marked with C, NO, and NC, meaning common, normally open, and normally closed, where normal refers to the button or lever not being pushed.

  • Momentary switches remain on only while they are being actuated, such as pressed or held.
  • Push-button switches are the classic momentary switch and often provide tactile clicky feedback.
  • Momentary switches are commonly used for intermittent user input such as reset or keypad buttons.
  • Maintained switches retain their state until actuated into a new one.
  • Maintained switches are useful for set-it-and-leave-it applications such as turning power on and off.
  • A wall light switch is a common example of a maintained switch.
Switch typeActuation behaviorState after releaseTypical use
MomentaryFunction is active only while the button is being pressedReturns to its original stateIntermittent input such as reset or keypad buttons
MaintainedFunction stays latched after actuationRemains in the new state until pressed againSet-and-leave applications such as power on/off

Push button switches can be classified as normally open (NO) or normally closed (NC). Normally open switches complete the circuit when actuated. Normally closed switches break the circuit when actuated. Common switching circuit configurations include SPST, SPDT, DPST, and DPDT. SPST has two terminals and can open/close a single circuit. SPDT has three terminals and can control two different circuits. DPST and DPDT allow simultaneous control of two isolated circuits.

Why Floating Pins Cause Unreliable Readings

A floating state occurs when a digital input pin connects to nothing. The pin then picks up stray electrical noise from the environment. The state of the switch becomes unpredictable. A pull-up resistor or pull-down resistor solves this problem. An external pull-up or pull-down resistor holds the pin at a known voltage. The internal pull-up inside the ATmega328P provides the same function without extra parts. Pull-up and pull-down resistors keep the input stable. A switch connects the pin to ground or to VCC. The Arduino Uno board reads a clean signal every time.

Wiring the Digital Input Module to the Arduino Uno Board

Wiring
Image Source: unsplash

A digital input module needs a complete circuit before the Arduino Uno board can read a stable signal. The wiring step determines whether the input pin sees a defined voltage or a floating one. Three wiring methods exist: an external pull-down resistor, an external pull-up resistor, and the Arduino's built-in INPUT_PULLUP mode. Each method produces a valid logic level, but each one changes the default state of the input.

Before connecting any wires, the builder should respect the electrical limits of the board. Each I/O pin handles a 40 mA absolute maximum, preferably 20 mA continuous, and the total current across all pins should stay below 200 mA. For digital inputs, the builder should enable INPUT_PULLUP and add external debouncing or filtering if the sensor signal is noisy or mechanical. Logic levels must remain compatible: on the Uno, digital inputs read LOW below 0.8 V and HIGH above 2.0 V, and voltages between these thresholds are undefined. A 3.3 V digital input module requires level shifting or an open-collector output so the Uno sees a valid logic level without over-driving the module. Noise reduction matters as well. The builder should connect grounds at a single point, keep signal wiring away from switching lines, use twisted-pair cable for long runs, and decouple power rails near devices with capacitors. The module should stay within the Uno's recommended 7–12 V supply range, use a separate supply for high-current loads, and add 100 nF and 10 µF decoupling capacitors near the power header. Pins 0 and 1 should remain free for serial communication, and interrupt-capable pins are only necessary when the application requires them.

Using an External Pull-Down Resistor

A pull-down resistor holds the input pin at ground when no switch presses it. The resistor typically has a value between 10 kΩ and 10 kΩ, a range that limits current while keeping the pin voltage near zero. The circuit diagram for this arrangement is simple and uses three connections.

  • Connect the external pull-down resistor between the digital input pin and GND.
  • Connect one side of the push button to the same digital input pin and the other side to VCC.
  • When the button is pressed, the input reads HIGH; when released, the resistor pulls the input back to LOW.

This arrangement gives the input a default LOW state. The switch connects the pin to VCC and overrides the resistor when pressed. The resistor returns the pin to ground when the switch releases. A pull-down resistor consumes a small amount of current while the switch is closed, but the value of 10 kΩ keeps that current near 0.5 mA at 5 V. The builder should place the resistor close to the input pin to reduce noise pickup.

Using an External Pull-Up Resistor

A pull-up resistor connects the signal line to its logic supply voltage, so an undriven input defaults to HIGH. The switch then connects the pin to ground when pressed. This arrangement reverses the logic of the pull-down circuit: the input reads HIGH when the switch is open and LOW when the switch is closed.

Button StatePin ConnectionVoltage at PindigitalRead() Result
OpenPull-up resistor connects pin to VCCPulled up to VCC (e.g., 5V on Arduino Uno)HIGH
ClosedSwitch connects pin directly to GND, overriding pull-up0V (GND)LOW

The table shows how the voltage at the pin changes with the switch state. When the switch is open, the resistor ensures the pin voltage is pulled up to VCC. When the switch is closed, it connects the pin directly to GND, overriding the pull-up and bringing the voltage to 0V. On the Arduino Uno, digitalRead() returns HIGH or LOW according to the microcontroller's input-voltage thresholds, not a universal 5V rule. The builder should remember that the pull-up resistor and the pull-down resistor perform the same job with opposite default states. The choice between them depends on the logic the project needs.

Using INPUT_PULLUP on the Arduino Uno Board

The Arduino Uno's microcontroller, the ATmega328P, has internal pull-up resistors for all IO pins. The builder can activate these resistors with the INPUT_PULLUP mode and avoid an external component. The pinMode() function sets the behavior of a specific digital IO pin to behave as an output pin or an input pin. It can also enable the internal pull-up resistor for input pins if the mode INPUT_PULLUP is selected. However, the mode INPUT sets the IO pin in input mode and explicitly disables the internal pull-up resistor.

To enable the Arduino's internal input pull-up resistor for a specific IO pin (let's say pin 8), you need to set the mode to be INPUT_PULLUP using the pinMode() function as shown below.

pinMode(8, INPUT_PULLUP);

And now you can safely connect your push button to pin 8 without having to connect an external pull-up resistor.

The internal pull-up resistor removes the need for an external pull-up or pull-down resistor in most projects. The builder connects one side of the switch to the input pin and the other side to ground. The pin then reads HIGH when the switch is open and LOW when the switch is closed. This behavior matches the external pull-up arrangement, so the code logic stays the same.

  • Arduino UNO's microcontroller (Atmega328p) has internal pull-up resistors for all IO pins that you can activate with the INPUT_PULLUP mode.
  • There is no INPUT_PULLDOWN mode for Arduino UNO, while it's supported by Arduino Zero.

The absence of an INPUT_PULLDOWN mode means the builder must add an external pull-down resistor when a default LOW state is required. The internal pull-up resistor also has a typical value between 20 kΩ and 50 kΩ, which is weaker than a 10 kΩ external resistor. A weaker pull-up makes the input more sensitive to noise, so long wires or noisy environments may still need an external resistor. The builder should also keep the VCC and GND pins of the module connected to the corresponding rails on the Arduino Uno board. A shared ground reference between the module and the board prevents voltage drift and false readings. With the wiring complete, the next step is to write the code that reads the switch and responds to each press.

Writing the Code to Respond to Button Presses

The wiring establishes the electrical path. The code defines how the Arduino Uno board interprets that path. A complete sketch needs three elements: pin configuration in setup(), state reading in loop(), and a response that drives an output. The builder writes these elements in sequence, and each one depends on the previous step.

Configuring Pins with pinMode()

The pinMode() function tells the microcontroller how each pin behaves. A pin configured as INPUT reads voltages from external components. A pin configured as OUTPUT drives voltages to external components. The builder calls this function inside setup(), which runs once when the board powers on or resets.

The pinMode() function accepts two arguments: the pin number and the mode. For digital input applications, three mode arguments exist. The table below lists each mode and its behavior.

Mode argumentMeaning
INPUTDigital input; default state at power-up
INPUT_PULLUPDigital input with an internal pull-up resistor to 3V3
INPUT_PULLDOWNDigital input with an internal pull-down resistor to GND

The INPUT mode leaves the pin floating when no external component drives it. The INPUT_PULLUP mode activates the internal pull-up resistor inside the ATmega328P. The INPUT_PULLDOWN mode works only on boards that support it, such as the Arduino Zero. The Arduino Uno does not support INPUT_PULLDOWN, so the builder must add an external resistor when a default LOW state is needed.

A typical setup() block configures both the button pin and the LED pin. The builder writes the LED pin as OUTPUT and the button pin as INPUT or INPUT_PULLUP.

void setup() { pinMode(ledPin, OUTPUT); // initialize the LED pin as an output pinMode(buttonPin, INPUT); // initialize the pushbutton pin as an input }

The order of configuration does not affect behavior. The builder can configure the LED first or the button first. Both pins must reach their correct mode before the loop() function begins reading and writing.

Reading the Push Button with digitalRead()

The digitalRead() function reads the voltage on a digital input pin and returns either HIGH or LOW. The function accepts one argument: the pin number. The return value depends on the voltage at the pin relative to the microcontroller's logic thresholds.

Pin StatedigitalRead() Return Value
Connected to GNDLOW (0)
Connected to VCCHIGH (1)

When a switch is not pressed with INPUT_PULLUP enabled, the digitalRead() function returns 1. When the switch is pressed and its terminal connects to GND, digitalRead() returns 0. This inversion surprises many beginners. The internal pull-up resistor holds the pin at VCC when the switch is open. Pressing the switch connects the pin to ground and overrides the pull-up.

The builder stores the return value in a variable for later use. An integer variable holds the state of the switch at the moment of reading. The code then compares this variable against HIGH or LOW to decide the next action.

A critical detail involves contact bounce. Without mechanical bounce, each switch press produces exactly one HIGH-to-LOW transition. The program increments its counter once and advances to the next LED. However, because of mechanical bounce, a single press generates more than one HIGH-to-LOW transition. The counter variable increments one, two, or more times, causing the program to skip LEDs. The same effect occurs on release, since bounce happens both when the switch is depressed and when it is released.

With many kinds of switches, you do not get a clean closed contact, you get a very short transition period where the switch very quickly closes, opens, closes, opens and closes before settling down and becoming fully closed. The contacts bounce a bit before becoming fully closed. Hence the name switch bounce.

A test sequence demonstrates this problem. The builder presses the button once and holds it, then releases it once. Instead of a single clean transition, digitalRead() returns many alternating values.

  1. Key Stable
  2. 1
  3. 0
  4. 1
  5. 0
  6. 1
  7. 0
  8. 1
  9. Key Stable
  10. 0
  11. 1
  12. 0
  13. 1
  14. 0
  15. 1
  16. 0
  17. 1
  18. 0
  19. 1
  20. 0

The builder sees this effect in real projects. The LED turns on and off but not reliably. Sometimes, when the builder tries to turn on the LED, it turns off straight away. When the builder tries to turn it off, it turns back on straight away. This behavior is due to bounce or switch bounce. Software debouncing solves this problem by sampling the button state and using millis() - lastDebounceTime > debounceDelay as a gate. Only a reading taken after enough time has passed is accepted as a legitimate press. This approach filters out the rapid HIGH/LOW transitions caused by mechanical bounce. The code intentionally ignores readings during the debounce window and only acts on the rising edge of a press, so one mechanical press produces one clean action. If bounce is still present, increasing debounceDelay extends the ignore period, but making it too long can make rapid button presses difficult to register.

Project: Push-Button LED Control

A complete push-button controlled LED project combines pin configuration, state reading, and output control. The builder connects the push button to pin 2 and the LED to pin 13. The internal pull-up resistor on pin 2 eliminates the need for an external resistor.

Complete Arduino sketch using INPUT_PULLUP and if/else:

const int buttonPin = 2;   // Button connected to pin 2
const int LED_PIN = 13;    // LED connected to pin 13

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);   // Input with internal pull-up
  pinMode(LED_PIN, OUTPUT);           // LED pin as output
}

void loop() {
  int buttonState = digitalRead(buttonPin);  // Read button state

  if (buttonState == LOW) {                  // Button pressed → pin pulled LOW
    digitalWrite(LED_PIN, HIGH);            // Turn LED ON
  } else {                                   // Button released → pin is HIGH
    digitalWrite(LED_PIN, LOW);             // Turn LED OFF
  }
}

This sketch shows the required structure. The setup() function configures the button pin with INPUT_PULLUP and the LED pin as OUTPUT. The loop() function calls digitalRead(buttonPin) to read the button state and uses digitalWrite(LED_PIN, HIGH/LOW) inside an if/else to control the LED. With INPUT_PULLUP, the pin reads HIGH when unpressed and LOW when pressed.

An alternative approach uses plain INPUT and mathematical inversion. This method requires an external pull-down resistor to hold the pin LOW when the button is not pressed.

Alternative complete Arduino sketch using plain INPUT and mathematical inversion:

#define led 13
#define button 2
int buttonState = 0;

void setup()
{
  pinMode(led, OUTPUT);
  pinMode(button, INPUT);
}

void loop()
{
  buttonState = digitalRead(button);
  digitalWrite(led, 1 - buttonState);
}

This sketch demonstrates the same required structure with a different approach. The setup() function sets the LED pin as OUTPUT and the button pin as plain INPUT. The loop() function reads the button with digitalRead(button) and writes to the LED with digitalWrite(led, 1 - buttonState), mathematically inverting the button state to control the LED.

A third example uses an if statement with digitalRead() to turn an LED on when a button is pressed and off when released.

Real Example: Button Controls LED

void setup() {
  pinMode(2, INPUT);      // Button connected to pin 2
  pinMode(13, OUTPUT);    // LED connected to pin 13
}

void loop() {
  int buttonState = digitalRead(2);  // Read the button
  if (buttonState == HIGH) {
    digitalWrite(13, HIGH);  // Button pressed → LED ON
  }
  else {
    digitalWrite(13, LOW);   // Button not pressed → LED OFF
  }
}

The behavior follows a predictable pattern. When the builder presses the button, pin 2 reads HIGH and the LED turns on. When the builder releases the button, pin 2 reads LOW and the LED turns off.

The builder can break down the code into three logical parts. First, variables and constants define buttonPin as pin 2, ledPin as pin 13, and buttonState as the stored HIGH/LOW status of the pushbutton. Second, setup() sets ledPin to OUTPUT and buttonPin to INPUT. Third, loop() reads the pushbutton's state with digitalRead(buttonPin), checks if the button is pressed (buttonState == HIGH), turns the LED on via digitalWrite(ledPin, HIGH), and turns the LED off via digitalWrite(ledPin, LOW) when the button is not pressed.

const int buttonPin = 2;
const int ledPin = 13;
int buttonState = 0;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);
}

void loop() {
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }
}

The builder should test the circuit with a simple modification. Instead of controlling an LED, the code can print the button state to the serial monitor. This test confirms the wiring before adding output components.

void setup() { Serial.begin(9600); pinMode(buttonPin1, INPUT); }

Troubleshooting Common Wiring and Code Issues

Most failures in digital input projects trace back to two wiring mistakes. The first mistake involves a floating input pin. A common error is to connect the tactile button so the Arduino input pin sees 5V when pressed and is disconnected from everything when released. Without a pull-down resistor, the pin does not reliably read 0V. The fix is to add a high-value pull-down resistor, typically 10kΩ, to ground so the pin always has a known voltage.

The second mistake involves a wrong assumption about the button's internal wiring. Tactile buttons often have internally connected pairs of legs. The two legs on the top side are always connected, and the two on the bottom side are always connected. Pressing the button connects the top pair to the bottom pair. Ignoring this can cause incorrect breadboard wiring. The builder should verify connectivity with a multimeter before powering the circuit.

Code issues also cause unreliable behavior. A missing pinMode() call leaves the pin in its default state, which may not match the expected input mode. A missing digitalRead() call means the code never checks the button state. A logic error in the if/else statement inverts the expected behavior. The builder should verify each line against the working examples above.

The builder should also check the serial monitor for debugging. Printing the button state to the serial monitor reveals whether the pin reads HIGH or LOW correctly. If the readings alternate rapidly without any button press, the pin is floating. If the readings never change, the button or wiring is faulty.

A final check involves the LED polarity. An LED has an anode and a cathode. The longer leg is the anode and connects to the output pin. The shorter leg is the cathode and connects to ground through a current-limiting resistor. Reversing the LED prevents it from lighting. The builder should verify the LED orientation before troubleshooting the code.


The reader successfully connected a digital input module to an Arduino and read its state in code. They learned how a pull-up resistor, a pull-down resistor, or the internal pull-up resistor prevents floating pins and false readings. They wrote a working sketch that uses a push button to control an LED. The state of the switch now drives real behavior. This programming guide showed each switch, each ground connection, and each code line. Next, the reader can add debouncing or multiple digital inputs. Each new switch makes projects more reliable.

FAQ

Why does the button read LOW when pressed?

The internal pull-up resistor holds the pin at 5V during idle. Pressing the switch connects the pin to ground. The pin then reads LOW. This inverted logic confuses beginners, so the code must check for LOW to detect a press.

Does a button need an external resistor with INPUT_PULLUP?

No. The ATmega328P provides an internal pull-up resistor between 20 kΩ and 50 kΩ. The builder activates it through pinMode(pin, INPUT_PULLUP). An external resistor only helps in noisy environments or with long wires.

How does a builder fix unstable button readings?

Mechanical bounce causes rapid HIGH and LOW transitions during one press. Software debouncing samples the pin and ignores changes inside a short window. A debounceDelay value around 50 milliseconds works for most tactile switches.

Keywords:


Related Products