Buttons, switches and bump sensors
What you’ll learn
- Why a floating input reads random values
- How pull-up and pull-down resistors fix it
- How to debounce a button
- How robots use switches as bump and limit sensors
The floating input problem
An input pin connected to nothing “floats”: it picks up electrical noise and flickers between HIGH and LOW. A button on its own only connects the pin to something while pressed, so it needs a resistor to hold the pin steady the rest of the time.
Pull-up and pull-down
| Wiring | Not pressed | Pressed |
|---|---|---|
| Pull-up: resistor from pin to 5 V, button from pin to GND | HIGH | LOW |
| Pull-down: resistor from pin to GND, button from pin to 5 V | LOW | HIGH |
The Arduino has built-in pull-up resistors. pinMode(pin, INPUT_PULLUP) turns one on, so you only need the button between the pin and GND.
Bounce
Inside a button, metal contacts bounce for a few milliseconds when pressed, so one press can read as several. Fix it by ignoring changes for about 20–50 ms after the first one (debouncing).
Switches on robots
- Bump switches (microswitches with a lever) on the front bumper tell the robot it has hit something.
- Limit switches stop a motor at the end of its travel, like an arm reaching its top position.
- Microswitches have three terminals: COM (common), NO (normally open: connects when pressed) and NC (normally closed: disconnects when pressed). For a bump sensor, wire COM to GND and NO to the input with
INPUT_PULLUP.
Example: a bump sensor that reverses the robot
const int BUMP = 4;
void setup() { pinMode(BUMP, INPUT_PULLUP);}
void loop() { if (digitalRead(BUMP) == LOW) { // bumper pressed // reverse for 0.5 s, then turn (motor code from the Actuators section) } else { // drive forward }}Example: debouncing with millis()
const int BUTTON = 2;int presses = 0;bool lastStable = HIGH, lastRead = HIGH;unsigned long changedAt = 0;
void setup() { pinMode(BUTTON, INPUT_PULLUP); Serial.begin(9600);}
void loop() { bool now = digitalRead(BUTTON); if (now != lastRead) changedAt = millis(); // still bouncing: restart the timer if (millis() - changedAt > 30 && now != lastStable) { lastStable = now; if (now == LOW) Serial.println(++presses); // a clean press } lastRead = now;}Unlike delay(), this never freezes the program, so a robot can keep driving while it watches the button.
Try it in the virtual Arduino
Press Start simulator, open the diagram.json tab and replace its contents with the wiring below, then paste the sketch into sketch.ino and press the green ▶.
{ "version": 1, "author": "RoboLearn", "editor": "wokwi", "parts": [ { "type": "wokwi-arduino-uno", "id": "uno", "top": 0, "left": 0, "attrs": {} }, { "type": "wokwi-pushbutton", "id": "btn1", "top": -90, "left": 200, "attrs": { "color": "red" } } ], "connections": [ [ "btn1:1.l", "uno:2", "green", [] ], [ "btn1:2.l", "uno:GND.1", "black", [] ] ], "dependencies": {}}const int BUTTON = 2;
void setup() { pinMode(BUTTON, INPUT_PULLUP); pinMode(LED_BUILTIN, OUTPUT); Serial.begin(9600);}
void loop() { bool pressed = digitalRead(BUTTON) == LOW; // LOW means pressed digitalWrite(LED_BUILTIN, pressed ? HIGH : LOW); Serial.println(pressed ? "pressed" : "released"); delay(100);}Runs on Wokwi, free, no account needed.
InvertedEasy
Make the LED turn off while the button is held and on otherwise.
Swap the HIGH and LOW: digitalWrite(LED_BUILTIN, pressed ? LOW : HIGH);
Hold for two secondsMedium
Turn the LED on only after the button has been held for 2 seconds.
Remember the millis() time when the press started.
Show an answer
unsigned long since = 0;void setup() { pinMode(2, INPUT_PULLUP); pinMode(LED_BUILTIN, OUTPUT); }void loop() { if (digitalRead(2) == HIGH) since = millis(); // not pressed: keep resetting digitalWrite(LED_BUILTIN, millis() - since > 2000 ? HIGH : LOW);}Two-button comboHard
Add a second button on pin 3. The LED lights only when both are pressed.
Wire the second button between pin 3 and GND, set pinMode(3, INPUT_PULLUP), and use if (digitalRead(2) == LOW && digitalRead(3) == LOW).
Quick check
Finished this lesson?