6.8 KiB
javis-slave on a Raspberry Pi
Turn a Raspberry Pi with a USB microphone and a speaker into a Javis satellite: it detects the wake word locally, streams the utterance to the Javis master on your LAN, and plays the spoken reply. No GPU, no models beyond the tiny openWakeWord ONNX file.
Hardware
| Part | Notes |
|---|---|
| Raspberry Pi 3B+ / 4 / 5 / Zero 2 W | 64-bit Raspberry Pi OS (Bookworm or newer). Zero 2 W works headless; wake-word inference uses ~15 % of one core. |
| USB microphone or USB sound card + mic | Most USB mics only offer 44.1/48 kHz — the slave resamples to 16 kHz automatically. Conference-style mics (e.g. Jabra, Anker) give the best wake-word range. |
| Speaker | 3.5 mm jack, HDMI, USB DAC or an I²S HAT. |
| Optional: official 7" touch display | Use setup-pi.sh --gui for the same touch UI as the Surface build; brightness control works via /sys/class/backlight. |
Install (5 minutes)
# On the Pi
git clone <your-repo> javis && cd javis/javis-slave # or scp the javis-slave folder
sudo ./setup-pi.sh # headless
# sudo ./setup-pi.sh --gui # with touch display
# add --with-vad for silero-vad end-pointing (pulls PyTorch, ~200 MB; RMS gate is used otherwise)
The script installs apt dependencies, creates /opt/javis-slave/.venv,
pre-downloads the wake-word models, grants the service user audio/backlight/power
permissions and installs a systemd unit (javis-slave.service, enabled but not
started).
Configure & pair
-
Pick audio devices
cd /opt/javis-slave export JAVIS_SLAVE_CONFIG_DIR=/opt/javis-slave/config .venv/bin/python -m javis_slave --list-devicesNote the index of your mic (
max_input_channels > 0) and speaker. -
Enable the API on the master (
config.yaml), then restart Javis:api: enabled: true host: 0.0.0.0 port: 8765Open the Devices tab → Generate pairing code. The tab shows the master's LAN address and the 6-digit code (valid 5 min, single use).
-
Pair and test in the foreground
.venv/bin/python -m javis_slave --headless \ --input-device 1 --output-device 0 \ --pair 192.168.1.10:8765 123456 --name KuecheSay the wake word (default hey jarvis) — you should hear the ascending two-tone chime, speak, and hear the reply. Device settings are persisted in
$JAVIS_SLAVE_CONFIG_DIR/slave_config.json. Stop with Ctrl+C. -
Run as a service
sudo systemctl start javis-slave journalctl -u javis-slave -fAssign a room in the master's Devices tab (e.g.
Küche); the slave picks it up within a minute and every utterance is tagged with it for the LLM.With
setup-pi.sh --guithe system service stays disabled and the touch UI autostarts in the desktop session instead (~/.config/autostart/javis-slave.desktop,--kiosk= fullscreen, hidden cursor; Esc leaves fullscreen, F11 toggles).
Logs: journalctl -u javis-slave (service) and
$JAVIS_SLAVE_CONFIG_DIR/logs/slave.log (rotating, both modes).
Audible feedback (no screen needed)
| Sound | Meaning |
|---|---|
| Two rising tones | Wake word heard — speak now |
| Two falling low tones | Something failed (master unreachable, mic error) |
| Spoken error phrase | The master received the request but its turn failed (STT/LLM); it sends its configured agent.error_phrase as speech |
Remote control from the master
The slave runs a small HTTP control server on port 8766 (/mcp/v1/...) that
the javis-slave-mcp server exposes as tools, so
you can say "mach die Küche leiser". On Linux the endpoints map to:
| Endpoint | Backend |
|---|---|
/mcp/v1/volume/* |
pactl (PipeWire/Pulse) → falls back to amixer (ALSA) |
/mcp/v1/brightness/* |
/sys/class/backlight/*/brightness (udev rule from setup-pi.sh) |
/mcp/v1/power/* |
systemctl suspend, shutdown via a scoped sudoers entry |
/mcp/v1/mic/*, /status, /restart |
Engine mute / status / restart |
Every endpoint except /mcp/v1/health needs the slave's control token
(Authorization: Bearer …) unless the caller runs on the Pi itself. Print it
with javis-slave --show-control-token and give it to javis-slave-mcp as
JAVIS_SLAVE_TOKEN (see its README).
CLI reference
javis-slave --headless service mode, no window
javis-slave --fullscreen | --kiosk GUI fullscreen / fullscreen + hidden cursor
javis-slave --pair HOST:PORT CODE --name N pair (works with and without --headless)
javis-slave --list-devices print sounddevice table
javis-slave --input-device N --output-device N --input-gain G --api-port P
persist settings and start
javis-slave --show-control-token print the control-API token and exit
javis-slave --control-token TOKEN persist your own token (>= 16 chars)
Environment: JAVIS_SLAVE_CONFIG_DIR overrides the config folder
(default ~/.javis-slave).
Troubleshooting
No devices listed / "Error querying device" — sudo apt install libportaudio2
and make sure the user is in the audio group (groups; log out/in after
setup-pi.sh).
Mic opens at 48 kHz, wake word never fires — normal; the log line
mic started @ 48000Hz→16000Hz confirms resampling is active. Raise
--input-gain 2.0 if the level is low; check with arecord -d 3 test.wav.
Stereo-only USB cards are opened with both channels and mixed to mono.
USB mic unplugged / stops delivering audio — after ~2 minutes of silence
from the device the engine logs reopening device and re-opens it; if the
device is gone the engine start is retried every 10 s.
"Master … not reachable" — the master must have api.enabled: true and its
Windows firewall must allow inbound TCP 8765 (Javis is a Python process; allow
python.exe on private networks).
Wake word too sensitive / not sensitive enough — change
wake_word.threshold on the master; slaves pull it on the next config poll
(≤ 60 s).
Reply is silent but text appears in the master's log — the master's TTS has
no voice profile with reference clips; add one in the Voices tab. Playback
device problems show up as speaker does not support … Hz (auto-resampled) or
an ALSA error in journalctl.
Service dies at boot — USB audio may enumerate after the service starts;
Restart=always + RestartSec=5 recover automatically. The engine also
retries every 10 s while the mic is unavailable.
Pi Zero 2 W CPU load — skip --with-vad; the RMS gate is much cheaper than
Silero on a single core. Keep only one wake-word model.
Uninstall
sudo systemctl disable --now javis-slave
sudo rm /etc/systemd/system/javis-slave.service /etc/sudoers.d/javis-slave \
/etc/udev/rules.d/90-javis-backlight.rules
sudo rm -rf /opt/javis-slave