If you want to build a raspberry pi fm radio receiver, skip the bulky RTL-SDR dongles and the obsolete TEA5767 shields. The direct answer for a low-cost, high-quality digital FM receiver is the RDA5807M I2C breakout board. It handles digital tuning, RDS (Radio Data System) decoding, and analog audio output natively, all controlled via a simple 4-wire I2C connection to your Pi.
This guide walks through the exact hardware selection, pin mapping, and Python code required to get your Pi tuning the FM band (87.0–108.0 MHz) without the endianness bugs that plague most online tutorials.
The Verdict: Which FM Module Should You Pick?
Before ordering parts, you need to choose the right receiver architecture. Here is the decision path to select your module based on your project goals.
| If your priority is... | Choose this module | Why? |
|---|---|---|
| Low cost (<$5) & direct audio out | RDA5807M Breakout | Native I2C, built-in DAC, RDS support, 3.3V logic. |
| Raw IQ data & wideband scanning | RTL-SDR V4 ($35) | Software-defined; requires heavy CPU DSP and external antenna. |
| Using legacy Arduino shields | TEA5767 ($4) | Analog tuning architecture; poor selectivity, no RDS. |
Parts List & Pin Mapping for the RDA5807M
This build targets the Raspberry Pi 4 Model B (4GB) running Raspberry Pi OS (64-bit, Bookworm or newer). The code and I2C bus mappings also apply directly to the Raspberry Pi 5, provided you use the standard I2C-1 bus.
| Component | Exact Variant / Spec | Est. Cost |
|---|---|---|
| Microcontroller | Raspberry Pi 4 Model B (4GB RAM) | $55.00 |
| FM Receiver | RDA5807M I2C Breakout (with 32.768kHz crystal) | $3.50 |
| Audio Output | 3.5mm TRS Audio Jack breakout or direct solder | $1.00 |
| Wiring | F/M Dupont jumper wires (22 AWG) | $4.00 |
| Pull-ups | 2x 4.7kΩ resistors (Only if breakout lacks them) | $0.10 |
GPIO Pin Mapping
The RDA5807M is strictly a 3.3V logic device. Feeding it 5V from Pin 2 or 4 will permanently destroy the silicon.
| RDA5807M Pin | Raspberry Pi 4 Pin | GPIO / Function |
|---|---|---|
| VCC | Pin 1 | 3.3V Power |
| GND | Pin 6 | Ground |
| SDA | Pin 3 | GPIO 2 (I2C1 SDA) |
| SCL | Pin 5 | GPIO 3 (I2C1 SCL) |
Wiring and I2C Bus Configuration
Follow these numbered steps to prepare the Pi's I2C bus and verify the hardware connection before writing any code.
- Enable I2C: Open a terminal and run
sudo raspi-config. Navigate to Interface Options > I2C and select Yes to enable the ARM I2C interface. Reboot the Pi. - Install I2C Tools: Run
sudo apt update && sudo apt install i2c-tools python3-smbus2. We usesmbus2instead of the legacysmbuslibrary for better error handling and Python 3.11+ compatibility. - Verify the Address: Run
i2cdetect -y 1. You should see a device at10or11.Address Note: The RDA5807M uses0x10for TEA5767-compatibility mode and0x11for native register access. Our code targets0x11. - Audio Routing: Solder a 3.5mm TRS jack to the
L,R, andGNDpads on the RDA5807M breakout. Do not route the audio through the Pi's ADC; the module's built-in DAC provides vastly superior signal-to-noise ratio (SNR) for direct headphone or amplifier listening.
Complete Python Control Script with Error Handling
The most common failure point in RDA5807M tutorials is endianness. The SMBus standard sends 16-bit words in little-endian format, but the RDA5807M expects big-endian (MSB first). To prevent silent tuning failures, we use write_i2c_block_data to send raw byte arrays.
Target Board: Raspberry Pi 4 / 5 (I2C Bus 1)
import sys
import time
from smbus2 import SMBus, i2c_msg
# --- PIN & BUS DEFINITIONS ---
# Physical Pin 3 (GPIO 2) = SDA
# Physical Pin 5 (GPIO 3) = SCL
I2C_BUS = 1
RDA5807M_ADDR = 0x11 # Native index access address
class RDA5807M:
def __init__(self, bus_num=I2C_BUS, addr=RDA5807M_ADDR):
self.addr = addr
self.bus = SMBus(bus_num)
self.init_radio()
def init_radio(self):
"""Power up and unmute the radio module."""
try:
# Reg 0x02: DHIZ=1, DMUTE=1, SOFT_RESET=0, RDS_EN=1 -> 0xC00D
self._write_reg(0x02, 0xC0, 0x0D)
time.sleep(0.1) # Wait for soft reset to clear
# Reg 0x02: Clear soft reset, keep unmuted
self._write_reg(0x02, 0xC0, 0x01)
print('[INFO] RDA5807M initialized successfully.')
except OSError as e:
print(f'[FATAL] I2C Initialization failed: {e}')
sys.exit(1)
def tune_freq(self, freq_mhz):
"""Tune to a specific FM frequency (e.g., 101.1)."""
if not (87.0 <= freq_mhz <= 108.0):
raise ValueError('Frequency must be between 87.0 and 108.0 MHz')
# Calculate channel register value
# Band 00 (87-108MHz), Space 00 (100kHz)
chan = int((freq_mhz - 87.0) / 0.1)
# Reg 0x03: CHAN[15:6], TUNE[4]=1, BAND[3:2]=00, SPACE[1:0]=00
reg_val = (chan << 6) | (1 << 4)
msb = (reg_val >> 8) & 0xFF
lsb = reg_val & 0xFF
self._write_reg(0x03, msb, lsb)
print(f'[INFO] Tuned to {freq_mhz} MHz')
def _write_reg(self, reg, msb, lsb):
"""Write 16-bit register using big-endian byte array to avoid SMBus swap."""
self.bus.write_i2c_block_data(self.addr, reg, [msb, lsb])
if __name__ == '__main__':
radio = RDA5807M()
# Tune to a local strong station for testing (e.g., NPR or local pop)
radio.tune_freq(101.1)
try:
while True:
time.sleep(1) # Keep script alive to maintain I2C state
except KeyboardInterrupt:
print('[INFO] Radio powered down.')
Debugging: 'No Device Found' and I2C Faults
When the script fails, it will throw one of two specific exceptions. Here is the exact error string, the ranked causes, and the first three things to check.
First 3 Things to Check When It Fails
- Run
i2cdetect -y 1: If the grid is entirely empty (only dashes), your wiring is wrong or the module is dead. If it showsUU, another kernel driver has claimed the chip. - Verify VCC Voltage: Use a multimeter to measure between the VCC and GND pins on the breakout. It must read 3.3V (±0.1V). If it reads 5V, you are on the wrong Pi pin and the module is likely fried.
- Check Pull-up Resistors: Measure resistance between SDA and 3.3V, and SCL and 3.3V. You should see ~4.7kΩ. If your cheap breakout omitted these, the Pi's internal pull-ups (50kΩ) are often too weak for reliable 400kHz I2C.
Error Decision Matrix
| Exact Error String | Ranked Cause | Fix |
|---|---|---|
FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1' |
1. I2C disabled in OS. 2. Using wrong Pi model mapping. |
Run sudo raspi-config and enable I2C. Reboot. |
OSError: [Errno 121] Remote I/O error |
1. SDA/SCL wires swapped. 2. Missing pull-up resistors. 3. Module locked up from 5V overvoltage. |
Swap Pin 3 and Pin 5 wires. Add 4.7kΩ pull-ups. Replace module if 5V was applied. |
OSError: [Errno 110] Connection timed out |
1. I2C bus capacitance too high (wires too long). | Keep I2C jumper wires under 30cm (12 inches). Reduce bus speed in /boot/config.txt using dtparam=i2c_baudrate=50000. |
Extending or Simplifying the Build
Once you have audio coming out of the 3.5mm jack and the Python script tuning reliably, you have a functional raspberry pi fm radio. Here is how to modify the build based on your end goal.
How to Extend the Build (Add Hardware UI)
To make this a standalone desktop radio without needing an SSH terminal, add a Rotary Encoder (KY-040) and a 16x2 I2C LCD (HD44780).
The wiring rule: Because the Pi only has one primary I2C bus (Bus 1) exposed on the standard header, you must wire the LCD to the same SDA/SCL lines as the RDA5807M. Ensure the LCD's I2C backpack address (usually 0x27) does not conflict with the radio's 0x11. Read the rotary encoder's CLK/DT pins using standard GPIO interrupts via the gpiozero library to increment or decrement the freq_mhz variable in 0.1 MHz steps.
How to Simplify the Build (Software Only)
If you don't want to write raw I2C register math, you can simplify the software side by installing the community-maintained rda5807m package via pip (pip3 install rda5807m). However, be aware that many PyPI wrappers for this chip still suffer from the little-endian byte-swap bug on Raspberry Pi OS Bookworm. The raw smbus2 block-write method provided in this article remains the most reliable, bulletproof way to guarantee correct register writes across all kernel versions.
For further reading on I2C bus configuration and pull-up resistor calculations, refer to the Adafruit I2C Sensor Guide and the official Raspberry Pi Configuration Documentation.






