Modding a DIY FM radio kit.


This project has a JumpStarter campaign, see on the left of this description. If enough people sign up, Elektor will produce the ATmega328PB adapter board and make it available for €15. The board will come with all SMT parts mounted and the ATmega328PB preprogrammed with Arduino bootloader and a default application.

On the internet, you can find cheap FM radio receiver kits based on the RDA5807 ‘single-chip FM broadcast stereo radio tuner’ by RDA Microelectronics. This IC contains everything to build an FM broadcast receiver with, including a stereo headphones output. The kits add a microcontroller, display, buttons, antenna, and an audio amplifier. Unfortunately, the kits run closed-source software on an STC15W408AS (an enhanced 8051) and expansion ports are not provided. The Schematic

The schematic of the radio is printed on the user manual, and shows a microcontroller (U2) with four pushbuttons. The MCU drives a 4-digit multiplexed common-anode 7-segment display through a 74HC595 shift register (U3) with the help of transistors Q1 to Q4. The MCU communicates with U1, the radio chip, over an I²C bus.

The voltage regulator U10 supports up to 15 V at its input, but there is no reverse-polarity protection. Also, make sure to remove the batteries when the radio is powered from an external power supply, as there is no protection for the batteries. If you forget this, the batteries will be damaged and start leaking (or worse).

Pin header P1 exposes a serial port, intended for in-system programming (ISP) of the STC15W408AS.

AFAIK, the only functions the radio kit offers are frequency up and down and volume up and down. This is a pity because the RDA5807 features more functions, notably RDS and bass boost. To make these accessible, you must either change the firmware of the provided MCU or connect another controller to the I²C port of the RDA5807. A third way is to replace the MCU altogether, which is what we will do here.

There exist good libraries to control the RDA5807, notably the Arduino library by Ricardo Lima Caratti. Furthermore, the 74HC595 that drives the 7-segment display is well known in the Arduino world, so writing new software for the radio using Arduino is relatively straightforward.

The STC15W408AS is a 28-pin device, just like the ATMega328 that we know from the Arduino UNO board. Both MCUs are similar, and we can replace one by the other. They are not pin compatible; therefore, a bit of rewiring has to be done to connect everything together.

Note that the ATmega328 is no longer recommended for new designs. Microchip, the manufacturer of the chip, suggests using the ATmega328PB instead (something Elektor already did in 2016). The ‘PB’ does not exist in a 28-pin DIP package, it only comes in 32-pin SMT packages. Since rewiring has to be done anyway, this is not really a problem. Actually, it is good, as it keeps the adapter height low.

The schematic attached to this post shows how I connected the ATmega328PB to the footprint of the original MCU. There exist many libraries for controlling the 74HC595 over SPI, but I preferred to reserve the SPI bus for MCU ISP programming (J1). J2 is available for connecting a reset pushbutton, which is practical once the new MCU contains an Arduino bootloader, as there is no automatic reset possible (without adding ugly wires).

I designed a small PCB for the adapter that fits in the space delimited by C5 and C8 on the left and Q1 to Q4 on the right.

New Software

The new software should have at least the same functionality as the original software, meaning volume up and down and frequency up and down by pressing the corresponding keys. As there are only four keys, they must also be used to access any other function that might be implemented. Since all possible single-key presses are already used, another mechanism is required. I chose for a long press of the F+ key to access other modes of operation.

Display Driver

The Arduino function shiftOut is used to print characters on the 7-segment display. The digits are multiplexed, meaning that only one can be active at any time. This implies that the display must be refreshed continuously for displaying multi-digit values and strings. This is done best by a timer task running in the background, which means that a timer is required.


Since a timer is needed, I opted for Timer1 as Timer0 is used for the Arduino functions millis() and delay() which are commonly used by Arduino libraries, and so it is better not to fool with it. TimerOne is a neat little library for using Timer1 on Arduino.

I set up Timer1 for a rate of 1 kHz. Every millisecond it calls the function my_millis_counter that in turn calls display_refresh. Timer1 also counts milliseconds for the pushbutton scanner. Therefore, display refresh and key scanning are synchronized. This ensures a non-flickering display and responsive, debounced keys.

Key Scanning & Debouncing

Key scanning and debouncing is implemented using a simple algorithm. When a press is detected (an event), the event start time is recorded. When the key is released, the event end time is recorded. If the period between the start and end time is too short, it is considered bounce, and the key is reset. If it is longer than a bounce, but shorter than a long press, it is a normal press. Anything else is a long press. Several keys can be pressed simultaneously.

Communication with the RDA5807 radio chip is handled by the library mentioned above. This library has many blocking functions (functions that wait for an action to terminate), yet another reason for a timer-controlled background task to refresh the display continuously.

Two Modes of Operation

After power-up, the software is in Mode 0, and it works as if you used the original firmware. A long press (one second or more) on F+ activates Mode 1. Now pressing the V+ button shows the signal strength (RSSI), pressing V− toggles between mono and stereo. Pressing F− toggles bass boost on and off (use headphones to hear it). Repeatedly pressing F+ circles through RDS information levels (0 means OFF). A long press on F+ returns to Mode 0.

The RDA5807 library has a few more functions to play with, but I leave those to the reader.


RDS data is sent to the serial port, not to the display (another exercise for the reader). Level 1 is for text group 2A (Radio Text), level 2 is for group 0A (basic tuning information) and level 3 reads RDS time. See for more information about RDS data. Be aware that not every radio station transmits RDS data and if they do, it may be incomplete and/or wrong. If you see a mix of readable and unreadable characters, then you probably need to adjust the antenna to improve reception. The new RSSI function can help you here.

Programming the ATmega328PB

To turn the ATmega328PB in an Arduino-compatible microcontroller, you should first load it with a bootloader. A suitable bootloader can be found below, it is called optiboot_elektor_uno_r4_8mhz.hex. Use an ISP programmer to flash the bootloader into the MCU and to set the fuses. The required fuse values are:

Fuse Value Low 0xe2 High 0xde Extended 0xf5

Here are detailed instructions on how to use the ATmega328PB with the Arduino IDE.

Connect a pushbutton to J2. This will be the reset button. Connect a Serial-to-USB adapter to J1. To upload a compiled sketch to the MCU, press the IDE's Upload button as usual while watching the IDE's output window. When the ‘Uploading...’ message appears in the IDE, press the reset button on the adapter board. The sketch should now upload as usual. Press reset once more to start the new program.

The PCB design files & source code, bootloader, etc., are available below.


This project has a JumpStarter campaign, see on the left of this description. If enough people sign up, Elektor will produce the ATmega328PB adapter board and make it available for €15. The board will come with all SMT parts mounted and the ATmega328PB preprogrammed with Arduino bootloader and a default application.