This course starter recognizes a small set of spoken commands entirely on an ESP32-S3. An INMP441 microphone captures audio, Espressif's ESP-SR audio front end processes it, and MultiNet6 recognizes yes, no, start, and stop. A Dear PyGui desktop application monitors the waveform, voice activity, confidence, and recent commands.
This is fixed-command recognition, not general speech-to-text. The desktop application displays results but does not classify audio.
You will create an embedded voice-command interaction. You will:
- Stream microphone audio into the ESP32-S3.
- Apply noise suppression and voice-activity detection.
- Recognize a fixed command grammar with MultiNet6 on the ESP32-S3.
- Monitor recognition events on the laptop.
- Adapt the grammar, confidence threshold, or physical outputs.
| Starter | Audio input | Model | Where inference runs |
|---|---|---|---|
| Sound classification | INMP441 on ESP32-S3 | Student-trained Linear SVM | Laptop or ESP32-S3 |
| Speech recognition (this project) | INMP441 on ESP32-S3 | Pretrained ESP-SR MultiNet6 | ESP32-S3 |
| Vibration interaction | Piezo sensor on laptop audio input | Student-trained scikit-learn classifier | Laptop; Linear SVM can be exported |
- ESP32-S3 development board with 8 MB flash and octal PSRAM
- INMP441 I2S microphone
- USB data cable
- Five female-to-female jumper wires
The INMP441 is a 3.3 V device. Do not connect VDD to 5 V. Disconnect USB power before changing wiring, and keep the I2S wires short.
| INMP441 | ESP32-S3 | Purpose |
|---|---|---|
| VDD | 3V3 | Microphone power |
| GND | GND | Common ground |
| SCK | GPIO 15 | I2S bit clock |
| WS | GPIO 16 | I2S word select |
| SD | GPIO 17 | Microphone data |
| L/R | GND | Select left channel |
Install Python 3.10–3.13 and uv, then install the locked environment:
uv syncPlatformIO runs through uvx; a global PlatformIO installation is not required. The firmware uses ESP-IDF because ESP-SR is distributed as an ESP-IDF component.
Close the visualizer and other serial monitors, then flash the application and separate ESP-SR model partition:
uv run flash.py
uv run visualizer.pySelect the ESP32 serial device, click Connect, allow the board to reset, and say one supported command clearly near the microphone.
To choose the port explicitly:
uv run flash.py --port /dev/cu.usbmodem1101Replace the example port with your device. A normal PlatformIO upload replaces only the application. Use flash.py for a new board or after changing the ESP-SR model because it installs both the application and model partition.
The application displays the processed waveform, AFE voice activity, latest accepted command, MultiNet confidence, and recent detections. Try the four default commands:
- yes
- no
- start
- stop
Speak one command at a time and pause between commands. All recognition runs on the ESP32-S3; the laptop only decodes and displays USB packets.
- The ESP32-S3 reads the INMP441 in Philips I2S mode at 16 kHz.
- Signed 24-bit samples arrive in 32-bit slots and are scaled to signed 16-bit PCM.
- ESP-SR applies NSNet2 noise suppression and VADNet voice-activity detection.
- The quantized English MultiNet6 model recognizes the fixed command grammar.
- The board sends processed PCM and accepted events over native USB Serial/JTAG.
- The desktop application displays the results without performing inference.
| Packet | Direction | Content |
|---|---|---|
AUD2 |
ESP32-S3 → laptop | Sample rate, count, voice state, level, and PCM |
CMD1 |
ESP32-S3 → laptop | Accepted command ID and confidence |
The incremental parser in protocol.py ignores unrelated serial bytes and resumes at the next valid packet.
The INMP441 uses Philips I2S timing, including its one-clock bit shift. Keep the firmware in Philips mode:
I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(
I2S_DATA_BIT_WIDTH_32BIT, I2S_SLOT_MODE_MONO)MSB mode misaligns samples. A common symptom is repeated exact +1.0 plateaus with few negative samples. The current raw[i] >> 14 conversion leaves headroom; a smaller shift adds gain but can clip, while a larger shift reduces sensitivity.
- Change the four
esp_mn_commands_addcalls insrc/main.cand matchingCOMMAND_LABELSinvisualizer.pyto adapt the grammar. - Adjust
COMMAND_CONFIDENCE_THRESHOLDinsrc/main.cafter confirming microphone level and alignment. Raise it to reduce false detections; lower it if correct commands are rejected. - Tune microphone scaling before relying only on the confidence threshold.
- Add an LED, display, motor, or other output in
src/main.cfor a standalone interaction. - Update
platformio.inifor another compatible ESP32-S3 board and confirm its flash and PSRAM configuration.
uv run pytest -q
uvx platformio run- Confirm the waveform moves and packet counter increases.
- Say one supported command at a time and pause between commands.
- Move closer and check that voice activity changes.
- Confirm both firmware and model were installed with
uv run flash.py.
- Confirm Philips I2S mode is selected.
- Confirm
L/Ris grounded and the firmware reads the left slot. - Increase the right shift if normal speech still clips.
- Close other applications using the serial port.
- Recheck GPIO 15, 16, and 17 and the shared ground.
- Use a USB data cable and select the ESP32 USB modem port.
| Path | Purpose |
|---|---|
src/main.c |
I2S, ESP-SR AFE, MultiNet, and USB packets |
src/idf_component.yml |
ESP-SR dependency |
visualizer.py |
Desktop monitor |
protocol.py |
Incremental packet parser |
flash.py |
Application and model-partition flashing |
tests/test_protocol.py |
Host-side parser tests |
platformio.ini |
PlatformIO/ESP-IDF configuration |
sdkconfig.defaults |
Reproducible ESP-IDF and model settings |
partitions.csv |
Flash partition layout |
pyproject.toml / uv.lock |
Python environment |
Do not edit generated sdkconfig.*, dependencies.lock, managed_components/, or .pio/ content; change sdkconfig.defaults or source files instead.