ESP32 I2S: digital audio in from a microphone and out to an amplifier
I2S is the three-wire serial bus for digital audio: a bit clock (BCLK), a word select line (WS, also called LRCLK) that says left or right channel, and a data line. The ESP32 has I2S hardware that can sit on almost any GPIO, which is how projects read an INMP441 MEMS microphone or drive a MAX98357A class D amplifier straight into a speaker with no DAC in between.
Built in on Mokxi: #include <ESP_I2S.h> and it builds, with nothing to install. Free, and no account needed.
What I2S (ESP32 core and ESP-IDF) does
Arduino-ESP32 3.x provides I2SClass in ESP_I2S.h: set the pins, then begin() with the mode, sample rate, bit width and mono or stereo. Older core 2.x sketches use the I2S object from I2S.h, and many tutorials call the ESP-IDF driver in driver/i2s.h directly; the three are different APIs for the same peripheral, so copy code from a tutorial that matches your core version.
The usual surprise is the sample format. An INMP441 sends 24 bits left-justified in a 32-bit slot, and only on the channel its L/R pin selects, so a sketch reading 16-bit stereo gets silence or noise until the width and slot match the microphone.
| Library Manager name | I2S (ESP32 core and ESP-IDF) |
|---|---|
| Author | Espressif |
| Include | #include <ESP_I2S.h>#include <I2S.h>#include <driver/i2s.h>#include <driver/i2s_std.h> |
| Boards | ESP32-C3, ESP32-C6, Seeed XIAO ESP32-C3, ESP32-C3 SuperMini, Seeed XIAO ESP32-C6, ESP32 DevKit, ESP32-DevKitC V4, LOLIN32 Lite, Wemos D1 R32, Cheap Yellow Display, TTGO T-Display, ESP32-S3-DevKitC-1, Seeed XIAO ESP32-S3, Arduino Nano ESP32 |
| In the simulator | Runs, with a gap the note below names. |
| Parts it drives | I2S microphone, INMP441, I2S amplifier, MAX98357A |
Install it
It builds on ESP32-C3, ESP32-C6, Seeed XIAO ESP32-C3, ESP32-C3 SuperMini, Seeed XIAO ESP32-C6, ESP32 DevKit, ESP32-DevKitC V4, LOLIN32 Lite, Wemos D1 R32, Cheap Yellow Display, TTGO T-Display, ESP32-S3-DevKitC-1, Seeed XIAO ESP32-S3, Arduino Nano ESP32.
In Mokxi it is built in: #include <ESP_I2S.h> and the sketch compiles, with no download, no zip and no Library Manager.
On a real board it comes with the board package (Espressif), so the Arduino IDE has it as soon as the board is installed: there is nothing to add from the Library Manager.
ESP32-C3 路 ESP32-C6 路 Seeed XIAO ESP32-C3 路 ESP32-C3 SuperMini 路 Seeed XIAO ESP32-C6 路 ESP32 DevKit 路 ESP32-DevKitC V4 路 LOLIN32 Lite 路 Wemos D1 R32 路 Cheap Yellow Display 路 TTGO T-Display 路 ESP32-S3-DevKitC-1 路 Seeed XIAO ESP32-S3 路 Arduino Nano ESP32
The calls you will use
In the order a sketch meets them.
I2SClass i2s
The I2S port object from ESP_I2S.h, one per peripheral you use.
i2s.setPins(26, 25, 22)
Sets BCLK, WS and the data out pin; a fourth argument is data in, for a microphone. Call it before begin().
i2s.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)
Starts standard (Philips) I2S at 16 kHz with 16-bit samples in stereo. Returns false if the settings or pins are rejected.
i2s.write((uint8_t *)samples, sizeof(samples))
Sends a buffer of samples to the amplifier and returns how many bytes went out. Interleave left and right in stereo.
i2s.readBytes((char *)buffer, sizeof(buffer))
Reads samples from the microphone into a buffer and returns the number of bytes read.
int32_t buffer[64];
size_t n = i2s.readBytes((char *)buffer, sizeof(buffer));
for (size_t i = 0; i < n / 4; i++) Serial.println(buffer[i] >> 8);i2s.end()
Stops the port and frees its pins.
Example: ESP32 I2S sine player
On the ESP32 DevKit V1. It runs in the browser exactly as printed.
// MAX98357A 440 Hz tone on an ESP32.
//
// The MAX98357A is an I2S amplifier: it takes the bit clock (BCLK), the word
// select (LRC) and the samples (DIN) and drives a speaker directly. With SD
// left open it plays the average of the left and right channels; GAIN left
// open is 9 dB.
//
// This sketch uses ESP_I2S.h, the I2S library of Arduino-ESP32 3.x. In
// setup() it fills a table with one cycle of a sine; loop() keeps writing the
// next block of samples, the same value to the left and the right slot, so
// the speaker hears a steady 440 Hz. At 16 kHz, 440 Hz is 36.36 samples a
// cycle, so the table is read with a 16.16 fixed-point phase rather than a
// whole number of samples, and no float is touched per sample.
//
// Wiring: VIN to 5V (VIN on the DevKit), GND to GND, BCLK to GPIO 26,
// LRC to GPIO 25, DIN to GPIO 22, a speaker across SPK+ and SPK-.
#include <ESP_I2S.h>
#include <math.h>
#define I2S_BCLK 26
#define I2S_LRC 25
#define I2S_DIN 22
const uint32_t kSampleRate = 16000;
const uint32_t kFrequency = 440;
const int kTable = 256;
const int16_t kAmplitude = 8000; // about a quarter of full scale
I2SClass i2s;
int16_t sine[kTable];
int16_t block[2 * 128]; // 128 stereo frames: left, right, left, right ...
uint32_t phase = 0;
// Table steps a sample, in 16.16 fixed point: 440 * 256 / 16000.
const uint32_t kStep = (uint32_t)(((uint64_t)kFrequency * kTable << 16) / kSampleRate);
void setup() {
Serial.begin(115200);
for (int i = 0; i < kTable; i++) {
sine[i] = (int16_t)(kAmplitude * sinf(2.0f * (float)M_PI * i / kTable));
}
i2s.setPins(I2S_BCLK, I2S_LRC, I2S_DIN);
if (!i2s.begin(I2S_MODE_STD, kSampleRate, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)) {
Serial.println("Failed to initialize I2S!");
while (true) delay(1000);
}
Serial.println("Playing 440 Hz");
}
void loop() {
for (int i = 0; i < 128; i++) {
int16_t s = sine[(phase >> 16) & (kTable - 1)];
block[2 * i] = s; // left
block[2 * i + 1] = s; // right
phase += kStep;
}
i2s.write((uint8_t *)block, sizeof(block));
}Open it in the editor, wired and ready to run 路 ESP32 I2S sine player: the project page
Common errors and how to fix them
The microphone reads all zeros
Check that the INMP441鈥檚 L/R pin and the slot you read agree (L/R to GND is the left channel), that the data pin is set as the input in setPins(), and that you read 32-bit samples. A 16-bit read of a 32-bit slot often lands on the empty bits.
The speaker plays loud static instead of the tone
The sample rate, bit width or byte order in the buffer does not match what begin() was told. For 16-bit stereo, write int16_t pairs, left then right, and pass the same rate you computed the wave for.
'I2SClass' does not name a type
ESP_I2S.h and I2SClass are Arduino-ESP32 3.x. On core 2.x use the I2S.h object or the ESP-IDF driver/i2s.h calls, or update the ESP32 core in the Boards Manager.
Projects that use I2S (ESP32 core and ESP-IDF)
Built-in projects that include it. Each page has the full sketch and opens in the editor, already wired.
Keep going
More communication libraries
Try I2S (ESP32 core and ESP-IDF) without installing anything
Open the example, change the sketch, and keep your version in a free account.