View All Posts
Want to keep up to date with the latest posts and videos? Subscribe to the newsletter
HELP SUPPORT MY WORK: If you're feeling flush then please stop by Patreon Or you can make a one off donation via ko-fi

There are a few options out there for getting analogue audio data into the ESP32.

  • Directly reading from the built-in Analogue to Digital Converts (ADCs)
    • This is useful for one-off readings, but not suitable for high sampling rates.
  • Using I2S to read from the built-in ADCs using DMA
    • Useful for analogue microphones such as the MAX4466 and the MAX9814
  • Using I2S to read directly from I2S compatible peripherals
    • Useful for microphones such as the SPH0645LM4H, INPM441, ICS43432 and ICS43434

I’ve covered these options in a couple of YouTube videos which are worth a watch and I’ve added details in the text below as well if videos aren’t your thing.

There’s also a GitHub repo with all the necessary code here.

Analogue Microphone input (MAX4466 and MAX9814)

Digital Microphone input (SPH0645LM4H, INPM441)

Reading from the ADC directly

There are two built-in ADCs on the ESP32, ADC1 and ADC2.

ADC1 has 8 channels:

Channel GPIO Channel GPIO

And ADC2 has 10 channels:

Channel GPIO Channel GPIO

There are some limitations though - ADC2 is also used by the WiFi sub-system and some of the pins are also used strapping pins that control boot behaviour. This means it’s safest to stick with ADC1 for projects.

Reading from the ADC is very straightforward - you can either use the Arduino functions or use the Espressif function directly:

// read using Arduino
int sample = analogRead(35)

// read using Espressif
int sample = adc1_get_raw(ADC1_CHANNEL_7);

The ESP32 ADC is quite inaccurate and if you want to have an accurate reading you can use calibration settings. These are now mostly done at the factory so your ESP32 should already have some calibration settings already. It’s also possible to manually calibrate the ADC but this is not for the faint hearted.

To read a calibrated value you use the following code which will give you a value in millivolts. The two calls to adc1_config_width and adc1_config_channel_atten are critical as the calibration characteristics needs to match the ADC configuration.

// calibration values for the adc
#define DEFAULT_VREF 1100
esp_adc_cal_characteristics_t *adc_chars;

//Range 0-4096

// full voltage range
adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_11);

// get the ADC characteristics

// read a sample from the ADC
int sample = adc1_get_raw(ADC1_CHANNEL_7);

// get the calibrated value
int milliVolts = esp_adc_cal_raw_to_voltage(sample, adc_chars);

High-Speed ADC Sampling Using I2S and DMA

Using the ADC directly is fine for low frequency and one-off sampling. For sampling high-quality audio data you will need to be sampling at 16-40KHz (watch the first video for some nice animation on this!). You can do this using a timer, but it’s not the best use of the ESP32’s CPU resources.

A better approach is to use the built-in I2S peripheral to read samples from the ADC directly into memory.

This is the basic setup for using I2S to read from the built-in ADC.

i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
    .sample_rate = 40000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = I2S_COMM_FORMAT_I2S_LSB,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 2,
    .dma_buf_len = 1024,
    .use_apll = false,
    .tx_desc_auto_clear = false,
    .fixed_mclk = 0};

//install and start i2s driver
i2s_driver_install(I2S_NUM_0, &i2s_config, 4, &i2s_queue);

//init ADC pad
i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_7);

// enable the ADC

// start a task to read samples from I2S
TaskHandle_t readerTaskHandle;
xTaskCreatePinnedToCore(readerTask, "Reader Task", 8192, this, 1, &readerTaskHandle, 0);

You can then read samples from the ADC using the following task:

void readerTask(void *param)
    I2SSampler *sampler = (I2SSampler *)param;
    while (true)
        // wait for some data to arrive on the queue
        i2s_event_t evt;
        if (xQueueReceive(sampler->i2s_queue, &evt, portMAX_DELAY) == pdPASS)
            if (evt.type == I2S_EVENT_RX_DONE)
                size_t bytesRead = 0;
                    // try and fill up our audio buffer
                    size_t bytesToRead = (ADC_SAMPLES_COUNT - sampler->audioBufferPos) * 2;
                    void *bufferPosition = (void *)(sampler->currentAudioBuffer + sampler->audioBufferPos);
                    // read from i2s
                    i2s_read(I2S_NUM_0, bufferPosition, bytesToRead, &bytesRead, 10 / portTICK_PERIOD_MS);
                    sampler->audioBufferPos += bytesRead / 2;
                    if (sampler->audioBufferPos == ADC_SAMPLES_COUNT)
                        // do something with the sample - e.g. notify another task to do some processing
                } while (bytesRead > 0);

Once you’ve read the samples you can do whatever processing you need to do, the I2S peripheral will continue in the background reading samples from the ADC into the DMA buffers.

Wiring up the MA4466 is very straightforward, you just need to connect VCC to 3v3, GND to GND and Out to the GPIO pin that corresponds to the ADC channel you are sampling from.


The same is try of the MAX9814 - you can also play with the gain on the MAX9814 by connecting the Gain pin to either VCC or GND.


My testing of both these boards highlighted an issue with noise coming from the 3v3 line (you can listen to the audio in the first video). We can easily see this by hooking up an oscilloscope on the 3v3 line and the microphone output.


This noise on the 3v3 line is also apparent on the 5v rail but we can generate a reasonably clean signal by filtering the 5v volt line and feeding it through a low dropout regulator.

For very low current draw like these microphone boards, we can have a simple RC filter on the 5v rail.

Power Supply Filter

I’ve gone slightly further and made up a more complex circuit with multiple filters, which I’ve also had made up into a circuit board, but this is not necessary.

Power Supply Filter V2

With either the simple filter or the more complex filter we get reasonably audio, but it is still quite noisy.

You can listen to the audio of the two boards at this point of the video:

I2S microphones

I’ve also tried out two MEMS (Micro-Electro-Mechanical Systems) microphones that have built-in I2S interfaces. I’ve got an INMP441 and an SPH0645LM4H for my experiments.

MEMS Microphones

These are tiny microphone modules that are packages up as tiny surface-mounted components. Typically they will have a hole in the bottom through the circuit board or in the top of the package. Both the boards that I have are bottom ported so there is a hole in the circuit board for the audio to enter the system.

MEMS Package

MEMS Construction

Once again the wiring up of these modules is very simple - we just need three GPIO pins.


The three required lines are:

  • Serial Clock - this is used to clock data to or from the peripheral
  • Word select (also called the left-right clock or LRCLK) - this selects the channel that you want to send or receive data for.
  • Serial Data - the data that is being either transmitted or received

The word select should in theory let us connect two different devices - one for the left channel and one for the right channel.

When the Word Select is low the right device with tri-state its output and the left channel will sever data. when the word select is high the left device will tri-state its output and the right device will server data.

I2S Signals

We can set up the I2S interface to read directly from these two microphones by configuring each board to be on a different channel.

The configuration for the I2S interface now looks like this:

i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = 40000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_I2S,
    .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};

I had read some forum posts on the SPH0645 that indicated there were some issues with this microphone and ESP32 and when we pull data back with just the SPH0645 plugged in we can see some issues:

Right Left Right Left
01 00 00 00 00 80 d2 f6 01 00 00 00 00 00 d1 f6
01 00 00 00 00 00 cc f6 01 00 00 00 00 00 ca f6
01 00 00 00 00 80 ca f6 01 00 00 00 00 00 c9 f6
01 00 00 00 00 00 c7 f6 01 00 00 00 00 80 c4 f6
01 00 00 00 00 80 c3 f6 01 00 00 00 00 80 c2 f6

We have some data in the Right channel even though I configured the SPH0645 to be on the left channel.

Apparently there are some timing changes required to make the SPH0645 work with the ESP32. The magic incantation to make it work properly is:

#include "soc/i2s_reg.h"

// FIXES for SPH0645

With this in place, we can record from both the SPH0645 and the INMP441 at the same time. You can watch a segment of the video here to listen to the audio recordings from the boards:

Conclusion and thoughts

After playing the four boards - the MAX4466, MAX9814, SPH0645 and the INMP441 - I would choose the INMP441 for my projects. If you can get hold of a break out board then the ICS4343 would be an even better option.

  • If you are short of GPIO pins then the MX9814 may be a good option
    • POSITIVE - it only needs one GPIO pin
    • POSITIVE - it has a built-in Automatic Gain Control (AGB).
    • NEGATIVE - it’s quite noisy
    • NEGATIVE - you will need a good clean power supply
  • If you can spare 3 GPIO pins then the INMP441 should be your choice
    • POSITIVE - much less noisy than the analog boards
    • POSITIVE - no need to power supply filter, handles the noisy power line without any issues
    • POSITIVE - very compact and small
    • NEGATIVE - no Automatic Gain Control (AGB)
    • NEGATIVE - no longer in production - replaced by the ICS4343 which doesn’t have a large supply of break out boards at the moment.

Related Posts

ESP32 I2S DMA Settings - dma_buf_len and dma_buf_count Explained - In this blog post, we delve deep into the intriguing concepts of I2S audio and DMA, particularly focusing on parameters like dma_buf_count and dma_buf_len. We explore their roles, ideal values, and the impacts they have on aspects such as CPU load and latency. We also discuss the limitations that come hand in hand with these parameters. This post aims to provide you some insights on trading-off between latency, CPU load, memory usage and the overall buffer space allocation. However, the primary takeaway remains that the optimal configurations largely depend on individual context and application needs.
Minimalist Microcontroller: Building a Bare-Bones Dev Board - In a thrilling DIY endeavour, I attempted to build the most minimalist ESP32 dev board possible. Diving deep into the schematic of the ESP32 S3 WROOM module, I chopped out the non-essentials and whittled our needs down to bare bones. The experiment saw me juggling USB data lines and voltage regulators, waving goodbye to an array of capacitors and connectors and boldly embracing the simplicity of direct connections. Despite a few hitches, the miniature Frankenboard came alive, proving that sometimes less is least in the world of microcontrollers.
DIY Alexa With the ESP32 and - This post provides a comprehensive guide to building a do-it-yourself (DIY) Alexa using an ESP32 and It illustrates how to create a wake word detection system, use Python for machine learning and employ TensorFlow for the 'wake' word identification. It also covers the usage of for intent recognition and managing commands. The post is fully backed with code snippets, examples and video tutorials to deliver an interactive learning experience to readers.
Self Organising WS2811 LEDs - I've successfully used addressable WS2811 LED strings and an ESP-CAM board to create an adjustable lighting system. The best part is that the image processing code can be duplicated in JavaScript which allows you to use a plain dev board to drive the LEDs instead of needing a camera on your ESP32 board. If you want to replicate this project, you'll need your own ESP32 dev board and some addressable LEDs. After figuring out the location of each LED in 2D space, it's easy to map from each LED's x and y location onto a pattern you want to show on the frame buffer. Desiring to keep it accessible, I've posted detailed instructions and my sample code on GitHub, making sure anyone with basic knowledge can undertake this fun technological DIY project!

Related Videos

ESP32 Audio Input Using I2S and Internal ADC - Learn how to effectively capture audio data using an ESP32 device and analog-to-digital converters in this detailed tutorial. Discover the power of I2S peripheral with DMA controller and optimize your system's audio performance with the MAX 4466 and MAX 9814 microphone breakout boards.
Unleash ESP32 Audio: I2S & Built-In DACs Explained! - Learn how to utilize ESP32's built-in Digital to Analog Converters (DACs) for outputting audio and arbitrary signals at high frequencies, along with a step-by-step guide on configuring the I2S peripheral for using DAC channels.
Play MP3 Files on ESP32 Without Codec Chip: Easy Guide - Learn how to decode and play MP3 audio files on the ESP32 with both headphone support and I2S digital amplifiers. Discover techniques to enhance audio quality and reduce power interference for clearer sound.
ESP32 Audio Input Showdown: INMP441 vs SPH0645 MEMS I2S Microphones! - Discover the performance of two MEMS microphone boards, the SPH0645 and the INMP441, when connected to an ESP32. This video showcases their audio recording capabilities, noise handling and overall usability, with the INMP441 emerging as the winner!
ESP32 Audio Output with I2S DMA and the MAX98357A Class D Amplifier - Learn how to use the MAX98357A breakout board with an ESP32 to output audio, create a digital audio path, configure the I2S interface, and read WAVE files from SPIFFS in this engaging tutorial.
HELP SUPPORT MY WORK: If you're feeling flush then please stop by Patreon Or you can make a one off donation via ko-fi
Want to keep up to date with the latest posts and videos? Subscribe to the newsletter
Blog Logo

Chris Greening


> Image


A collection of slightly mad projects, instructive/educational videos, and generally interesting stuff. Building projects around the Arduino and ESP32 platforms - we'll be exploring AI, Computer Vision, Audio, 3D Printing - it may get a bit eclectic...

View All Posts