porting I2S code from legacy driver to new driver
Posted: Mon Mar 24, 2025 8:31 am
I'm trying to port some code (microphone input from an INMP441 microphone) from the old legacy driver to the new driver. The code works on the legacy driver code path, but with the new driver I'm only getting audio noise out (with a very distored version of the actual audio somewhat audible somewhere under that noise).
It's an arduino sketch but the code in question isn't arduino specific and uses i2s.h and i2s_std.h directly. What exactly did I miss when porting the code?
instead of I2S_STD_MSB_SLOT_DEFAULT_CONFIG I also tried I2S_STD_PCM_SLOT_DEFAULT_CONFIG and I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG - with the last one I don't get any noise but the recording is extremely distorted and slowed down.
It's an arduino sketch but the code in question isn't arduino specific and uses i2s.h and i2s_std.h directly. What exactly did I miss when porting the code?
instead of I2S_STD_MSB_SLOT_DEFAULT_CONFIG I also tried I2S_STD_PCM_SLOT_DEFAULT_CONFIG and I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG - with the last one I don't get any noise but the recording is extremely distorted and slowed down.
Code: Select all
#define USE_LEGACY_DRIVER 0
#if USE_LEGACY_DRIVER
#include <driver/i2s.h>
#else
#include <driver/i2s_std.h>
#endif
#include <WiFi.h>
#include <WebServer.h>
// ===========================
// Enter your WiFi credentials
// ===========================
const char* ssid = "******";
const char* password = "******";
// I2S pins
#define I2S_WS 46
#define I2S_SCK 3
#define I2S_SD 45
// I2S peripheral to use (0 or 1)
#define I2S_PORT I2S_NUM_0
//---- Sampling ------------
#define SAMPLE_RATE 22050
#define SAMPLE_BITS 16
#define DMA_BUF_COUNT 2
#define DMA_BUF_LEN 1024 // number of samples, dma buffer bytes = DMA_BUF_LEN * NUM_CHANNELS * SAMPLE_BITS / 8
#define NUM_CHANNELS 1
WebServer Audioserver(82);
//---- Audio WAV configuration ------------
struct WAVHeader {
char chunkId[4]; // 4 bytes
uint32_t chunkSize; // 4 bytes
char format[4]; // 4 bytes
char subchunk1Id[4]; // 4 bytes
uint32_t subchunk1Size; // 4 bytes
uint16_t audioFormat; // 2 bytes
uint16_t numChannels; // 2 bytes
uint32_t sampleRate; // 4 bytes
uint32_t byteRate; // 4 bytes
uint16_t blockAlign; // 2 bytes
uint16_t bitsPerSample; // 2 bytes
char subchunk2Id[4]; // 4 bytes
uint32_t subchunk2Size; // 4 bytes
};
void initializeWAVHeader(WAVHeader &header, uint32_t sampleRate, uint16_t bitsPerSample, uint16_t numChannels) {
strncpy(header.chunkId, "RIFF", 4);
strncpy(header.format, "WAVE", 4);
strncpy(header.subchunk1Id, "fmt ", 4);
strncpy(header.subchunk2Id, "data", 4);
header.chunkSize = 0; // Placeholder for Chunk Size (to be updated later)
header.subchunk1Size = 16; // PCM format size (constant for uncompressed audio)
header.audioFormat = 1; // PCM audio format (constant for uncompressed audio)
header.numChannels = numChannels;
header.sampleRate = sampleRate;
header.bitsPerSample = bitsPerSample;
header.byteRate = (sampleRate * bitsPerSample * numChannels) / 8;
header.blockAlign = (bitsPerSample * numChannels) / 8;
header.subchunk2Size = 0; // Placeholder for data size (to be updated later)
}
#if USE_LEGACY_DRIVER
void mic_i2s_init() {
i2s_config_t i2sConfig = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = SAMPLE_RATE ,
.bits_per_sample = i2s_bits_per_sample_t(SAMPLE_BITS),
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = 0,
.dma_buf_count = DMA_BUF_COUNT,
.dma_buf_len = DMA_BUF_LEN,
.use_apll = false
};
i2s_driver_install(I2S_PORT, &i2sConfig, 0, NULL);
i2s_pin_config_t pinConfig = {
.bck_io_num = I2S_SCK,
.ws_io_num = I2S_WS ,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = I2S_SD
};
i2s_set_pin(I2S_PORT, &pinConfig);
}
#else
i2s_chan_handle_t mic_i2s_init() {
i2s_chan_handle_t rx_chan = NULL;
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_PORT, I2S_ROLE_MASTER);
chan_cfg.dma_desc_num = DMA_BUF_COUNT;
chan_cfg.dma_frame_num = DMA_BUF_LEN;
i2s_new_channel(&chan_cfg, NULL, &rx_chan)
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
.slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = static_cast<gpio_num_t>(I2S_SCK),
.ws = static_cast<gpio_num_t>(I2S_WS),
.dout = I2S_GPIO_UNUSED,//DATA_OUT_IO,
.din = static_cast<gpio_num_t>(I2S_SD),
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
i2s_channel_init_std_mode(rx_chan, &std_cfg);
i2s_channel_enable(rx_chan);
return rx_chan;
}
#endif
void handleAudioStream() {
const int buf_size = DMA_BUF_LEN*NUM_CHANNELS*sizeof(uint16_t);
uint8_t buf[buf_size];
// I2S
#if USE_LEGACY_DRIVER
mic_i2s_init();
#else
i2s_chan_handle_t rx_chan = mic_i2s_init();
#endif
// Get access to the client object
WiFiClient Audioclient = Audioserver.client();
// Send the 200 OK response with the headers
Audioclient.print("HTTP/1.1 200 OK\r\n");
Audioclient.print("Content-Type: audio/wav\r\n");
Audioclient.print("Access-Control-Allow-Origin: *\r\n");
Audioclient.print("\r\n");
// Send the initial part of the WAV header
WAVHeader wavHeader;
initializeWAVHeader(wavHeader, SAMPLE_RATE, SAMPLE_BITS, NUM_CHANNELS);
Audioclient.write(reinterpret_cast<const uint8_t*>(&wavHeader), sizeof(wavHeader));
size_t bytesRead = 0;
unsigned long last_time_stamp = millis();
while (true) {
if (!Audioclient.connected()) {
#if USE_LEGACY_DRIVER
i2s_driver_uninstall(I2S_PORT);
#else
i2s_channel_disable(rx_chan);
i2s_del_channel(rx_chan);
#endif
break;
}
// Read audio data from I2S DMA
#if USE_LEGACY_DRIVER
i2s_read(I2S_PORT, buf, buf_size, &bytesRead, portMAX_DELAY);
#else
i2s_channel_read(rx_chan, buf, buf_size, &bytesRead, portMAX_DELAY);
#endif
// Send audio data
if (bytesRead > 0) {
Audioclient.write((uint8_t*)buf, bytesRead);
}
}
}
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
WiFi.begin(ssid, password);
WiFi.setSleep(false);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Audioserver.on("/audio", HTTP_GET, handleAudioStream);
Audioserver.begin();
Serial.print("Server Ready! Use 'http://");
Serial.print(WiFi.localIP());
Serial.print(":82/audio");
Serial.println("' to connect");
}
void loop() {
Audioserver.handleClient();
}