Summary
On the M5Stack StopWatch (board_M5StopWatch), the microphone returns buffers of
exact zeros once M5.Speaker.begin() has been called. The microphone works
correctly if the speaker is never started in that boot, so the capture path
itself is functional — it appears to be released incompletely when the speaker
takes the shared codec.
Environment
- Board: M5Stack StopWatch (C152), ESP32-S3
- M5Unified:
774d920cd6851a5231748b56ece1b073645f313f
- M5GFX:
93b480bb349749202c8a2a953065c8ae95f58320
- platform:
espressif32 @ 6.12.0, Arduino framework
Expected
After ending the speaker, M5.Mic.begin() + M5.Mic.record() should capture
audio, allowing an application to alternate between playback and capture on the
shared ES8311.
Actual
M5.Mic.begin() and M5.Mic.record() both return true, and
M5.Mic.isRecording() completes normally, but every sample in the buffer is
0.
Reproduction
void setup() {
auto cfg = M5.config();
M5.begin(cfg); // internal_spk and internal_mic both default true
M5.Speaker.begin(); // <-- capture is dead from here on
}
void probe() {
M5.Speaker.end();
M5.Mic.begin();
delay(200); // generous settle
static int16_t buf[4800];
memset(buf, 0, sizeof(buf));
M5.Mic.record(buf, 4800, 16000);
delay(320);
while (M5.Mic.isRecording()) delay(1);
M5.Mic.end();
int16_t lo = 0, hi = 0;
for (auto s : buf) { if (s < lo) lo = s; if (s > hi) hi = s; }
Serial.printf("raw=[%d..%d]\n", lo, hi); // raw=[0..0]
}
Comment out M5.Speaker.begin() and set cfg.internal_spk = false, and the
same probe() reports plausible audio, e.g. raw=[-9296..32751], around
-43 dBFS, stable across repeated calls and responsive to room noise.
Things that did not help
| Attempt |
Result |
M5.Speaker.end() before M5.Mic.begin() |
zeros |
cfg.internal_spk = false, speaker never begun |
works |
cfg.internal_spk = false, speaker configured via M5.Speaker.config() and started manually |
zeros |
gpio_reset_pin() on the shared MCK/BCK/WS (18/17/15) between the two |
zeros |
periph_module_reset(PERIPH_I2S0_MODULE) after Speaker_Class::end() |
zeros |
Notes
The two paths share MCK 18 / BCK 17 / WS 15 and differ in data pin (mic 16 on
I2S_NUM_1, speaker 21 on I2S_NUM_0) and in the ES8311 clock-manager value
each enable callback writes (0x01 → 0xBA for capture, 0xB5 for playback).
Speaker_Class::end() does call _i2s_driver_uninstall(_cfg.i2s_port), so the
driver is released; whatever keeps the codec in playback appears to sit below
that. Since cfg.internal_spk = false at M5.begin() is sufficient to keep
capture working, something in the speaker's initialisation may be leaving state
that end() does not undo.
Happy to run further tests on the hardware if useful.
Summary
On the M5Stack StopWatch (
board_M5StopWatch), the microphone returns buffers ofexact zeros once
M5.Speaker.begin()has been called. The microphone workscorrectly if the speaker is never started in that boot, so the capture path
itself is functional — it appears to be released incompletely when the speaker
takes the shared codec.
Environment
774d920cd6851a5231748b56ece1b073645f313f93b480bb349749202c8a2a953065c8ae95f58320espressif32 @ 6.12.0, Arduino frameworkExpected
After ending the speaker,
M5.Mic.begin()+M5.Mic.record()should captureaudio, allowing an application to alternate between playback and capture on the
shared ES8311.
Actual
M5.Mic.begin()andM5.Mic.record()both returntrue, andM5.Mic.isRecording()completes normally, but every sample in the buffer is0.Reproduction
Comment out
M5.Speaker.begin()and setcfg.internal_spk = false, and thesame
probe()reports plausible audio, e.g.raw=[-9296..32751], around-43 dBFS, stable across repeated calls and responsive to room noise.Things that did not help
M5.Speaker.end()beforeM5.Mic.begin()cfg.internal_spk = false, speaker never beguncfg.internal_spk = false, speaker configured viaM5.Speaker.config()and started manuallygpio_reset_pin()on the shared MCK/BCK/WS (18/17/15) between the twoperiph_module_reset(PERIPH_I2S0_MODULE)afterSpeaker_Class::end()Notes
The two paths share MCK 18 / BCK 17 / WS 15 and differ in data pin (mic
16onI2S_NUM_1, speaker21onI2S_NUM_0) and in the ES8311 clock-manager valueeach enable callback writes (
0x01 → 0xBAfor capture,0xB5for playback).Speaker_Class::end()does call_i2s_driver_uninstall(_cfg.i2s_port), so thedriver is released; whatever keeps the codec in playback appears to sit below
that. Since
cfg.internal_spk = falseatM5.begin()is sufficient to keepcapture working, something in the speaker's initialisation may be leaving state
that
end()does not undo.Happy to run further tests on the hardware if useful.