1. Introduction
This guide covers a small set of hands-on practice exercises for interfacing push-button switches with the ESP32. We start with an example project — a serial down-counter controlled by two switches — and then move on to two independent practice tasks involving LED control logic driven by switch presses.
2. Understanding Switch Debouncing (Polling Delay)
When a mechanical switch is pressed, it does not produce a single clean signal — the contacts can "bounce",
causing the microcontroller to register multiple presses for what feels like a single press. This is why
a short delay() is often added right after a switch press is detected.
This technique is sometimes called polling with a delay — it deliberately pauses the microcontroller briefly so it does not re-read the same press multiple times. It's a simple (though blocking) form of debouncing suitable for beginner projects.
3. Example Project — Serial Down/Up Counter with Two Switches
This is the demonstration project: two switches control a count variable. One switch
increments the count, the other decrements it. The count is printed to the Serial Monitor after each press.
A guard condition prevents the counter from going below zero.
Wiring Diagram
INPUT_PULLUP), so the pin reads
HIGH when not pressed and LOW when pressed.
Full Sketch
#define SW1 4
#define SW2 5
int count = 0;
void setup() {
Serial.begin(115200);
pinMode(SW1, INPUT_PULLUP);
pinMode(SW2, INPUT_PULLUP);
}
void loop() {
if (digitalRead(SW1) == LOW) {
count = count + 1;
Serial.println(count);
delay(500); // simple debounce delay
}
if (digitalRead(SW2) == LOW) {
count = count - 1;
if (count < 0) count = 0; // prevent negative values
Serial.println(count);
delay(500); // simple debounce delay
}
}
Key Commands Used
| Line | Explanation |
|---|---|
count = count + 1; | Increases the counter by 1 each time SW1 is pressed. |
count = count - 1; | Decreases the counter by 1 each time SW2 is pressed. |
if (count < 0) count = 0; | Single-line if — no braces required for one statement. Prevents negative counts. |
delay(500); | Debounce delay — prevents one press from being counted multiple times. |
delay() blocks the ESP32 from doing anything else during that
time. For simple learning exercises this is fine, but in advanced projects, non-blocking debounce
techniques (using millis()) are preferred.
4. Dual-Switch Toggle — Control Two LEDs Together
Goal: Use two switches and two LEDs. Pressing one specific switch turns both LEDs ON. Pressing that same switch again turns both LEDs OFF (i.e., toggle behavior). The second switch can be reserved for the individual-control task below.
Wiring Diagram
Sketch
#define SW_TOGGLE 4
#define LED1 18
#define LED2 19
bool ledState = false; // false = OFF, true = ON
void setup() {
pinMode(SW_TOGGLE, INPUT_PULLUP);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
}
void loop() {
if (digitalRead(SW_TOGGLE) == LOW) {
ledState = !ledState; // flip the state
digitalWrite(LED1, ledState);
digitalWrite(LED2, ledState);
delay(300); // debounce
}
}
Key Commands Used
bool ledState variable acts as a memory flag. Each press inverts it with
!ledState, so the same switch alternates between turning both LEDs on and off — this is the
classic toggle pattern.
5. Independent Switch Control — Each Switch Controls a Different LED
Goal: Two switches, two LEDs. Pressing Switch 1 turns LED1 ON. Pressing Switch 2 turns LED1 OFF and LED2 ON. Essentially, each switch has its own independent effect rather than a shared toggle.
Wiring Diagram
Sketch
#define SW1 4
#define SW2 5
#define LED1 18
#define LED2 19
void setup() {
pinMode(SW1, INPUT_PULLUP);
pinMode(SW2, INPUT_PULLUP);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
}
void loop() {
if (digitalRead(SW1) == LOW) {
digitalWrite(LED1, HIGH); // SW1 turns LED1 ON
delay(300);
}
if (digitalRead(SW2) == LOW) {
digitalWrite(LED1, LOW); // SW2 turns LED1 OFF
digitalWrite(LED2, HIGH); // SW2 turns LED2 ON
delay(300);
}
}
Key Commands Used
if (digitalRead(SW1)==LOW && digitalRead(SW2)==LOW) to detect both switches
pressed together. This introduces basic multi-input logic, but it's optional for this exercise.
6. Tips, Best Practices & Next Steps
- Always use
INPUT_PULLUPmode when a switch connects the pin to GND, so you don't need an external resistor. - Add a short
delay()after detecting a press to avoid the same press being registered multiple times. - Use a single-line
ifstatement without braces only when there is exactly one statement to execute. - For more advanced designs, replace
delay()based debouncing with amillis()timer to keep the program non-blocking. - Practice wiring these circuits on a breadboard before moving to a permanent build — this builds strong hands-on understanding of GPIO behavior.