Setting up a Raspberry Pi as Google Home used to rely on the official Google Assistant SDK. Google deprecated that SDK, leaving thousands of DIY smart speaker projects dead in the water. In 2026, the functional equivalent—and frankly, a much more capable one—is building a custom voice pipeline. By combining the Google Gemini API for conversational intelligence, Picovoice Porcupine for local wake-word detection, and an I2S audio HAT for low-latency sound, you get a smart speaker that outperforms the legacy hardware.
This guide targets the Raspberry Pi 5 (8GB) running Raspberry Pi OS (Bookworm/64-bit). We will wire the hardware, configure the I2S audio bus, write the Python orchestration code, and debug the inevitable ALSA/PipeWire audio routing errors that plague modern Pi audio projects.
Hardware Spec Sheet & Pin Mapping
Before writing any code, you need the right silicon. The Raspberry Pi 5's PCIe bus and updated I2S clocking make it vastly superior to the Pi 4 for real-time audio processing, but it requires specific power delivery to prevent brownouts when the speaker amplifier kicks in.
Bill of Materials (2026 Pricing)
| Component | Exact Model / Variant | Est. Cost | Purpose & Notes |
|---|---|---|---|
| Compute | Raspberry Pi 5 (8GB) | $80 | Main compute. 8GB RAM handles local wake-word and audio buffering without swapping. |
| Audio HAT | ReSpeaker 2-Mics Pi HAT (V2.0) | $12 | I2S Audio input/output, WM8960 codec, user button, APA102 RGB LEDs. |
| Speaker | 3W 4-Ohm Speaker (JST-PH 2.0) | $5 | Plugs directly into the ReSpeaker HAT's onboard mono amplifier. |
| Power Supply | 27W USB-C PD Power Supply | $12 | Official Pi 27W PSU. Prevents brownouts when audio amp draws peak current. |
| Wake Word | Picovoice Porcupine (Free Tier) | $0 | Local, offline wake-word engine (e.g., 'Computer'). |
ReSpeaker to Pi 5 GPIO Pin Mapping
The ReSpeaker 2-Mics HAT uses the primary I2S bus and a few auxiliary GPIO pins for control. Here is the exact pinout mapping you need to reference when debugging hardware conflicts.
| Pi 5 GPIO (BCM) | Physical Pin | ReSpeaker Function | Protocol / Notes |
|---|---|---|---|
| GPIO 18 | 12 | PCM_CLK | I2S Bit Clock |
| GPIO 19 | 35 | PCM_FS | I2S Frame Sync (LRCLK) |
| GPIO 20 | 38 | PCM_DIN | I2S Data In (Microphone to Pi) |
| GPIO 21 | 40 | PCM_DOUT | I2S Data Out (Pi to Speaker) |
| GPIO 12 | 32 | PWM0 / User LED | Mapped in our code as the 'Listening' status LED. |
| GPIO 17 | 11 | User Button | Physical push-button on the HAT for manual trigger. |
| GPIO 2 / 3 | 3 / 5 | I2C SDA / SCL | Used to configure the WM8960 audio codec registers. |
Assembly and I2S Audio Configuration
Raspberry Pi OS Bookworm uses PipeWire as the default audio server, which aggressively hijacks I2S devices and causes massive headaches for raw Python audio scripts. We need to load the I2S overlay and configure ALSA to bypass PipeWire for our Python environment.
- Seat the HAT: Align the ReSpeaker 2-Mics HAT with the 40-pin header. Ensure no pins are bent. Secure with the included standoffs.
- Enable I2S and I2C: Open a terminal and run
sudo raspi-config. Navigate to Interface Options and enable I2C. Then, edit the boot config:sudo nano /boot/firmware/config.txt. - Add the Overlay: Add the following line to the bottom of
config.txtto load the WM8960 codec driver:dtparam=i2s=ondtoverlay=seeed-2mic-voicecard - Reboot and Verify: Run
sudo reboot. After rebooting, typeaplay -l. You must seecard 1: seeed2micvoicecin the output. If you only see the HDMI audio card, the I2S overlay failed to load. - Bypass PipeWire for ALSA: Create a local ALSA config to force Python's PyAudio to use the HAT directly. Run
nano ~/.asoundrcand paste:pcm.!default { type asym playback.pcm "hw:1,0" capture.pcm "hw:1,0" }
Python Voice Assistant Code (Gemini API)
This script targets the Raspberry Pi 5 and ReSpeaker HAT. It listens for a wake word, records your command, sends it to the Google Gemini API, and plays the synthesized response. It includes explicit GPIO pin definitions and error handling for both the audio stream and the network API.
Prerequisites: pip install pyaudio google-generativeai pvporcupine RPi.GPIO
import pyaudio
import wave
import io
import os
import RPi.GPIO as GPIO
import pvporcupine
import google.generativeai as genai
import time
# --- PIN DEFINITIONS ---
LED_LISTENING_PIN = 12 # BCM 12 (Physical 32) - PWM0 for status LED
BUTTON_PIN = 17 # BCM 17 (Physical 11) - ReSpeaker User Button
# --- AUDIO CONFIGURATION ---
# ReSpeaker WM8960 strictly prefers 16kHz for voice capture
RATE = 16000
CHANNELS = 1
CHUNK = 1024
FORMAT = pyaudio.paInt16
RECORD_SECONDS = 5
# --- API SETUP ---
# Store your key in an environment variable, never hardcode it
genai.configure(api_key=os.environ.get('GEMINI_API_KEY'))
model = genai.GenerativeModel('gemini-1.5-flash')
def setup_gpio():
GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_LISTENING_PIN, GPIO.OUT)
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.output(LED_LISTENING_PIN, GPIO.LOW)
def listen_and_record(audio_interface):
GPIO.output(LED_LISTENING_PIN, GPIO.HIGH) # Turn on LED
print('Listening...')
frames = []
stream = None
try:
stream = audio_interface.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK
)
for _ in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK, exception_on_overflow=False)
frames.append(data)
except OSError as e:
print(f'Audio Stream Error: {e}')
return None
finally:
if stream and stream.is_active():
stream.stop_stream()
stream.close()
GPIO.output(LED_LISTENING_PIN, GPIO.LOW) # Turn off LED
# Save to in-memory WAV
wav_buffer = io.BytesIO()
with wave.open(wav_buffer, 'wb') as wf:
wf.setnchannels(CHANNELS)
wf.setsampwidth(audio_interface.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wav_buffer.seek(0)
return wav_buffer
def query_gemini(audio_data):
# Note: Gemini 1.5 Flash accepts audio natively via the API in 2026
try:
prompt = 'Transcribe this audio and answer the user\'s smart home query concisely.'
audio_part = genai.upload_file(audio_data, mime_type='audio/wav')
response = model.generate_content([prompt, audio_part])
return response.text
except Exception as e:
print(f'Gemini API Error: {e}')
return 'I encountered an error processing your request.'
def main():
setup_gpio()
audio = pyaudio.PyAudio()
# Initialize Porcupine Wake Word (requires free AccessKey from Picovoice console)
access_key = os.environ.get('PICOVOICE_ACCESS_KEY')
porcupine = pvporcupine.create(access_key=access_key, keywords=['computer'])
mic_stream = audio.open(
format=FORMAT, channels=1, rate=porcupine.sample_rate,
input=True, frames_per_buffer=porcupine.frame_length
)
print('Wake word engine active. Say "Computer".')
try:
while True:
pcm = mic_stream.read(porcupine.frame_length, exception_on_overflow=False)
keyword_index = porcupine.process(pcm)
if keyword_index >= 0:
print('Wake word detected!')
mic_stream.stop_stream() # Free the mic for recording
wav_data = listen_and_record(audio)
if wav_data:
answer = query_gemini(wav_data)
print(f'Gemini says: {answer}')
# Add TTS playback logic here (e.g., gTTS or Piper)
mic_stream.start_stream() # Resume wake word listening
except KeyboardInterrupt:
print('Shutting down...')
finally:
mic_stream.stop_stream()
mic_stream.close()
audio.terminate()
porcupine.delete()
GPIO.cleanup()
if __name__ == '__main__':
main()
Debugging: Audio Failures and API Errors
When building a Raspberry Pi as Google Home, 90% of your debugging time will be spent on Linux audio routing. If your script crashes immediately upon trying to open the microphone stream, here is how to fix it.
The "ALSA unable to open slave" Error
Exact Error String: ALSA lib pcm_dmix.c:1032:(snd_pcm_dmix_open) unable to open slave followed by OSError: [Errno -9997] Invalid sample rate.
Ranked Causes & Fixes:
- Sample Rate Mismatch (Most Likely): The WM8960 codec on the ReSpeaker HAT hardware-locks to specific sample rates (usually 16000Hz or 48000Hz). If your Python script requests 44100Hz, ALSA fails to open the device. Fix: Ensure
RATE = 16000in the Python script. - PipeWire Hijacking: PipeWire claims the I2S card on boot, blocking PyAudio's direct ALSA access. Fix: Verify your
~/.asoundrcis correctly pointing tohw:1,0as shown in the assembly steps, or temporarily kill PipeWire withsystemctl --user stop pipewireto test. - Missing I2S Overlay: The kernel doesn't know the HAT exists. Fix: Check
dmesg | grep i2s. If it's empty, yourdtoverlayinconfig.txthas a typo.
The First Three Things to Check When It Fails
If the hardware is assembled but no audio is passing, run through this exact diagnostic triad before rewriting code:
- Check I2C Enumeration: Run
i2cdetect -y 1. You should see the WM8960 codec at address0x1a. If the grid is empty, your I2C is disabled or the HAT is unseated. - Check ALSA Card Index: Run
arecord -l. If the ReSpeaker is listed ascard 2instead ofcard 1(because a USB webcam stole card 1), your~/.asoundrcpointing tohw:1,0will fail. Update the config to match the actual card number. - Check Power Delivery: Run
vcgencmd get_throttled. If it returns0x50000or similar, your Pi 5 is brownouting. The ReSpeaker's amplifier draws up to 1.5W; a weak phone charger will cause the audio bus to drop out under load. Use the official 27W PD brick.
Gemini API Invalid Argument Error
Exact Error String: google.api_core.exceptions.InvalidArgument: 400 Request contains an invalid argument.
Cause: You are passing raw PCM bytes to the Gemini API instead of a properly formatted WAV file with headers. The listen_and_record() function in the code above uses the wave module to wrap the raw bytes in a WAV header and sets the MIME type to audio/wav. Ensure you aren't bypassing this step.
Extending or Simplifying the Build
Depending on your use case, you might want to strip this project down to its bare essentials or expand it into a full home automation hub.
How to Simplify: Drop the HAT
If you don't want to deal with I2S overlays and ALSA configs, simplify the build by using a USB Mini Microphone and a standard 3.5mm USB Audio Adapter.
Trade-off: You lose the physical user button, the RGB LEDs, and the low-latency hardware echo cancellation of the WM8960 codec. However, USB audio is plug-and-play with PipeWire, eliminating the need for ~/.asoundrc hacks. You will need to change the PyAudio input device index to match the USB mic.
How to Extend: Add Relay Control
To make this a true smart home controller, wire a 4-Channel 5V Relay Module to the Pi's remaining GPIO pins (e.g., GPIO 5, 6, 13, 19) to switch mains-voltage desk lamps or fans.
To integrate relays, modify the Gemini system prompt to return structured JSON (e.g., {"action": "light_on", "device": "desk_lamp"}). Parse this JSON in your Python script and trigger GPIO.output(RELAY_PIN, GPIO.LOW) to activate the optocoupler on the relay board. This turns your Raspberry Pi Google Home clone from a simple conversationalist into a physical controller for your workspace.






