A "broken" master clock has never been right any time of day

Hello. I'll get right to the point of this post. For 2 months, I've been trying to create a speaker system that allows me to hear my voice as I talk into the mic, in real time, through a speaker. I'm not trying to record anything and I'm not trying to send it over a server to do it. The audio is processed through one microcontroller locally.

Here are the components that I am working with.

1x INMP441 mic
1x ESP32 WROOM 32 board that has 38 pins
1x MAX98357A Amplifier
1x 4 Ohm 3 Watt Speaker.

Here are the connections between the components. I have these connected through a breadboard.

For the INMP441 Mic:

VDD to 3.3V Positive Rail
GND to GND Negative Rail
SD to GPIO 33
SCK to GPIO 32
WS to GPIO 25

For the MAX98357A:

VIN to 3.3V
GND to GND
DIN to GPIO 16
BCLK to GPIO 17
LRC to GPIO 18

I am not a programmer and I have searched through the internet for any tutorials on how this can be made. I even tried to combine the sketches through tutorials on Youtube, but I have not been successful. I have downloaded some sketches that allowed me to see the waveform in the Serial Plotter. I have downloaded some sketches that allowed me to make a sine wave generator, bluetooth speaker, etc.

In effort to make a sketch, I have been using ChatGPT in an effort to help me out. However, despite its usefulness, you can only use it for so long before it reaches its limit and you have to wait. However, I was able to have one sketch created in order to try and get the effect needed, but the serial monitor keeps saying:

E (7) esp_clock_output: esp_clock_output_start(207): Selected io is already mapped by another signal
E (8) i2s(legacy): i2s_check_set_mclk(1878): mclk configure failed
E (14) i2s(legacy): i2s_set_pin(1947): mclk config failed

I tried changing the bit rate to fix the clock frequency ( I did do some reading on the subject, but I am not a programmer) but it keeps messing up.

Here's is the code that I am currently using.

#include <driver/i2s.h>

// === Mic (INMP441) Pins ===
#define I2S_MIC_WS   25  // LRCL / WS
#define I2S_MIC_SD   33  // Data Out
#define I2S_MIC_SCK  32  // BCLK

// === Speaker (MAX98357A) Pins ===
#define I2S_SPK_DOUT 16  // DIN
#define I2S_SPK_BCLK 17  // BCLK
#define I2S_SPK_LRC  18  // LRC / WS

#define I2S_PORT_MIC I2S_NUM_0
#define I2S_PORT_SPK I2S_NUM_1

// === Buffer size ===
#define BUFFER_LEN 512
int16_t buffer[BUFFER_LEN];

void setup() {
  Serial.begin(115200);
  Serial.println("Starting real-time mic-to-speaker passthrough...");

  // === Setup MIC (RX) ===
  const i2s_config_t mic_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = 16000,
    .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 = 8,
    .dma_buf_len = 64,
    .use_apll = false,
    .tx_desc_auto_clear = false,
    .fixed_mclk = 0
  };

  const i2s_pin_config_t mic_pins = {
    .bck_io_num = I2S_MIC_SCK,
    .ws_io_num = I2S_MIC_WS,
    .data_out_num = I2S_PIN_NO_CHANGE,
    .data_in_num = I2S_MIC_SD
  };

  i2s_driver_install(I2S_PORT_MIC, &mic_config, 0, NULL);
  i2s_set_pin(I2S_PORT_MIC, &mic_pins);
  i2s_start(I2S_PORT_MIC);

  // === Setup SPEAKER (TX) ===
  const i2s_config_t spk_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = 16000,
    .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 = 8,
    .dma_buf_len = 64,
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0
  };

  const i2s_pin_config_t spk_pins = {
    .bck_io_num = I2S_SPK_BCLK,
    .ws_io_num = I2S_SPK_LRC,
    .data_out_num = I2S_SPK_DOUT,
    .data_in_num = I2S_PIN_NO_CHANGE
  };

  i2s_driver_install(I2S_PORT_SPK, &spk_config, 0, NULL);
  i2s_set_pin(I2S_PORT_SPK, &spk_pins);
  i2s_start(I2S_PORT_SPK);
}

void loop() {
  size_t bytes_read, bytes_written;

  // Read audio from mic
  esp_err_t read_err = i2s_read(I2S_PORT_MIC, (void*)buffer, sizeof(buffer), &bytes_read, portMAX_DELAY);

  if (read_err == ESP_OK && bytes_read > 0) {
    // Write audio to speaker
    i2s_write(I2S_PORT_SPK, (const void*)buffer, bytes_read, &bytes_written, portMAX_DELAY);
  }
}

I don't know if I'm missing any I2S drivers to pull this off or maybe it's just not possible on one board. I would like to get this fixed if any one knows where I went wrong.

If this is not the right area for this topic, please move it at your best convenience.

Edit: I tried using the feature before posting this and it didn't show up correctly. Sorry in advance.

Hey, it works! Thanks. Last thing I needed was a wall of text being posted here.

A schematic picture would be worth at least 1000 words.

Also, push-in breadboards are notorious for having poor, intermittent connections. Especially when you consider that MCLK has a fairly high frequency. You should consider some form of soldered breadboard assembly.

I don't know how to make one in the traditional sense, but I can provide a screenshot from Cirkit Designer.

This is the best I can come up with to show the connection. In the image, the components are not placed on a breadboard since the size of the components and the size of the breadboard does not match. I understand your concern about using the breadboard for making connections. I just feel that it's better, for me, this way before I make a indefinite connection soldering the parts. I will make the connections if I can get this issue resolved.

Tbh, you have kind of jumped into the deep end with this. Rather than use ChatGPT, I would look for real examples of where people have done similar things. You may not find an example of exactly what you want, but you can learn from them.

I would start with the speaker only and get that working. You can generate a waveform in RAM and play that through the speaker.

I was that foolhardy, huh? So what you are mentioning is something like a sine wave generator? If so, I have a sketch of that as well as a sketch where it's just showing the waveform as I talk into the mic. My ambition and curiosity might be grand, but I guess my practicality needs to be scaled back.

From what I can remember during my time with creating a sketch with ChatGPT, other than the pins and the driver setup, the most important part of the sketch is the void loop section. Maybe the problem is there?

I can't tell where the error messages come from, probably in setup(). If you put in some print statements we can see which call is generating them.

The error report tells you.

chatGPT does not program. It uses statistics and use that to guess. With you, it wrongly guessed to remap a previously mapped pin.

Throw chatGPT code away.

Write a description of your desired program configuration, input, processing and output.

From that description, write small functions to do those tasks, which can be called when needed.

Return here for help.

I'll discard the code, but where do I start with making the new code? I'm still new to programming.

Glad you asked.

....

What has this topic got to do with a master clock being wrong at all times?

In the past couple of weeks, due to my lack of programming knowledge, I have been depending on AI to create a sketch for a live voice system. Recently, it keeps generating a sketch that affected with the mclk function. I've been trying to avoid that, but I have been advised to abandon that method, the existing sketch, and start from scratch.

From what xfpd, if I'm interpreting the user's advice correctly, is to create a description of what I want the sketch to do and then learn how to build it piece by piece until I get the desired result. If that is correct, then I have a long way to go and a long time to learn. For its role in a project I'm working on, I don't have much time.

Did you try asking AI for help (not being funny!)

FWIW I asked DeepSeek and it had some possibly useful advice. IN particular,

The ESP32 has limited I2S clock output pins. When using two I2S peripherals (I2S_NUM_0 and I2S_NUM_1), they share some internal clock resources.

Try using only one I2S peripheral for both input and output. The ESP32 can handle full-duplex on a single I2S interface.

Consider switching from ESP32 to a Teensy Board. Doing so will give you access to the Teensy Audio Library. This library includes drivers for I2S peripheral as well as dozens of audio functions (filters, delays, ramps, etc) implemented within the processor. There's also a Graphical Design Tool that turns a diagram showing connections of your audio system into skeleton Arduino code.

I actually have one of those boards! I didn't think that it was going to be possible with that board when I was following a tutorial from Sparkfun. I can try using that and hopefully, I can try to build it off of that.

I was using AI for the entire process of making a sketch, but I was told to abandon that method in this thread.

Alright, I'm back. After trying my best to search online how to fix the problem with the master clock configuration on my own, I could not crack the issue. I went through a website called Cirkit Designer and used the A.I. on the site to help me out. I took an existing sketch that I found that was taking existing audio data and reading it through the serial plotter. I asked the A.I. to build upon that sketch to process the audio and sent it through the speakers in real time. After a few attempts, the information below shows the setup and the code.

// Include I2S driver
#include <driver/i2s.h>

// Connections to INMP441 I2S microphone
#define I2S_WS 25  // Word Select (WS)
#define I2S_SD 33  // Serial Data (SD)
#define I2S_SCK 32 // Serial Clock (SCK)

// Connections to MAX98357A
#define MAX98357A_LRCLK 18 // Left/Right Clock (LRCLK)
#define MAX98357A_BCLK 19   // Bit Clock (BCLK)
#define MAX98357A_DIN 22    // Data In (DIN)

// Use I2S Processor 0
#define I2S_PORT I2S_NUM_0

// Define input buffer length
#define bufferLen 64
int16_t sBuffer[bufferLen];

void i2s_install() {
  // Set up I2S Processor configuration
  const i2s_config_t i2s_config = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX), // Enable both RX and TX
    .sample_rate = 44100,
    .bits_per_sample = i2s_bits_per_sample_t(16),
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
    .intr_alloc_flags = 0,
    .dma_buf_count = 8,
    .dma_buf_len = bufferLen,
    .use_apll = false
  };

  i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
}

void i2s_setpin() {
  // Set I2S pin configuration for microphone
  const i2s_pin_config_t mic_pin_config = {
    .bck_io_num = I2S_SCK,
    .ws_io_num = I2S_WS,
    .data_out_num = -1, // Not used for microphone
    .data_in_num = I2S_SD
  };

  i2s_set_pin(I2S_PORT, &mic_pin_config);
}

void setup() {
  // Set up Serial Monitor
  Serial.begin(115200);
  Serial.println("Starting...");

  delay(1000);

  // Set up I2S for microphone
  i2s_install();
  i2s_setpin();
  i2s_start(I2S_PORT);

  // Set up I2S for MAX98357A
  const i2s_pin_config_t amp_pin_config = {
    .bck_io_num = MAX98357A_BCLK,
    .ws_io_num = MAX98357A_LRCLK,
    .data_out_num = MAX98357A_DIN, // Use DIN for audio data
    .data_in_num = -1 // Not used for MAX98357A
  };

  i2s_set_pin(I2S_PORT, &amp_pin_config);
}

void loop() {
  // Get I2S data and place in data buffer
  size_t bytesIn = 0;
  esp_err_t result = i2s_read(I2S_PORT, &sBuffer, bufferLen, &bytesIn, portMAX_DELAY);

  if (result == ESP_OK) {
    // Send audio data to MAX98357A
    i2s_write(I2S_PORT, sBuffer, bytesIn, &bytesIn, portMAX_DELAY);
  }
}

The problem is now solved. I got the effect I wanted. I would like to thank you all who took time out to post in this sub thread. I can now finally move on to the next phase of the costume.