I2S microphone (RX)

amvox2
Posts: 1
Joined: Tue Mar 15, 2022 3:51 am

Re: I2S microphone (RX)

Postby amvox2 » Wed Mar 16, 2022 11:54 pm

This is certainly the best solution out there for this product.

Many thanks to BuddyCasino for the base, andriy for the optimizations, and everyone else who started asking questions and looking for answers.

If you're working with the newer API, circa 2020, I'd recommend using I2S_NUM_0 for TX and RX, using i2s_write(...) instead of i2s_write_bytes and i2s_read(...) instead of i2s_read_bytes.

Here are the changes I applied

Code: Select all


    i2s_config_t i2s_config_tx = {
	.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
	.sample_rate = sample_rate,
	.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 32,
    .dma_buf_len = 64,
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0};
    
    i2s_config_t i2s_config_rx = {
	.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
	.sample_rate = sample_rate,
	.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 32,
    .dma_buf_len = 64,
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0};
    
void task_megaphone(void *pvParams)
{
	uint16_t buf_len = 1024;
	char buf[1024];

	struct timeval tv = {0};
	struct timezone *tz = {0};
	gettimeofday(&tv, &tz);
	uint64_t micros = tv.tv_usec + tv.tv_sec * 1000000;
	uint64_t micros_prev = micros;
	uint64_t delta = 0;

init_i2s();
	int cnt = 0;
	size_t bytes_written = 0;

	while(1)
	{
		char *buf_ptr_read = buf;
		char *buf_ptr_write = buf;

		// read whole block of samples
		size_t bytes_read = 0;
		while(bytes_read == 0) {
      i2s_read(
        I2S_NUM_0, 
        buf, 
        buf_len, 
        &bytes_read, 
        portMAX_DELAY);
		}

		uint32_t samples_read = bytes_read / (I2S_BITS_PER_SAMPLE_32BIT / 8);

		//  convert 2x 32 bit stereo -> 1 x 16 bit mono
		for(int i = 0; i < samples_read; i++) {

            buf_ptr_write[0] = buf_ptr_read[2]; // mid
            buf_ptr_write[1] = buf_ptr_read[3]; // high     
		}

		// local echo
		bytes_written = samples_read * (I2S_BITS_PER_SAMPLE_32BIT / 8);
    i2s_write(I2S_NUM_0, buf, bytes_written, &bytes_written, portMAX_DELAY);
    i2s_write(I2S_NUM_0, buf, bytes_written, &bytes_written, portMAX_DELAY);

		cnt += samples_read;

		if(cnt >= 44100) {
			gettimeofday(&tv, &tz);
			micros = tv.tv_usec + tv.tv_sec * 1000000;
			delta = micros - micros_prev;
			micros_prev = micros;
			printf("%d samples in %" PRIu64 " usecs\n", cnt, delta);

			cnt = 0;
		}
	}
}
andriy wrote:
Tue May 30, 2017 6:03 pm
BuddyCasino wrote:Here is a complete example, I verified it works:

Code: Select all

/*
 * app_main.c
 *
 *  Created on: 30.03.2017
 *      Author: michaelboeckling
 */

#include <stdlib.h>
#include <stddef.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/time.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/i2s.h"

#define TAG "main"

static void init_i2s()
{
	const int sample_rate = 44100;

	/* TX: I2S_NUM_0 */
    i2s_config_t i2s_config_tx = {
	.mode = I2S_MODE_MASTER | I2S_MODE_TX,
	.sample_rate = sample_rate,
	.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
	.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,   // 2-channels
	.communication_format = I2S_COMM_FORMAT_I2S_MSB,
	.dma_buf_count = 32,                            // number of buffers, 128 max.
	.dma_buf_len = 32 * 2,                          // size of each buffer
	.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1        // Interrupt level 1
    };

    i2s_pin_config_t pin_config_tx = {
			.bck_io_num = GPIO_NUM_26,
			.ws_io_num = GPIO_NUM_25,
			.data_out_num = GPIO_NUM_22,
			.data_in_num = GPIO_NUM_23
	};
    i2s_driver_install(I2S_NUM_0, &i2s_config_tx, 0, NULL);
    i2s_set_pin(I2S_NUM_0, &pin_config_tx);


    /* RX: I2S_NUM_1 */
    i2s_config_t i2s_config_rx = {
	.mode = I2S_MODE_MASTER | I2S_MODE_RX, // Only TX
	.sample_rate = sample_rate,
	.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,    // Only 8-bit DAC support
	.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,   // 2-channels
	.communication_format = I2S_COMM_FORMAT_I2S_MSB,
	.dma_buf_count = 32,                            // number of buffers, 128 max.
	.dma_buf_len = 32 * 2,                          // size of each buffer
	.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1        // Interrupt level 1
	};

	i2s_pin_config_t pin_config_rx = {
		.bck_io_num = GPIO_NUM_17,
		.ws_io_num = GPIO_NUM_18,
		.data_out_num = I2S_PIN_NO_CHANGE,
		.data_in_num = GPIO_NUM_5
	};

	i2s_driver_install(I2S_NUM_1, &i2s_config_rx, 0, NULL);
	i2s_set_pin(I2S_NUM_1, &pin_config_rx);

}


void task_megaphone(void *pvParams)
{
	uint16_t buf_len = 1024;
	char *buf = calloc(buf_len, sizeof(char));

	struct timeval tv = {0};
	struct timezone *tz = {0};
	gettimeofday(&tv, &tz);
	uint64_t micros = tv.tv_usec + tv.tv_sec * 1000000;
	uint64_t micros_prev = micros;
	uint64_t delta = 0;

	init_i2s();

	int cnt = 0;
	int bytes_written = 0;

	while(1)
	{
		char *buf_ptr_read = buf;
		char *buf_ptr_write = buf;

		// read whole block of samples
		int bytes_read = 0;
		while(bytes_read == 0) {
			bytes_read = i2s_read_bytes(I2S_NUM_1, buf, buf_len, 0);
		}

		uint32_t samples_read = bytes_read / 2 / (I2S_BITS_PER_SAMPLE_32BIT / 8);

		//  convert 2x 32 bit stereo -> 1 x 16 bit mono
		for(int i = 0; i < samples_read; i++) {

			// const char samp32[4] = {ptr_l[0], ptr_l[1], ptr_r[0], ptr_r[1]};

			// left
			buf_ptr_write[0] = buf_ptr_read[2]; // mid
			buf_ptr_write[1] = buf_ptr_read[3]; // high

			// right
			buf_ptr_write[2] = buf_ptr_write[0]; // mid
			buf_ptr_write[3] = buf_ptr_write[1]; // high


			buf_ptr_write += 2 * (I2S_BITS_PER_SAMPLE_16BIT / 8);
			buf_ptr_read += 2 * (I2S_BITS_PER_SAMPLE_32BIT / 8);
		}

		// local echo
		bytes_written = samples_read * 2 * (I2S_BITS_PER_SAMPLE_16BIT / 8);
		i2s_write_bytes(I2S_NUM_0, buf, bytes_written, portMAX_DELAY);

		cnt += samples_read;

		if(cnt >= 44100) {
			gettimeofday(&tv, &tz);
			micros = tv.tv_usec + tv.tv_sec * 1000000;
			delta = micros - micros_prev;
			micros_prev = micros;
			printf("%d samples in %" PRIu64 " usecs\n", cnt, delta);

			cnt = 0;
		}
	}
}

/**
 * entry point
 */
void app_main()
{
    printf("starting app_main()\n");
    xTaskCreatePinnedToCore(&task_megaphone, "task_megaphone", 16384, NULL, 20, NULL, 0);
}

Wow.... super thanks @BuddyCasino
I verify it works flawlessly!

I can also make it work if I use "bits_per_sample : I2S_BITS_PER_SAMPLE_32BIT" as I2S TX config, by changing the code a bit:

Code: Select all

        int bytes_read = 0;
        while(bytes_read == 0) {
            bytes_read = i2s_read_bytes(I2S_NUM_1, buf, sizeof(buf), portMAX_DELAY);
        }

        uint32_t samples_read = bytes_read / (I2S_BITS_PER_SAMPLE_32BIT / 8);

        for (int i = 0; i < samples_read; i++)
        {
            buf_ptr_write[0] = buf_ptr_read[2]; // mid
            buf_ptr_write[1] = buf_ptr_read[3]; // high        
        }

        int bytes_written = samples_read * (I2S_BITS_PER_SAMPLE_32BIT / 8);

        i2s_write_bytes(I2S_NUM_0, (const char*)buf, bytes_written, portMAX_DELAY);

Lena_nbn
Posts: 1
Joined: Mon Oct 21, 2024 5:00 pm

Re: I2S microphone (RX)

Postby Lena_nbn » Mon Oct 21, 2024 5:11 pm

Hi everyone,

I hope someone can help me figure out an issue I’ve been having with my audio level measurement setup. I’m using an ICS-43434 microphone with an ESP32-WROOM-32 to measure the sound dB levels in my environment. Despite my efforts, I'm getting very similar dB levels whether the room is calm or noisy, which isn’t expected.

Setup Details:
Microphone: ICS-43434
Microcontroller: ESP32-WROOM-32
Sampling Rate: 16000 Hz
Gain Factor: Initially set to 3.5, adjusted to 2.0 during troubleshooting
Environment: Quiet room typically around 25 dB, noisy room (with music) around 60 dB (measured using the Decibel X app)

Issue: The dB levels reported by my system are consistently in the same range, regardless of the noise level in the room. For example:

Calm Room: ~22.8 dB (expected ~25 dB)
Noisy Room: ~22.8 dB (expected ~60 dB)

I've tried lots of recommendations, but still can't get accurate results that reflect the environment's noise levels.

Any suggestions or insights would be greatly appreciated!

Thanks in advance! 🎧

Who is online

Users browsing this forum: No registered users and 150 guests