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();
}