Project Overview & Difficulty Rating
The Raspberry Pi lineup has never included a native analog audio input. While HDMI and USB offer workarounds, low-latency, high-fidelity raspberry pi audio capture requires tapping directly into the I2S (Inter-IC Sound) bus on the 40-pin GPIO header. This guide walks through building a robust audio capture pipeline on the Raspberry Pi 5 (4GB) using a digital MEMS microphone, bypassing the noisy analog-to-digital conversion stages found in cheap USB dongles.
- Target Board: Raspberry Pi 5 (4GB or 8GB variant), running Raspberry Pi OS Bookworm (64-bit)
- Microphone: Adafruit I2S MEMS Microphone Breakout (SPH0645LM4H)
- Difficulty: Intermediate (Requires Device Tree Overlay configuration and Python ALSA debugging)
- Time to Build: 45 minutes
- Estimated Cost: $72 USD ($60 Pi 5 + $7 Mic + $5 accessories)
Hardware Wiring & Pin Mapping
The SPH0645LM4H is a digital microphone. It does not output an analog voltage; instead, it outputs a pulse-density modulated (PDM) or I2S bitstream directly. This means we must wire it to the Pi's dedicated PCM/I2S pins. The Raspberry Pi 5 uses the RP1 southbridge chip, but the primary I2S0 pin functions remain mapped to the same physical header locations as the Pi 4.
| Mic Breakout Pin | Pi 5 GPIO | Physical Pin # | Wire Color | Function / Notes |
|---|---|---|---|---|
| VIN | 3.3V Power | Pin 1 | Red | Do NOT use 5V. The SPH0645 is strictly 3.3V tolerant. |
| GND | Ground | Pin 6 | Black | Common ground reference. |
| BCLK | GPIO 18 | Pin 12 | Yellow | Bit Clock (PCM_CLK). Synchronizes data bits. |
| LRCLK | GPIO 19 | Pin 35 | Green | Left/Right Word Select (PCM_FS). Sets sample rate. |
| DOUT | GPIO 21 | Pin 40 | Blue | Serial Data Out (PCM_DIN). The actual audio bitstream. |
Software Configuration: Enabling I2S on Pi 5
Out of the box, Raspberry Pi OS does not route the I2S pins to the audio subsystem. You must load a Device Tree Overlay (DTO) to tell the kernel to initialize the I2S hardware. On the Pi 5 running Bookworm, the configuration file has moved from the legacy /boot/config.txt to /boot/firmware/config.txt.
Open the configuration file in your terminal:
sudo nano /boot/firmware/config.txt
Scroll to the bottom and add the following lines to enable the I2S bus and load the generic memory-mapped I2S overlay, which handles the SPH0645's specific data formatting:
# Enable I2S interface
dtparam=i2s=on
# Load the I2S mmap overlay for MEMS mics
dtoverlay=i2s-mmap
# Disable the onboard PWM audio to prevent ALSA card conflicts
dtparam=audio=off
Save the file (Ctrl+O, Enter, Ctrl+X) and reboot the Pi. After rebooting, verify the kernel has loaded the audio card by running aplay -l. You should see Card 0: sndrpii2scard listed in the output. If it says no soundcards found, your overlay failed to load (see the debugging section below).
Python Audio Capture Script
We will use the sounddevice library, which wraps the PortAudio C library, combined with numpy and the standard wave module. This approach is significantly more stable on modern Pi OS than legacy pyaudio implementations.
First, install the system dependencies and Python packages:
sudo apt update
sudo apt install libportaudio2 python3-numpy
pip3 install sounddevice wave
Below is the complete, compilable Python script. It includes a signal handler to gracefully close the WAV file header on Ctrl+C—a critical step, as force-killing an audio script will corrupt the WAV header, rendering the file unreadable in audio editors.
import sounddevice as sd
import wave
import numpy as np
import signal
import sys
import time
# ---------------------------------------------------------
# HARDWARE PIN DEFINITIONS (Mapped via Device Tree Overlay)
# BCLK: GPIO 18 (Physical Pin 12)
# LRCLK: GPIO 19 (Physical Pin 35)
# DOUT: GPIO 21 (Physical Pin 40)
# ---------------------------------------------------------
FILENAME = "i2s_capture.wav"
SAMPLE_RATE = 32000 # SPH0645 sweet spot; 16kHz or 44.1kHz also supported
CHANNELS = 1
DURATION_SECONDS = 10
# The SPH0645 outputs 24-bit audio, left-justified in a 32-bit word.
# We capture as 32-bit signed integers to prevent bit-shift static.
DTYPE = 'int32'
wav_file = None
audio_stream = None
def graceful_exit(signum, frame):
"""Handles Ctrl+C to ensure WAV header is written correctly."""
print("\n[INFO] Capture interrupted. Finalizing WAV header...")
if audio_stream is not None:
audio_stream.stop()
audio_stream.close()
if wav_file is not None:
wav_file.close()
sys.exit(0)
# Register signal handler for SIGINT (Ctrl+C)
signal.signal(signal.SIGINT, graceful_exit)
def audio_callback(indata, frames, time_info, status):
"""Called by PortAudio for every audio block."""
if status:
print(f"[WARN] Audio status flag: {status}", file=sys.stderr)
wav_file.writeframes(indata.tobytes())
def main():
global wav_file, audio_stream
# Initialize WAV file
wav_file = wave.open(FILENAME, 'wb')
wav_file.setnchannels(CHANNELS)
wav_file.setsampwidth(4) # 4 bytes = 32-bit
wav_file.setframerate(SAMPLE_RATE)
print(f"[INFO] Starting raspberry pi audio capture to {FILENAME}...")
print(f"[INFO] Sample Rate: {SAMPLE_RATE}Hz | Channels: {CHANNELS} | Bit Depth: 32")
try:
# Open the InputStream
audio_stream = sd.InputStream(
samplerate=SAMPLE_RATE,
channels=CHANNELS,
dtype=DTYPE,
callback=audio_callback,
blocksize=1024
)
with audio_stream:
print("[INFO] Recording... Press Ctrl+C to stop.")
if DURATION_SECONDS > 0:
sd.sleep(DURATION_SECONDS * 1000)
else:
# Record indefinitely until Ctrl+C
while True:
sd.sleep(1000)
except sd.PortAudioError as e:
print(f"[FATAL] PortAudio Error: {e}")
print("[HINT] Check 'aplay -l' and ensure dtoverlay=i2s-mmap is in config.txt")
graceful_exit(None, None)
except Exception as e:
print(f"[FATAL] Unexpected error: {e}")
graceful_exit(None, None)
print("[INFO] Capture complete. File saved.")
wav_file.close()
if __name__ == "__main__":
main()
Debugging: "Device Unavailable" & ALSA Errors
Audio on Linux is notoriously layered (ALSA -> PulseAudio/PipeWire -> Application). When capturing I2S audio on the Pi 5, you will inevitably hit ALSA conflicts. If your script crashes, look at the exact error string to diagnose the issue.
Error: sounddevice.PortAudioError: Error opening InputStream: Device unavailable [PaErrorCode -9985]
This is the most common failure mode. It means PortAudio can see the ALSA sound card, but the kernel or audio server is actively blocking access to the hardware buffer.
The First Three Things to Check:
- Is the I2S overlay actually loaded? Run
dmesg | grep i2s. If you don't see initialization logs for thei2s-mmaporsnd-rpi-simpledriver, yourconfig.txtsyntax is wrong, or you forgot to reboot. - Is PipeWire hogging the device? Raspberry Pi OS Bookworm uses PipeWire by default. If the desktop environment has opened the audio device for system sounds, it may lock the I2S card. Suspend PipeWire temporarily by running
systemctl --user stop wireplumber pipewirebefore executing your Python script. - Are you requesting an unsupported sample rate? The SPH0645 hardware does not support 48kHz natively. If your script requests 48000Hz, ALSA will attempt software resampling, which often fails on raw I2S mmap devices. Stick to 16000Hz, 32000Hz, or 44100Hz.
Error: ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
This occurs when the ALSA configuration file (/etc/asound.conf) is hardcoded to look for a USB or onboard PWM card that no longer exists because we disabled dtparam=audio=on. Delete or rename any custom /etc/asound.conf files and let ALSA auto-detect the I2S card 0.
Extending and Simplifying the Build
Depending on your end goal, you may want to alter the complexity of this raspberry pi audio capture setup.
How to Simplify: The USB Audio Route
If you do not strictly need the low-latency and low-CPU-overhead benefits of the I2S bus, bypass the GPIO wiring entirely. Purchase a Sabrent USB External Stereo Sound Adapter (approx. $10). Plug it into a USB 2.0 port, plug a standard 3.5mm electret microphone into the pink jack, and change the Python script's sd.InputStream(device=X) parameter to target the USB card. This eliminates the need for Device Tree Overlays and I2S bit-shift debugging entirely.
How to Extend: Network Streaming & VAD
To turn this Pi into a remote acoustic sensor, extend the Python script to stream the indata numpy arrays over an MQTT broker or a raw TCP socket instead of writing to a local WAV file. For Voice Activity Detection (VAD), integrate the WebRTC VAD library. By passing the 16kHz or 32kHz PCM chunks to the VAD engine, the Pi can discard silence and only transmit audio frames containing human speech, reducing network bandwidth by up to 85%.
Frequently Asked Questions
Can I use the Raspberry Pi 5 built-in audio jack for microphone input?
No. While the Raspberry Pi 4 and 5 feature a 4-pole TRRS (Tip-Ring-Ring-Sleeve) 3.5mm jack, the hardware DAC only routes analog audio output to the Tip and Ring 1. The Ring 2 (microphone) contact is physically unconnected on the PCB. There is no analog-to-digital converter (ADC) tied to that jack on the BCM2712 or RP1 chips. You must use an I2S MEMS mic or a USB audio interface for input.
Why is my I2S microphone audio full of static or shifted bits?
This is the hallmark of the SPH0645 "bit-shift" bug. The SPH0645 outputs 24-bit audio, but it left-justifies the data in a 32-bit word, leaving the 8 least-significant bits as zeros. If your ALSA driver or Python script interprets this as standard 24-bit or 16-bit audio, the data is read as massive integer values, resulting in deafening white noise. The fix is to capture the stream as int32 (as shown in our script) and optionally bit-shift the array right by 8 bits in software (audio_data = audio_data >> 8) before saving, or rely on the i2s-mmap overlay which attempts to handle the ALSA format translation automatically.
How do I capture raspberry pi audio continuously without filling the SD card?
Continuous 32-bit audio capture at 32kHz consumes roughly 128KB per second, or about 11GB per day. To prevent SD card wear and capacity issues, implement a ring buffer in RAM. Use Python's collections.deque to hold the last 60 seconds of audio in memory. Only write the buffer to the SD card when a specific trigger occurs (e.g., a GPIO button press, or a software VAD threshold being crossed). Alternatively, mount a tmpfs RAM disk and write your temporary WAV files there before uploading them to an S3 bucket or local NAS.






