ESP32 Mic Testing With INMP441 and DumbDisplay (2023)

By Trevor Lee in CircuitsArduino




Introduction: ESP32 Mic Testing With INMP441 and DumbDisplay

By Trevor Lee

More by the author:

(Video) ESP32 Audio Input - INMP441 and SPH0645 MEMS I2S Breakout Boards

In this post, I will try to demonstrate a fairly easy way to do experiment on INMP441 module acting as mic input to ESP32 board via a I2S channel of the microcontroller.

  • INMP441 module will be acting as a mic input for capturing mono 16-bit audio signals at rate 8000 samples per second.
  • DumbDisplay is used as the UI. Additionally, DumbDisplay also acts as the speaker of the captured audio signals, as well as the recorder of them (in WAV format).

Step 1: DumbDisplay As UI

The UI realized by DumbDisplay provides three functions:

  1. DumbDisplay app acting as a speaker that reproduces the sound signal captured and shipped to DumbDisplay app realtime wirelessly.
  2. DumbDisplay app acting as a recorder of the sound signal captured and shipped to DumsDisplay app. Note that the audio file format will be WAV.
  3. DumbDisplay app acting as a player for playing the sound signal recorded in WAV format.

A specific function is selected by clicking on the correcting tab -- MIC, REC, or PLAY.

Whatever the selected function be, the Start button starts the selected function; and the Stop button stops it.

The "meter" at the bottom is for setting the signal amplification (simple software-based multiplying the signals by some "amplification factor"). The possible range is from 1 to 20. And 10 is the initial setting.

Yes, there is also a "plotter" at the top. It is intended to show the sound wave captured from INMP441; however, it is apparent that wave form shown is very rough, much rougher than wanted :-(

Step 2: Connections

Here are the needed connections between ESP32 and INMP441:

  • connect ESP32 3.3V to VDD of INMP441
  • connect ESP32 GND to GND and L/R of INMP441 (connecting L/R to GND means using a single I2S for capturing mono sound)
  • connect ESP32 GPIO25 to WS of INMP441
  • connect ESP32 GPIO33 to SD of INMP441
  • connect ESP32 GPIO32 to SCK of INMP441

Note that the picture shows the back of a normal pre-solider INMP441 board. As a matter of fact, the mic input is really on the back as shown.

Step 3: Prepare Arduino IDE

In order to be able to compile and run the sketch shown here, you will first need to install theDumbDisplay Arduino library. Open your Arduino IDE; go to the menu itemTools | Manage Libraries, and type "dumbdisplay" in the search box there.

On the other side -- your Android phone side -- you will need to install theDumbDisplay Android app.

Step 4: The Sketch

You can download the sketch here.

(Video) #419 ESP32 Audio Tutorial with lots of examples

If your actual wiring between ESP32 and INMP441 is different from stated in above section, please modify the sketch where the wiring mappings are defined.

// INMP441 I2S pin assignment
#define I2S_WS 25
#define I2S_SD 33
#define I2S_SCK 32

As hinted previously, the sketch will be using DumbDisplay as UI of the experiment in this post. Even though you can choose to use ESP32's WIFI to make connection with DumbDisplay app, it is strongly recommended that Bluetooth be used. Indeed, Bluetooth with name ESP32 is the default way of connection.

#if defined(USE_BLUETOOTH)
// ESP32 Bluetooth with name ESP32
#include "esp32dumbdisplay.h"
DumbDisplay dumbdisplay(new DDBluetoothSerialIO("ESP32"));
// ESP32 WiFi
#include "wifidumbdisplay.h"
DumbDisplay dumbdisplay(new DDWiFiServerIO(WIFI_SSID, WIFI_PASSWORD));

If you want to try out WIFI connectivity, please comment out the line that defines USE_BLUETOOTH, and specify WIFI_SSID and WIFI_PASSWORD.

The name of the recorded audio WAV file will always be recorded_sound.wav.

const char* SoundName = "recorded_sound"

Note that only a single name is used; and hence, new recording will always overwrite old one. BTW, since files will be read/write from your phone's storage, permission is needed. Please grant media access from the settings page of your DumbDisplay app installation.

Now, let's examine some core areas of the sketch.

I2S is initialized the standard way, like in the setup block:

 // set up I2S

In fact, I copied those routines from resources easily found on the Internet.

Notice that only a single channel I2S_PORT is used for capturing mono sound. And the audio samples are read from the I2S channel, like in the loop block:

 // read I2S data and place in data buffer
size_t bytesRead = 0;
esp_err_t result = i2s_read(I2S_PORT, &StreamBuffer, StreamBufferNumBytes, &bytesRead, portMAX_DELAY);

int samplesRead = 0;
if (result == ESP_OK) {
samplesRead = bytesRead / 2; // 16 bit per sample

And those audio samples read are streamed to DumbDisplay app like

dumbdisplay.sendSoundChunk16(soundChunkId, StreamBuffer, samplesRead, isFinalChunk);

Yes, audio samples are sent to DumbDisplay app in chunks (and hence a soundChunkId is needed), and you get such soundChunkId by properly initiating sound streaming like

if (what == 1) {
// start streaming sound, and get the assigned "chunk id"
soundChunkId = dumbdisplay.streamSound16(SoundSampleRate, SoundNumChannels); // sound is 16 bits per sample
dumbdisplay.writeComment(String("STARTED mic streaming with chunk id [") + soundChunkId + "]");
} else if (what == 2) {
// started saving sound, and get the assigned "chunk id"
soundChunkId = dumbdisplay.saveSoundChunked16(SoundName, SoundSampleRate, SoundNumChannels);
dumbdisplay.writeComment(String("STARTED record streaming with chunk id [") + soundChunkId + "]");

Step 5: Notes on Using DumbDisplay

As shown above, a dumbdisplay global object is declared at the beginning of the sketch

 // ESP32 Bluetooth with name ESP32
#include "esp32dumbdisplay.h"
DumbDisplay dumbdisplay(new DDBluetoothSerialIO("ESP32"));


 // ESP32 WiFi
#include "wifidumbdisplay.h"
DumbDisplay dumbdisplay(new DDWiFiServerIO(WIFI_SSID, WIFI_PASSWORD));

Depending on the way of making connection with DumbDisplay app, different type of "SerialIO" will be passed as an argument when constructing the DumbDisplay object.

The different UI DumbDisplay layers will normally be declared global variables

PlotterDDLayer* plotterLayer;
LcdDDLayer* micTabLayer;
LcdDDLayer* recTabLayer;
LcdDDLayer* playTabLayer;
LcdDDLayer* startBtnLayer;
LcdDDLayer* stopBtnLayer;
LcdDDLayer* amplifyLblLayer;
LedGridDDLayer* amplifyMeterLayer;

And those layers will be created in the setup block, like

micTabLayer = dumbdisplay.createLcdLayer(8, 1);
micTabLayer->border(1, "gray");
amplifyMeterLayer = dumbdisplay.createLedGridLayer(MaxAmplifyFactor, 1, 1, 2);
amplifyMeterLayer->border(0.2, "blue");
amplifyMeterLayer->enableFeedback("fa:rpt50"); // rep50 means auto repeat every 50 milli-seconds

The different layers are laid out like

(Video) ESP32 | INMP441 | Tutorial - [Part.0] Set up I2S for Microphone

 DDAutoPinConfigBuilder<1> builder('V'); // vertical
.beginGroup('H') // horizontal
.beginGroup('H') // horizontal
.beginGroup('S') // stacked, one on top of another

Note that in the sketch, the "layer creation and layout" is enclosed with "record / playback" layout commands

 dumbdisplay.recordLayerSetupCommands(); // start recording the layout commands
dumbdisplay.playbackLayerSetupCommands("esp32ddmice"); // playback the stored layout commands, as well as persist the layout to phone, so that can reconnect

The use of "record / playback" layout commands is not absolutely necessary. Nevertheless, the use of the pair of commands will allow DumbDisplay app to meaningfully reconnect to the running ESP32 sketch.

  • Even though the states of the different layers are not persisted, and will be gone when once disconnected.
  • The layer "creation and layout" is re-play automatically upon reconnection.

The DD connection version (reconnection) is tracked with the help of DDConnectVersionTracker. And it is declared and used like

DDConnectVersionTracker cvTracker(-1); // it is for tracking [new] DD connection established 
if (cvTracker.checkChanged(dumbdisplay)) {

If DD connection version is changed (i.e. a fresh connection), better update the UI components, since all the layers will be just like freshly recreated.

Simply checking whether a layer has "feedback" (e.g. clicking) can be like

 if (micTabLayer->getFeedback()) {
what = 1;
} else if (recTabLayer->getFeedback()) {
what = 2;
} else if (playTabLayer->getFeedback()) {
what = 3;

Nevertheless, more detailed "feedback" checking will be like

 const DDFeedback* feedback = amplifyMeterLayer->getFeedback();
if (feedback != NULL) {
amplifyFactor = feedback->x + 1;
updateAmplifyFactor = true;

Here, it is checked to see if any "feedback" from the amplifyMeterLayer -- feedback. If it is not NULL, the X position of the layer where the click was, is treated as the "amplification factor".

One more thing. The sketch will automatically "stop" once it is detected "idle" (i.e. disconnected from DumbDisplay app). And here is the needed code in the setup block.

 // set when DD idle handler ... here is a lambda expression
dumbdisplay.setIdleCalback([](long idleForMillis) {
started = false; // if idle, e.g. disconnected, stop whatever

Note that

[](long idleForMillis) { ... }

is a C++ lambda expression -- an in-place function; and therefore can actually be implemented with a regular callback function.

Step 6: Tunning

A reminder: Turn off the "show commands" options of DumbDisplay app, so that the sound capturing qualify can be better.

Even with "show commands" turned off, I have to admit that the effect of the sketch shown here is somewhat off what is desired :-(

And here are several "parameters" that you may be interested in tuning:

  • Setting SoundSampleRate to something other than 8000. Note that 8000 is the usual sample rate for voice. You may try setting it to 44100, but I seriously doubt that it will work for the sketch.
  • Setting StreamBufferNumBytes to something other than 256. The smaller the value is, the lesser samples to ship to DumbDisplay app each chunk. However, the smaller the value is, the higher is also the "overhead".
  • Setting I2S_DMA_BUF_LEN (# samples) to something other than 1024. Note that I2S makes use of DMA buffers for storing real-time audio signal samples. After each DMA buffer is filled up, ESP32 is "notified" of the fact, making the data available for reading. Effectively, the bigger the value is, the higher the lag. (In fact, the actual audio lag of this sketch is not from the DMA buffer, but from latency sending samples from ESP32 to DumbDisplay app.
  • Setting I2S_DMA_BUF_COUNT to something other than 8. Note that I2S_DMA_BUF_COUNT is the number of DMA buffers that I2S can use. Normally, 2 should be good enough -- one used for buffering; one used by ESP32 for reading -- nevertheless, it is hard to ensure the responsiveness of the code run by ESP32; hence it makes sense to set it to something higher.

Step 7: Enjoy!

Anyway, I still wish that you will find this to be a little fun ESP32 mic testing experiment.

Hopefully, I will try to combine this and the concept of my previous post -- Yes! Voice Recognization Experiment With -- to come up with a more interesting application. Until then, enjoy!

Peace be with you! May God bless you! Jesus loves you!

Be the First to Share


    LEVITATING BANANA - Electromagnetic Levitation by ElectroBing in Electronics

    14 1.1K

    Wi-Fi Control of a Motor With Quadrature Feedback by Palingenesis in Arduino

    81 9.5K

    PCB Hotplate Mini Edition by Arnov Sharma in Arduino

    1 188 19K

    Lily∞Bot: Obstacle Avoidance With 4 Sonar (HC-SR04) by carlottaberry in Robots

    (Video) Setting a Mic for use with wled on an ESP32

    7 725

    (Video) ESP32 | INMP441 | Tutorial - [Part.4] Capturing audio from i2s mic to save WAV file (I2S interface)
    • Game Design: Student Design Challenge

    • Make it Glow Contest

    • Baking Contest



    1. 3D-printable ESP32 powered VU display with the INMP441 digital microphone and a WS2812B matrix
    (Scott Marley)
    2. Wireless Wiretap System using 2x ESP32, INMP441 & MAX98357A
    (That Project)
    3. Can we make a wireless microphone? ESP32 Bluetooth Fail
    4. ESP32 Audio Input Using I2S and Internal ADC
    5. ESP32 Sound - Working with I2S
    (DroneBot Workshop)
    Top Articles
    Latest Posts
    Article information

    Author: Prof. An Powlowski

    Last Updated: 04/14/2023

    Views: 5923

    Rating: 4.3 / 5 (44 voted)

    Reviews: 91% of readers found this page helpful

    Author information

    Name: Prof. An Powlowski

    Birthday: 1992-09-29

    Address: Apt. 994 8891 Orval Hill, Brittnyburgh, AZ 41023-0398

    Phone: +26417467956738

    Job: District Marketing Strategist

    Hobby: Embroidery, Bodybuilding, Motor sports, Amateur radio, Wood carving, Whittling, Air sports

    Introduction: My name is Prof. An Powlowski, I am a charming, helpful, attractive, good, graceful, thoughtful, vast person who loves writing and wants to share my knowledge and understanding with you.