SnapSwitch: Building a Clap-Controlled RGB LED with ESP32 & I2S
Greetings from my most recent microcontroller project! In this post, I’ll demonstrate how I used an ESP32 and an INMP441 digital I2S microphone module to create an audio-reactive lighting system that transforms a simple snap of your fingers into a dynamic RGB light show.

View the Video
For written instructions, continue reading this page, or watch the video guide.
Required Components & Wiring Pinout
- INMP441 I2S Omnidirectional Digital Microphone Module
- ESP32-S3

How SnapSwitch Works: I2S Sound Detection
The INMP441 is an omnidirectional, digital-output, low-power, high-performance MEMS microphone with a bottom port. A MEMS sensor, signal conditioning, an analog-to-digital converter, anti-aliasing filters, power management, and an industry-standard 24-bit I2S interface make up the entire INMP441 system. Without the requirement for an audio codec in the system, the INMP441 can connect directly to digital processors like DSPs and microcontrollers thanks to the I2S interface.
INMP441 pinout

ESP32 to INMP441 Microphone Connections

Arduino C++ Code Setup
#include <driver/i2s.h>
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
// --- I2S Microphone Pin Definitions ---
#define I2S_BCLK_MIC 47 // SCK
#define I2S_LRC_MIC 10 // WS
#define I2S_DIN_MIC 21 // SD
#define I2S_SD_SPK 4 // Speaker DAC shutdown pin
#define I2S_MIC_PORT I2S_NUM_0
// --- NeoPixel Setup ---
#define LED_PIN 48 // NeoPixel GPIO
#define NUM_PIXELS 1 // Built-in single RGB pixel
Adafruit_NeoPixel pixels(NUM_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
// --- Audio & State Variables ---
#define SAMPLE_BUFFER_SIZE 512
#define SAMPLE_RATE 16000
const int soundThreshold = 2500; // Adjust based on room noise
unsigned long lastTriggerTime = 0; // Timestamp for snap debouncing
const unsigned long cooldownMs = 400; // Debounce cooldown in milliseconds
// State Variables
bool isAnimating = false; // Toggle: false = OFF, true = Playing RGB cycle
int colorIndex = 0; // Tracks current active color (0 = Red, 1 = Green, 2 = Blue)
unsigned long lastColorChange = 0; // Timestamp for color animation loop
const unsigned long colorInterval = 300; // Time in ms before shifting to the next color
int16_t raw_samples[SAMPLE_BUFFER_SIZE];
// --- I2S Configuration ---
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 4,
.dma_buf_len = 1024,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
i2s_pin_config_t i2s_mic_pins = {
.bck_io_num = I2S_BCLK_MIC,
.ws_io_num = I2S_LRC_MIC,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = I2S_DIN_MIC
};
// Handles smooth non-blocking RGB color rotation
void handleRGBAnimation() {
if (!isAnimating) {
pixels.setPixelColor(0, pixels.Color(0, 0, 0)); // Turn OFF
pixels.show();
return;
}
// Check if it's time to change color based on colorInterval
unsigned long currentMillis = millis();
if (currentMillis - lastColorChange >= colorInterval) {
lastColorChange = currentMillis;
switch (colorIndex) {
case 0: // RED
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
break;
case 1: // GREEN
pixels.setPixelColor(0, pixels.Color(0, 255, 0));
break;
case 2: // BLUE
pixels.setPixelColor(0, pixels.Color(0, 0, 255));
break;
}
pixels.show();
// Advance to next color
colorIndex = (colorIndex + 1) % 3;
}
}
void setup() {
Serial.begin(115200);
// Initialize NeoPixel
pixels.begin();
pixels.setBrightness(50);
pixels.show(); // Ensure off at start
// Initialize I2S Audio Driver
if (i2s_driver_install(I2S_MIC_PORT, &i2s_config, 0, NULL) != ESP_OK) {
Serial.println("I2S driver installation failed!");
while(1);
}
if (i2s_set_pin(I2S_MIC_PORT, &i2s_mic_pins) != ESP_OK) {
Serial.println("I2S pin configuration failed!");
while(1);
}
// Disable Speaker DAC pin if defined
pinMode(I2S_SD_SPK, OUTPUT);
digitalWrite(I2S_SD_SPK, LOW);
Serial.println("ESP32 Sound Animation Switch Started");
}
void loop() {
size_t bytes_read = 0;
// Read raw audio from I2S DMA
esp_err_t result = i2s_read(I2S_MIC_PORT, raw_samples, sizeof(int16_t) * SAMPLE_BUFFER_SIZE, &bytes_read, portMAX_DELAY);
if (result == ESP_OK && bytes_read > 0) {
int samples_read = bytes_read / sizeof(int16_t);
int maxIntensity = 0;
// Find peak sound amplitude in current buffer
for (int i = 0; i < samples_read; i++) {
int soundIntensity = abs(raw_samples[i]);
if (soundIntensity > maxIntensity) {
maxIntensity = soundIntensity;
}
}
// Toggle mode on snap/clap
unsigned long currentTime = millis();
if (maxIntensity > soundThreshold && (currentTime - lastTriggerTime > cooldownMs)) {
isAnimating = !isAnimating; // Toggle animation ON/OFF
colorIndex = 0; // Reset color sequence back to RED
lastTriggerTime = currentTime;
Serial.print("Snap detected! Animation is now: ");
Serial.println(isAnimating ? "RUNNING" : "OFF");
}
} else if (result != ESP_OK) {
Serial.println("Failed to read from I2S");
delay(1000);
}
// Continually handle light update step
handleRGBAnimation();
}
How it works?
To detect peak sound strength in real time, the ESP32 continuously samples raw audio data from the INMP441 microphone via the I2S bus.

It functions as a toggle switch that continuously cycles through the colors Red, Green, and Blue when a snap or loud sound exceeds the predetermined volume level. The LED is turned off entirely on the subsequent snap.

