yahboom
HomeLearn
Solution
Blog
Online Store
About Us
EN/中文
ESP32 Core Board📖
1. ESP32 Introduction
  • 1. ESP32S3 Overview
  • 2. Environment Setup Introduction
  • 3. Dev Board Introduction
2. ESP-IDF Development Tutorial
1. Before Development
  • 1. Environment Setup (Windows)
  • 2. Environment Setup (Linux)
  • 3. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. DMA
  • 8. ADC
  • 9. PWM Breathing LED
  • 10. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. PSRAM External Memory Experiment
  • 5. Flash Read and Write Experiment
  • 6. USB Emulated Mass Storage Experiment
4. FreeRTOS operating system
  • 1.FreeRTOS Core Concepts Theory
  • 2.Task Creation and Management
  • 3.Queue
  • 4.Binary Semaphore
  • 5.Mutex
  • 6.Event Groups
  • 7.Software Timer
  • 8.Task Scheduler
  • 9.Low Power Mode
  • 10.Deadlock
  • 11.Priority Inversion
5. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. Socket Programming Basics
  • 9. HTTP Client Experiment
  • 10. HTTP Server Experiment
  • 11. MQTT IoT Communication Protocol
  • 12. WIFI OTA Upgrade
  • 13. Bluetooth Fundamentals
  • 14. Bluetooth Slave Mode
  • 15. Bluetooth Master Mode
6. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
7. Xiaozhi AI Voice
  • 1. Burn Xiaozhi Firmware
  • 2. Network and Configuration
  • 3. AI Agent Settings
  • 4. Program Analysis
  • 5. Free Chat
3. Arduino Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. ADC
  • 8. PWM Breathing LED
  • 9. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication
  • 2. I2C OLED Display
  • 3. SPI Bus Communication
  • 4. Flash Read and Write
  • 5. USB Emulated Mass Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Server Experiment
  • 5. UDP Client Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT IoT Communication Protocol
  • 11. WIFI OTA Upgrade
  • 12. Bluetooth Fundamentals
  • 13. Bluetooth Slave Mode
  • 14. Bluetooth Master Mode
5. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
4. MicroPython Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
  • 3. Thonny IDE
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. TIMER_IT
  • 5. Watchdog Timer
  • 6. ADC
  • 7. PWM Breathing LED
  • 8. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. Flash Read and Write Experiment
  • 5. Print Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT
  • 11. Bluetooth Fundamentals
  • 12. Bluetooth Slave Mode
  • 13. Bluetooth Master Mode
5. Audio Applications
  • 1. I2S Audio Playback Experiment
  • 2. Repeater Experiment
Download
  • Code
  • Hardware_Info
  • 3D_Model_File
  • Software_Tools
  • VMware
  • VM_File
Download ZIP

Welcome to ESP32 Core Board repository

2. Button Detection

Button Detection


Button Detection1. Button Detection Overview2. Core Concepts2.1 GPIO Input Mode2.2 Mechanical Button Bounce2.3 Debounce State Machine3. Project Architecture and Flow4. Hardware Dependencies5. Source Code Analysis5.1 Macro Definitions and Global Variables5.2 led_init() — LED Output Configuration5.3 key_init() — Button Input Configuration5.4 key_scan() — Debounced Scanning (Core)5.5 led_control() — State Output5.6 app_main() — Main Entry6. Operation Flow6.1 Build and Flash6.2 Normal Operation6.3 Boundary Testing7. Common Issues


1. Button Detection Overview

Button is the most common human-machine interaction input device. The MCU judges button state (pressed/released) by reading GPIO input levels. This experiment uses the BOOT button (GPIO0) to control LED (GPIO43) on/off toggle, demonstrating GPIO input configuration, software debouncing, and state machine programming.

Each button press → executes one LED state toggle (ON↔OFF), holding down does not trigger repeatedly.

Typical Application Scenarios:

Application TypeExample
Digital InputUser buttons, switch state reading, sensor digital signals, rotary encoder
Pull-up InputButton connected to GND, limit switch, reed switch, photoelectric switch
Debounce ProcessingMechanical buttons, relay contact detection, DIP switch

2. Core Concepts

2.1 GPIO Input Mode

When configured as input mode, pins present high-impedance state, with level determined by external circuit.

Key Role of Pull-up/Pull-down Resistors:

ConfigurationGPIO Default LevelButton Pressed LevelUse Case
Internal Pull-UpHigh (1)Low (0)One end of button connected to GND, conducts when pressed
Internal Pull-DownLow (0)High (1)One end of button connected to VDD, conducts when pressed
No Pull-up/downUncertain (floating)UncertainNot recommended, floating pins susceptible to interference

This experiment uses internal pull-up (GPIO_PULLUP_ENABLE), with one end of button connected to GND and the other end to GPIO0:

​x
VDD ──→ Internal pull-up resistor (~45kΩ) ──→ GPIO0 ──→ Button ──→ GND

Button released: GPIO0 pulled to VDD → reads 1
Button pressed: GPIO0 directly connected to GND → reads 0

2.2 Mechanical Button Bounce

Mechanical buttons experience bouncing at the moment of press and release, generating a string of irregular high/low level pulses:

xxxxxxxxxx
Ideal waveform:  ────┐            ┌────
                     └────────────┘
Actual waveform:  ────┐┌┐┌┐┌┐┌┐┌┐┌────
                       └┘└┘└┘└┘└┘└┘└┘
                       ├─ Bounce period ─┤
                      (typical 5~20ms)

If not debounced, MCU might misread one press as multiple triggers. Software debounce (delay and then confirm) is the simplest and most effective method.

2.3 Debounce State Machine

xxxxxxxxxx
                  ┌─────────────┐
     gpio=1      │   IDLE      │  gpio=1
   ┌─────────────│  Idle State │◄─────────────┐
   │             └──────┬──────┘             │
   │                    │ gpio=0 (press detected)   │
   │             ┌──────▼──────┐             │
   │             │  DEBOUNCE   │             │
   │             │  Delay 50ms │              │
   │             └──────┬──────┘             │
   │                    │                    │
   │           ┌───────▼───────┐             │
   │           │ gpio=0 still 0? │  ──No──→ Back to previous step (bounce)
   │           └───────┬───────┘             │
   │                   │ Yes (confirmed valid)        │
   │            ┌──────▼──────┐              │
   │            │  WAIT_RELEASE│             │
   │            │  Wait for release│               │
   │            └──────┬──────┘              │
   │                   │ gpio=1 (released)        │
   │                   └─────────────────────┘
   │
   └── Bounce interference path (gpio already returned to 1 within 50ms)

Meaning of four states:

StateEntry ConditionOperationOutput
IDLEPrevious processing completeDetect gpio=0?—
DEBOUNCEFirst low level detectedvTaskDelay(50ms)—
CONFIRMRe-detect after 50msIf still low=valid, otherwise=bouncetrue/false
WAIT_RELEASEConfirmed valid presswhile loop waiting for gpio=1Returns true only after release

3. Project Architecture and Flow

xxxxxxxxxx
app_main()
  ├─ led_init()         ← Configure GPIO43 push-pull output
  ├─ key_init()         ← Configure GPIO0 input + internal pull-up
  ├─ led_control()      ← Initial state: LED ON
  │
  └─ while(1) Main Loop
      ├─ key_scan()     ← Debounced scan, returns bool
      │   ├─ false → skip this iteration
      │   └─ true  → led_state = !led_state (toggle)
      │              led_control()            (update GPIO)
      └─ vTaskDelay(20ms)

Key Design Principles:

  • led_state saves logical state (true=ON, false=OFF)

  • led_control() is the only function that operates GPIO, translating logical state to physical level

  • key_scan() returns true means "one valid press completed (including release)"


4. Hardware Dependencies

Hardware Connection:

PinPeripheralConfigurationDescription
GPIO43LEDPush-pull outputCommon-anode, low level turns on
GPIO0BOOT ButtonInput + internal pull-upPressed=low, released=high

Dependencies: freertos, driver/gpio


5. Source Code Analysis

5.1 Macro Definitions and Global Variables

First define pin numbers and debounce time, then use a global variable to remember whether LED is currently ON or OFF.

xxxxxxxxxx
#define LED_GPIO         43
// Button: GPIO0 with pull-up
#define KEY_GPIO          0
#define KEY_DEBOUNCE_MS  50              // Debounce time (ms)
static bool led_state = true;

Why is debounce time 50ms? Mechanical button bouncing typically lasts 5~20ms. 50ms provides sufficient safety margin while being imperceptible to humans (humans are insensitive to button delays below 50ms). If debounce is too short (<10ms), it may not fully filter bouncing; if too long (>200ms), the button feels sluggish.

5.2 led_init() — LED Output Configuration

Configure GPIO43 as output mode to drive LED.

xxxxxxxxxx
void led_init(void)
{
    gpio_config_t led_config = {
        .pin_bit_mask = 1ULL << LED_GPIO,
        // Push-pull output
        .mode         = GPIO_MODE_OUTPUT,
        .pull_up_en   = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type    = GPIO_INTR_DISABLE,
    };
    gpio_config(&led_config);
}

This experiment configures GPIO directly in main (suitable for quick prototyping).

5.3 key_init() — Button Input Configuration

Configure GPIO0 as input mode with internal pull-up to detect button presses.

xxxxxxxxxx
void key_init(void)
{
    gpio_config_t key_config = {
        .pin_bit_mask = 1ULL << KEY_GPIO,
        .mode         = GPIO_MODE_INPUT,           // Input mode
        // Internal pull-up
        .pull_up_en   = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type    = GPIO_INTR_DISABLE,
    };
    gpio_config(&key_config);
}

Why use internal pull-up instead of external pull-up resistor? ESP32-S3 has built-in ~45kΩ weak pull-up, which is fully sufficient for button applications, saving BOM cost and PCB space. External pull-up (such as 10kΩ) is suitable for longer traces requiring more stable levels.

5.4 key_scan() — Debounced Scanning (Core)

Core logic of debounced scanning: detect low level → wait 50ms debounce → confirm still low → wait indefinitely for release → return true.

xxxxxxxxxx
bool key_scan(void)
{
    if (gpio_get_level(KEY_GPIO) == 0) {           // ① Detect LOW
        // ② Debounce delay 50ms
        vTaskDelay(KEY_DEBOUNCE_MS / portTICK_PERIOD_MS);
        if (gpio_get_level(KEY_GPIO) == 0) {       // ③ Re-confirm
            while (gpio_get_level(KEY_GPIO) == 0)   // ④ Wait for release
                vTaskDelay(10 / portTICK_PERIOD_MS);
            return true;                           // Valid press
        }
    }
    return false;                                  // No press or bounce
}

Execution flow analysis (for one complete button press):

xxxxxxxxxx
Timeline         GPIO0 Level       key_scan() Behavior
─────────────────────────────────────────────────
0ms            1 (released)      ①Read 1 → return false
...continuous polling...
100ms          0 (just pressed)   ①Read 0 → enter debounce
100~150ms      0 (may be bouncing) ②vTaskDelay(50ms) suspends
150ms          0 (stable)         ③Read again = 0 → confirmed valid
150~300ms      0 (holding)        ④while(0) loop waiting
300ms          1 (released)       ④while exits → return true

Why wait for release inside the debounce function? If returning true directly without waiting, the main loop calls key_scan() again after 20ms and would detect low level → toggle again → one press triggers multiple times. Embedded wait-for-release ensures "one press = one toggle", while preventing repeated triggers during long press.

5.5 led_control() — State Output

According to global variable led_state, actually set GPIO43 high/low level. Whether LED is ON or OFF is ultimately decided here.

xxxxxxxxxx
void led_control(void)
{
    gpio_set_level(LED_GPIO, led_state ? 0 : 1);
}

Centralizes mapping between logical state and physical level in this function. Currently common-anode connection (? 0 : 1, low level turns on). If hardware is common-cathode, just change ? 0 : 1 to ? 1 : 0, no changes needed in any callers.

5.6 app_main() — Main Entry

Main function: first initialize LED and button to their positions, then in an infinite loop continuously scan button; once a valid press is detected, toggle LED state.

xxxxxxxxxx
void app_main(void)
{
    led_init();
    key_init();
    led_control();

    while (1) {
        if (key_scan()) {                   // Valid press detected
            // Toggle logical state
            led_state = !led_state;
            // Update hardware output
            led_control();
        }
        vTaskDelay(20 / portTICK_PERIOD_MS); // 20ms polling interval
    }
}

Why 20ms polling interval: FreeRTOS default tick is 100Hz (10ms). 20ms is approximately 2 ticks, ensuring reasonable button response delay (worst case: just missed poll at press moment → maximum 20ms delay). Also very low CPU usage.


6. Operation Flow

6.1 Build and Flash

For detailed flashing process, see: 1. Before Development → 3. Build and Flash

6.2 Normal Operation

  • LED initially ON after power-on

  • Each press of BOOT button → LED toggles once (ON→OFF or OFF→ON)

  • Holding down: Only toggles once, next toggle only after release

    按下按键前

    按下按键之后

6.3 Boundary Testing

Test ItemOperationExpected Result
Short pressQuickly click BOOTLED toggles once
Long pressHold BOOT buttonLED toggles once (no repeated toggle while holding)
Rapid continuous pressContinuous fast clicksEach valid press toggles once (may lose very short pulses due to debounce)
Power-on defaultPower on without pressing buttonLED ON (led_state initially true)

7. Common Issues

PhenomenonCauseSolution
One press triggers multiple togglesDebounce time insufficientIncrease KEY_DEBOUNCE_MS (recommended 30~50ms)
Button response sluggishDebounce time too long / main loop interval too largeDecrease KEY_DEBOUNCE_MS or main loop delay
Button completely unresponsiveGPIO0 misconfigured / no internal pull-upCheck pull_up_en = GPIO_PULLUP_ENABLE
Toggles by itself without pressingGPIO0 floating + no pull-up/down → random levelEnable internal pull-up (GPIO_PULLUP_ENABLE)
LED and control logic reversedCommon-anode/common-cathode connection errorModify ternary mapping in led_control()
Compilation error driver/gpio.h not foundMissing dependency componentAdd driver to REQUIRES in CMakeLists.txt
LED Control
Peripheral Interrupts

COMPANY

  • About us
  • Team Style
  • Blog

LEARNING & EXPLORATION

  • Tutorials
  • Edge AI hardware
  • Educational robots
  • Embedded intelligent hardware

WHERE TO BUY

  • Yahboom Online Store
  • Amazon
  • Alibaba
  • AliExpress
  • Mercado

TECHNOLOGY SUPPORT

  • Email: support@yahboom.com

BE A DISTRIBUTOR

  • Email: sales@yahboom.com
QR Code

Copyright © 2017-2026 Yahboom. All Rights Reserved.

EN/中文