Remote-controlled Arduino FM radio

Lead Image © yupiramos,

Tune Me In

, ,

A low-cost FM receiver connected to an Arduino module lets you play your favorite stations.

Building your own radio has been a popular hobby since the invention of the first crystal sets [1]. Our dad and grandad made radios when they were kids, so we wanted to make our version of an FM radio.

For this project we used:

  • An I2C FM receiver module [2] ($13)
  • Any Arduino module that has SDA and SCL pins
  • An IR receiver [3] ($1)
  • A small breadboard and some jumpers

A number of FM receiver modules are available. The TEA5767 FM module is very low cost ($3) [4], and it has some excellent documentation [5]. Unfortunately, it requires some careful soldering, and it does not support volume adjustments.

For this reason, we looked at an FM module based on the RDA5807M chip. The only problem with this module is that the documentation is very weak [6]. You can run the RDA5807M FM module in TEA5767 mode if you want to keep things simple, but we chose not to because we wanted volume control and because we didn't like the sound quality.

Figure 1 shows how we set up this project. The IR receiver, which captures the button presses from the TV remote, is at lower left. The three wires are red (3.3V), black (GND), and blue (digital pin 11 on the Arduino). I2C communication takes place through just two wires: SCL and SDA.

Figure 1: Fritzing diagram of connections.

The FM receiver module in the upper right has four connections. The yellow wire connects the Arduino A5 (SCL) pin to the FM receiver SCLK pin. The white wire connects the Arduino A4 (SDA) pin to the receiver's SDIO pin. The red and black wires are as described for the IR receiver. The module also has a headset jack, which we used to connect speakers (Figure 2).

Figure 2: FM radio controller with a TV remote.

Addressing Chip Registers

In this project, we write directly to the RDA5807M registers using hexadecimal (hex) notation. Computer programmers in particular use hexadecimal as an easier way to understand binary values, which is a computer's native tongue. Hexadecimal numerical notation is base 16, as opposed to the familiar base 10 that we use or base 2 that computers use. Base 2 represents values with the digits 0 and 1 and base 10 with the digits 0-9.

In hex, 0-9 and A-F represent the decimal numbers 0-9 and 10-15. Four binary bits become a hex digit, and a full byte is then a hex pair. Therefore, 0000-1111 binary becomes 00-FF hex (0-255 decimal). In programming languages, hex numbers are often identified with the 0x prefix.

The question is: How do you know what to write and where? The chip spec sheet is your friend here, so you better hope it's well documented. Unfortunately, in our case, it wasn't, so we had to do a little reverse engineering.

FM Receiver Module

The FM receiver module works on the I2C bus [7] that connects to the SDA and SCL pins. The I2C bus is used on both Arduino and Raspberry Pi hardware and it allows intelligent devices like GPS devices, accelerometers, and real-time clocks to be wired in on a common bus.

Each device on the I2C bus will have a unique address, and for many smart devices, libraries will exist to simply the communications. For devices that do not have a specific library, the Arduino Wire.h library [8] is used to read and write to I2C devices.

The RDA5807M chip [9] on the FM receiver module has a number of register addresses that perform a number of different functions. For example, you can scan for radio stations or have radio station information dynamically returned. For this project, we kept it simple and only wrote to three different registers (Table 1). To write to a register, the important commands are Wire.beginTransmission, Wire.write, and Wire.endTransmission.

Table 1

FM Receiver Registers




Initialize the chip and enable communications


Set the frequency


Set the volume

An example using these commands would look like:

Wire.beginTransmission(0x<nn>); \
  // the device address
Wire.write(0x<nn>); \
  // the register to which you write
Wire.write(0x<nn>); Wire.write(0x<nn>); \
  // data sent to the first and second \
     bytes of the register
Wire.endTransmission(); \
  // end of data transmission

The first line signals the beginning of a transmission to the I2C device with the address specified. For the FM module, the device is 0x11. The second line indicates the register to which you are writing. The third line with the next two write commands queues the data to be sent to the specified register. The last line transmits all of the bytes queued with the write() statements. A simple radio test program (Listing 1) sets up the radio to a local frequency at volume 1 (0xD1).

Listing 1


01 #include <Wire.h>
03 int freq;
04 int freqB;
05 byte freqH, freqL;
07 void setup()
08 {
09    Wire.begin();
11 // Initialize the RDA5807M chip
13    Wire.beginTransmission(0x11);          // Device address is 0x11
14    Wire.write(0x02);                      // Register address 0x02
15    Wire.write(0xC0); Wire.write(0x03);    // Initialize the settings
16    Wire.endTransmission();                // stop condition
17    delay(500);                            // wait 500ms
19    Wire.beginTransmission(0x11);          // Device address is 0x11
20    Wire.write(0x02);                      // Register address 0x02
21    Wire.write(0xC0); Wire.write(0x0D);    // Set up the radio for communications
22    Wire.endTransmission();
23    delay(500);
25 // Define an FM station to listen to
27    freq = 1079;                 // 107.9MHz is our local FM station
28    freqB = freq - 870;          // chip needs to have freq offset from lowest freq (870)
29    freqH = freqB>>2;            // you need to break the offset freq into 2 parts (hi/low)
30    freqL = (freqB&3)<<6;        // Shift channel selection for matching register 0x03
32    Wire.beginTransmission(0x11);
33    Wire.write(0x03);
34    Wire.write(freqH);               // write High freq byte
35    Wire.write(freqL + 0x10);        // write Low freq byte
36    Wire.endTransmission();
38 // The volume is from 0-F, the first bytes; leave all the bytes (0x84D0-0x84DF)
40    Wire.beginTransmission(0x11);        // 0x11 is the RDA5807M chip
41    Wire.write(0x05);                    // address 0x05 is where you want to write
42    Wire.write(0x84); Wire.write(0xD1);  // set volume to 1
43    Wire.endTransmission();
44 }
45 void loop()
46 {
47 }

Because initializing the chip (lines 3-16) and enabling radio communications (lines 19-22) use the same register, we had to pause between these steps (lines 17 and 23). We found that we did not need to include pauses when we changed the radio frequency or volume. To set the radio frequency, we offset the frequency from the minimum value (i.e., the beginning of the FM broadcast band).

The spec sheet shows for register 3 that the minimum FM broadcast value is 87MHz for the Americas and Europe (band 0, the default), 76MHz for Japan or worldwide (band 1 or 2), and 65MHz for parts of Eastern Europe (band 3).

Because we live in North America, we had to offset the frequency from the minimum value of 870 (i.e., 87MHz, line 28) in band 0. Lines 29 and 30 show the bit shifting needed to split the offset frequency into high and low bytes. We used the wiki examples [6], and we added some diagnostics to help us understand the frequency and volume formats.

The volume can be adjusted between a value of 0 (0x0), the lowest volume, and 15 (0xF), the highest. Because register 5 (0x05) serves a number of purposes other than setting the volume, the code for the values must range between 0x84D0 and 0x84DF (line 42).

Buy this article as PDF

Express-Checkout as PDF

Pages: 8

Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content