Notes / m5stackcardputeres8311esp32arduinoaudio
M5Stack Cardputer-ADV plays a loud constant whine over all audio (ES8311, Arduino M5Unified)
On a Cardputer-ADV with custom Arduino firmware, every sound (test tone or MP3) had a loud constant whine on top. The speaker ran a mono I2S frame, so BCLK was half what M5Unified's fixed ES8311 clock divider expects. Setting spk_cfg.stereo = true fixed it.
Symptoms #
Custom Arduino firmware on an M5Stack Cardputer-ADV (an MP3 player sketch built on M5Cardputer / M5Unified and ESP8266Audio) produced a loud, constant whine over all audio:
- the same with a generated test tone and with MP3 playback,
- also when running on battery,
- at every sample rate tried.
The pitch of the actual audio was correct; only the added tone was wrong. The stock UIFlow firmware plays through the same ES8311 codec cleanly, which showed it was a configuration problem, not hardware.
Root cause #
A clock mismatch between the I2S frame and the codec's clock divider:
- The speaker was configured mono, so the I2S frame was 16 bits per sample period: BCLK = fs × 16 = 768 kHz at 48 kHz.
- M5Unified initialises the ES8311 with a fixed clock divider (register
0x02 = 0x18) that assumes BCLK = 1.536 MHz (a 32-bit stereo frame). - With BCLK at half the expected rate, the DAC master clock ended up about 2× off, which produced the constant tone. The audio pitch stayed right because LRCK still locked the sample rate.
The fix below confirms the mechanism. Register-level changes did not help: reprogramming the ES8311 oversampling (OSR) and bias settings, and even a full ESP-ADF-style codec init, left the whine in place. It took about a dozen reflashes to find.
Fix #
Configure the speaker as stereo before starting it. That gives a 32-bit frame and BCLK = 1.536 MHz, which matches the divider:
cppauto spk_cfg = M5Cardputer.Speaker.config();
spk_cfg.stereo = true; // 32-bit I2S frame -> BCLK 1.536 MHz at 48 kHz
M5Cardputer.Speaker.config(spk_cfg);
M5Cardputer.Speaker.begin();
spk_cfg.stereo = true was the whole fix; no codec registers needed changing. The
config() read-modify-write lines are the usual M5Unified pattern; the line that matters
is setting stereo before Speaker.begin().