Build a Sound-Activated Servo Motor with ESP32 and INMP441 Microphone
This tutorial will teach you how to use an ESP32-S3 and an INMP441 digital I2S microphone to construct a sound-triggered servo motor control system. The experiment uses non-blocking code to detect acute sounds, such as a loud clap or a snap of your fingers, and then switches a servo motor to smoothly sweep between 0° and 180° in 10-degree steps.

Key Hardware Components
- ESP32 Development Board (e.g., ESP32-WROOM-32 or ESP32-S3)
- INMP441 I2S Digital MEMS Microphone Module
- SG90 Servo Motor
- Breadboard & Jumper Wires

Do you want to buy these items in this blog post?
Download the TEMU app: Download TEMU app on your phone
After downloading the TEMU app, click on these links:
Pinout Wiring Guide
| INMP441 Pin | ESP32 GPIO Pin |
| VCC | 3.3V |
| GND | GND |
| SCK | GPIO 47 |
| WS | GPIO 10 |
| SD | GPIO 21 |
| L/R | GND |

| Servo Motor Pin | ESP32-S3 GPIO Pin |
| Signal (Yellow) | GPIO 3 |
| VCC (Red) | 3.3V |
| GND (Brown) | GND |

Required Libraries
Make sure you have the following libraries installed using the Arduino libraries Manager in order to compile and execute this sketch:
- ESP32Servo by Kevin Harrington (
<ESP32Servo.h>) - Standard ESP32 Core (Includes built-in support for standard
<driver/i2s.h>)

Arduino Source Code
The complete non-blocking code is available here. To identify peaks in sound intensity, the system reads 16-bit audio samples directly via I2S. The servo animation state is toggled when a snap surpasses the threshold.
#include <driver/i2s.h>
#include <Arduino.h>
#include <ESP32Servo.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 (if applicable)
#define I2S_MIC_PORT I2S_NUM_0
// --- Servo Setup ---
#define SERVO_PIN 3 // Servo control pin
Servo myServo;
// --- Audio & State Variables ---
#define SAMPLE_BUFFER_SIZE 512
#define SAMPLE_RATE 16000
const int soundThreshold = 2500; // Adjust based on ambient noise
unsigned long lastTriggerTime = 0; // Timestamp for snap debouncing
const unsigned long cooldownMs = 400; // Debounce cooldown in milliseconds
// State Variables
bool isAnimating = false; // Toggle state: false = OFF (0°), true = Sweeping
int currentAngle = 0; // Current angle position (0 to 180)
int stepDirection = 10; // Step increment (+10 or -10)
unsigned long lastServoChange = 0; // Timestamp for step interval
const unsigned long servoInterval = 30;// Delay in ms per 10° step
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 10° back-and-forth sweep non-blockingly
void handleServoAnimation() {
if (!isAnimating) {
if (currentAngle != 0) {
currentAngle = 0;
myServo.write(currentAngle); // Return to rest position on stop
}
return;
}
unsigned long currentMillis = millis();
if (currentMillis - lastServoChange >= servoInterval) {
lastServoChange = currentMillis;
// Update angle
currentAngle += stepDirection;
// Reverse direction at limits
if (currentAngle >= 180) {
currentAngle = 180;
stepDirection = -10; // Reverse direction downwards
} else if (currentAngle <= 0) {
currentAngle = 0;
stepDirection = 10; // Reverse direction upwards
}
myServo.write(currentAngle);
}
}
void setup() {
Serial.begin(115200);
// Initialize Servo
ESP32PWM::allocateTimer(0);
myServo.setPeriodHertz(50); // Standard 50Hz servo
myServo.attach(SERVO_PIN, 500, 2400); // Attach servo to pin 3
myServo.write(0); // Start position at 0 degrees
// 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 Servo 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 movement ON/OFF
if (isAnimating) {
currentAngle = 0;
stepDirection = 10;
}
lastTriggerTime = currentTime;
Serial.print("Snap detected! Servo sweep is now: ");
Serial.println(isAnimating ? "RUNNING" : "OFF");
}
} else if (result != ESP_OK) {
Serial.println("Failed to read from I2S");
delay(1000);
}
// Continually handle non-blocking servo position updates
handleServoAnimation();
}
How It Works
While the servo motor is locked in its default rest position at 0°, the INMP441 microphone constantly listens for background noise while the system is idle.

The ESP32 begins smoothly sweeping the servo motor back and forth between 0° and 180° in 10-degree steps as soon as a sharp sound, such as a snap, surpasses the volume threshold.

