Pi FM Radio

There are few options for FM radio projects on the Pi, such as the RDA5807M and TEA5767 chips. We’ve built an Arduino FM radio using the RDA5807M chips but we found the documentation to be quite weak. There are some Python libraries for TEA5767 chips, unfortunately however you have no volume control with this chip.

RTL-SDR is a software defined radio (SDR) that uses a DVB-T TV tuner dongle based on the RTL2832U chipset. RTL-SDR dongles are well priced at $10-$15 and they are easy to use for Pi FM radio projects. Software Defined Radios have a large list applications that can done, some of the cooler projects include: tracking airplanes and ships, free-to-air TV, and monitoring satellite data.

Getting Started

For setup an external speaker is required (powered speakers work the best). The RTL-SDR dongle includes an externally connected antenna, if possible try to place the antenna close to a window.

fm_setup

To install the basic software enter:


sudo apt-get install rtl-sdr

The rtl_fm utility is an FM demodulator that is used to read and sample a selected frequency. The output of rtl_fm needs to be directed to an audio player program such as aplay. There are a few sampling and buffering options that must be defined. The syntax with the required options to play an FM radio station at 107.9 MHz is:

rtl_fm -f 107.9e6 -M wbfm -s 200000 -r 48000 | aplay -r 48k -f S16_LE

The rtl_fm application needs to be stopped when a new radio station is selected. If the rtl_fm is running in the background the ps command can be used to find process IDs. The kill command can then be used to terminate the task. An example to find and terminate the rtl_fm task:

pi@raspberrypi:~ $ ps -e | grep rtl_fm
1709 pts/0 00:00:33 rtl_fm
pi@raspberrypi:~ $ kill 1709
pi@raspberrypi:~ $ ps -e | grep rtl_fm
pi@raspberrypi:~ $

Adjusting the Volume

Typically the audio would be from an external speaker rather than the HDMI connection. To force the audio connection, use raspi-config and select the “Advanced” menu option, then selection “Audio”.

advanced

There are a few ways to adjust the volume on the audio. One method is to use the amixer utility. An example to change the radio volume to 70%:

amixer sset "PCM" 70%

A Python Example

For our basic testing we created a simple Python command line application. This application accepts new FM radio frequencies and volume settings. When a new radio frequency is selected the rtl_fm task needs to be stopped and restarted.

This application used a few Python libraries. The os library has the os.system call to shell out to external programs like amixer. The os.kill call terminates processes with a give process ID. The subprocess library has the call:


# simple PI FM radio utility using a RTL SDR dongle 

import subprocess, signal, os

def newstation(station):
    global process, stnum
      
    part1 = "rtl_fm -f " 
    part2 = "e6 -M wbfm -s 200000 -r 48000 | aplay -r 48k -f S16_LE"
    cmd = part1 + station + part2
    
    print 'Playing station :', station 

    # kill the old fm connection
    if process != 0:
        process = int(subprocess.check_output(["pidof","rtl_fm"] ))
        print "Process pid = ", process
        os.kill(process,signal.SIGINT) 

    # start the new fm connection
    print cmd
    process = subprocess.Popen(cmd, shell=True)


def setvolume(thevolume):

    os.system('amixer sset "PCM" ' + thevolume)
    print 'volume = ' , thevolume


process = 0

while True:
    answer = raw_input("Enter a radio station (i.e. 107.9) or volume (i.e. 50%): ")

    if answer.find('%') > 0:  
        setvolume(answer)
    else: 
        newstation(answer)                      

Summary

After the command line FM radio is working, it is possible to create standalone PI radio projects that use pushbuttons, TV remotes or Wii controllers to adjust the volume and stations. We built some projects using PiFace and LCD button shields.

The LCD Python code is below:


#!/usr/bin/python
# Pi FM Radio using an LCD Shield for controlling the stations and volume 

import os, subprocess, signal
import time
import Adafruit_CharLCD as LCD


def newstation(direction):
        global stnum, stations, process

        print 'stnum=',stnum,'direction=',direction              
          
        part1 = "rtl_fm -f " 
        part2 = " -M wbfm -s 200000 -r 48000 | aplay -r 48k -f S16_LE"

        if (stnum + direction  -1):
                stnum = stnum + direction
                print('Playing station :', stations[stnum]) 
                cmd = part1 + stations[stnum] + part2
                if process != 0:
                        process = int(subprocess.check_output(["pidof","rtl_fm"] ))
                        print "Process pid = ", process
                        os.kill(process,signal.SIGINT) 
                # start the new fm connection
                print cmd
                process = subprocess.Popen(cmd, shell=True)
            


def setvolume(voldif):
        global thevolume
        if (thevolume + voldif > 0) and (thevolume + voldif <100):
                thevolume = thevolume + voldif
                os.system('amixer sset "PCM" ' + str(thevolume) + '%')
                print 'volume = ' , thevolume
                

lcd = LCD.Adafruit_CharLCDPlate()
lcd.set_color(0,0,0)

## Add your own stations and station info
stations = ['95.3e6','94.7e6','102.9e6','107.9e6']
sinfo = ['95.3 country', '94.7 light','102.9 easy','Y108 Rock\nHamilton ']
thevolume = 40

stnum = 1               #pick a starting station
process = 0
newstation(0)
lcd.message(sinfo[stnum])
setvolume(thevolume)


print 'Press Ctrl-C to quit.'
while True:
        if lcd.is_pressed(LCD.UP):
                setvolume(5)
                time.sleep(0.25)
        if lcd.is_pressed(LCD.DOWN):
                setvolume(-5)
                time.sleep(0.25)
        if lcd.is_pressed(LCD.LEFT):
                newstation(-1)
                lcd.clear()
                lcd.message(sinfo[stnum])
                time.sleep(0.25)
        if lcd.is_pressed(LCD.RIGHT):
                newstation(1)
                lcd.clear()
                lcd.message(sinfo[stnum])
                time.sleep(0.25)   

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s