Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 82d56bc

Browse files
SuGliderCopilotpre-commit-ci-lite[bot]me-no-dev
authored
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>
1 parent 1f0d4b5 commit 82d56bc

File tree

2 files changed

+304
-0
lines changed

2 files changed

+304
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
ESP32 Lambda FunctionalInterrupt Example
19+
========================================
20+
21+
This example demonstrates how to use lambda functions with FunctionalInterrupt
22+
for GPIO pin interrupt callbacks on ESP32. It shows CHANGE mode detection
23+
with LED toggle functionality and proper debouncing.
24+
25+
Hardware Setup:
26+
- Use BOOT Button or connect a button between BUTTON_PIN and GND (with internal pullup)
27+
- Use Builtin Board LED or connect an LED with resistor to GPIO 2 (LED_PIN)
28+
29+
Features Demonstrated:
30+
1. CHANGE mode lambda to detect both RISING and FALLING edges
31+
2. LED toggle on button press (FALLING edge)
32+
3. Edge type detection using digitalRead() within ISR
33+
4. Hardware debouncing with configurable timeout
34+
35+
IMPORTANT NOTE ABOUT ESP32 INTERRUPT BEHAVIOR:
36+
- Only ONE interrupt handler can be attached per GPIO pin at a time
37+
- Calling attachInterrupt() on a pin that already has an interrupt will override the previous one
38+
- This applies regardless of edge type (RISING, FALLING, CHANGE)
39+
- If you need both RISING and FALLING detection on the same pin, use CHANGE mode
40+
and determine the edge type within your handler by reading the pin state
41+
*/
42+
43+
#include <Arduino.h>
44+
#include <FunctionalInterrupt.h>
45+
46+
// Pin definitions
47+
#define BUTTON_PIN BOOT_PIN // BOOT BUTTON - change as needed
48+
#ifdef LED_BUILTIN
49+
#define LED_PIN LED_BUILTIN
50+
#else
51+
#warning Using LED_PIN = GPIO 2 as default - change as needed
52+
#define LED_PIN 2 // change as needed
53+
#endif
54+
55+
// Global variables for interrupt handling (volatile for ISR safety)
56+
volatile uint32_t buttonPressCount = 0;
57+
volatile uint32_t buttonReleaseCount = 0;
58+
volatile bool buttonPressed = false;
59+
volatile bool buttonReleased = false;
60+
volatile bool ledState = false;
61+
volatile bool ledStateChanged = false; // Flag to indicate LED needs updating
62+
63+
// Debouncing variables (volatile for ISR safety)
64+
volatile unsigned long lastButtonInterruptTime = 0;
65+
const unsigned long DEBOUNCE_DELAY_MS = 50; // 50ms debounce delay
66+
67+
// State-based debouncing to prevent hysteresis issues
68+
volatile bool lastButtonState = HIGH; // Track last stable state (HIGH = released)
69+
70+
// Global lambda function (declared at file scope) - ISR in IRAM
71+
IRAM_ATTR std::function<void()> changeModeLambda = []() {
72+
// Simple debouncing: check if enough time has passed since last interrupt
73+
unsigned long currentTime = millis();
74+
if (currentTime - lastButtonInterruptTime < DEBOUNCE_DELAY_MS) {
75+
return; // Ignore this interrupt due to bouncing
76+
}
77+
78+
// Read current pin state to determine edge type
79+
bool currentState = digitalRead(BUTTON_PIN);
80+
81+
// State-based debouncing: only process if state actually changed
82+
if (currentState == lastButtonState) {
83+
return; // No real state change, ignore (hysteresis/noise)
84+
}
85+
86+
// Update timing and state
87+
lastButtonInterruptTime = currentTime;
88+
lastButtonState = currentState;
89+
90+
if (currentState == LOW) {
91+
// FALLING edge detected (button pressed) - set flag for main loop
92+
// volatile variables require use of temporary value transfer
93+
uint32_t temp = buttonPressCount + 1;
94+
buttonPressCount = temp;
95+
buttonPressed = true;
96+
ledStateChanged = true; // Signal main loop to toggle LED
97+
} else {
98+
// RISING edge detected (button released) - set flag for main loop
99+
// volatile variables require use of temporary value transfer
100+
uint32_t temp = buttonReleaseCount + 1;
101+
buttonReleaseCount = temp;
102+
buttonReleased = true;
103+
}
104+
};
105+
106+
void setup() {
107+
Serial.begin(115200);
108+
delay(1000); // Allow serial monitor to connect
109+
110+
Serial.println("ESP32 Lambda FunctionalInterrupt Example");
111+
Serial.println("========================================");
112+
113+
// Configure pins
114+
pinMode(BUTTON_PIN, INPUT_PULLUP);
115+
pinMode(LED_PIN, OUTPUT);
116+
digitalWrite(LED_PIN, LOW);
117+
118+
// CHANGE mode lambda to handle both RISING and FALLING edges
119+
// This toggles the LED on button press (FALLING edge)
120+
Serial.println("Setting up CHANGE mode lambda for LED toggle");
121+
122+
// Use the global lambda function
123+
attachInterrupt(BUTTON_PIN, changeModeLambda, CHANGE);
124+
125+
Serial.println();
126+
Serial.printf("Lambda interrupt configured on Pin %d (CHANGE mode)\r\n", BUTTON_PIN);
127+
Serial.printf("Debounce delay: %lu ms\r\n", DEBOUNCE_DELAY_MS);
128+
Serial.println();
129+
Serial.println("Press the button to toggle the LED!");
130+
Serial.println("Button press (FALLING edge) will toggle the LED.");
131+
Serial.println("Button release (RISING edge) will be detected and reported.");
132+
Serial.println("Button includes debouncing to prevent mechanical bounce issues.");
133+
Serial.println();
134+
}
135+
136+
void loop() {
137+
// Handle LED state changes (ISR-safe approach)
138+
if (ledStateChanged) {
139+
ledStateChanged = false;
140+
ledState = !ledState; // Toggle LED state in main loop
141+
digitalWrite(LED_PIN, ledState);
142+
}
143+
144+
// Check for button presses
145+
if (buttonPressed) {
146+
buttonPressed = false;
147+
Serial.printf("==> Button PRESSED! Count: %lu, LED: %s (FALLING edge)\r\n", buttonPressCount, ledState ? "ON" : "OFF");
148+
}
149+
150+
// Check for button releases
151+
if (buttonReleased) {
152+
buttonReleased = false;
153+
Serial.printf("==> Button RELEASED! Count: %lu (RISING edge)\r\n", buttonReleaseCount);
154+
}
155+
156+
delay(10);
157+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# ESP32 Lambda FunctionalInterrupt Example
2+
3+
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.
4+
5+
## Features Demonstrated
6+
7+
1. **CHANGE mode lambda** to detect both RISING and FALLING edges
8+
2. **LED toggle on button press** (FALLING edge)
9+
3. **Edge type detection** using digitalRead() within ISR
10+
4. **Hardware debouncing** with configurable timeout
11+
5. **IRAM_ATTR lambda declaration** for optimal ISR performance in RAM
12+
13+
## Hardware Setup
14+
15+
- Use BOOT Button or connect a button between BUTTON_PIN and GND (with internal pullup)
16+
- Use Builtin Board LED (no special hardware setup) or connect an LED with resistor to GPIO assigned as LED_PIN.\
17+
Some boards have an RGB LED that needs no special hardware setup to work as a simple white on/off LED.
18+
19+
```
20+
ESP32 Board Button/LED
21+
----------- ---------
22+
BOOT_PIN ------------ [BUTTON] ---- GND
23+
LED_PIN --------------- [LED] ----- GND
24+
¦
25+
[330O] (*) Only needed when using an external LED attached to the GPIO.
26+
¦
27+
3V3
28+
```
29+
30+
## Important ESP32 Interrupt Behavior
31+
32+
**CRITICAL:** Only ONE interrupt handler can be attached per GPIO pin at a time on ESP32.
33+
34+
- Calling `attachInterrupt()` on a pin that already has an interrupt will **override** the previous one
35+
- This applies regardless of edge type (RISING, FALLING, CHANGE)
36+
- 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
37+
38+
## Code Overview
39+
40+
This example demonstrates a simple CHANGE mode lambda interrupt that:
41+
42+
- **Detects both button press and release** using a single interrupt handler
43+
- **Toggles LED only on button press** (FALLING edge)
44+
- **Reports both press and release events** to Serial output
45+
- **Uses proper debouncing** to prevent switch bounce issues
46+
- **Implements minimal lambda captures** for simplicity
47+
48+
## Lambda Function Pattern
49+
50+
### CHANGE Mode Lambda with IRAM Declaration
51+
```cpp
52+
// Global lambda declared with IRAM_ATTR for optimal ISR performance
53+
IRAM_ATTR std::function<void()> changeModeLambda = []() {
54+
// Debouncing check
55+
unsigned long currentTime = millis();
56+
if (currentTime - lastButtonInterruptTime < DEBOUNCE_DELAY_MS) {
57+
return; // Ignore bouncing
58+
}
59+
60+
// Determine edge type
61+
bool currentState = digitalRead(BUTTON_PIN);
62+
if (currentState == lastButtonState) {
63+
return; // No real state change
64+
}
65+
66+
// Update state and handle edges
67+
lastButtonInterruptTime = currentTime;
68+
lastButtonState = currentState;
69+
70+
if (currentState == LOW) {
71+
// Button pressed (FALLING edge)
72+
buttonPressCount++;
73+
buttonPressed = true;
74+
ledStateChanged = true; // Signal LED toggle
75+
} else {
76+
// Button released (RISING edge)
77+
buttonReleaseCount++;
78+
buttonReleased = true;
79+
}
80+
};
81+
82+
attachInterrupt(BUTTON_PIN, changeModeLambda, CHANGE);
83+
```
84+
85+
## Key Concepts
86+
87+
### Edge Detection in CHANGE Mode
88+
```cpp
89+
if (digitalRead(pin) == LOW) {
90+
// FALLING edge detected (button pressed)
91+
} else {
92+
// RISING edge detected (button released)
93+
}
94+
```
95+
96+
### Debouncing Strategy
97+
This example implements dual-layer debouncing:
98+
1. **Time-based**: Ignores interrupts within 50 ms of previous one
99+
2. **State-based**: Only processes actual state changes
100+
101+
### Main Loop Processing
102+
```cpp
103+
void loop() {
104+
// Handle LED changes safely outside ISR
105+
if (ledStateChanged) {
106+
ledStateChanged = false;
107+
ledState = !ledState;
108+
digitalWrite(LED_PIN, ledState);
109+
}
110+
111+
// Report button events
112+
if (buttonPressed) {
113+
// Handle press event
114+
}
115+
if (buttonReleased) {
116+
// Handle release event
117+
}
118+
}
119+
```
120+
121+
## Expected Output
122+
123+
```
124+
ESP32 Lambda FunctionalInterrupt Example
125+
========================================
126+
Setting up CHANGE mode lambda for LED toggle
127+
128+
Lambda interrupt configured on Pin 0 (CHANGE mode)
129+
Debounce delay: 50 ms
130+
131+
Press the button to toggle the LED!
132+
Button press (FALLING edge) will toggle the LED.
133+
Button release (RISING edge) will be detected and reported.
134+
Button includes debouncing to prevent mechanical bounce issues.
135+
136+
==> Button PRESSED! Count: 1, LED: ON (FALLING edge)
137+
==> Button RELEASED! Count: 1 (RISING edge)
138+
==> Button PRESSED! Count: 2, LED: OFF (FALLING edge)
139+
==> Button RELEASED! Count: 2 (RISING edge)
140+
```
141+
142+
## Pin Configuration
143+
144+
The example uses these default pins:
145+
146+
- `BUTTON_PIN`: BOOT_PIN (automatically assigned by the Arduino Core)
147+
- `LED_PIN`: LED_BUILTIN (may not be available for your board - please verify it)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /