Debouncing a button in software in Arduino - Time & Space Complexity
Start learning this pattern below
Jump into concepts and practice - no test required
When we debounce a button in software, we want to avoid false triggers caused by quick, repeated presses.
We ask: how does the time spent checking the button grow as we wait longer or check more often?
Analyze the time complexity of the following code snippet.
const int buttonPin = 2;
int buttonState;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
void loop() {
int reading = digitalRead(buttonPin);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonState) {
buttonState = reading;
}
}
lastButtonState = reading;
}
This code reads a button and waits 50 milliseconds to confirm the press is stable before accepting it.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The loop() function runs repeatedly, reading the button state each time.
- How many times: It runs continuously, many times per second, checking the button state every cycle.
Explain the growth pattern intuitively.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 (checks) | 10 reads and comparisons |
| 100 (checks) | 100 reads and comparisons |
| 1000 (checks) | 1000 reads and comparisons |
Pattern observation: The number of operations grows directly with how many times the button is checked.
Time Complexity: O(n)
This means the time spent checking the button grows linearly with the number of checks performed.
[X] Wrong: "Debouncing adds extra loops that make the program slower exponentially."
[OK] Correct: The code only checks the button once per loop cycle and uses simple timing checks, so the work grows steadily, not exponentially.
Understanding how repeated checks affect performance helps you write responsive and efficient embedded programs.
"What if we changed the debounce delay from 50ms to 500ms? How would the time complexity change?"
Practice
Solution
Step 1: Understand button noise
Mechanical buttons create multiple quick signals when pressed due to bouncing contacts.Step 2: Purpose of debouncing
Debouncing filters these quick repeated signals to register only one clean press.Final Answer:
To ignore rapid, repeated signals caused by mechanical noise -> Option AQuick Check:
Debouncing = Ignore noise [OK]
- Thinking debouncing speeds up button presses
- Confusing debouncing with power saving
- Assuming debouncing controls LED blinking
Solution
Step 1: Identify timing functions
delay() pauses the program but is not ideal for debouncing timing checks.Step 2: Use millis() for non-blocking timing
millis() returns the time since the program started, allowing to check elapsed time without stopping code.Final Answer:
millis() -> Option DQuick Check:
Debounce timing uses millis() [OK]
- Using delay() which blocks code execution
- Confusing digitalRead() with timing
- Using analogWrite() which controls PWM output
const int buttonPin = 2;
int buttonState = 0;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
void setup() {
pinMode(buttonPin, INPUT);
Serial.begin(9600);
}
void loop() {
int reading = digitalRead(buttonPin);
if (reading != buttonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == HIGH) {
Serial.println("Button pressed");
}
}
}
}Solution
Step 1: Analyze initial buttonState and reading
buttonState starts at 0 (LOW). If button is pressed, reading becomes HIGH (1).Step 2: Check debounce logic
The code updates lastDebounceTime when reading differs from buttonState, and after debounce delay, if reading still differs, buttonState updates and prints.Step 3: Confirm output
After debounce delay, buttonState updates and "Button pressed" is printed once.Final Answer:
Button pressed printed once -> Option BQuick Check:
Debounce logic allows one print after stable press [OK]
- Assuming print happens immediately on press
- Ignoring debounce delay effect
- Thinking code has syntax errors
const int buttonPin = 3;
int buttonState = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
void loop() {
int reading = digitalRead(buttonPin);
if (reading != buttonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
buttonState = reading;
if (buttonState == HIGH) {
Serial.println("Pressed");
}
}
}Solution
Step 1: Check hardware setup assumptions
Without enabling INPUT_PULLUP, the button pin may float causing unreliable readings.Step 2: Importance of INPUT_PULLUP
Using INPUT_PULLUP activates internal pull-up resistor, stabilizing input and making debounce logic reliable.Final Answer:
Add pinMode(buttonPin, INPUT_PULLUP) in setup() -> Option CQuick Check:
Use INPUT_PULLUP for stable button input [OK]
- Changing initial buttonState without hardware reason
- Moving state update incorrectly breaking debounce logic
- Setting too long debounce delay unnecessarily
const int buttonPin = 4;
const int ledPin = 13;
int ledState = LOW;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, ledState);
Serial.begin(9600);
}
void loop() {
int reading = digitalRead(buttonPin);
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != lastButtonState) {
lastButtonState = reading;
if (lastButtonState == LOW) {
ledState = !ledState;
digitalWrite(ledPin, ledState);
Serial.println(ledState ? "LED ON" : "LED OFF");
}
}
}
}Solution
Step 1: Check debounce timing logic
The code updates lastDebounceTime when reading changes, then waits debounceDelay before accepting new state.Step 2: Confirm state update and toggle
Inside debounce check, lastButtonState updates to reading, and LED toggles only when button is pressed (LOW due to INPUT_PULLUP).Step 3: Verify output and toggle behavior
LED state flips once per valid press, and serial prints correct status.Final Answer:
This code toggles LED once per press correctly -> Option AQuick Check:
Debounce + toggle once per press = Correct [OK]
- Not updating lastButtonState causing repeated toggles
- Ignoring debounce delay causing multiple toggles
- Confusing HIGH/LOW logic with INPUT_PULLUP
