ESP32 Switch Control

A practical guide to interfacing push-button switches with the ESP32 — from a simple down-counter to multi-switch LED control logic.

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.

Goal: Understand how to read digital inputs from switches, apply simple debounce logic, and use conditional statements to drive digital outputs (LEDs) based on switch state.

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.

Switch signal (bouncing) ← noisy edge After delay-based debounce ← clean edge

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

ESP32 SW1 (GPIO4) SW2 (GPIO5) GND — other leg of SW1 GND — other leg of SW2
Both switches use the ESP32's internal pull-up resistor (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

pinMode(SW1, INPUT_PULLUP);
pinMode(SW2, INPUT_PULLUP);
digitalRead(SW1);
digitalRead(SW2);
Serial.println(count);
delay(500);
LineExplanation
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.
Note: Using 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.
Practice Task 1

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

ESP32 Toggle SW (GPIO4) LED1 (GPIO18) LED2 (GPIO19)

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

pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
digitalWrite(LED1, ledState);
digitalWrite(LED2, ledState);
ledState = !ledState;
The 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.
Practice Task 2

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

ESP32 SW1 (GPIO4) SW2 (GPIO5) LED1 (GPIO18) LED2 (GPIO19)

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

digitalRead(SW1);
digitalRead(SW2);
digitalWrite(LED1, HIGH);
digitalWrite(LED1, LOW);
digitalWrite(LED2, HIGH);
You can extend this pattern further — for example, combine conditions such as 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

If you are new to breadboards, review a basic breadboard tutorial first. The core wiring concepts (rows, columns, power rails) have not changed over the years even in older tutorial videos.