Setting Up Both Raspberry Pis

Setting up two Raspberry Pis for a local voice assistant

Setting Up Both Raspberry Pis

This post is part of the Voice Assistant on Raspberry Pi series.

Two Raspberry Pi 4s, a few hours of setup, and at the end you have a voice assistant running entirely on your local network. Before writing a single line of .NET code, here’s the setup for both Pis.

Two Pis, two roles

Each Pi has a distinct job in this setup:

Pi Client (2 GB)Pi Brain (4 GB)
RoleAudio, GPIO, orchestrationLocal LLM (Ollama)
Talks toPi Brain over HTTP-
Stack.NET 10 Worker Service, Whisper, Piper TTSOllama + Llama 3.2 3B
Hostnamepi-clientpi-cerveau

Step 1: Flash Raspberry Pi OS Lite 64-bit

On both Pis, use the same base image: Raspberry Pi OS Lite (64-bit). No desktop environment, everything runs over SSH.

Download and install Raspberry Pi Imager.

In the Imager:

  1. OS: Raspberry Pi OS (other)Raspberry Pi OS Lite (64-bit)
  2. Storage: your microSD card
  3. Click Edit Settings (⚙️) before flashing:
Hostname    : pi-client   (or pi-cerveau for the second one)
Username    : gabriel
Password    : [your choice]
Wi-Fi SSID  : [your network]
Wi-Fi Pass  : [your password]
Locale      : America/Toronto
SSH         : ✅ Enable SSH (Use password authentication)

Repeat for the second Pi, changing only the hostname to pi-cerveau.

Step 2: First boot and SSH

Once both Pis are plugged in and booted, find their IPs:

# Ping by hostname (mDNS)
ping pi-client.local
ping pi-cerveau.local

If mDNS doesn’t work, check your router’s DHCP lease table for the assigned IPs.

Connect via SSH:

ssh gabriel@pi-client.local
ssh gabriel@pi-cerveau.local

Step 3: System update

On both Pis:

sudo apt update && sudo apt full-upgrade -y
sudo apt autoremove -y
sudo reboot

Reconnect after the reboot.

Step 4: Install .NET 10

On both Pis:

curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 10.0

Add .NET to your PATH permanently:

echo 'export DOTNET_ROOT=$HOME/.dotnet' >> ~/.bashrc
echo 'export PATH=$PATH:$HOME/.dotnet:$HOME/.dotnet/tools' >> ~/.bashrc
source ~/.bashrc

Verify the install:

dotnet --version
# Expected: 10.x.x

Pi Client only: if you have a monitor attached, enable Screen Blanking via sudo raspi-configDisplay OptionsScreen BlankingEnable. Avoid editing /boot/firmware/cmdline.txt for this; consoleblank=300 can be silently ignored on Pi OS Bookworm when plymouth is active.

Step 5: ALSA audio (Pi Client only)

The Pi Client handles the microphone and speaker. Install the ALSA libraries:

sudo apt install -y \
  alsa-utils \
  libsndfile1 \
  portaudio19-dev \
  ffmpeg

Test the microphone

Plug in your USB microphone:

# List input devices
arecord -l

# Record 5 seconds
arecord -D hw:1,0 -f cd -t wav -d 5 test.wav

# Play it back
aplay test.wav

If arecord -l doesn’t see your mic, try hw:2,0 or hw:0,0 depending on what’s listed.

Test the speaker

# List output devices
aplay -l

# Audio test
speaker-test -t wav -c 2

Step 6: Whisper system dependencies (Pi Client only)

We use Whisper.net with its native runtime. Install the required system packages:

sudo apt install -y cmake build-essential

The Whisper.net NuGet package and its runtimes get added directly in the .NET project. We’ll get to that in article #2.

Step 7: Install Piper TTS (Pi Client only)

Piper is a lightweight TTS engine that runs natively on ARM64.

mkdir -p ~/piper && cd ~/piper

# Download the ARM64 binary
wget https://github.com/rhasspy/piper/releases/latest/download/piper_linux_aarch64.tar.gz
tar -xzf piper_linux_aarch64.tar.gz

# The tar extracts into a piper/ subdirectory — PATH needs to point to ~/piper/piper
echo 'export PATH=$PATH:$HOME/piper/piper' >> ~/.bashrc
source ~/.bashrc

piper --version

Download a French voice

mkdir -p ~/piper-voices && cd ~/piper-voices

wget https://huggingface.co/rhasspy/piper-voices/resolve/main/fr/fr_FR/siwis/low/fr_FR-siwis-low.onnx
wget https://huggingface.co/rhasspy/piper-voices/resolve/main/fr/fr_FR/siwis/low/fr_FR-siwis-low.onnx.json

Test Piper

echo "Bonjour, je suis votre assistant vocal." | \
  piper --model ~/piper-voices/fr_FR-siwis-low.onnx --output_raw | \
  aplay -r 22050 -f S16_LE -t raw -

That’s the wow moment of the whole setup.

Step 8: Install Ollama (Pi Brain only)

curl -fsSL https://ollama.com/install.sh | sh

Ollama installs as a systemd service automatically. Verify:

ollama --version
systemctl status ollama

Pull the model

# Llama 3.2 3B: solid quality/speed balance for 4 GB of RAM
ollama pull llama3.2:3b

About 2 GB to download. In article #4 we switch to llama3.2:1b because 3B consistently hits the timeout on a loaded Pi 4 GB. Pull 3B for now to test, but expect to swap it.

Quick French test

ollama run llama3.2:3b "Réponds en français : quelle est la capitale du Québec?"

Expose Ollama on the local network

By default, Ollama only listens on localhost (127.0.0.1:11434). You need to expose it so the Pi Client can reach it. sudo systemctl edit ollama may not create the file correctly on Pi OS, so do it manually:

sudo mkdir -p /etc/systemd/system/ollama.service.d/
sudo nano /etc/systemd/system/ollama.service.d/override.conf

Paste exactly this:

[Service]
Environment="OLLAMA_HOST=0.0.0.0"

Save (Ctrl+X, Y, Enter), then:

sudo systemctl daemon-reload
sudo systemctl restart ollama

Check that it’s now listening on all interfaces:

systemctl status ollama | grep Listening
# Expected: Listening on [::]:11434

Then from the Pi Client:

curl http://pi-cerveau.local:11434/api/tags

You should get a JSON response listing llama3.2:3b.

Step 9: Static IPs (optional)

To keep both Pis consistently reachable, set up fixed IPs in your router using DHCP reservations by MAC address:

# Find the MAC address
ip link show eth0 | grep ether

Both Pis are reachable on the network, .NET 10 is installed, and Piper speaks. Ready for code.

Series articles

  1. Setting Up Both Raspberry Pis (this article)
  2. .NET 10 Worker Service and Audio Pipeline
  3. Ollama Integration and Home Context
  4. Memory, Silence Detection, and systemd
  5. Real-Time Weather and Swapping to the Claude API
  6. Function Calling: Teaching Tools to the Assistant
  7. Retrospective, Lessons Learned, and v2 Roadmap

In article #2, we’ll build the .NET 10 Worker Service and wire up the GPIO button to the full audio pipeline, no LLM yet, just to confirm the entire audio chain works end to end.


This post was written with AI assistance and edited by me.


See also