To build a reliable Raspberry Pi FM receiver, you need an I2C tuner module like the TEA5767 wired to the Pi's GPIO header, controlled via Python's smbus2 library to calculate and write the PLL tuning registers. Unlike USB software-defined radios that hog CPU cycles, a dedicated hardware tuner offloads the demodulation, outputting clean analog audio directly from the breakout board.
Hardware Decision Path: Which FM Receiver Module?
Before ordering parts, you must match the receiver module to your project's end goal. The embedded market offers three primary paths for adding FM reception to a Raspberry Pi. Use this decision matrix to select the right hardware.
| Module / Hardware | Interface | Best For | Drawbacks |
|---|---|---|---|
| RTL-SDR V3 Dongle | USB 2.0 | Spectrum analysis, decoding RDS/RBDS, wideband scanning (24MHz - 1.7GHz). | High CPU usage for software demodulation; requires external rtl_fm pipeline. |
| Si4703 Breakout | I2C / SPI | Projects requiring RDS (Radio Data System) text parsing and high sensitivity. | More expensive (~$15); complex 16-bit register mapping; 3.3V strict logic. |
| TEA5767 Module | I2C | Simple bench audio projects, basic I2C learning, low-cost headless radios. | No RDS support; analog audio out requires external amplification for loud playback. |
Spec Sheet and I2C Pin Mapping
The TEA5767 operates on a 5-byte write sequence to set the Phase-Locked Loop (PLL) frequency synthesizer. The target board for the code below is the Raspberry Pi 4 Model B (4GB RAM) running Raspberry Pi OS (Bookworm, 64-bit). We will use the primary I2C bus (Bus 1).
Wiring Table
| Raspberry Pi 4 Pin | GPIO / Function | TEA5767 Module Pin | Notes |
|---|---|---|---|
| Pin 1 | 3.3V Power | VCC | The TEA5767 chip accepts 2.5V-5V. Using 3.3V protects the Pi's I2C pull-ups from 5V backfeed. |
| Pin 6 | Ground | GND | Keep ground wires short to minimize analog noise floor. |
| Pin 3 | GPIO 2 (SDA1) | SDA | Hardware I2C data line. Do not add external pull-ups; the Pi has 1.8kΩ onboard. |
| Pin 5 | GPIO 3 (SCL1) | SCL | Hardware I2C clock line. Default speed is 100kHz, sufficient for this module. |
Antenna Note: The module includes a 3.5mm headphone jack for audio out and a dedicated ANT pad. Solder a 75cm (approx. 29.5 inches) piece of 22 AWG stranded copper wire to the ANT pad. This length acts as a quarter-wave monopole for the center of the 88-108 MHz FM broadcast band.
Wiring Steps and Python Tuning Code
Follow these steps to configure the OS and deploy the tuning script. We use the smbus2 library for reliable I2C block writes.
- Enable I2C: Open the terminal and run
sudo raspi-config. Navigate to Interface Options > I2C and enable it. Reboot the Pi. - Verify Hardware: Run
sudo i2cdetect -y 1. You should see60in the grid. This is the hardcoded I2C address of the TEA5767. - Install Dependencies: Run
pip3 install smbus2in your virtual environment. - Deploy Code: Save the following script as
fm_radio.py.
import smbus2
import time
import sys
# Target Board: Raspberry Pi 4 Model B (I2C Bus 1)
I2C_BUS = 1
TEA5767_ADDR = 0x60
def calculate_pll(freq_mhz):
"""
Calculates the PLL word for High-Side Injection.
Formula derived from NXP TEA5767 datasheet.
"""
freq_hz = freq_mhz * 1000000
intermediate_freq = 225000 # 225 kHz IF
ref_freq = 32768 # 32.768 kHz crystal reference
# High-side injection multiplier is 4
pll = int(4 * (freq_hz + intermediate_freq) / ref_freq)
return pll
def set_frequency(bus, freq_mhz):
pll = calculate_pll(freq_mhz)
pll_high = (pll >> 8) & 0xFF
pll_low = pll & 0xFF
# Byte 3: 0x90 -> High-side injection, Stereo mode, PLL enabled
# Byte 4: 0x11 -> Crystal ref (32.768kHz), Soft mute off, SNC on
# Byte 5: 0x00 -> Standby off, port configurations
data = [pll_high, pll_low, 0x90, 0x11, 0x00]
try:
# smbus2 write_i2c_block_data sends the first byte as 'cmd' and the rest as 'vals'
bus.write_i2c_block_data(TEA5767_ADDR, data[0], data[1:])
print(f"Successfully tuned to {freq_mhz} MHz")
except OSError as e:
print(f"I2C Communication Failed: {e}")
sys.exit(1)
if __name__ == "__main__":
try:
bus = smbus2.SMBus(I2C_BUS)
except FileNotFoundError as e:
print(f"Bus Init Error: {e}")
print("Fix: I2C bus not found. Ensure I2C is enabled via raspi-config.")
sys.exit(1)
# Hardcoded target: 97.3 MHz (Replace with your local station)
target_freq = 97.3
set_frequency(bus, target_freq)
print("Radio active. Audio output is on the module's 3.5mm jack.")
print("Press Ctrl+C to exit.")
try:
while True:
time.sleep(1) # Keep script alive for monitoring
except KeyboardInterrupt:
print("\nRadio script terminated.")
Debugging: First Three Things to Check When It Fails
I2C is notoriously unforgiving of marginal connections. If your script crashes, do not guess. Match the exact Python traceback to the ranked causes below.
1. The 'No such file' Bus Error
Exact Error String: FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1'
- Cause A (Most Likely): I2C is disabled in the OS. Fix: Run
sudo raspi-configand enable the I2C interface, then reboot. - Cause B: You are running on a compute module or older Pi Zero where the primary bus maps to
/dev/i2c-0. Fix: ChangeI2C_BUS = 1to0in the script.
2. The 'Remote I/O' NACK Error
Exact Error String: OSError: [Errno 121] Remote I/O error
- Cause A (Most Likely): The TEA5767 is not acknowledging its address. Fix: Run
i2cdetect -y 1. If the grid is empty, check your SDA/SCL wiring. Ensure you haven't swapped them. - Cause B: Insufficient power to the module causing brownouts during the PLL charge pump write. Fix: Move the VCC connection from Pi Pin 1 (3.3V) to Pi Pin 2 (5V), but only if your specific breakout board has an onboard 3.3V LDO regulator for the I2C lines. If it lacks an LDO, feeding 5V directly will fry the Pi's GPIO. Stick to 3.3V and verify your USB power supply can deliver 3A.
3. The 'Device Busy' Lock Error
Exact Error String: OSError: [Errno 16] Device or resource busy
- Cause A: Another process (like a background MQTT radio script or an
i2cdetectloop) has an open file handle to/dev/i2c-1. Fix: Runsudo fuser /dev/i2c-1to find the PID, thenkill -9 [PID].
Extending and Simplifying the Build
Once you have clean I2C communication and audio output, you must decide how to package the receiver for its final environment.
How to Extend (Add Amplification and Control)
The TEA5767 analog audio out is roughly 50mV RMS—enough to drive high-impedance headphones, but far too weak for standard 8-ohm speakers.
The Fix: Wire a PAM8403 3W Stereo Class-D Amplifier board ($2) to the TEA5767's 3.5mm jack. Feed the PAM8403 from the Pi's 5V rail (Pin 2). To add physical tuning, wire a KY-040 Rotary Encoder to GPIO 17 and 27, using the gpiozero library's RotaryEncoder class to increment or decrement the target_freq variable in 0.1 MHz steps.
How to Simplify (Headless MQTT Node)
If you are integrating this into a home automation setup and don't need physical knobs, strip the build down to a headless node.
The Fix: Remove the while True loop. Write the frequency once, then let the script exit. The TEA5767 is a stateful hardware synthesizer; it will hold the PLL lock and continue outputting audio as long as VCC is applied, even if the Python script terminates and the Pi's CPU goes to sleep. Trigger the script via an MQTT payload using os.system('python3 fm_radio.py 101.1') from your home automation broker.






