View All Posts
read
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
#ADC #ANALOGUE TO DIGITAL CONVERTS #AUDIO INPUT #DMA #ESP32 #I2S #INPM441 #MAX4466 #MAX9814 #MICROPHONES #SPH0645

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
ADC1_CHANNEL_0 GPIO36 ADC1_CHANNEL_4 GPIO32
ADC1_CHANNEL_1 GPIO37 ADC1_CHANNEL_5 GPIO33
ADC1_CHANNEL_2 GPIO38 ADC1_CHANNEL_6 GPIO34
ADC1_CHANNEL_3 GPIO39 ADC1_CHANNEL_7 GPIO35

And ADC2 has 10 channels:

Channel GPIO Channel GPIO
ADC2_CHANNEL_0 GPIO4 ADC2_CHANNEL_5 GPIO12
ADC2_CHANNEL_1 GPIO0 ADC2_CHANNEL_6 GPIO14
ADC2_CHANNEL_2 GPIO2 ADC2_CHANNEL_7 GPIO27
ADC2_CHANNEL_3 GPIO15 ADC2_CHANNEL_8 GPIO25
ADC2_CHANNEL_4 GPIO13 ADC2_CHANNEL_9 GPIO26

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
adc1_config_width(ADC_WIDTH_BIT_12);

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

// get the ADC characteristics
esp_adc_cal_characterize(
  ADC_UNIT_1,
  ADC_ATTEN_DB_11,
  ADC_WIDTH_BIT_12,
  DEFAULT_VREF,
  adc_chars);

// 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
i2s_adc_enable(I2S_NUM_0);

// 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;
                do
                {
                    // 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.

MAX4466

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.

MAX9814

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.

Noise

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.

Wiring

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
REG_SET_BIT(I2S_TIMING_REG(I2S_PORT), BIT(9));
REG_SET_BIT(I2S_CONF_REG(I2S_PORT), I2S_RX_MSB_SHIFT);

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.
#ADC #ANALOGUE TO DIGITAL CONVERTS #AUDIO INPUT #DMA #ESP32 #I2S #INPM441 #MAX4466 #MAX9814 #MICROPHONES #SPH0645

Related Posts

E32-S3 no DAC - No Problem! We'll Use PDM - In this post, I tackle the lack of a DAC on the ESP32-S3 by demonstrating how to use Pulse Density Modulated (PDM) audio with Sigma Delta Modulation to achieve analog audio output. I explore the simplicity of creating a PDM signal and its reconstruction into an audio signal using a low pass filter, even an RC filter, though a more sophisticated active filter is recommended. I guide through using both a timer and the I2S peripheral on the ESP32 for outputting PDM data, noting the quirks and solutions for each method. And I wrap up with how straight PDM signals can drive headphones or work with various amplifiers, including the MAX98358 or SSM2537, exhibiting the versatility of PDM in audio applications with the ESP32-S3.
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.
Decoding AVI Files for Fun and... - After some quality time with my ESP32 microcontroller, I've developed a version of the TinyTV and learned a lot about video and audio streaming along the way. Using Python and Wi-Fi technology, I was able to set up the streaming server with audio data, video frames, and metadata. I've can also explored the picture quality challenges of uncompressed image data and learned about MJPEG frames. Together with JPEGDEC for depth decoding, I've managed to effectively use ESP32's dual cores to achieve an inspiring 28 frames per second. Discussing audio sync, storage options and the intricacies of container file formats for video storage led me to the AVI format. The process of reading and processing AVI file headers and the listing subtype 'movi' allowed me to make significant headway in my project. All in all, I'm pretty chuffed with my portable battery powered video player. You can check out my code over on Github!
ESP32 TV Version 3 - In the latest board revision, I've successfully resolved some key issues, including a USB interface conflict between the USB2244 and the ESP32 and a risky battery charging mistake—no more direct USB 5V to the battery! Plus, I managed to wrap this up without any clumsy bodge wiring. I've even introduced a new feature: a microphone is now on board, setting the stage for some exciting future projects. Stay tuned for what's coming!
ESP32-S3: Which Pins Should I Use? - As an enthusiast of the ESP32-S3's versatility, I recognized the importance of understanding which pins are best to avoid. Inspired by the Random Nerds page for the classic ESP32, I've created a comprehensive pinout for the ESP32-S3 available on GitHub. The community's input is highly valued – suggestions and corrections are welcome to refine this resource into a dynamic guide for developers.

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.
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


Published

> Image

atomic14

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