Sound-Controlled RGB Smart Lamp Using ESP32
01 Overview
We are building a sound-controlled RGB smart lamp using the ESP32 with a sound detector sensor. This smart lamp lets you control RGB NeoPixel LEDs by sound cues such as claps or taps detected by the sensor. The 0.96-inch OLED display provides real-time feedback on the lamp's status, such as color and brightness. A passive buzzer gives an audible beep whenever the lamp changes color, providing confirmation of the action.
The ESP32 processes the sound input locally, enabling hands-free control to turn the light on/off, change colors, and activate lighting effects, all without needing an internet connection.
Project Use Case
This project is ideal for anyone who wants a hands-free, gesture-free way to control ambient lighting at home — for example, in a bedroom, living room, or study space. Since it relies purely on sound cues and runs entirely offline, it's well suited for makers exploring embedded audio processing, adaptive calibration, and interactive smart home devices without needing WiFi or a companion app.
02 Hardware and Software Components
Gather everything below before you start. This is your checklist — names, models, and versions only.
Hardware Components
| Component | Description |
|---|---|
| ESP32 | Main microcontroller that reads the sound sensor and drives the LEDs, OLED, and buzzer. |
| NeoPixel Ring RGB LED | Addressable RGB LED ring used for the lamp's light output. |
| Sound Detector Sensor | Microphone module that detects clap/sound events used as input triggers. |
| 0.96" OLED Display | Shows the lamp's current mode/status (color, calibration, etc.). |
| Jumper Wires | Used to connect all components to the ESP32. |
| Breadboard | Used for prototyping the circuit connections. |
| Passive Buzzer | Provides an audible beep whenever the lamp's mode changes. |
Software Tools
| Software | Version / Details |
|---|---|
| Arduino IDE | Used to write and upload the sketch to the ESP32. |
03 Application Discussion
Here is what each component does and why it is part of this project.
ESP32
ESP-32 is a development board that is built around the powerful ESP32 system on a chip microcontroller. It is a development platform with a programmer, Serial-to-USB module, voltage regulator, and several peripherals. The most relevant feature: it combines WiFi and Bluetooth wireless capabilities and it's dual-core for more complex tasks that demand to multitask.
NeoPixel Ring RGB LED
The NeoPixel Ring RGB LED is a circular array of individually addressable RGB LEDs controlled using a single data line. Each LED can produce millions of colors by mixing red, green, and blue light with 8-bit PWM control. It allows for dynamic lighting effects such as color transitions, animations, and visual indicators. The ring is powered by 5V and controlled by a microcontroller like the ESP32 using libraries such as Adafruit NeoPixel. Its compact design makes it ideal for wearable tech, smart lamps, and interactive displays.
Sound Detector Sensor
The Sound Detector Sensor is a small microphone-based module that detects the presence and intensity of sound in its surroundings. It provides three types of outputs: an envelope output (sound level), an audio output (raw signal), and a gate output (digital trigger when sound exceeds a threshold). This makes it useful for applications like sound-activated devices, audio analysis, and environmental monitoring.
0.96" OLED Display
The 0.96 inch OLED is a small and low-power display that can be used to display text, graphic images, or sensory data. This display uses organic light-emitting diode (OLED) technology, meaning each pixel emits its own light without needing a backlight. This also means it provides lots of contrast and rich colors, with low power, and a nice wide viewing angle. It uses the SSD1306 driver chip that controls the operation of each of the pixels. It communicates via either I2C or serial (SPI) with the microcontroller — I2C is typically used, as it requires less wiring.
Passive Buzzer
A passive buzzer is an electronic component that produces sound when an external signal, usually a square wave from a microcontroller, drives it. Unlike an active buzzer, it does not have a built-in oscillator, so the frequency and tone of the sound can be controlled by the input signal. This flexibility allows it to generate different pitches, tones, or even simple melodies. It is commonly used in alarms, timers, and notification systems. Because it requires a driving circuit, it is more versatile but slightly harder to use than an active buzzer.
04 Hardware Setup
Wire the components to the ESP32 using the tables below.
NeoPixel Ring
| Pin / Signal | Connects To |
|---|---|
| GPIO 5 | NeoPixel Data Pin |
| GND | GND |
| 5V | Power |
Sound Detector Sensor
| Pin / Signal | Connects To |
|---|---|
| GPIO 34 | Analog Output |
| GND | GND |
| 3.3V | Power |
OLED Display
| Pin / Signal | Connects To |
|---|---|
| GPIO 21 | SDA |
| GPIO 22 | SCL |
| 3.3V | Power |
| GND | GND |
Buzzer
| Pin / Signal | Connects To |
|---|---|
| GPIO 25 | Signal |
| GND | GND |
Assembly Instructions
- Connect the NeoPixel ring's data pin to GPIO 5, its GND to ESP32 GND, and its power line to 5V.
- Connect the sound detector sensor's analog output to GPIO 34, GND to GND, and power to 3.3V.
- Connect the OLED display's SDA to GPIO 21, SCL to GPIO 22, power to 3.3V, and GND to GND.
- Connect the buzzer's signal pin to GPIO 25 and its GND to ESP32 GND.
- Double-check all connections against the wiring diagram above before powering on the ESP32.
05 Software Setup
Follow these steps in order. Do not skip any step.
Step 1 — Install the Arduino IDE
- Download and install the Arduino IDE if you don't already have it.
- Make sure ESP32 board support is installed under Tools > Board > Boards Manager.
Step 2 — Install Libraries
Install the following libraries via the Library Manager (Sketch > Include Library > Manage Libraries):
- Adafruit GFX Library
- Adafruit SSD1306
- Adafruit NeoPixel
Step 3 — Upload the Code
- Open the sketch from the Code section below in the Arduino IDE.
- Select your ESP32 board and the correct COM port under the Tools menu.
- Click Upload.
06 Code
Copy the file below into your Arduino IDE as described in the Software Setup section. Read the Code Breakdown section to understand what each part does.
sound_controlled_lamp.ino
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_NeoPixel.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define LED_PIN 5
#define NUM_LEDS 16
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
#define MIC_PIN 34
#define BUZZER_PIN 25
const int CLAP_SENSITIVITY = 500;
const unsigned long CLAP_DEBOUNCE = 250;
const unsigned long CLAP_WINDOW = 500;
unsigned long lastClapTime = 0;
int clapCount = 0;
unsigned long lastClapDetected = 0;
int mode = 0;
unsigned long lastRainbowUpdate = 0;
const unsigned long rainbowInterval = 20;
uint16_t rainbowIndex = 0;
long ambientNoiseSum = 0;
int ambientNoiseCount = 0;
const int AMBIENT_CALIBRATION_SAMPLES = 500;
int adaptiveThreshold = 0;
const int CALIBRATION_DELAY = 10;
void setup() {
Serial.begin(115200);
delay(1000);
strip.begin();
strip.setBrightness(100);
strip.show();
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED init failed");
while (1);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("Calibrating...");
display.display();
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
calibrateAmbientNoise();
}
void calibrateAmbientNoise() {
display.clearDisplay();
display.setCursor(0, 0);
display.println("Calibrating...");
display.println("Please be quiet.");
display.display();
ambientNoiseSum = 0;
ambientNoiseCount = 0;
for (int i = 0; i < AMBIENT_CALIBRATION_SAMPLES; i++) {
int sample = analogRead(MIC_PIN);
ambientNoiseSum += sample;
ambientNoiseCount++;
delay(CALIBRATION_DELAY);
}
int ambientNoiseAverage = ambientNoiseSum / ambientNoiseCount;
adaptiveThreshold = ambientNoiseAverage + CLAP_SENSITIVITY;
Serial.print("Ambient Noise Average: ");
Serial.println(ambientNoiseAverage);
Serial.print("Adaptive Threshold set to: ");
Serial.println(adaptiveThreshold);
display.clearDisplay();
display.setCursor(0, 0);
display.println("Clap Detection Ready!");
display.print("Threshold: ");
display.println(adaptiveThreshold);
display.display();
}
void loop() {
int volume = analogRead(MIC_PIN);
unsigned long currentTime = millis();
if (volume > adaptiveThreshold && (currentTime - lastClapTime > CLAP_DEBOUNCE)) {
lastClapTime = currentTime;
clapCount++;
lastClapDetected = currentTime;
Serial.print("Clap detected! Value: ");
Serial.print(volume);
Serial.print(" | Clap count: ");
Serial.println(clapCount);
}
if (clapCount > 0 && (currentTime - lastClapDetected > CLAP_WINDOW)) {
Serial.print("Clap sequence ended with ");
Serial.print(clapCount);
Serial.println(" clap(s).");
switch (clapCount) {
case 1: mode = 1; break;
case 2: mode = 2; break;
case 3: mode = 3; break;
case 4: mode = 4; break;
case 5: mode = 5; break;
case 6: mode = 0; break;
default: mode = 0; break;
}
updateState(mode);
clapCount = 0;
}
if (mode == 5) {
if (currentTime - lastRainbowUpdate > rainbowInterval) {
lastRainbowUpdate = currentTime;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i * 256 / strip.numPixels() + rainbowIndex) & 255));
}
strip.show();
rainbowIndex++;
if (rainbowIndex >= 256) rainbowIndex = 0;
}
}
}
void updateState(int newMode) {
mode = newMode;
display.clearDisplay();
display.setCursor(0, 0);
strip.clear();
switch (mode) {
case 0:
strip.show();
display.println("Clap 6: Turn OFF");
break;
case 1:
setColor(255, 255, 255);
display.println("Clap 1: White");
break;
case 2:
setColor(255, 0, 0);
display.println("Clap 2: Red");
break;
case 3:
setColor(0, 0, 255);
display.println("Clap 3: Blue");
break;
case 4:
setColor(0, 255, 0);
display.println("Clap 4: Green");
break;
case 5:
display.println("Clap 5: Rainbow");
break;
}
display.display();
beepBuzzer();
}
void setColor(uint8_t r, uint8_t g, uint8_t b) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(r, g, b));
}
strip.show();
}
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
void beepBuzzer() {
tone(BUZZER_PIN, 3000, 150);
delay(300);
noTone(BUZZER_PIN);
}
07 Code Breakdown
Here is what each part of the code does. Read this after uploading.
Libraries
| Library | Purpose |
|---|---|
| Wire.h | For I2C communication, used by the OLED display. |
| Adafruit_GFX.h | The core graphics library for the display. |
| Adafruit_SSD1306.h | The specific driver for the SSD1306 OLED display. |
| Adafruit_NeoPixel.h | The driver for the NeoPixel RGB LEDs. |
Key Functions
setup()
Initializes the components. It starts the serial communication, sets up the NeoPixel strip, and initializes the OLED display.
calibrateAmbientNoise()
Measures the average sound level in the room to create an adaptive sound threshold for detecting claps.
loop()
Continuously reads the sound sensor's value and compares the current sound level to the adaptive threshold. CLAP_DEBOUNCE and CLAP_WINDOW are crucial for ensuring that a single clap isn't counted multiple times and that a sequence of claps (like two or three) is counted as one event. The code waits for a brief CLAP_WINDOW to pass after the last clap before processing the total clap count.
updateState()
Changes the NeoPixel's color and updates the OLED display based on the number of claps. One clap sets the LEDs to white, two claps to red, and so on. Six claps turn them off. Five claps activate a rainbow animation.
setColor()
Sets all the LEDs to a single, solid color.
Wheel()
A helper function used to generate the color sequence for the rainbow animation.
beepBuzzer()
Plays a short tone on the passive buzzer to provide audible feedback when the mode changes.
General Program Workflow
- On startup, the ESP32 initializes the NeoPixel strip and OLED display, then runs ambient noise calibration.
- The loop continuously samples the sound sensor and compares each reading to the adaptive threshold.
- When a sound spike is detected, it's registered as a clap and counted within the clap window.
- Once the clap window closes, the total clap count determines the new mode (color, rainbow, or off).
- The OLED and buzzer update to reflect the new state, and the NeoPixel ring changes accordingly.
08 Testing and Calibration
After uploading, verify each of the following to confirm the system is working correctly.
Ambient Noise Calibration
On power-up, the OLED should display "Calibrating..." followed by "Please be quiet." Stay silent during this period so the sensor can establish an accurate ambient noise baseline. Once complete, the OLED should show "Clap Detection Ready!" along with the calculated threshold value.
Clap Detection
Try clapping once, twice, up to six times in quick succession. The NeoPixel ring should change color (or turn off, or run the rainbow effect) according to the clap count, the OLED should display the corresponding mode, and the buzzer should beep once to confirm the change.
calibrateAmbientNoise(), or adjust CLAP_SENSITIVITY in the code.
09 System Demonstration
The clap sequence controls the lamp as follows:
| Clap Count | Result |
|---|---|
| Clap 1 | White (ON) |
| Clap 2 | Red |
| Clap 3 | Blue |
| Clap 4 | Green |
| Clap 5 | Rainbow |
| Clap 6 | OFF |
Lamp in Operation
Video Demonstration
10 Conclusion
This project is a functional prototype of a sound-controlled smart lamp that uses an ESP32 to process audio input from a sound sensor. The system allows a user to control the lighting without physical interaction, relying on specific sound cues like claps. It clearly demonstrates how to interface multiple hardware elements — an input sound sensor, a user-feedback OLED display, and an addressable LED strip for output — into one integrated device.
The strength of the system lies in its adaptive calibration procedure, which allows the device to monitor and compensate for ambient noise levels in its environment, making clap detection accurate and consistent. By linking specific clap sequences to different operations, such as changing the LED color or enabling a rainbow effect, the project offers an intuitive, hands-free way of controlling the light — successfully combining hardware and software into a usable smart home application.
Possible Improvements and Future Enhancements
- Add WiFi/Bluetooth connectivity for app or voice control alongside the existing clap control.
- Support custom clap patterns (e.g., double-clap vs. long clap) for more distinct commands.
- Add brightness control via additional clap patterns or a potentiometer.
- Run the lamp on a rechargeable battery with a charging circuit for a fully portable design.
- Save the last-used mode to non-volatile memory so it's restored after a power loss.
11 References
- ESP32 + Sound Detector Sensor for Sound-Activated LED Control
12 Project Authors
- Glydelle Arellano
- Arcel Alfaro
Quality Checked by:
- Alexander Maiso
- Jhon Ronan Limbadan
