I've been troubleshooting my I²S microphone ICS-43434 with an ESP32-S3 for the past week
Microphone and I²S Configuration
- ICS-43434 from InvenSense
- 24-bit I²S interface
- 32-bit word length
- 1-bit shift (I believe - Philips preset)
- Only the left channel is transmitted - hardware configuration
https://media-hosting.imagekit.io/43de7 ... Lg0CpO6w__
IMAGE: The ESP32-S3 and microphone are on a custom PCB, and I’ve verified the I²S signal using a Saleae logic analyzer:
https://media-hosting.imagekit.io/17cf6 ... WGtwwX2A__
Problem Description
The captured I²S signal looks correct, but when processing the audio with the ESP32-S3, the values stay around ±20 and do not respond to sound.
What I’ve Tried So Far
- My code is based on the ESP-IDF I²S recorder example, with the following modifications:
- Changed the microphone configuration from PDM to Standard I²S (STD mode).
- Initially saved data to an SD card, but only noise was recorded.
- To debug, I commented out the write function and observed the raw data. - I read data using into a uint8_t array.
Code: Select all
i2s_channel_read() - Since I²S slots are 32-bit, I attempted to reconstruct the audio by combining the first two bytes of each sample.
- However, the values still don’t react to sound.
IMAGE: Here’s a terminal output screenshot showing the issue:
https://media-hosting.imagekit.io/ef444 ... hAU6nOgQ__
CODE: My modified recorder example
Code: Select all
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* I2S Digital Microphone Recording Example */
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_system.h"
#include "esp_vfs_fat.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "driver/gpio.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "format_wav.h"
#include "esp_log.h"
static const char *TAG = "std_rec_example";
#define CONFIG_EXAMPLE_BIT_SAMPLE 32
#define CONFIG_EXAMPLE_SAMPLE_RATE 44100
#define CONFIG_EXAMPLE_SPI_MOSI_GPIO GPIO_NUM_5
#define CONFIG_EXAMPLE_SPI_MISO_GPIO GPIO_NUM_2
#define CONFIG_EXAMPLE_SPI_SCLK_GPIO GPIO_NUM_4
#define CONFIG_EXAMPLE_SPI_CS_GPIO GPIO_NUM_6
#define CONFIG_EXAMPLE_I2S_BCLK_GPIO GPIO_NUM_40
#define CONFIG_EXAMPLE_I2S_DATA_GPIO GPIO_NUM_41
#define CONFIG_EXAMPLE_I2S_WS_GPIO GPIO_NUM_39
#define CONFIG_EXAMPLE_REC_TIME 5
#define SPI_DMA_CHAN SPI_DMA_CH_AUTO
#define NUM_CHANNELS (1) // For mono recording only!
#define SD_MOUNT_POINT "/sdcard"
#define SAMPLE_SIZE (CONFIG_EXAMPLE_BIT_SAMPLE * 1024)
#define BYTE_RATE (CONFIG_EXAMPLE_SAMPLE_RATE * (CONFIG_EXAMPLE_BIT_SAMPLE / 8)) * NUM_CHANNELS
// When testing SD and SPI modes, keep in mind that once the card has been
// initialized in SPI mode, it can not be reinitialized in SD mode without
// toggling power to the card.
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
sdmmc_card_t *card;
i2s_chan_handle_t rx_handle = NULL;
static uint8_t i2s_readraw_buff[SAMPLE_SIZE * 2];
size_t bytes_read;
const int WAVE_HEADER_SIZE = 44;
void mount_sdcard(void)
{
esp_err_t ret;
// Options for mounting the filesystem.
// If format_if_mount_failed is set to true, SD card will be partitioned and
// formatted in case when mounting fails.
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
.allocation_unit_size = 8 * 1024
};
ESP_LOGI(TAG, "Initializing SD card");
spi_bus_config_t bus_cfg = {
.mosi_io_num = CONFIG_EXAMPLE_SPI_MOSI_GPIO,
.miso_io_num = CONFIG_EXAMPLE_SPI_MISO_GPIO,
.sclk_io_num = CONFIG_EXAMPLE_SPI_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4000,
};
ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize bus.");
return;
}
// This initializes the slot without card detect (CD) and write protect (WP) signals.
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
slot_config.gpio_cs = CONFIG_EXAMPLE_SPI_CS_GPIO;
slot_config.host_id = host.slot;
ret = esp_vfs_fat_sdspi_mount(SD_MOUNT_POINT, &host, &slot_config, &mount_config, &card);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount filesystem.");
} else {
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
}
return;
}
// Card has been initialized, print its properties
sdmmc_card_print_info(stdout, card);
}
void record_wav(uint32_t rec_time)
{
// Use POSIX and C standard library functions to work with files.
int flash_wr_size = 0;
ESP_LOGI(TAG, "Opening file");
uint32_t flash_rec_time = BYTE_RATE * rec_time;
const wav_header_t wav_header =
WAV_HEADER_PCM_DEFAULT(flash_rec_time, 16, CONFIG_EXAMPLE_SAMPLE_RATE, 1);
// First check if file exists before creating a new file.
struct stat st;
if (stat(SD_MOUNT_POINT"/record.wav", &st) == 0) {
// Delete it if it exists
unlink(SD_MOUNT_POINT"/record.wav");
}
// Create new WAV file
FILE *f = fopen(SD_MOUNT_POINT"/record.wav", "a");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for writing");
return;
}
// Write the header to the WAV file
fwrite(&wav_header, sizeof(wav_header), 1, f);
// Start recording
while (flash_wr_size < flash_rec_time) {
// Read the RAW samples from the microphone
if (i2s_channel_read(rx_handle, (char *)i2s_readraw_buff, SAMPLE_SIZE, &bytes_read, 1000) == ESP_OK) {
printf("[0] %u [1] %u [2] %u [3]%u [4] %u [5] %u [6] %u [7]%u...\n", i2s_readraw_buff[0], i2s_readraw_buff[1], i2s_readraw_buff[2], i2s_readraw_buff[3], i2s_readraw_buff[4], i2s_readraw_buff[5], i2s_readraw_buff[6], i2s_readraw_buff[7]);
// For debuging purposes
int16_t sample = (int16_t)((i2s_readraw_buff[0] << 8) | (i2s_readraw_buff[1]));
printf("%d\n", sample);
// Write the samples to the WAV file
//fwrite(i2s_readraw_buff, bytes_read, 1, f);
flash_wr_size += bytes_read;
} else {
printf("Read Failed!\n");
}
}
ESP_LOGI(TAG, "Recording done!");
fclose(f);
ESP_LOGI(TAG, "File written on SDCard");
// All done, unmount partition and disable SPI peripheral
esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, card);
ESP_LOGI(TAG, "Card unmounted");
// Deinitialize the bus after all devices are removed
spi_bus_free(host.slot);
}
void init_microphone(void)
{
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &rx_handle));
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(CONFIG_EXAMPLE_SAMPLE_RATE),
.slot_cfg = {
.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT,
.slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT,
.slot_mode = I2S_SLOT_MODE_MONO,
.slot_mask = I2S_STD_SLOT_LEFT,
.ws_width = I2S_DATA_BIT_WIDTH_32BIT,
.ws_pol = false,
.bit_shift = true,
.left_align = true,
.big_endian = true,
.bit_order_lsb = false
},
.gpio_cfg = {
.bclk = CONFIG_EXAMPLE_I2S_BCLK_GPIO,
.din = CONFIG_EXAMPLE_I2S_DATA_GPIO,
.ws = CONFIG_EXAMPLE_I2S_WS_GPIO,
.invert_flags = {
.bclk_inv = false, //mogoče samo tukaj treba true
},
},
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
}
void app_main(void)
{
printf("STD microphone recording example start\n--------------------------------------\n");
// Mount the SDCard for recording the audio file
mount_sdcard();
// Acquire a I2S PDM channel for the PDM digital microphone
init_microphone();
ESP_LOGI(TAG, "Starting recording for %d seconds!", CONFIG_EXAMPLE_REC_TIME);
// Start Recording
record_wav(CONFIG_EXAMPLE_REC_TIME);
// Stop I2S driver and destroy
ESP_ERROR_CHECK(i2s_channel_disable(rx_handle));
ESP_ERROR_CHECK(i2s_del_channel(rx_handle));
}
- Am I misinterpreting how the ESP32-S3 handles 24-bit I²S data?
- Is there a better way to extract meaningful audio data from the 32-bit slots?
I'm truly stuck on this issue, and any insights or suggestions would mean the world to me!
Thank you so much in advance!