Streams 16-bit PCM audio from a USB microphone straight to AssemblyAI's Universal-Streaming WebSocket API and prints text as you speak.
Two entrypoints:
| Script | What it does |
|---|---|
transcribe_mic.py |
prints live transcription of everything it hears |
voice_assistant.py |
waits for a wake word, then answers out loud on a USB speaker |
chmod +x setup.sh
./setup.shThis installs PortAudio (libportaudio2, needed by sounddevice) and espeak-ng
(text-to-speech), creates a venv, and installs the assemblyai + sounddevice
packages.
Get a key from https://www.assemblyai.com/dashboard/home, then:
echo 'export ASSEMBLYAI_API_KEY=<your-key>' >> ~/.bashrc
source ~/.bashrcarecord -l # ALSA card/device numbers
./.venv/bin/python transcribe_mic.py --list-devices./.venv/bin/python transcribe_mic.py --device USBPartial results update in place on one line (… hello wor), finalized turns print
on their own line. Ctrl+C ends the session cleanly (important: AssemblyAI bills
streaming per second of open connection, not per second of audio).
Useful flags:
| Flag | Purpose |
|---|---|
--device 2 / --device USB |
pick input by index or name substring |
--sample-rate 48000 |
force a rate if auto-detection picks a bad one |
--keyterms GPIO Raspberry |
bias recognition toward domain words |
--save transcript.txt |
append timestamped final turns to a file |
--speech-model universal-streaming-multilingual --language-code de |
non-English |
--wav sample.wav |
smoke-test the API/network with a 16-bit mono WAV, no mic needed |
./.venv/bin/python voice_assistant.py --device USB --speaker USBSay hey pi, what time is it — or just hey pi, wait for “Yes?”, then speak the
command. hey pi, goodbye ends the session.
The wake word is matched on the text AssemblyAI returns, not on raw audio, so there is
no second model to run on the Pi. Matching tolerates one letter per word, because
short names come back spelled inconsistently (hey pie, hey py). The microphone is
ignored while a reply is playing, so the assistant never answers itself.
| Flag | Purpose |
|---|---|
--speaker USB / --speaker 1 |
output device by name substring or index |
--wake-word 'hey computer' |
change the phrase; repeat the flag for several |
--wake-threshold 1.0 |
require the wake phrase to be transcribed exactly |
--no-follow-up |
ignore a bare wake word instead of asking for the command |
--respond-command ./answer.sh |
own reply logic: command text on stdin, reply on stdout |
--tts-backend piper --piper-model en_US-amy-medium.onnx |
neural voice instead of espeak-ng |
--voice en-gb --words-per-minute 150 |
espeak-ng voice and pace |
Without --respond-command the assistant answers the time and date and otherwise
echoes what it heard. Any executable works as the brain — for example an LLM call:
#!/usr/bin/env bash
# answer.sh — stdin is what the user said, stdout is what the Pi says back
read -r command
curl -s https://api.example.com/chat -d "{\"prompt\":\"$command\"}" | jq -r .replySpeaker check on its own, no microphone or API key needed:
./.venv/bin/python tts.py --speaker USB "Raspberry Pi audio is working"./.venv/bin/python -m pytest tests
./.venv/bin/ruff check .Wake-word matching, the reply handlers and the audio muting logic are covered without hardware or network access.
- Sample rate: the script tries 16 kHz first and falls back to 48/44.1 kHz if the mic doesn't support it, then tells AssemblyAI the rate actually used. No resampling, so there's no quality loss or CPU cost.
- Make the USB mic the default (optional) — create
~/.asoundrcwith card N fromarecord -l:pcm.!default { type asym capture.pcm "hw:N,0" } - Input too quiet:
alsamixer→ F6 (pick the USB device) → F4 (capture) → raise gain. - No sound from the speaker:
aplay -llists playback devices;speaker-test -c2 -twavverifies the default. Pass the USB device by name with--speaker USB, or make it the default by addingplayback.pcm "hw:M,0"to thepcm.!defaultblock in~/.asoundrc. - Assistant hears itself: keep the speaker off the mic's desk, or lower the volume with
alsamixer→ F6 → F3 — capture is muted during playback, but a loud room still echoes into the next turn. - Overflow warnings (
input overflow): raiseBLOCK_MSin the script to 100. - Headless/boot autostart —
/etc/systemd/system/stt.service:Then[Unit] Description=AssemblyAI live transcription After=network-online.target sound.target [Service] User=pi WorkingDirectory=/home/pi/pi5-assemblyai-stt Environment=ASSEMBLYAI_API_KEY=<your-key> ExecStart=/home/pi/pi5-assemblyai-stt/.venv/bin/python voice_assistant.py --device USB --speaker USB Restart=always [Install] WantedBy=multi-user.target
sudo systemctl enable --now stt. - Network: streaming needs a stable outbound WSS connection to
streaming.assemblyai.com; prefer Ethernet or 5 GHz Wi-Fi. A Pi 5 uses <5% CPU for capture since all recognition happens server-side.