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.
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
- Open Tools → Serial Plotter — you'll see a sound level graph.
- A quiet room is around 30–40 dB, conversation 55–65 dB, a hand clap 80+ dB.
- Once the level goes above 70 dB, the LED lights up.
Accuracy
This is an estimate, not a certified sound level meter:
- The
+120offset 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 adjustingDBFS_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.