Tune Me In
A low-cost FM receiver connected to an Arduino module lets you play your favorite stations.
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:
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.
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).
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.
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
Register | Action |
---|---|
2 |
Initialize the chip and enable communications |
3 |
Set the frequency |
5 |
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
radio1.ino
01 #include <Wire.h> 02 03 int freq; 04 int freqB; 05 byte freqH, freqL; 06 07 void setup() 08 { 09 Wire.begin(); 10 11 // Initialize the RDA5807M chip 12 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 18 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); 24 25 // Define an FM station to listen to 26 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 31 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(); 37 38 // The volume is from 0-F, the first bytes; leave all the bytes (0x84D0-0x84DF) 39 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).
Pages: 8
Price $15.99
(incl. VAT)