Building a high-fidelity Raspberry Pi sound recorder requires bypassing the Pi's lack of onboard analog-to-digital audio conversion. The direct answer for a low-noise, high-sample-rate build: use a Raspberry Pi 4 Model B paired with an I2S MEMS microphone (Adafruit SPH0645LM4H, Product ID 3421) and the Python sounddevice library. This setup captures 24kHz to 48kHz audio directly via the I2S bus, avoiding the noisy ADCs and ground-loop hum found on cheap $10 USB sound cards.
This guide targets the Raspberry Pi 4 Model B (Rev 1.4/1.5) running Raspberry Pi OS Bookworm (64-bit). We will cover the exact I2S pin mapping, the updated Bookworm boot configuration, a complete Python recording script with error handling, and how to debug the inevitable ALSA buffer errors.
Hardware Specs and I2S Pin Mapping
Unlike analog microphones that require an ADC, the SPH0645LM4H has a built-in PDM-to-I2S converter. It outputs digital audio directly to the Pi's I2S peripheral pins (PCM). Below is the hardware specification sheet and the exact pin mapping you need for your breadboard.
Adafruit SPH0645LM4H Specification Sheet
| Parameter | Value | Notes |
|---|---|---|
| Signal-to-Noise Ratio (SNR) | 65 dB(A) | Comparable to entry-level studio condenser mics |
| Sensitivity | -26 dBFS | Measured at 1kHz, 94dB SPL |
| Operating Voltage | 2.5V to 3.3V | Do NOT connect to Pi 5V pin; use 3V3 |
| Sample Rate Support | 8kHz to 48kHz | Hardware clock dependent; 44.1kHz/48kHz ideal |
| Output Type | I2S (24-bit data, 18-bit resolution) | Lower 6 bits are zero-padded by the sensor |
Pi 4 to I2S MEMS Pin Mapping
Use 24 AWG solid-core jumper wires for breadboard connections to ensure reliable contact with the MEMS breakout castellations. The I2S bus on the Pi uses specific BCM (Broadcom) GPIO pins, not physical pin numbers.
| Mic Breakout Pin | Pi 4 BCM GPIO | Pi 4 Physical Pin | Function |
|---|---|---|---|
| VIN | N/A | 1 (3V3) | Power (3.3V) |
| GND | N/A | 6 (Ground) | Common Ground |
| BCLK | GPIO 18 | 12 | Bit Clock (PCM_CLK) |
| DOUT | GPIO 20 | 38 | Data Out (PCM_DIN) |
| LRCL | GPIO 21 | 40 | Left/Right Clock (PCM_FS) |
OS Configuration and Wiring Steps
Before writing code, you must enable the I2S hardware overlay in the Pi's boot configuration. In Raspberry Pi OS Bookworm, the configuration file moved from /boot/config.txt to /boot/firmware/config.txt. Many outdated tutorials miss this, resulting in silent failures.
- Wire the hardware: Connect the 5 pins from the breakout board to the Pi 4 as defined in the pin mapping table above. Double-check that VCC is on 3.3V. Feeding 5V to the SPH0645 will permanently brick the MEMS capsule.
- Edit the boot config: Open a terminal and run
sudo nano /boot/firmware/config.txt. - Enable I2S: Add the following lines to the bottom of the file:
dtparam=i2s=on dtoverlay=i2s-mmap - Blacklist the onboard audio (optional but recommended): To prevent ALSA from prioritizing the HDMI audio jack, comment out the onboard audio line by adding a
#:#dtparam=audio=on. - Reboot: Run
sudo reboot. - Install Python dependencies: After rebooting, install PortAudio and the Python libraries:
sudo apt update sudo apt install libportaudio2 python3-pip pip3 install sounddevice numpy wave - Verify the device: Run
python3 -c 'import sounddevice as sd; print(sd.query_devices())'. You should see a device namedi2s-mmaporsnd_rpi_hifiberry_dacin the list. Note its index number.
The Python Recording Script
This script targets the Raspberry Pi 4 Model B running Bookworm. It uses sounddevice to capture audio into a NumPy array, extracts the mono channel, and writes it to a 16-bit WAV file. It includes explicit error handling for ALSA buffer underruns and device selection failures.
import sounddevice as sd
import numpy as np
import wave
import sys
import time
from datetime import datetime
# --- Hardware & Pin Definitions ---
# Target: Raspberry Pi 4 Model B
# Mic: Adafruit I2S MEMS (SPH0645LM4H)
# Pins: BCLK=GPIO18, DOUT=GPIO20, LRCL=GPIO21
SAMPLE_RATE = 44100 # Hz
CHANNELS = 2 # I2S outputs stereo, mic is on Left channel
DURATION = 10 # Recording length in seconds
BLOCKSIZE = 4096 # Larger blocksize prevents ALSA underruns on Pi
OUTPUT_FILE = f'recording_{datetime.now().strftime("%Y%m%d_%H%M%S")}.wav'
def find_i2s_device():
"""Finds the I2S microphone index by name."""
devices = sd.query_devices()
for i, dev in enumerate(devices):
# The i2s-mmap overlay usually registers as 'i2s' or 'hifiberry'
if 'i2s' in dev['name'].lower() or 'hifiberry' in dev['name'].lower():
if dev['max_input_channels'] > 0:
return i
return None
def record_audio():
print(f'Starting {DURATION}s recording at {SAMPLE_RATE}Hz...')
device_idx = find_i2s_device()
if device_idx is None:
print('ERROR: I2S microphone not found. Check dtoverlay in config.txt.')
sys.exit(1)
print(f'Using Device {device_idx}: {sd.query_devices(device_idx)["name"]}')
try:
# Record into a NumPy array
# latency='high' and explicit blocksize prevent ALSA xrun errors
audio_data = sd.rec(
frames=int(DURATION * SAMPLE_RATE),
samplerate=SAMPLE_RATE,
channels=CHANNELS,
dtype='int16',
device=device_idx,
blocking=True,
latency='high'
)
# SPH0645 outputs on Left channel (index 0), Right is silent
mono_data = audio_data[:, 0].reshape(-1, 1)
# Write to WAV file
with wave.open(OUTPUT_FILE, 'wb') as wf:
wf.setnchannels(1)
wf.setsampwidth(2) # 16-bit = 2 bytes
wf.setframerate(SAMPLE_RATE)
wf.writeframes(mono_data.tobytes())
print(f'Successfully saved to {OUTPUT_FILE}')
except sd.PortAudioError as e:
print(f'PortAudio Error: {e}')
print('Fix: Ensure no other process (like PulseAudio) is holding the ALSA device.')
except Exception as e:
print(f'Unexpected Error: {e}')
finally:
sd.stop()
if __name__ == '__main__':
record_audio()
Debugging I2S Audio Failures
When your Raspberry Pi sound recorder fails to capture audio, the issue is almost always at the ALSA (Advanced Linux Sound Architecture) layer or the physical clock wiring. Before tearing apart your breadboard, check these first three things:
- Verify the Overlay Loaded: Run
dtoverlay -l. Ifi2s-mmapis not listed, your/boot/firmware/config.txtsyntax is wrong or you edited the legacy/boot/config.txtpath. - Check BCLK and LRCL Swap: The most common wiring error is swapping Bit Clock (GPIO 18) and Left/Right Clock (GPIO 21). If the recording is pure static or plays back at 10x speed, these two pins are reversed.
- Kill PulseAudio/PipeWire: Modern Raspberry Pi OS runs PipeWire or PulseAudio, which can lock the I2S device. Run
systemctl --user stop pipewire pulseaudiobefore running the script.
Common Error Strings and Fixes
Error 1: ALSA lib pcm.c:8568:(snd_pcm_recover) underrun occurred
- Cause: The CPU failed to read the I2S buffer fast enough, causing an ALSA xrun (buffer underrun). This is common on Pi 4 when background tasks spike.
- Fix: Increase the
BLOCKSIZEin the Python script from 1024 to 4096 or 8192. Alternatively, addlatency='high'to thesd.rec()parameters as shown in the code above.
Error 2: Expression 'parameters->channelCount <= maxChannels' failed
- Cause: The script requested 2 channels (stereo), but the ALSA device registered as mono-only due to a misconfigured
asound.confor incorrect overlay. - Fix: Change
CHANNELS = 2toCHANNELS = 1in the script, or ensure you are using thei2s-mmapoverlay which correctly exposes the stereo I2S bus to ALSA.
Error 3: sounddevice.PortAudioError: Error opening InputStream: Invalid device [PaErrorCode -9996]
- Cause: The device index or name string passed to PortAudio does not match the loaded ALSA cards.
- Fix: Run
aplay -lto list hardware devices. Match the card number and subdevice to your Python script's device selection logic.
Extending and Simplifying the Build
Not every project requires bare-metal I2S configuration. Here is how to adjust the complexity based on your actual use case.
How to Simplify (The USB Route)
If you are building a simple voice memo recorder or a bird-feeder audio logger and do not want to deal with ALSA overlays, swap the MEMS breakout for a USB Mini Microphone (e.g., the generic CM108-based USB mics or a Samson Meteor).
- Trade-off: You gain plug-and-play simplicity (it just shows up as a standard USB audio class device), but you lose SNR performance and introduce potential USB bus noise.
- Code change: Simply change the device selection in the Python script to target the USB audio card name. No
config.txtedits required.
How to Extend (Push-to-Talk and MQTT)
To turn this into an IoT audio node:
- Add a Push-to-Talk Button: Wire a momentary tactile switch between GPIO 17 and GND. Enable the internal pull-up resistor in Python using the
gpiozerolibrary (button = Button(17, pull_up=True)), and wrap thesd.rec()call in abutton.wait_for_press()loop. - Stream via MQTT: Instead of writing to a WAV file, use the
sounddevice.InputStreamcallback to capture chunks of audio, encode them to Opus format using theopuslibPython wrapper, and publish the binary payload to an MQTT broker (like Mosquitto) for real-time monitoring on another machine.
Safety & Code Note: When wiring GPIO pins on the Pi 4, always de-energize the board (unplug the USB-C power supply) before moving jumper wires. A slipped 24 AWG wire bridging 3.3V and an I2S data line can instantly destroy the Pi's BCM2711 SoC. For further reading on Pi hardware interfaces, consult the official Raspberry Pi configuration documentation and the Adafruit I2S MEMS wiring guide. For deep ALSA debugging, the ALSA Project Wiki remains the definitive reference.






