XIAO ESP32S3 SENSE - Arduino - I2S MIC init question

Hi,
I have tested with success capture audio from the microphone using the instructions on the XIAO pages…

Using Arduino IDE, something like this initialization works:

#include <I2S.h>

I2S.setAllPins(-1, 42, 41, -1, -1);
if (!I2S.begin(PDM_MONO_MODE, SAMPLE_RATE, SAMPLE_BITS)) {
  Serial.println("Failed to initialize I2S!");
  while (1) ;
}

Which works fine!

My question is, how can I initialize, using the following approach?

#include "driver/i2s.h"

i2s_config_t i2s_config = {
  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX ),
  .sample_rate = sampling_rate,
  .bits_per_sample = (i2s_bits_per_sample_t)16,
  .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
  .communication_format = I2S_COMM_FORMAT_STAND_PCM_LONG,
  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
  .dma_buf_count = 8,
  .dma_buf_len = 512,
  .use_apll = false,
  .tx_desc_auto_clear = false,
  .fixed_mclk = -1,  
};

i2s_pin_config_t pin_config = {
  .bck_io_num = -1,
  .ws_io_num = 42,
  .data_out_num = -1,
  .data_in_num = 41,

};

ret = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
if (ret != ESP_OK) {
  ei_printf("Error in i2s_driver_install");
}

ret = i2s_set_pin((i2s_port_t)0, &pin_config);
if (ret != ESP_OK) {
  ei_printf("Error in i2s_set_pin");
}

ret = i2s_zero_dma_buffer((i2s_port_t)0);
if (ret != ESP_OK) {
  ei_printf("Error in initializing dma buffer with 0");
}

Is it possible?
If so, what are the correct parameters?

I am trying to adapt a code for the ESP-EYE generated by the Edge Impulse Studio, to the XIAO ESP32S3 SENSE…
Trying to change some parameters… so far NO success…

Thanks all,
Valter

SUCCESS!

After some try-and-error, I have a working configuration!
I tested with a little Edge Impulse Studio AI Keyword Spotting App, it is WORKING OK!

The WORKING config shown below…
The following is a FULL CODE sample using the I2S MIC PDM config for use with XIAO ESP32S3 SENSE { After uploading, open the Serial Plotter to see the MIC output }:

#include <driver/i2s.h>

i2s_config_t i2s_config = {
    .mode                 = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM ),
    .sample_rate          = 16000U,
    .bits_per_sample      = I2S_BITS_PER_SAMPLE_16BIT,
    //.channel_format       = I2S_CHANNEL_FMT_ONLY_RIGHT,     // Also works
    .channel_format       = I2S_CHANNEL_FMT_ONLY_LEFT,
    //.communication_format = I2S_COMM_FORMAT_PCM,            // Also works
    .communication_format = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags     = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count        = 4,
    .dma_buf_len          = 1024,
    .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      = 42,
    .data_out_num   = I2S_PIN_NO_CHANGE,
    .data_in_num    = 41   
};

void setup() {
  Serial.begin(115200);
  
  // start up the I2S peripheral
  i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_NUM_0, &i2s_mic_pins);
}

#define BUFFER_SIZE 512
int16_t raw_samples[BUFFER_SIZE];

void loop() {
  // read from the I2S device
  size_t bytes_read = 0;
  i2s_read(I2S_NUM_0, raw_samples, sizeof(int16_t) * BUFFER_SIZE, &bytes_read, portMAX_DELAY);
  int samples_read = bytes_read / sizeof(int16_t);
  
  for (int i = 0; i < samples_read; i++) {
    Serial.printf("%ld\n", raw_samples[i]);
  }
}

Edge Impulse Studio
Edge Impulse Studio does not yet support ESP32S3 officially, so, the NPU functions of the CHIP cannot be used yet (we need to disable NN [neural net] function calls in code)… but, at least, now I can use the generated (there) code to run on the XIAO ESP32S3 SENSE with minimalist changes/adaptations…
It is nice to be able to run some basic AI-MACHINE LEARNING stuff on the XIAO ESP32S3, even without the full benefit of the NPU functions on the CHIP…

Regards all,
Valter

Hi all,
below is the WAV RECORDER example adapted to use the same approach:
{ Record MIC to Micro SD Card, wav format }

/* 
 * DI2S Version, 
 * WAV Recorder for Seeed XIAO ESP32S3 Sense
 * 
 * Original: https://wiki.seeedstudio.com/xiao_esp32s3_sense_mic/
 */

#include "driver/i2s.h"

#include "FS.h"
#include "SD.h"
#include "SPI.h"

// make changes as needed
#define RECORD_TIME   20  // seconds, The maximum value is 240
#define WAV_FILE_NAME "arduino_rec_DI2S"

// do not change for best
#define SAMPLE_RATE 16000U
#define SAMPLE_BITS 16
#define WAV_HEADER_SIZE 44
#define VOLUME_GAIN 2

void setup() {
  Serial.begin(115200);
  while (!Serial) ;

  i2s_init();
  
  if(!SD.begin(21)){
    Serial.println("Failed to mount SD Card!");
    while (1) ;
  }
  record_wav();

  i2s_deinit();
}

void loop() {
  delay(1000);
  Serial.printf(".");
}

void record_wav() {
  delay(2000); 
  
  uint32_t sample_size = 0;
  uint32_t record_size = (SAMPLE_RATE * SAMPLE_BITS / 8) * RECORD_TIME;
  uint8_t *rec_buffer = NULL;
  Serial.printf("Ready to start recording ...\n");

  File file = SD.open("/"WAV_FILE_NAME".wav", FILE_WRITE);
  // Write the header to the WAV file
  uint8_t wav_header[WAV_HEADER_SIZE];
  generate_wav_header(wav_header, record_size, SAMPLE_RATE);
  file.write(wav_header, WAV_HEADER_SIZE);

  // PSRAM malloc for recording
  rec_buffer = (uint8_t *)ps_malloc(record_size);
  if (rec_buffer == NULL) {
    Serial.printf("malloc failed!\n");
    while(1) ;
  }
  Serial.printf("Buffer: %d bytes\n", ESP.getPsramSize() - ESP.getFreePsram());

  // Start recording
  i2s_read((i2s_port_t)0, (void*)rec_buffer, record_size, &sample_size, portMAX_DELAY);
  
  if (sample_size == 0) {
    Serial.printf("Record Failed!\n");
  } else {
    Serial.printf("Record %d bytes\n", sample_size);
  }

  // Increase volume
  for (uint32_t i = 0; i < sample_size; i += SAMPLE_BITS/8) {
    (*(uint16_t *)(rec_buffer+i)) <<= VOLUME_GAIN;
  }

  // Write data to the WAV file
  Serial.printf("Writing to the file ...\n");
  if (file.write(rec_buffer, record_size) != record_size)
    Serial.printf("Write file Failed!\n");

  free(rec_buffer);
  file.close();
  Serial.printf("The recording is over.\n");
}

void generate_wav_header(uint8_t *wav_header, uint32_t wav_size, uint32_t sample_rate) {
  // See this for reference: http://soundfile.sapp.org/doc/WaveFormat/
  uint32_t file_size = wav_size + WAV_HEADER_SIZE - 8;
  uint32_t byte_rate = SAMPLE_RATE * SAMPLE_BITS / 8;
  const uint8_t set_wav_header[] = {
    'R', 'I', 'F', 'F', // ChunkID
    file_size, file_size >> 8, file_size >> 16, file_size >> 24, // ChunkSize
    'W', 'A', 'V', 'E', // Format
    'f', 'm', 't', ' ', // Subchunk1ID
    0x10, 0x00, 0x00, 0x00, // Subchunk1Size (16 for PCM)
    0x01, 0x00, // AudioFormat (1 for PCM)
    0x01, 0x00, // NumChannels (1 channel)
    sample_rate, sample_rate >> 8, sample_rate >> 16, sample_rate >> 24, // SampleRate
    byte_rate, byte_rate >> 8, byte_rate >> 16, byte_rate >> 24, // ByteRate
    0x02, 0x00, // BlockAlign
    0x10, 0x00, // BitsPerSample (16 bits)
    'd', 'a', 't', 'a', // Subchunk2ID
    wav_size, wav_size >> 8, wav_size >> 16, wav_size >> 24, // Subchunk2Size
  };
  memcpy(wav_header, set_wav_header, sizeof(set_wav_header));
}

static int i2s_init() {
  // Start listening for audio: MONO @ 16/16KHz
  i2s_config_t i2s_config = {
    .mode                 = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM ),
    .sample_rate          = 16000U,
    .bits_per_sample      = I2S_BITS_PER_SAMPLE_16BIT,
    //.channel_format       = I2S_CHANNEL_FMT_ONLY_RIGHT,     // Also works
    .channel_format       = I2S_CHANNEL_FMT_ONLY_LEFT,
    //.communication_format = I2S_COMM_FORMAT_PCM,            // Also works
    .communication_format = I2S_COMM_FORMAT_I2S,   
    .intr_alloc_flags     = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count        = 8,
    .dma_buf_len          = 512,    
    .use_apll             = false,
    .tx_desc_auto_clear   = false,
    .fixed_mclk           = 0
  };
  
  i2s_pin_config_t pin_config = {
      .bck_io_num = -1,    // IIS_SCLK
      .ws_io_num = 42,     // IIS_LCLK
      .data_out_num = -1,  // IIS_DSIN
      .data_in_num = 41,   // IIS_DOUT
  
  };
  esp_err_t ret = 0;

  ret = i2s_driver_install((i2s_port_t)0, &i2s_config, 0, NULL);
  if (ret != ESP_OK) {
    Serial.printf("Error in i2s_driver_install");
  }

  ret = i2s_set_pin((i2s_port_t)0, &pin_config);
  if (ret != ESP_OK) {
    Serial.printf("Error in i2s_set_pin");
  }

  ret = i2s_zero_dma_buffer((i2s_port_t)0);
  if (ret != ESP_OK) {
    Serial.printf("Error in initializing dma buffer with 0");
  }

  return int(ret);
}

static int i2s_deinit(void) {
    i2s_driver_uninstall((i2s_port_t)0); //stop & destroy i2s driver
    return 0;
}

Regards all,
Valter