Some hardware links on this page are affiliate links. As an Amazon Associate I earn from qualifying purchases. We may earn a commission if you buy through them, at no extra cost to you. See the affiliate disclosure.
A phone is a poor intercom. This pack turns an ESP32-S3, a digital microphone, and a small amplifier into a Home Assistant Assist satellite: the wake word runs on the board, audio streams to the Assist pipeline you already configured, and the spoken reply comes back out the speaker. An optional continued-listening switch opens another listen window after the reply, so a follow-up does not need the wake word again.
GPIO numbers and the friendly name are substitutions. Wi-Fi credentials stay in secrets.yaml. Do not publish a live device name.
Pairs with voice “find my phone” once a satellite exists, and with the ESPHome AirPlay receiver if you want music on a different board. This pack is speech in and speech out, not a music streamer.
What problem it solves
You want a wake word and a spoken reply in a room without a commercial satellite, and you want the wake word decided on the ESP32-S3 instead of streaming audio all day.
How it works
- Two I2S buses — the INMP441 and the MAX98357A do not share bit clock and word select. One bus is the microphone, the other is the speaker. Sharing one bus is the usual reason a build hears itself or records silence.
- Local wake word —
micro_wake_wordlistens to the microphone with an on-device model (okay_nabuin the pack default). A voice-activity model sits in front so a thud is less likely to count. Detection starts the Assist session and passes the phrase that fired. - Assist session —
voice_assistantstreams the microphone to Home Assistant and plays the reply on the I2S speaker. Speech-to-text, the conversation step, and the reply voice are configured in the Assist pipeline, not in this YAML. - Continued listening — when the reply finishes, if the continued-listening switch is on, the node starts another listen for a few seconds. Silence or a stop phrase such as “never mind” ends the session. The switch is how you turn follow-ups off in a noisy room.
- Wake word comes back —
stop_after_detectiondefaults to true, so the pack restartsmicro_wake_wordwhen the session returns to idle. Without that, the second wake word never hears. - A status LED is optional — one GPIO follows listening versus idle. The pack does not require a ring of pixels.
This is one satellite per board. Echo cancellation is out of scope: put the microphone off-axis from the speaker, or the reply will wake the next listen.
Hardware notes
- Board. ESP32-S3 with PSRAM (N8R8 or N16R8). The pack YAML uses
esp32-s3-devkitc-1and ESP-IDF. A board without PSRAM will not hold the wake-word model. - Microphone. INMP441: VDD to 3.3 V (not 5 V), GND, BCLK, WS, and SD. Tie L/R to GND so the mic is the left channel, and set
channel: left. Use a short lead. A 3.3 V pin next to the antenna is a common source of hiss; power the mic from a quieter 3.3 V pin. - Amplifier. MAX98357A VIN to 5 V, GND, BCLK, LRC, and DIN. The shutdown pin floats enabled on most breakouts and mixes left and right into mono, which is what you want. Do not feed the speaker pins into another amp.
- Speaker. 4 Ω to 8 Ω, a few watts. The outputs are bridge-tied: both speaker wires go to the amp, neither goes to ground.
- Pins. Substitutions. Avoid the USB pins and the strapping pins on your module. The pack’s example uses one trio for the mic and another for the amp; change them to match the silkscreen.
Snippet
Trimmed from the pack YAML. Pins are substitutions. The follow-up switch is a global in the pack. Credentials stay in secrets.yaml.
substitutions:
friendly_name: Room satellite
mic_bclk: GPIO4
mic_lrclk: GPIO5
mic_din: GPIO6
spk_bclk: GPIO15
spk_lrclk: GPIO16
spk_dout: GPIO17
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
psram:
i2s_audio:
- id: i2s_mic_bus
i2s_bclk_pin: ${mic_bclk}
i2s_lrclk_pin: ${mic_lrclk}
- id: i2s_spk_bus
i2s_bclk_pin: ${spk_bclk}
i2s_lrclk_pin: ${spk_lrclk}
microphone:
- platform: i2s_audio
id: mic_raw
i2s_audio_id: i2s_mic_bus
i2s_din_pin: ${mic_din}
adc_type: external
pdm: false
channel: left
sample_rate: 16000
speaker:
- platform: i2s_audio
id: amp_speaker
i2s_audio_id: i2s_spk_bus
i2s_dout_pin: ${spk_dout}
dac_type: external
channel: mono
micro_wake_word:
id: room_wake
microphone:
microphone: mic_raw
models:
- model: okay_nabu
vad:
on_wake_word_detected:
- voice_assistant.start:
wake_word: !lambda return wake_word;
voice_assistant:
microphone: mic_raw
speaker: amp_speaker
micro_wake_word: room_wake
on_end:
- if:
condition:
lambda: 'return id(follow_up);'
then:
- voice_assistant.start:
- micro_wake_word.start:
follow_up is a global the pack sets from a dashboard switch. The status LED is in the full pack YAML. A follow-up ends on Assist’s own end-of-speech timeout, and “never mind” is Home Assistant’s built-in cancel phrase; neither needs extra YAML.
Setup outline
- In Home Assistant, open Settings → Voice assistants and confirm a pipeline has speech-to-text and a reply voice. A satellite with nowhere to send audio will look “wired” and do nothing useful.
- Flash a speaker-only config and play a short file from Home Assistant. Fix pins and power before you add the microphone.
- Add the microphone bus, confirm a recording in the Assist audio debug, then add
micro_wake_wordandvoice_assistant. - Adopt the node. Say the wake word, ask for the time, and confirm the reply comes from the speaker rather than a phone.
- Turn continued listening on, ask a follow-up without the wake word, then say “never mind” and confirm the session ends.
Troubleshooting
- Wake word never fires. PSRAM is off, the model name is wrong, or the mic channel does not match L/R. Record a clip first; if the clip is silent, the wake word cannot hear either.
- Hiss or a constant false wake. The mic is on a noisy 3.3 V pin or shares a ground loop with the amp. Shorten the mic leads and power it away from the antenna. Raise the model probability cutoff before you chase YAML.
- Reply plays, then the board is deaf.
micro_wake_wordstopped after the first detection and was not started again on idle. The pack restarts it inon_end. - The reply wakes another listen. The microphone faces the speaker and there is no echo cancellation in this pack. Aim the mic away, lower the amp gain, or turn continued listening off.
- Assist connects, no reply audio. The pipeline has no text-to-speech, or the speaker bus pins are the microphone pins. The speaker-only test in step 2 is the check.
- Brown-outs when the reply starts. The amp is on a data-only USB port. Feed 5 V that can supply the speaker.
Credits
Continued listening after a reply follows the pattern in mitrokun/esp32s3-voice_assistant. Wake word and the Assist session use ESPHome’s micro wake word and voice assistant components. This write-up is an original overview; the pack YAML is a generic two-bus satellite, not a copy of that firmware.