Skip to content

Button Circuit Design

Button Circuit Design

This section covers the hardware design for connecting a physical push button to the ESP32-XIAO for IoT button applications. By the end of this section, you will be able to:

  • Design a button circuit with proper pull-up/pull-down resistors
  • Implement hardware debounce for reliable button presses
  • Configure GPIO interrupts for wake-from-sleep detection
  • Plan a 3D-printed enclosure for the button assembly

Before starting this section, please ensure:

  • Basic understanding of GPIO pins and digital input
  • Familiarity with pull-up/pull-down resistor concepts
  • Completed 04-01. ESP32-XIAO Hardware Overview
  • Arduino IDE or PlatformIO environment ready

A push button is a simple mechanical switch that connects two terminals when pressed. In an IoT button circuit, the button connects a GPIO pin to ground (or VCC) to signal a press event.

Standard "Normally Open" Push Button:
┌───┐
Pin ─┤ ├── GND
└───┘
(open when not pressed)
(closed when pressed)
ConfigurationIdle StatePressed StateConnection
Pull-UpHIGH (3.3V)LOW (GND)Button connects GPIO to GND
Pull-DownLOW (GND)HIGH (3.3V)Button connects GPIO to VCC

For the IoT button project, we use the pull-up configuration with the internal pull-up resistor:

3.3V
R (10kΩ internal pull-up)
├──── GPIO Pin (input)
───
│ ● │ Push Button
───
GND

When button is not pressed: GPIO reads HIGH (3.3V through pull-up resistor) When button is pressed: GPIO reads LOW (connected directly to GND)

  1. ESP32 internal pull-up resistors are available (no external component needed)
  2. GND reference is more stable than VCC in battery-powered circuits
  3. Lower power consumption — no current flows in the default (HIGH) state
  4. Compatible with deep sleep wake-up — GPIO can wake from LOW signal

Mechanical buttons exhibit “bounce” — the contacts make and break contact multiple times (for 5-20ms) before settling:

Ideal Signal: ───────┐
└──────
Actual Signal: ───┐┌──┐┌──┐┌──┐┌──┐┌─
││ ││ ││ ││ ││
└┘ └┘ └┘ └┘ └┘
← bounce period →
(5-20ms typical)

Without debouncing, a single button press can be interpreted as multiple presses.

On the XIAO ESP32-C3, the following pins are suitable for button input:

GPIOWake-Up CapableNotes
GPIO 0YesAlso BOOT button — avoid
GPIO 1YesAvailable, good for button
GPIO 2YesAvailable
GPIO 3YesAvailable

Recommendation: Use GPIO 2 for the button — it has wake-up capability and no special functions.

const int BUTTON_PIN = 2;
const unsigned long DEBOUNCE_DELAY = 50; // ms
void setup() {
Serial.begin(115200);
// Configure button pin with internal pull-up
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.println("Button initialized on GPIO " + String(BUTTON_PIN));
}
// Debounce variables
int lastButtonState = HIGH; // Previous reading
unsigned long lastDebounceTime = 0; // Last time pin changed
void loop() {
int reading = digitalRead(BUTTON_PIN);
// If the button state changed, reset the debounce timer
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
// If enough time has passed, consider it a stable reading
if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
// If the button state has actually changed
if (reading != buttonState) {
buttonState = reading;
// Button pressed (LOW = pressed with pull-up)
if (buttonState == LOW) {
Serial.println("Button pressed!");
// Trigger action here
}
}
}
lastButtonState = reading;
}

Step 4: GPIO Interrupt for Wake-from-Sleep

Section titled “Step 4: GPIO Interrupt for Wake-from-Sleep”

For battery-powered operation, the ESP32 should be in deep sleep and wake on button press:

// Wake-up source: GPIO 2 (falling edge = button press)
void setupWakeUp() {
// Configure GPIO as wake-up source
esp_sleep_enable_ext0_wakeup(GPIO_NUM_2, 0); // 0 = LOW level wakes
Serial.println("Deep sleep configured - wake on button press (GPIO 2)");
}
void goToSleep() {
Serial.println("Entering deep sleep...");
Serial.flush();
esp_deep_sleep_start(); // Never returns
}
void setup() {
Serial.begin(115200);
// Check what woke us up
esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) {
Serial.println("Woke up by button press!");
} else {
Serial.println("First boot (not from deep sleep)");
}
// Perform button action here
// ...
// Go back to sleep
goToSleep();
}
void loop() {
// Not used - everything happens in setup()
}

For the 3D-printed enclosure, consider:

┌─────────────────────────┐
│ │
│ ┌──────┐ │
│ │ Button│ ← Exposed │
│ │ Cap │ button │
│ └──────┘ │
│ │
│ [ XIAO Board ] │ ← Inside
│ [ Battery ] │
│ │
│ ┌────┐ │
│ │USB-C│ ← Charging │
│ └────┘ access │
└─────────────────────────┘

Key design features:

  • Button cap should have a small travel distance (1-2mm)
  • USB-C port must be accessible for charging
  • Battery compartment should have a snug fit
  • Wall thickness: 1.2-2mm recommended for PLA/PETG
  • Consider a small LED hole for status indication
  • Button press consistently reads LOW
  • No false triggers from bounce (debounce working)
  • ESP32 wakes from deep sleep on button press
  • System goes back to sleep after action completes
  • Button works reliably across multiple presses

Symptom: GPIO always reads HIGH, regardless of button press

Possible Causes:

  • Wrong GPIO pin number in code
  • Button not connected to correct pin
  • Internal pull-up not enabled

Solution:

// Debug: read raw pin value
Serial.print("GPIO "); Serial.print(BUTTON_PIN);
Serial.print(" = "); Serial.println(digitalRead(BUTTON_PIN));
// Should read LOW when button is pressed

Symptom: One press triggers two actions

Cause: Debounce delay too short

Solution: Increase debounce delay to 100ms or implement edge detection:

static bool lastPressed = false;
if (buttonState == LOW && !lastPressed) {
// First press detection only
triggerAction();
lastPressed = true;
}
if (buttonState == HIGH) {
lastPressed = false; // Reset when released
}
  • Use INPUT_PULLUP mode to avoid external resistors
  • Implement debounce with at least 50ms delay
  • Configure wake-up pins for deep sleep operation
  • Add a pull-up resistor if using long wires (to prevent EMI triggering)
  • Do not use GPIO 0 as it is also the BOOT button
  • Avoid floating pins — always use pull-up or pull-down
  1. Pull-up configuration with internal resistor is the simplest and most reliable approach
  2. Software debounce prevents false triggers from mechanical switch bounce
  3. GPIO interrupt with ext0_wakeup enables wake-from-sleep on button press
  4. GPIO 2 is the recommended pin for the button on XIAO ESP32-C3
  5. 3D enclosure should expose the button cap while protecting internal components

Target Audience: Alibaba.com IoT Pre-sales Engineers
Status: ✅ Completed