Reading from INMP441 with ESP32-WROOM-32results in loud noise

robertfent
Posts: 6
Joined: Mon Aug 16, 2021 10:28 am

Reading from INMP441 with ESP32-WROOM-32results in loud noise

Postby robertfent » Mon Aug 16, 2021 10:48 am

Hello,
I am currently desperatly trying to get a NMP441 microphone properly working with a ESP32.
The code I am using seems to work but when I send the data off to my server hosted on a rpi the audio sounds broken and is just a very loud noise. My guess is that I am parsing the data from the microphone wrong. To setup the code and trying to understand what using the I2S interface means I used this doku: https://docs.espressif.com/projects/esp ... s/i2s.htmland this github repo: https://github.com/atomic14/esp32_audio
This is the code for setting up the mic:

Code: Select all

#define I2S_PORT (i2s_port_t)(0) // set main I2S port to I2S_NUM_0

// i2s config
const i2s_config_t i2s_config = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX), // receive
    .sample_rate = 44100,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // also tried 32 bit before
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, // use right channel
    .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // interrupt level 1
    .dma_buf_count = 64,                      // number of buffers
    .dma_buf_len = 512
};

// pin config
const i2s_pin_config_t pin_config = {
    .bck_io_num = 33,            // serial clock, sck
    .ws_io_num = 32,              // word select, ws
    .data_out_num = I2S_PIN_NO_CHANGE, // only used for speakers
    .data_in_num = 34            // serial data, sd
};

Serial.println("-----Mic Controller-----");

// config i2s driver and pins
// fct must be called before any read/write
esp_err_t err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
if (err != ESP_OK)
{
    Serial.printf("Failed installing the driver: %d\n", err);
}

err = i2s_set_pin(I2S_PORT, &pin_config);
if (err != ESP_OK)
{
    Serial.printf("Failed setting pin: %d\n", err);
}

Serial.println("I2S driver installed! :-)");

Serial.println("------------------------");
This the code for reading from the i2s peripheral:

Code: Select all

// 44,1KHz (SAMPLE_RATE) * Byte per sample * time in seconds = total size in bytes
const size_t recordSize = (44100 * I2S_BITS_PER_SAMPLE_16BIT/ 8) * recordTime; //recordTime = 2s

// size in bytes
size_t totalReadSize = 0;

// 16 bits per sample set in config * 1024 samples per buffers = total bits per buffer
const uint16_t totalBitsPerBuffer = 44100 * I2S_BITS_PER_SAMPLE_16BIT;
char *samples = (char *)calloc(totalBitsPerBuffer, sizeof(char));

// number of bytes read
size_t bytesRead;

Serial.println("Start recording...");
// read until wanted size is reached
while (totalReadSize < recordSize)
{
    // read to buffer
    esp_err_t err = i2s_read(I2S_PORT, (void *)samples, totalBitsPerBuffer, &bytesRead, portMAX_DELAY);

    // check if error occurd, if so stop recording
    if (err != ESP_OK)
    {
        Serial.println("Error while recording!");
        break;
    }

    printf("=======\n");
    for (int i = 0; i < bytesRead; i++)
    {
        printf("%02x ", (uint8_t)samples[i]);
        if ((i + 1) % 8 == 0)
        {
            printf("\n");
        }
    }
    printf("=======\n");

    // add read size to total read size
    totalReadSize += bytesRead;
}

HttpController httpController("http://192.168.2.100:8080");
httpController.postRequest("/esp/audio", "application/octet-stream", "", (uint8_t *)samples, totalReadSize);
while recording the output looks like this:
```
ec ff ef ff e7 ff ec ff
e3 ff e7 ff df ff e3 ff
db ff df ff d7 ff db ff
d4 ff d7 ff d0 ff d4 ff
cb ff d0 ff c8 ff cb ff
c4 ff c8 ff bf ff c4 ff
```

the server code receiving the buffer looks like this:

Code: Select all

app.post('/esp/audio', (req, res) => {
    console.log(`Got ${req.body.length} I2S bytes`);
    fs.writeFile('audio.raw', req.body, () => {
        res.sendStatus(200);
    });
});
and outputs this:
```
Got 180224 I2S bytes
<Buffer 02 00 02 00 02 00 02 00 02 00 02 00 02 00 02 00 03 00 03 00 04 00 04 00 03 00 03 00 03 00 03 00 04 00 04 00 05 00 05 00 05 00 05 00 05 00 05 00 04 00 ... 180174 more bytes>
```

But importing and reading this audio.raw into audacity results in just loud noise which shows that something is broken.

Does anybody have an idea what could be wrong?
Regards,
Robert

ESP_Minatel
Posts: 364
Joined: Mon Jan 04, 2021 2:06 pm

Re: Reading from INMP441 with ESP32-WROOM-32results in loud noise

Postby ESP_Minatel » Mon Aug 16, 2021 1:22 pm

Hi,

Can you test using this code?

https://github.com/pedrominatel/esp32-p ... d_spectrum

This code supports all the 3 microphones modes (ADC, PDM and I2S).
Using this example you can check if there is any noise being generated by your microphone (HW issue).

You can also try this code example. You'll need to do some small modification to change to I2S mic.

https://github.com/espressif/esp-idf/tr ... der_sdcard

You can compare the results and check if you are messing somehow the PDM data when sending over HTTP.

ESP_Sprite
Posts: 9770
Joined: Thu Nov 26, 2015 4:08 am

Re: Reading from INMP441 with ESP32-WROOM-32results in loud noise

Postby ESP_Sprite » Tue Aug 17, 2021 12:56 am

How do you import the raw file into Audacity? From your data, you should import it as signed 16-bit, little-endian. If you make a mistake there, you'll indeed get weird noises.

robertfent
Posts: 6
Joined: Mon Aug 16, 2021 10:28 am

Re: Reading from INMP441 with ESP32-WROOM-32results in loud noise

Postby robertfent » Tue Aug 17, 2021 9:24 am

ESP_Minatel wrote:
Mon Aug 16, 2021 1:22 pm
Hi,

Can you test using this code?

https://github.com/pedrominatel/esp32-p ... d_spectrum

This code supports all the 3 microphones modes (ADC, PDM and I2S).
Using this example you can check if there is any noise being generated by your microphone (HW issue).

You can also try this code example. You'll need to do some small modification to change to I2S mic.

https://github.com/espressif/esp-idf/tr ... der_sdcard

You can compare the results and check if you are messing somehow the PDM data when sending over HTTP.
Hello,
thank you for your answer! Using a static int16_t buffer and passing another size param to the is2_read did the trick.
The working code for recording for one second look like this:

Code: Select all

#define SAMPLE_RATE (16000)
const i2s_bits_per_sample_t BITS_PER_SAMPLE = I2S_BITS_PER_SAMPLE_16BIT;

[...]


// record time in seconds
uint8_t *MicController::record(uint8_t recordTime)
{
    // 16KHz * Byte per sample * time in seconds = total size in bytes
    const size_t recordSize = (SAMPLE_RATE * BITS_PER_SAMPLE / 8) * recordTime;
    const size_t recordSize1s = (SAMPLE_RATE * BITS_PER_SAMPLE / 8) * 1;
    static int16_t i2sBuff[recordSize1s];

    // size in bytes
    size_t totalReadSize = 0;

    // 32 bits per sample set in config * 1024 samples per buffers = total bits per buffer
    char *samples = (char *)calloc(totalBitsPerBuffer, sizeof(char));

    // number of bytes read
    size_t bytesRead;

    Serial.println("Start recording...");
    // read until wanted size is reached
    while (totalReadSize < recordSize1s)
    {
        // read to buffer
        // esp_err_t err = i2s_read(I2S_PORT, (void *)samples, totalBitsPerBuffer, &bytesRead, portMAX_DELAY);
        
        // using recordSize1s and i2sBuff makes the reading from i2s work
        esp_err_t err = i2s_read(I2S_PORT, (char *)i2sBuff, recordSize1s, &bytesRead, (100 / portTICK_RATE_MS));

        // check if error occurd, if so stop recording
        if (err != ESP_OK)
        {
            Serial.println("Error while recording!");
            break;
        }

        // add read size to total read size
        totalReadSize += bytesRead;
    }

    // convert bytes to mb
    double_t totalReadSizeMB = (double_t)totalReadSize / 1e+6;

    Serial.printf("Total read size: %fMb\n", totalReadSizeMB);

    // todo send data in main.cpp
    HttpController httpController("http://192.168.2.100:8080");
    httpController.postRequest("/esp/audio", "application/octet-stream", "", (uint8_t *)i2sBuff, totalReadSize);

    return (uint8_t *)i2sBuff;
}
But I have one problem with this approach: if I want to record two seconds:

Code: Select all

const size_t recordSize2s = (SAMPLE_RATE * BITS_PER_SAMPLE / 8) * 2;
I get the following error while building:
```
c:/users/rober/.platformio/packages/toolchain-xtensa32/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\esp32dev\firmware.elf section `.dram0.bss' will not fit in region `dram0_0_seg'
c:/users/rober/.platformio/packages/toolchain-xtensa32/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: DRAM segment data does not fit.
c:/users/rober/.platformio/packages/toolchain-xtensa32/bin/../lib/gcc/xtensa-esp32-elf/5.2.0/../../../../xtensa-esp32-elf/bin/ld.exe: region `dram0_0_seg' overflowed by 47448 bytes
```

Do you have any idea how I can increase the record time with this approach?
Thank you again,
Robert

robertfent
Posts: 6
Joined: Mon Aug 16, 2021 10:28 am

Re: Reading from INMP441 with ESP32-WROOM-32results in loud noise

Postby robertfent » Tue Aug 17, 2021 9:26 am

ESP_Sprite wrote:
Tue Aug 17, 2021 12:56 am
How do you import the raw file into Audacity? From your data, you should import it as signed 16-bit, little-endian. If you make a mistake there, you'll indeed get weird noises.
Thank you for your answer. Unfortunatley this was not the problem because I imported the raw file with 16 bit unsigned PCM, little endian and mono channel.
The solution for my answer were some changes in the cpp code which I mentioned in another comment :-)

ESP_Minatel
Posts: 364
Joined: Mon Jan 04, 2021 2:06 pm

Re: Reading from INMP441 with ESP32-WROOM-32results in loud noise

Postby ESP_Minatel » Tue Aug 17, 2021 12:27 pm

Hi,

You can try:

Code: Select all

const size_t recordSize2s = (SAMPLE_RATE * (BITS_PER_SAMPLE / 8)) * 2;

robertfent
Posts: 6
Joined: Mon Aug 16, 2021 10:28 am

Re: Reading from INMP441 with ESP32-WROOM-32results in loud noise

Postby robertfent » Tue Aug 17, 2021 3:17 pm

ESP_Minatel wrote:
Tue Aug 17, 2021 12:27 pm
Hi,

You can try:

Code: Select all

const size_t recordSize2s = (SAMPLE_RATE * (BITS_PER_SAMPLE / 8)) * 2;
Hello,
using this still leads to the same error. Passing a size with the value 64000 to init the buffer

Code: Select all

static int16_t i2sBuff[recordSize2s];
is just too big and overflows the DRAM I guess.
I found a solution for this to record up to 3 seconds but everything more than 3 seconds leads to my esp crashing permanently while running. This is the 'solution':

Code: Select all

int16_t *i2sBuff = (int16_t *)calloc(recordSize2s, sizeof(char));
Do you know another way?

robertfent
Posts: 6
Joined: Mon Aug 16, 2021 10:28 am

Re: Reading from INMP441 with ESP32-WROOM-32results in loud noise

Postby robertfent » Wed Aug 18, 2021 8:03 am

Using the example code from https://github.com/espressif/esp-idf/bl ... der_main.c results in loud noise again and sounds broken again:

Code: Select all

#define SAMPLE_RATE (44100)
#define SAMPLES_PER_BUFFER (1024)                           // 256 samples per buffer
#define BITS_PER_SAMPLE (I2S_BITS_PER_SAMPLE_16BIT)        // num of bits per sample
#define SAMPLE_SIZE (BITS_PER_SAMPLE * SAMPLES_PER_BUFFER) // amount of bits per buffer
#define BYTES_PER_SAMPLE (BITS_PER_SAMPLE / 8)             // amount of bytes per sample
#define BYTE_RATE (SAMPLE_RATE * BYTES_PER_SAMPLE)         // amount of bytes per second

static int16_t i2s_buff[SAMPLE_SIZE];

// i2s config
const i2s_config_t i2s_config = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX), // receive
    .sample_rate = SAMPLE_RATE,
    .bits_per_sample = BITS_PER_SAMPLE,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, // use left channel
    .communication_format = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // interrupt level 1
    .dma_buf_count = 64,                      // number of buffers
    .dma_buf_len = SAMPLES_PER_BUFFER,        // number of sample per buffer
    .use_apll = 1                             // enable this to get accurate clock
};

[...]

// record for 2 seconds
const int32_t flashRecordSize = BYTE_RATE * 2;

// size in bytes
int totalReadSize = 0;

// number of bytes read
size_t bytesRead;

// start recording
while (totalReadSize < flashRecordSize)
{
    // read to buffer
    esp_err_t err = i2s_read(I2S_PORT, (char *)i2s_buff, SAMPLE_SIZE, &bytesRead, portMAX_DELAY);

    // check if error occurd, if so stop recording
    if (err != ESP_OK)
    {
        Serial.println("Error while recording!");
        break;
    }

    // add read size to total read size
    totalReadSize += bytesRead;
}

Who is online

Users browsing this forum: TobyG99 and 85 guests