* 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> |
||
|---|---|---|
| .. | ||
| FunctionalInterruptLambda.ino | ||
| README.md | ||
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
- CHANGE mode lambda to detect both RISING and FALLING edges
- LED toggle on button press (FALLING edge)
- Edge type detection using digitalRead() within ISR
- Hardware debouncing with configurable timeout
- 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:
- Time-based: Ignores interrupts within 50 ms of previous one
- 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)