Guide no. 20
Why an input left unconnected reads random values, which resistor to use, when the internal one is enough and how to stop one press from being counted three times.
By Ada Intelisano · informaticait.it technical team
- A digital input connected to nothing is floating: it picks up noise and reads HIGH and LOW at random.
- A resistor holds it at a known level: pull-up to VCC (HIGH at rest) or pull-down to GND (LOW at rest). Arduino suggests 10 kΩ as a good value.
- Often the resistor is already inside the chip:
INPUT_PULLUPon the UNO and on the ESP32, which also hasINPUT_PULLDOWN. The exceptions are the ESP32's GPIO 34–39. - A mechanical contact bounces for a few milliseconds: the sketch has to wait for the signal to settle.
Three ways to wire a button
In the first case, when the button is released the pin is connected to nothing: Arduino warns that an input like this reports random changes, because it picks up the electrical noise around it. With the pull-up, the pin at rest sees VCC through the resistor and the button pulls it to GND: pressed = LOW, and in the sketch you check for == LOW. The pull-down does the opposite. The resistor also limits the current when the button is pressed: with 10 kΩ at 5 V, 0.5 mA flows.
The internal resistors
| Arduino UNO (ATmega328P) | ESP32 | |
|---|---|---|
| Value | 20 to 50 kΩ | about 45 kΩ |
| How to enable it | pinMode(pin, INPUT_PULLUP) | INPUT_PULLUP or INPUT_PULLDOWN |
| Exceptions | pin 13: the board's LED pulls the voltage down to about 1.7 V; Arduino recommends INPUT with an external pull-down | GPIO 34–39 are input only and have no pull-up or pull-down: you need an external resistor |
| Good to know | during reset the internal pull-ups are off | some pins decide how the chip boots: see below |
The internal ones are fine for a button close to the board. With long wires or in noisy environments, use a lower external resistor, for example 10 kΩ, which holds the level more firmly.
ESP32: the pins that decide how it boots
At power-on the ESP32 reads the level of five pins (strapping pins) to decide how to start. Each one has an internal resistor that holds it at the right value: a button, or an external pull-up or pull-down on these pins, can change the boot. Choose other pins for buttons.
| Pin | At rest | What it decides at boot |
|---|---|---|
| GPIO 0 | pull-up | at 0 (together with GPIO 2 at 0) the chip enters upload mode instead of running the sketch |
| GPIO 2 | pull-down | must be at 0 for upload mode |
| GPIO 12 (MTDI) | pull-down | at 1 it sets the flash memory supply to 1.8 V |
| GPIO 15 (MTDO) | pull-up | at 0 it turns off the boot messages on the serial port |
| GPIO 5 | pull-up | together with GPIO 15, an SDIO interface timing setting |
Bounce
When the contacts of a button touch, they don't close cleanly: they bounce, and the signal switches several times between HIGH and LOW. For the Omron B3F tactile switch the manufacturer states a bounce of 5 ms maximum. The microcontroller reads thousands of times per second and sees every jump as a press. The simplest fix is in the sketch: accept the change only when the pin has been steady for a certain time. The official Arduino example uses 50 ms.
Wiring with the ESP32
The button straddles the center channel: one side goes to GPIO 27, the other to GND. No resistor: we use the internal pull-up. With an Arduino UNO the same wiring goes to pin 7.
The sketch
// Button with the internal pull-up and debounce: counts the presses without counting fake ones. // The button connects the pin to GND: pressed = LOW. Same sketch for Arduino UNO and ESP32. #include <Arduino.h> #ifdef ARDUINO_ARCH_ESP32 const int BUTTON_PIN = 27; // do not use GPIO 34-39 (no internal pull-up) or the boot pins #else const int BUTTON_PIN = 7; #endif const unsigned long DEBOUNCE_MS = 50; // debounce time: increase it if it still counts double int stableState = HIGH; // last accepted state int lastReading = HIGH; // last raw reading unsigned long changedAt = 0; unsigned long presses = 0; void setup() { Serial.begin(115200); pinMode(BUTTON_PIN, INPUT_PULLUP); } void loop() { int reading = digitalRead(BUTTON_PIN); if (reading != lastReading) { changedAt = millis(); // the contact is still bouncing: start over lastReading = reading; } if (millis() - changedAt > DEBOUNCE_MS && reading != stableState) { stableState = reading; // steady for long enough: it's a real change if (stableState == LOW) { presses++; Serial.print("Presses: "); Serial.println(presses); } } }
The sketch counts the presses without ever stopping with delay(), so it can do other things in the meantime. If a press is still counted twice, increase DEBOUNCE_MS.
Common mistakes
| What happens | Why | Fix |
|---|---|---|
| The pin changes by itself, without touching anything | floating input | INPUT_PULLUP or an external resistor |
| One press counted two or three times | contact bounce | debounce in the sketch; increase the wait if needed |
| The button works backwards | with the pull-up, pressed reads LOW | check for == LOW |
| Button on GPIO 34 that does not respond | GPIO 34–39 have no internal pull-up | 10 kΩ resistor to 3.3 V, or another pin |
| The ESP32 does not start or stays waiting for an upload | button or resistor on a boot pin | move the button to a free pin, for example 27 |
UNO pin 13 reading wrong with INPUT_PULLUP | the board's LED is on the same pin | use another pin or an external pull-down |
Useful products
Other guides
- Resistor colour code and tolerances
- N-channel and P-channel MOSFETs
- Bipolar transistor (BJT) or MOSFET?
- Relays: how to drive them and how much current they draw
- THT and SMD/SMT components
- XIAO board pinouts
- LEDs: forward voltage and series resistor
- The multimeter: measuring voltage and current
- Wemos D1 mini: first steps
- MOSFET module with Arduino and ESP32
- Relay module with Arduino and ESP32
- 3D printing filaments: PLA, PETG and fibre-filled versions
- Analog and digital: the differences
- ADS1115: 16-bit converter with Arduino and ESP32
- From bits to real values: converting sensor readings
- DS3231 RTC clock with Arduino and ESP32
- Linear or switching regulator: which one to choose
- Step-down, step-up and buck-boost: choosing the right module
- Voltage divider: reading voltages higher than 5 V or 3.3 V
- 3.3 V and 5 V logic levels: getting an ESP32 and an Arduino to talk
- The I²C bus: addresses, pull-ups and the scanner
- Lithium batteries: voltages, charging and protection
- ESP32 power consumption and deep sleep: how long the battery lasts
- Temperature sensors compared
- ESP32: which pins to use and which to avoid
- Getting started with the ESP32 in Arduino IDE 2 and PlatformIO
- SSD1306 OLED display and 16×2 I²C LCD with Arduino and ESP32
- PIR motion sensor and HC-SR04 ultrasonic sensor
- Date and time from the Internet with the ESP32: NTP, Italian time zone and daylight saving time
- MicroSD data logger: saving measurements to CSV
- Solar-powered ESP32: panel, charger and the numbers for Italy
- Web server on the ESP32: control a relay from your phone
- Your first LED: wire it up and make it blink
- The button: let Arduino decide
- The potentiometer: turn a knob and read its value
- Soft light: adjusting the brightness of an LED
- The buzzer: playing notes and melodies
- The traffic light: three LEDs in sequence
- The thermometer: reading the temperature with the TMP36
- The servo motor: moving an arm with Arduino
- The night light: the photoresistor and your first decision
- The relay: switching on a light bulb with Arduino
- The LCD display: making Arduino write
- The electronic dice: your first complete project
- The RGB LED: every color from three lights
- The joystick: two-axis control with a shield
- The LED ring: twelve colors from a single pin
- The DC motor: driving it with a MOSFET
- Soil moisture: the plant that asks for water
- The stepper motor: turn exactly as far as you want
- The parking sensor: faster and faster beeps
- The 7-segment display: big, bright numbers
- Automatic watering: the plant that waters itself
- The keypad: a lock with a secret code
- Networked weather station: values and chart on your phone
- A Telegram bot on the ESP32: your home from your phone
- MQTT with the ESP32: the language of connected devices
- Updating the ESP32 over Wi-Fi (OTA)
- ESP-NOW: two ESP32 boards talking without a router
- WebSocket with the ESP32: the LED that follows the slider
- Readings on a Google sheet, with deep sleep
- WiFiManager: choose the Wi-Fi from your phone
- Bluetooth LE with the ESP32: the phone without a network
- The camera in your browser with the ESP32-S3
- A portable Bluetooth speaker you build yourself
- Bluetooth desktop speakers, 2 × 20 W
- Stereo MP3 player with two buttons
- The shelf that plays music: a Visaton exciter
- How to solder well
- How much does it draw? Current and power with the INA219
- Load-cell scale with the HX711
- CO2 traffic light: when to open the windows
- RFID card door opener
- GPS: track logger on microSD
- LoRa: how far does it go? A range test with two boards
- Battery weather sign with an e-paper display
- Web radio with an ESP32 and an I2S amplifier
- Getting started with the Raspberry Pi Pico 2 in MicroPython
- Your first amplifier: the LM386 and a speaker
- Talking Arduino: the DFR0760 speech synthesis module
- The spectrum of your music on an LED matrix
- Drawing with 64 LEDs: the 8×8 matrix with the MAX7219
- A real voltage: the 12-bit MCP4725 DAC
- The heartbeat on Arduino: an ECG with the AD8232