ESP32 I2S Audio: Playing and Recording Sound with a MAX98357A DAC and INMP441 Microphone
Most ESP32 audio tutorials stop at "play a tone with the built-in DAC," which sounds thin and is limited to two low-quality analog output pins. I2S (Inter-IC Sound) is the actual path to decent audio on the ESP32 — a dedicated hardware peripheral that streams digital audio directly to a DAC/amp chip or from a digital microphone, with no CPU-heavy bit-banging and real audio quality. This guide covers wiring and code for both playback (MAX98357A I2S DAC/amp) and recording (INMP441 I2S MEMS microphone) on the same board.
Why I2S Instead of Analog
- The ESP32's built-in DAC pins (GPIO25/26 on the original ESP32) are 8-bit, noisy, and only exist on two pins — fine for a simple beep, not for real audio.
- I2S is a synchronous serial protocol purpose-built for streaming audio: a bit clock (BCLK), a word/channel select clock (WS or LRCLK), and a data line (DIN for output, DOUT for input) — the ESP32 has dedicated I2S peripherals that handle the timing in hardware, freeing the CPU for everything else.
- The ESP32 has two independent I2S peripherals, which means you can run a microphone on one and a speaker/amp on the other simultaneously without them fighting for the same hardware timing.
Hardware Overview
ModuleFunctionInterfaceTypical cost class MAX98357AI2S digital input, Class-D mono amp output, drives a speaker directlyI2S in (BCLK, LRC, DIN), speaker outBreakout board, a few dollars INMP441I2S MEMS digital microphoneI2S out (SCK, WS, SD)Breakout board, a few dollarsWiring — Both Modules on One ESP32
Because the ESP32 has two I2S peripherals, wire the mic and the amp to separate pin sets rather than trying to share one bus:
MAX98357A pinESP32 pin (I2S0) BCLKGPIO27 LRC (WS)GPIO25 DINGPIO26 GAINLeave floating for 9dB default, or tie to GND/VDD per datasheet for other gain steps SD (shutdown)Tie high (3.3V) to enable, or drive from a GPIO to mute/unmute in software VIN / GND5V and GND — the amp output stage wants 5V for real speaker volume, not 3.3V INMP441 pinESP32 pin (I2S1) SCK (BCLK)GPIO14 WSGPIO15 SD (data out)GPIO32 L/RTie to GND for left channel (mono capture, the standard wiring for a single mic) VDD / GND3.3V and GND — this module is 3.3V only, do not feed it 5VPlayback Code (Arduino IDE)
Install the ESP32-audioI2S library (or use the ESP-IDF native driver/i2s.h for a lower-level approach). A minimal WAV-from-SPIFFS playback sketch:
#include "Audio.h" Audio audio; void setup() { Serial.begin(115200); SPIFFS.begin(true); audio.setPinout(27, 25, 26); // BCLK, LRC, DIN audio.setVolume(12); // 0-21 audio.connecttoFS(SPIFFS, "/chime.wav"); } void loop() { audio.loop(); }For streaming from an SD card instead of SPIFFS, swap connecttoFS(SPIFFS, ...) for the SD-card variant the library exposes — the I2S pinout call and playback loop structure stay the same.
Recording Code (Arduino IDE, ESP-IDF driver)
#include "driver/i2s.h" #define I2S_WS 15 #define I2S_SD 32 #define I2S_SCK 14 #define I2S_PORT I2S_NUM_1 void setup() { Serial.begin(115200); i2s_config_t config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), .sample_rate = 16000, .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 8, .dma_buf_len = 256 }; i2s_pin_config_t pins = { .bck_io_num = I2S_SCK, .ws_io_num = I2S_WS, .data_out_num = I2S_PIN_NO_CHANGE, .data_in_num = I2S_SD }; i2s_driver_install(I2S_PORT, &config, 0, NULL); i2s_set_pin(I2S_PORT, &pins); } void loop() { int32_t samples[256]; size_t bytes_read; i2s_read(I2S_PORT, samples, sizeof(samples), &bytes_read, portMAX_DELAY); // samples now hold raw mic data — write to SD as WAV, stream over WiFi, or feed to a VAD/wake-word routine }The INMP441 outputs 24-bit data left-justified in a 32-bit word — shift and mask accordingly if you're processing samples rather than just writing them straight to a WAV container.
Project Ideas This Unlocks
- Two-way intercom — mic on one ESP32, streamed over WiFi (UDP for low latency) to a second ESP32 driving a MAX98357A speaker.
- Voice recorder / sound logger — INMP441 plus an SD card module, triggered by a button or a simple amplitude threshold for hands-free "record when loud" logging.
- Smart doorbell chime — combine with this site's ESP32-CAM doorbell content for a camera-plus-audio notification build.
- Wake-word or voice-command front end — the INMP441's clean digital output is the standard mic choice for on-device keyword spotting (TensorFlow Lite Micro and similar), covered at a model level in this site's ESP32-CAM AI vision content.
Power and Noise Notes
- Power the MAX98357A's amp stage from 5V (USB or a dedicated regulator), not the ESP32's 3.3V rail — the amp needs headroom to actually drive a speaker at usable volume.
- Keep I2S data/clock lines short and away from motor or WiFi antenna traces if this shares a board with other high-noise peripherals — I2S is digital and fairly robust, but long unshielded runs can still pick up audible clock jitter.
- Add a decoupling capacitor (10-100uF) close to the MAX98357A's power pins if you hear a faint buzz correlated with WiFi activity — this is a shared symptom with many small amp boards on any microcontroller, not an ESP32-specific issue.
Troubleshooting
SymptomLikely cause No sound from speakerSD (shutdown) pin not pulled high, or BCLK/LRC/DIN pins swapped Distorted or clipped audioVolume set too high in software, or 3.3V powering the amp instead of 5V Mic reads all zeros or silenceL/R pin not tied to GND (or VDD, if you intended right-channel), or WS/SCK swapped Static/whine synced to WiFi transmitMissing decoupling capacitor, or I2S wiring running parallel to the antenna trace Playback stuttersSPIFFS/SD read speed bottleneck — increase DMA buffer count/size, or move the audio file to a faster SD cardOnce both directions are working independently, combining them into a full duplex system (record and play simultaneously, e.g. for a real intercom) mostly comes down to running both I2S peripherals concurrently in separate FreeRTOS tasks pinned to different cores — see this site's dual-core ESP32 FreeRTOS content for that pattern.
Related Guides
- Watchdog Timers for Arduino and ESP32: Hardware WDT, Task Watchdogs, and Recovering from Hangs
- Build a Bluetooth A2DP Audio Receiver with ESP32
- Build an ESP32 Internet Radio Streamer: I2S DAC, WiFi Station Presets, and Rotary Encoder Control
- How to Program Addressable LED Strips: WS2812B Patterns, Effects, and Power Design
- I2C vs SPI vs UART: How to Choose and Use Serial Communication Protocols
- How to Use Sensors with Arduino and ESP32: Temperature, Distance, Load, Current, and Hall Effect
- How to Control Motors with Arduino and ESP32: Stepper, DC, and Servo Drivers
- ESP32: Setting Up for Arduino IDE