← All projects
Sensors Advanced

Sound Level Meter with an INMP441 Digital Microphone (I2S)

A digital MEMS microphone streams audio over I2S with no ADC noise. We compute RMS, convert it to decibels and raise an alert when the room gets too loud. A foundation for voice and sound-reactive projects.

Difficulty
Advanced
Time
2 h
Category
Sensors

Why I2S instead of an analog mic

Analog modules (MAX4466, KY-038) go through the ESP32's ADC, which is noisy and non-linear. The INMP441 is a digital microphone: it digitises sound itself (24 bits) and streams it over the I2S bus. The result is a clean signal that's good for loudness measurement, FFT and even voice recognition.

Parts

  • ESP32 DevKit
  • INMP441 microphone module
  • An LED (or the on-board one on GPIO 2) — the "too loud" indicator

Wiring

INMP441 ESP32
VDD 3V3
GND GND
L/R GND (left channel)
WS GPIO 25
SCK GPIO 32
SD GPIO 33

Code

We use the ESP_I2S library from the arduino-esp32 3.x core — nothing to install.

#include <ESP_I2S.h>
#include <math.h>

const int PIN_SCK = 32;
const int PIN_WS  = 25;
const int PIN_SD  = 33;
const int LED_PIN = 2;

const uint32_t SAMPLE_RATE = 16000;
const int SAMPLES = 1024;               // 64 ms of audio
const float LOUD_DB = 70.0;             // "too loud" threshold

// INMP441: −26 dBFS at 94 dB SPL → dB SPL ≈ dBFS + 120
const float DBFS_TO_SPL = 120.0;

I2SClass i2s;
int32_t buffer[SAMPLES];
float smoothed = 0;

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);

  i2s.setPins(PIN_SCK, PIN_WS, -1, PIN_SD);   // bclk, ws, dout (unused), din
  if (!i2s.begin(I2S_MODE_STD, SAMPLE_RATE, I2S_DATA_BIT_WIDTH_32BIT,
                 I2S_SLOT_MODE_MONO, I2S_STD_SLOT_LEFT)) {
    Serial.println("I2S init failed");
    while (true) delay(1000);
  }
  // The mic outputs garbage right after start-up — skip it
  i2s.readBytes((char *)buffer, sizeof(buffer));
}

void loop() {
  size_t bytes = i2s.readBytes((char *)buffer, sizeof(buffer));
  int n = bytes / sizeof(int32_t);
  if (n == 0) return;

  // 24-bit audio sits in the upper bits of each 32-bit word
  double sum = 0;
  for (int i = 0; i < n; i++) sum += buffer[i] >> 8;
  double mean = sum / n;                     // DC offset

  double sq = 0;
  for (int i = 0; i < n; i++) {
    double s = (buffer[i] >> 8) - mean;
    sq += s * s;
  }
  double rms = sqrt(sq / n);
  if (rms < 1) rms = 1;

  float dbfs = 20.0 * log10(rms / 8388608.0);   // 2^23 — full scale
  float spl = dbfs + DBFS_TO_SPL;

  // Smoothing so the numbers don't jump around
  smoothed = smoothed == 0 ? spl : smoothed * 0.8 + spl * 0.2;

  digitalWrite(LED_PIN, smoothed > LOUD_DB ? HIGH : LOW);
  Serial.printf("dB:%.1f\n", smoothed);          // Serial Plotter format
}

Testing

  1. Open Tools → Serial Plotter — you'll see a sound level graph.
  2. A quiet room is around 30–40 dB, conversation 55–65 dB, a hand clap 80+ dB.
  3. Once the level goes above 70 dB, the LED lights up.

Accuracy

This is an estimate, not a certified sound level meter:

  • The +120 offset comes from the INMP441's datasheet sensitivity (−26 dBFS @ 94 dB SPL); individual units can differ by ±1–3 dB. Calibrate against a real meter or a phone app by adjusting DBFS_TO_SPL.
  • Real meters apply A-weighting (dBA). That needs a digital filter — a nice next challenge.
  • The mic dislikes moisture and dust — leave a mesh-covered hole for it in your enclosure.

Ideas

  • A noise indicator for a classroom or open space: green / yellow / red on a WS2812.
  • Log night-time loudness to Home Assistant (see the "MQTT Discovery" project).
  • An FFT spectrum analyser on an LED matrix (arduinoFFT library).
  • Sound-reactive WLED: the INMP441 is the standard mic for WLED Sound Reactive builds.