Circuit Diagram:
🌟 ESP32 18-LED Pattern Controller
Professional-Grade Dynamic Lighting Sequencer
This high-density LED sequencer for the ESP32 is designed to create beautiful, dynamic lighting effects across 18 individual LEDs. The system utilizes modular code, allowing developers to easily expand, modify, or customize patterns for visual displays or decorative lighting.
🛠️ Hardware Configuration
The system is optimized for standard ESP32 Development Boards. All LEDs must share a common Ground (GND) and utilize current-limiting resistors (220Ω - 470Ω) to prevent hardware damage.
| LED # | GPIO Pin | LED # | GPIO Pin |
|---|---|---|---|
| 1 | 22 | 10 | 2 |
| 2 | 23 | 11 | 4 |
| 3 | 13 | 12 | 5 |
| 4 | 12 | 13 | 15 |
| 5 | 14 | 14 | 16 |
| 6 | 27 | 15 | 17 |
| 7 | 26 | 16 | 18 |
| 8 | 25 | 17 | 19 |
| 9 | 33 | 18 | 20* |
*Note: GPIO 20 may be internal on some DevKits; if LED 18 fails, re-route to GPIO 21 or 32.
✨ Included Visual Patterns
⚠️ Critical Operational Notes
- Power Draw: Lighting 18 LEDs simultaneously can cause ESP32 brownouts. Increase resistor values or use an external 5V supply if stability issues occur.
- Logic Level: ESP32 uses 3.3V logic. Do not connect pins to 5V sources without appropriate level shifters.
ESP32 18-LED Interactive Controller: Multi-Mode Pattern Hub
In this project, we are engineering a high-density, interactive **18-LED Multi-Mode Pattern Array** using an **ESP32**. Utilizing hardware-level interrupts for seamless transition sequences, this system runs three separate visual profiles including aggressive chasers, randomized organic matrices, and ambient breathing pulses controlled with a single button input.
System Architecture & Mode Management
The control firmware relies on an asynchronous hardware interrupt matrix to track state changes instantly without waiting for blocking loop cycles:
- Asynchronous Interrupt Debouncing: Instead of blocking digital reads, the tactile switch triggers an isolated Interrupt Service Routine (ISR) mapped directly onto RAM (
IRAM_ATTR). Software-based debounce calculations intercept false triggers within a 250ms hardware settling window. - Visual Profile Framework: State transitions actively cycle a mode tracking parameter from 0 to 2, forcing processing pipelines to cleanly skip long delay blocks and jump to the chosen animation cluster instantly.
Hardware Connection Pinout
To safely scale an 18-channel digital matrix off an ESP32 board without overlapping essential hardware UART, SPI, or boot strapping configurations, wire your modules directly to these mapped GPIO channels:
| LED Element No. | ESP32 GPIO Pin | System Module Connection | Signal Interface Type |
|---|---|---|---|
| LED 1 — LED 5 | GPIO 22, 23, 13, 12, 14 | Anode Pins via 220Ω Resistors | Digital Logic Output |
| LED 6 — LED 10 | GPIO 27, 26, 25, 33, 2 | Anode Pins via 220Ω Resistors | Digital Logic Output |
| LED 11 — LED 15 | GPIO 4, 5, 15, 16, 17 | Anode Pins via 220Ω Resistors | Digital Logic Output |
| LED 16 — LED 18 | GPIO 18, 19, 21 | Anode Pins via 220Ω Resistors | Digital Logic Output |
| Tactile Switch | GPIO 32 | Connect between Pin 32 and GND | Digital Input (Internal Pullup) |
Arduino Code Configuration
Here is the full interactive source firmware. This script requires no third-party library dependencies, running entirely on optimized native digital arrays and hardware timers:
/*
* ESP32 18-LED Interactive Controller - FULL VERSION
* Pins: 22, 23, 13, 12, 14, 27, 26, 25, 33, 2, 4, 5, 15, 16, 17, 18, 19, 21
* Button: GPIO 32 (Connect to Pin 32 and GND)
*/
// LED Pin Mapping
const int ledPins[] = {22, 23, 13, 12, 14, 27, 26, 25, 33, 2, 4, 5, 15, 16, 17, 18, 19, 21};
const int totalLeds = 18;
const int buttonPin = 32;
// Variables for Mode Control
volatile int mode = 0;
unsigned long lastDebounceTime = 0;
const int debounceDelay = 250;
// Interrupt Service Routine (ISR) for the button
void IRAM_ATTR changeMode() {
if ((millis() - lastDebounceTime) > debounceDelay) {
mode++;
if (mode > 2) mode = 0;
lastDebounceTime = millis();
}
}
void setup() {
for (int i = 0; i < totalLeds; i++) {
pinMode(ledPins[i], OUTPUT);
digitalWrite(ledPins[i], LOW);
}
// Set up button with internal pullup
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(buttonPin), changeMode, FALLING);
randomSeed(analogRead(0));
}
void loop() {
switch (mode) {
case 0: // Mode 1: Energetic
simpleChase(50);
meetingPoint(80);
break;
case 1: // Mode 2: Shimmering
theaterChase(100);
sparkle(20, 50);
break;
case 2: // Mode 3: Atmospheric
heartbeat(1);
breathingEffect();
break;
}
}
// --- PATTERN FUNCTIONS ---
void simpleChase(int speed) {
for (int i = 0; i < totalLeds; i++) {
digitalWrite(ledPins[i], HIGH);
delay(speed);
digitalWrite(ledPins[i], LOW);
if (mode != 0) return; // Exit early if mode changed
}
}
void meetingPoint(int speed) {
int half = totalLeds / 2;
// Inward
for (int i = 0; i < half; i++) {
digitalWrite(ledPins[i], HIGH);
digitalWrite(ledPins[totalLeds - 1 - i], HIGH);
delay(speed);
digitalWrite(ledPins[i], LOW);
digitalWrite(ledPins[totalLeds - 1 - i], LOW);
}
// Outward
for (int i = half - 1; i >= 0; i--) {
digitalWrite(ledPins[i], HIGH);
digitalWrite(ledPins[totalLeds - 1 - i], HIGH);
delay(speed);
digitalWrite(ledPins[i], LOW);
digitalWrite(ledPins[totalLeds - 1 - i], LOW);
}
}
void theaterChase(int speed) {
for (int q = 0; q < 3; q++) {
for (int i = 0; i < totalLeds; i = i + 3) {
if (i + q < totalLeds) digitalWrite(ledPins[i + q], HIGH);
}
delay(speed);
for (int i = 0; i < totalLeds; i = i + 3) {
if (i + q < totalLeds) digitalWrite(ledPins[i + q], LOW);
}
}
}
void sparkle(int count, int duration) {
for (int i = 0; i < count; i++) {
int index = random(0, totalLeds);
digitalWrite(ledPins[index], HIGH);
delay(duration);
digitalWrite(ledPins[index], LOW);
if (mode != 1) return;
}
}
void heartbeat(int times) {
for (int i = 0; i < times; i++) {
allLeds(HIGH); delay(100);
allLeds(LOW); delay(100);
allLeds(HIGH); delay(150);
allLeds(LOW); delay(600);
}
}
void breathingEffect() {
// Simulating a glow since simple digital pins can't fade without PWM
allLeds(HIGH);
delay(500);
allLeds(LOW);
delay(500);
}
void allLeds(int state) {
for (int i = 0; i < totalLeds; i++) {
digitalWrite(ledPins[i], state);
}
}
System Testing Checklist
- Boot Diagnostics: On powerup, all 18 pins register low initialization code. The hardware maps automatically into Mode 0, executing a sharp 50ms chaser routine.
- Interrupt Verification: Pressing the tactile pin drops the state voltage low. The interrupt service loop executes immediately, stepping straight to the theater chase sequence without completing the previous chaser loops.
- Early Return Safeguards: Functions like
simpleChaseandsparklerun an active conditional flag test (if (mode != X) return;) within their active internal loops to eliminate lag between button presses.
LED Pattern Simulator
Experience all 5 lighting patterns (Chase, Sparkle, Heartbeat, and more) in this interactive virtual environment. Watch how the ESP32 manages high-density GPIO signaling in real-time.
🎥 Project Demonstration: 18-LED Dynamic Patterns
0 Comments