Skip to content

Commit 0695104

Browse files
committed
Add interrupt handling examples
Add three comprehensive examples demonstrating interrupt functionality: 1. InterruptBasic: - Simple single-button interrupt handling - Demonstrates FALLING (press) and RISING (release) modes - Shows proper ISR implementation with debouncing - Uses SimpleCallback for button events 2. InterruptMultipleButtons: - Handle interrupts from all D-PAD buttons simultaneously - Demonstrates CHANGE mode with level parameter - Per-button callbacks with event tracking - Press statistics for all buttons 3. InterruptWithPin: - Uses Pin-compatible API with interrupts - Demonstrates attachInterrupt/detachInterrupt - LED control triggered by button interrupts - Long press detection (>2s) for special actions All examples include: - STEAMI board I2C configuration - Proper debouncing (50ms) - Detailed comments and usage instructions - Error handling checks - Status reporting Updated README with interrupt examples section.
1 parent 4af5843 commit 0695104

4 files changed

Lines changed: 465 additions & 0 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,16 @@ All methods are identical to `MCP23009Pin`, but logic is automatically inverted.
232232
233233
The library includes several examples:
234234
235+
### Basic Examples
235236
- **BasicUsage** - Read D-PAD buttons and print states
236237
- **LED_ActiveLow** - Control LEDs in active-low configuration with effects
237238
- **ErrorHandling** - Demonstrates I2C error detection and handling
238239
240+
### Interrupt Examples
241+
- **InterruptBasic** - Simple interrupt handling with one button (FALLING/RISING)
242+
- **InterruptMultipleButtons** - Handle interrupts from multiple buttons (CHANGE mode)
243+
- **InterruptWithPin** - Use Pin API with interrupts and LED control
244+
239245
## Hardware Considerations
240246
241247
### I2C Address
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* Basic Interrupt Example for MCP23009E Library
3+
*
4+
* This example demonstrates how to use hardware interrupts with the MCP23009E.
5+
* When a button is pressed, an interrupt is triggered and handled by the ESP32.
6+
*
7+
* Hardware Setup:
8+
* - MCP23009E connected via I2C
9+
* - Button on GPIO 7 (with pull-up enabled)
10+
* - MCP23009E INT pin connected to ESP32 GPIO (INT_EXPANDER)
11+
*
12+
* Interrupt modes:
13+
* - FALLING: Triggered when button is pressed (HIGH to LOW)
14+
* - RISING: Triggered when button is released (LOW to HIGH)
15+
* - CHANGE: Triggered on both press and release
16+
*/
17+
18+
#include <Wire.h>
19+
#include <MCP23009E.h>
20+
21+
// Create MCP23009E instance
22+
MCP23009E mcp(Wire, MCP23009_I2C_ADDR);
23+
24+
// Reset pin (adjust for your board)
25+
const int RESET_PIN = RST_EXPANDER; // Set to your reset pin number, or -1 if none
26+
const int INTERRUPT_PIN = INT_EXPANDER; // ESP32 pin connected to MCP23009E INT
27+
28+
// Button configuration
29+
const int BUTTON_PIN = MCP23009_BTN_UP; // Use D-PAD UP button (GPIO 7)
30+
31+
// Volatile variables for interrupt handling
32+
volatile bool buttonPressed = false;
33+
volatile bool buttonReleased = false;
34+
volatile uint32_t lastInterruptTime = 0;
35+
const uint32_t DEBOUNCE_DELAY = 50; // 50ms debounce
36+
37+
// Interrupt Service Routine (ISR)
38+
void IRAM_ATTR handleMCP23009Interrupt() {
39+
uint32_t currentTime = millis();
40+
41+
// Simple debouncing
42+
if (currentTime - lastInterruptTime > DEBOUNCE_DELAY) {
43+
mcp.handleInterrupt(); // Let the library handle the interrupt
44+
lastInterruptTime = currentTime;
45+
}
46+
}
47+
48+
// Callback for button press (falling edge)
49+
void onButtonPress() {
50+
buttonPressed = true;
51+
}
52+
53+
// Callback for button release (rising edge)
54+
void onButtonRelease() {
55+
buttonReleased = true;
56+
}
57+
58+
void setup() {
59+
Serial.begin(115200);
60+
while (!Serial) delay(10);
61+
62+
Serial.println("Initializing I2C...");
63+
Wire.setSDA(I2C_INT_SDA);
64+
Wire.setSCL(I2C_INT_SCL);
65+
Wire.begin();
66+
67+
Serial.println("================================");
68+
Serial.println("MCP23009E Interrupt Basic Example");
69+
Serial.println("================================\n");
70+
71+
// Initialize MCP23009E
72+
mcp.begin(RESET_PIN, INTERRUPT_PIN);
73+
74+
// Configure button as input with pull-up
75+
Serial.println("Configuring button with interrupts...");
76+
mcp.setup(BUTTON_PIN, MCP23009_DIR_INPUT, MCP23009_PULLUP);
77+
78+
// Configure interrupts on the MCP23009E
79+
mcp.interruptOnFalling(BUTTON_PIN, onButtonPress);
80+
mcp.interruptOnRaising(BUTTON_PIN, onButtonRelease);
81+
82+
// Configure ESP32 interrupt pin
83+
pinMode(INTERRUPT_PIN, INPUT_PULLUP);
84+
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), handleMCP23009Interrupt, FALLING);
85+
86+
Serial.println("✓ Interrupt configuration complete");
87+
Serial.println("\nPress the UP button to trigger interrupts...\n");
88+
Serial.println("(Button should be connected with pull-up)");
89+
Serial.println("Press = FALLING interrupt");
90+
Serial.println("Release = RISING interrupt\n");
91+
}
92+
93+
uint32_t pressCount = 0;
94+
uint32_t releaseCount = 0;
95+
96+
void loop() {
97+
// Handle button press events in main loop (not in ISR)
98+
if (buttonPressed) {
99+
buttonPressed = false;
100+
pressCount++;
101+
Serial.print("✓ Button PRESSED (count: ");
102+
Serial.print(pressCount);
103+
Serial.println(")");
104+
}
105+
106+
// Handle button release events in main loop (not in ISR)
107+
if (buttonReleased) {
108+
buttonReleased = false;
109+
releaseCount++;
110+
Serial.print("✓ Button RELEASED (count: ");
111+
Serial.print(releaseCount);
112+
Serial.println(")");
113+
}
114+
115+
// Print status every 5 seconds
116+
static uint32_t lastStatusTime = 0;
117+
if (millis() - lastStatusTime > 5000) {
118+
lastStatusTime = millis();
119+
Serial.println("\n--- Status ---");
120+
Serial.print("Total presses: ");
121+
Serial.println(pressCount);
122+
Serial.print("Total releases: ");
123+
Serial.println(releaseCount);
124+
Serial.println("Waiting for interrupts...\n");
125+
}
126+
127+
// Small delay to avoid excessive CPU usage
128+
delay(10);
129+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/**
2+
* Multiple Buttons Interrupt Example for MCP23009E Library
3+
*
4+
* This example demonstrates handling interrupts from multiple buttons (D-PAD).
5+
* Each button can trigger an interrupt and the handler identifies which button
6+
* caused the interrupt and its state (pressed/released).
7+
*
8+
* Hardware Setup:
9+
* - MCP23009E connected via I2C
10+
* - D-PAD buttons on GPIO 4, 5, 6, 7 (with pull-ups enabled)
11+
* - MCP23009E INT pin connected to ESP32 GPIO (INT_EXPANDER)
12+
*
13+
* D-PAD Button Mapping:
14+
* - GPIO 7: UP
15+
* - GPIO 5: DOWN
16+
* - GPIO 6: LEFT
17+
* - GPIO 4: RIGHT
18+
*/
19+
20+
#include <Wire.h>
21+
#include <MCP23009E.h>
22+
23+
// Create MCP23009E instance
24+
MCP23009E mcp(Wire, MCP23009_I2C_ADDR);
25+
26+
// Reset pin (adjust for your board)
27+
const int RESET_PIN = RST_EXPANDER;
28+
const int INTERRUPT_PIN = INT_EXPANDER;
29+
30+
// Button states
31+
struct ButtonState {
32+
bool changed;
33+
uint8_t level;
34+
uint32_t pressCount;
35+
};
36+
37+
volatile ButtonState upButton = {false, HIGH, 0};
38+
volatile ButtonState downButton = {false, HIGH, 0};
39+
volatile ButtonState leftButton = {false, HIGH, 0};
40+
volatile ButtonState rightButton = {false, HIGH, 0};
41+
42+
volatile uint32_t lastInterruptTime = 0;
43+
const uint32_t DEBOUNCE_DELAY = 50;
44+
45+
// Interrupt Service Routine
46+
void IRAM_ATTR handleMCP23009Interrupt() {
47+
uint32_t currentTime = millis();
48+
49+
if (currentTime - lastInterruptTime > DEBOUNCE_DELAY) {
50+
mcp.handleInterrupt();
51+
lastInterruptTime = currentTime;
52+
}
53+
}
54+
55+
// Individual button callbacks using CHANGE mode
56+
void onUpButtonChange(uint8_t level) {
57+
upButton.changed = true;
58+
upButton.level = level;
59+
if (level == LOW) upButton.pressCount++;
60+
}
61+
62+
void onDownButtonChange(uint8_t level) {
63+
downButton.changed = true;
64+
downButton.level = level;
65+
if (level == LOW) downButton.pressCount++;
66+
}
67+
68+
void onLeftButtonChange(uint8_t level) {
69+
leftButton.changed = true;
70+
leftButton.level = level;
71+
if (level == LOW) leftButton.pressCount++;
72+
}
73+
74+
void onRightButtonChange(uint8_t level) {
75+
rightButton.changed = true;
76+
rightButton.level = level;
77+
if (level == LOW) rightButton.pressCount++;
78+
}
79+
80+
void setup() {
81+
Serial.begin(115200);
82+
while (!Serial) delay(10);
83+
84+
Serial.println("Initializing I2C...");
85+
Wire.setSDA(I2C_INT_SDA);
86+
Wire.setSCL(I2C_INT_SCL);
87+
Wire.begin();
88+
89+
Serial.println("========================================");
90+
Serial.println("MCP23009E Multiple Buttons Interrupt Example");
91+
Serial.println("========================================\n");
92+
93+
// Initialize MCP23009E
94+
mcp.begin(RESET_PIN, INTERRUPT_PIN);
95+
96+
// Configure all D-PAD buttons with pull-ups
97+
Serial.println("Configuring D-PAD buttons with interrupts...");
98+
mcp.setup(MCP23009_BTN_UP, MCP23009_DIR_INPUT, MCP23009_PULLUP);
99+
mcp.setup(MCP23009_BTN_DOWN, MCP23009_DIR_INPUT, MCP23009_PULLUP);
100+
mcp.setup(MCP23009_BTN_LEFT, MCP23009_DIR_INPUT, MCP23009_PULLUP);
101+
mcp.setup(MCP23009_BTN_RIGHT, MCP23009_DIR_INPUT, MCP23009_PULLUP);
102+
103+
// Configure interrupts for all buttons (CHANGE mode with level callback)
104+
mcp.interruptOnChange(MCP23009_BTN_UP, onUpButtonChange);
105+
mcp.interruptOnChange(MCP23009_BTN_DOWN, onDownButtonChange);
106+
mcp.interruptOnChange(MCP23009_BTN_LEFT, onLeftButtonChange);
107+
mcp.interruptOnChange(MCP23009_BTN_RIGHT, onRightButtonChange);
108+
109+
// Configure ESP32 interrupt pin
110+
pinMode(INTERRUPT_PIN, INPUT_PULLUP);
111+
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), handleMCP23009Interrupt, FALLING);
112+
113+
Serial.println("✓ All interrupts configured");
114+
Serial.println("\nPress any D-PAD button to see interrupt handling...");
115+
Serial.println("(Buttons: UP, DOWN, LEFT, RIGHT)\n");
116+
}
117+
118+
void printButtonEvent(const char* buttonName, const ButtonState& button) {
119+
if (button.changed) {
120+
Serial.print("");
121+
Serial.print(buttonName);
122+
Serial.print(": ");
123+
Serial.print(button.level ? "RELEASED" : "PRESSED");
124+
Serial.print(" (total presses: ");
125+
Serial.print(button.pressCount);
126+
Serial.println(")");
127+
}
128+
}
129+
130+
void loop() {
131+
// Check and handle all button events
132+
bool anyChange = false;
133+
134+
if (upButton.changed) {
135+
printButtonEvent("UP ", upButton);
136+
upButton.changed = false;
137+
anyChange = true;
138+
}
139+
140+
if (downButton.changed) {
141+
printButtonEvent("DOWN ", downButton);
142+
downButton.changed = false;
143+
anyChange = true;
144+
}
145+
146+
if (leftButton.changed) {
147+
printButtonEvent("LEFT ", leftButton);
148+
leftButton.changed = false;
149+
anyChange = true;
150+
}
151+
152+
if (rightButton.changed) {
153+
printButtonEvent("RIGHT", rightButton);
154+
rightButton.changed = false;
155+
anyChange = true;
156+
}
157+
158+
// Add a blank line after processing all changes
159+
if (anyChange) {
160+
Serial.println();
161+
}
162+
163+
// Print statistics every 10 seconds
164+
static uint32_t lastStatsTime = 0;
165+
if (millis() - lastStatsTime > 10000) {
166+
lastStatsTime = millis();
167+
Serial.println("--- Button Press Statistics ---");
168+
Serial.print("UP: ");
169+
Serial.print(upButton.pressCount);
170+
Serial.print(" | DOWN: ");
171+
Serial.print(downButton.pressCount);
172+
Serial.print(" | LEFT: ");
173+
Serial.print(leftButton.pressCount);
174+
Serial.print(" | RIGHT: ");
175+
Serial.println(rightButton.pressCount);
176+
Serial.println("-------------------------------\n");
177+
}
178+
179+
// Small delay
180+
delay(10);
181+
}

0 commit comments

Comments
 (0)