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 PinESP32 GPIO Pin
VCC3.3V
GNDGND
SCKGPIO 47
WSGPIO 10
SDGPIO 21
L/RGND
Servo Motor PinESP32-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:

  1. ESP32Servo by Kevin Harrington (<ESP32Servo.h>)
  2. 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.

Leave a Reply