Xiao Sense ESP32S3 Mic -- Struggling with getting clear samples using Arduino I2S API and mic noise

Hi Everyone.

I’m looking at this code in the docs, and ported it to PIO using C++ and the Arduino Framework.

If I use this code sample settings exactly, I can both set up an I2S connection and record clear audio to SD card.

If I change the sample rate to 3000Hz, which is my desired usecase, I get crunchy, crackling noise. It seems like if it is anything but having 16kHz sampling rate “breaks” the mic. The language in the docs suggests variance is possible…

After digging around in the <I2S.h> and related functionality in that sketch I put together an I2S connection procedure using the Arduino Framework interface for the ESP IDF I2S API. I get no errorcheck failures etc – by all accounts a connection is made!

#include <Arduino.h>
#include <stdlib.h>
#include <driver/i2s.h>
#include <driver/gpio.h>

#define MIC_WS GPIO_NUM_42
#define MIC_SD GPIO_NUM_41

#define MIC_SAMPLE_RATE 3000

i2s_config_t I2S_MIC_CONFIG;
i2s_pin_config_t I2S_MIC_PINS;
i2s_port_t I2S_MIC_PORT;

void setupMic(void);

void setupMic() {

  i2s_config_t I2S_MIC_CONFIG = {
    .mode                 = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM ),
    .sample_rate          = MIC_SAMPLE_RATE,
    .bits_per_sample      = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format       = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags     = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count        = MIC_DMA_BUFFER_COUNT,
    .dma_buf_len          = MIC_DMA_BUFFER_LENGTH,
    .use_apll             = false,
    .tx_desc_auto_clear   = false,
    .fixed_mclk           = 0
i2s_pin_config_t I2S_MIC_PINS = {
    .bck_io_num     = I2S_PIN_NO_CHANGE,
    .ws_io_num      = MIC_WS,
    .data_out_num   = I2S_PIN_NO_CHANGE,
    .data_in_num    = MIC_SD   

  esp_err_t ret = mic_init(I2S_MIC_PORT, I2S_MIC_PINS, I2S_MIC_CONFIG);

int mic_read(i2s_port_t port, int16_t *samples, int count)
  size_t bytes_read = 0;
  i2s_read(port, samples, (sizeof(int16_t) * count), &bytes_read, portMAX_DELAY);
  return bytes_read;

void setup() {

  /// etc etc

When I run stuff this way, e.g. just doing a single read of samples into a 1024 length buffer like so

int16_t* audioSamples = (int16_t *)malloc(MIC_DMA_BUFFER_LENGTH * sizeof(int16_t));
// error checks & memset here!
mic_read(I2S_MIC_PORT, audioSamples, MIC_DMA_BUFFER_LENGTH));
// etc

and doing an FFT on it, I get effectively a flatline. Nothing. All zeroes in my normalised amplitudes. I run the same core workflow on e.g. my INMP441, and straight away all is working as it should be.

I’m really struggling, as the docs and Arduino sketch both are so “high level” and there’s so little online about it in terms of debugging/forum posts that I’m finding it difficult to get real insight into how this thing works or is setup, or what could be going wrong.

I’d be very grateful to anyone who can offer an experienced eye.

Hi there,
Yes the I2S has always been a tougher area to master with Xiao, Check out the other “I2S” search threads , someone else worked through it to with “crucky sound” as AFAIK and doing both recording and playing back or something like recording and trans codding?
Also there is a Youtube DoorKey tracker demo or nerds article, that uses it (I2S) to play a sound when you leave the area with the Keyfob with the Xiao in it, speaker and amp board. pack of cards size thing.
GL :slight_smile: PJ