arduino-esp32/libraries/ESP32/examples/GPIO/FunctionalInterruptLambda
Sugar Glider 82d56bc679
feat(gpio): new functional interrupt lambda example (#11589)
* feat(gpio): new functional interrupt lambda example

* fix(readme): schematic diagram allignment

* fix(example): uses volatile for ISR variables

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix(example): uses volatile for ISR variables

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix(example): uses volatile data type also for the pointers

* fix(readme): clear documentation

* feat(example): improves ISR execution time

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* feat(gpio): simplifies the example and documentation

* feat(gpio): uses IRAM lambda and fixes volatile operation

* fix(doc): fixing documentation apresentation

* ci(pre-commit): Apply automatic fixes

* fix(ci): Update README.md

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Me No Dev <me-no-dev@users.noreply.github.com>
2025-07-16 11:51:31 +03:00
..
FunctionalInterruptLambda.ino feat(gpio): new functional interrupt lambda example (#11589) 2025-07-16 11:51:31 +03:00
README.md feat(gpio): new functional interrupt lambda example (#11589) 2025-07-16 11:51:31 +03:00

ESP32 Lambda FunctionalInterrupt Example

This example demonstrates how to use lambda functions with FunctionalInterrupt for GPIO pin interrupt callbacks on ESP32. It shows CHANGE mode detection with LED toggle functionality and proper debouncing.

Features Demonstrated

  1. CHANGE mode lambda to detect both RISING and FALLING edges
  2. LED toggle on button press (FALLING edge)
  3. Edge type detection using digitalRead() within ISR
  4. Hardware debouncing with configurable timeout
  5. IRAM_ATTR lambda declaration for optimal ISR performance in RAM

Hardware Setup

  • Use BOOT Button or connect a button between BUTTON_PIN and GND (with internal pullup)
  • Use Builtin Board LED (no special hardware setup) or connect an LED with resistor to GPIO assigned as LED_PIN.
    Some boards have an RGB LED that needs no special hardware setup to work as a simple white on/off LED.
ESP32 Board          Button/LED
-----------          ---------
BOOT_PIN ------------ [BUTTON] ---- GND
LED_PIN --------------- [LED] ----- GND
                          ¦
                        [330O] (*) Only needed when using an external LED attached to the GPIO.
                          ¦
                         3V3

Important ESP32 Interrupt Behavior

CRITICAL: Only ONE interrupt handler can be attached per GPIO pin at a time on ESP32.

  • Calling attachInterrupt() on a pin that already has an interrupt will override the previous one
  • This applies regardless of edge type (RISING, FALLING, CHANGE)
  • If you need both RISING and FALLING detection on the same pin, use CHANGE mode and determine the edge type within your handler by reading the pin state

Code Overview

This example demonstrates a simple CHANGE mode lambda interrupt that:

  • Detects both button press and release using a single interrupt handler
  • Toggles LED only on button press (FALLING edge)
  • Reports both press and release events to Serial output
  • Uses proper debouncing to prevent switch bounce issues
  • Implements minimal lambda captures for simplicity

Lambda Function Pattern

CHANGE Mode Lambda with IRAM Declaration

// Global lambda declared with IRAM_ATTR for optimal ISR performance
IRAM_ATTR std::function<void()> changeModeLambda = []() {
    // Debouncing check
    unsigned long currentTime = millis();
    if (currentTime - lastButtonInterruptTime < DEBOUNCE_DELAY_MS) {
        return; // Ignore bouncing
    }

    // Determine edge type
    bool currentState = digitalRead(BUTTON_PIN);
    if (currentState == lastButtonState) {
        return; // No real state change
    }

    // Update state and handle edges
    lastButtonInterruptTime = currentTime;
    lastButtonState = currentState;

    if (currentState == LOW) {
        // Button pressed (FALLING edge)
        buttonPressCount++;
        buttonPressed = true;
        ledStateChanged = true;  // Signal LED toggle
    } else {
        // Button released (RISING edge)  
        buttonReleaseCount++;
        buttonReleased = true;
    }
};

attachInterrupt(BUTTON_PIN, changeModeLambda, CHANGE);

Key Concepts

Edge Detection in CHANGE Mode

if (digitalRead(pin) == LOW) {
  // FALLING edge detected (button pressed)
} else {
  // RISING edge detected (button released)
}

Debouncing Strategy

This example implements dual-layer debouncing:

  1. Time-based: Ignores interrupts within 50 ms of previous one
  2. State-based: Only processes actual state changes

Main Loop Processing

void loop() {
  // Handle LED changes safely outside ISR
  if (ledStateChanged) {
    ledStateChanged = false;
    ledState = !ledState;
    digitalWrite(LED_PIN, ledState);
  }

  // Report button events
  if (buttonPressed) {
    // Handle press event
  }
  if (buttonReleased) {
    // Handle release event  
  }
}

Expected Output

ESP32 Lambda FunctionalInterrupt Example
========================================
Setting up CHANGE mode lambda for LED toggle

Lambda interrupt configured on Pin 0 (CHANGE mode)
Debounce delay: 50 ms

Press the button to toggle the LED!
Button press (FALLING edge) will toggle the LED.
Button release (RISING edge) will be detected and reported.
Button includes debouncing to prevent mechanical bounce issues.

==> Button PRESSED! Count: 1, LED: ON (FALLING edge)
==> Button RELEASED! Count: 1 (RISING edge)
==> Button PRESSED! Count: 2, LED: OFF (FALLING edge)
==> Button RELEASED! Count: 2 (RISING edge)

Pin Configuration

The example uses these default pins:

  • BUTTON_PIN: BOOT_PIN (automatically assigned by the Arduino Core)
  • LED_PIN: LED_BUILTIN (may not be available for your board - please verify it)