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.
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.
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”.
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)
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)