RDA5807M (+ arduino nano clone) FM radio

Simple FM radio receiver based on RDA5807M and AVR microcontroller with IR controller. Cheap arduino nano clone is used - it's actually cheaper than mega8 from local distributor (taking shipping cost into account), it provides convenient way of programming (bootloader), debugging (integrated USB-to-serial CH340 bridge) and can also provide 3.3V voltage required by RDA5807M.

Cheap ($0.99 for 2 pieces) RDA5807M are available on ebay and aliexpress. Depending on register address used for communication, RDA5807M can be used as compatible with TEA5767 or in "native" mode. I've tested TEA5767 mode first and having some problems with station seeking switched to "native" mode.
RDA5807M module - front RDA5807M module - back
RDA5807M vs TEA5767:

Main issue with arduino is in my opinion licensing - as arduino libraries are covered by LGPL their commecial use is problematic - using it might just be not worth the trouble. As my ATmega8 version of this radio was built with avr-gcc/WinAVR there was also no compelling reason for switching to arduino ecosystem - all the missing parts (NEC IR decoding code) are also available in pure C as well (and with BSD-like licensing).

Few notes on arduino / AVR bootloader

Arduino nano clone I bought (popular <$2 board with mini-USB socket) had bootloader already programmed.

ATmega328P has special Flash area designated for bootloaders - located at the end of the Flash area. Flash programming can be initiated only if code is running from this area. Size of the bootloader area is determined by two fuse bits: BOOTSZ1 and BOOTSZ0 - can be set to either 256 words (512B or 4 Flash pages), 512 words, 1024 words or 2048 words (4kB). Entering the bootloader could be done either from application (jump) or by programming BOOTRST fuse (setting value = 0) that would change reset vector from application (0x0000) to bootloader making bootloader running by default. This is what arduino is using. Including code that jumps to bootloader in user application would result in problems as user application might just hung, disable interrupts or serial port. Instead each time programming is requested PC application sets DTR line low for a short time. This line is connected with AVR RESET input with 100nF capacitor and since BOOTRST fuse is programmed microcontroller starts executing bootloader code on reset. Bootloader waits few seconds for communication, jumps to application on timeout. As same PC application is controlling DTR state and initiates communication bootloader timeout can be short and all works smoothly. Note that while bootloader is very convenient it adds few seconds to cold startup (i.e. when turning power on) which might be annoying.

As user application Flash area starts from 0x0000 regardless whether bootloader is used or not no changes to the application are required if working with or without bootloader. Application can be developed with help of the bootloader but does not depend on it.

Here is arduino configuration (excerpt from boards.txt) for arduino nano:

nano.name=Arduino Nano

nano.upload.tool=avrdude
nano.upload.protocol=arduino

nano.bootloader.tool=avrdude
nano.bootloader.unlock_bits=0x3F
nano.bootloader.lock_bits=0x0F

nano.build.f_cpu=16000000L
nano.build.board=AVR_NANO
nano.build.core=arduino
nano.build.variant=eightanaloginputs

## Arduino Nano w/ ATmega328
nano.menu.cpu.atmega328=ATmega328
nano.menu.cpu.atmega328.upload.maximum_size=30720
nano.menu.cpu.atmega328.upload.maximum_data_size=2048
nano.menu.cpu.atmega328.upload.speed=57600
nano.menu.cpu.atmega328.bootloader.low_fuses=0xFF
nano.menu.cpu.atmega328.bootloader.high_fuses=0xDA
nano.menu.cpu.atmega328.bootloader.extended_fuses=0x05
nano.menu.cpu.atmega328.bootloader.file=atmega/ATmegaBOOT_168_atmega328.hex
nano.menu.cpu.atmega328.build.mcu=atmega328p

This bootloader (atmega/ATmegaBOOT) is GPL-licensed. Optiboot has same license yet arduino FAQ mentions only LGPL licensing. In bird culture, this is considered a dick move.

For firmware loading arduino is using avrdude version 6.0.1. As WinAVR is not maintained anymore and contains version 5.10 from 2010 you might need to update these files. Avrdude command line for writing Flash (replace COM16 with own port number):

avrdude -p atmega328p -c arduino -P COM16 -b 57600 -D -U flash:w:main.hex:i

These parameters are also included in project makefile (make program).

Arduino bootloader is not able to read fuses (returning 0) but fuses can be verified from avr-gcc application itself:

static void dump_fuses(void)
{
	uint8_t lfuse, hfuse, efuse, lock;

	cli();
	lfuse = boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS);
	hfuse = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
	efuse = boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS);
	lock = boot_lock_fuse_bits_get(GET_LOCK_BITS);
	sei();
	printf("LFUSE = %02X, HFUSE = %02X, EFUSE = %02X, LOCK = %02X\n", lfuse, hfuse, efuse, lock);
}

Fuses on my board are little weird (LFUSE = FF, HFUSE = DA, EFUSE = F8, LOCK = CF) as BOD level in EFUSE was programmed to restricted value.

IR kit

Main addition to previous (ATmega8) version is infrared remote control. For this purpose I've bought cheapest "arduino" IR kit: tiny remote controller, VS1838B IR receiver and (unnecessary) board for this receiver. Remote controller seems to be really low quality - it was only $1.27 including shipping and I'm not even sure if it is worth it, so only plus is it is easily replaceable and manufactured in millions. Mine is almost unused, just laying around few months but front is already delaminated.

Controller is intented to be powered with CR2025 cell but popular and cheap CR2032 fits also. I've accidentally discovered that this kit can work with depleted cell (2.90V with no load, 1.25V on 1k load) but range is limited to ~30 cm then. With fresh cell this remote + receiver set works across the room (few meters) with no problems, although it requires aiming.
NEC remote controller

This remote sends signals following NEC protocol (basic variant with 8 bit address)
NEC protocol
For precise time readings: NEC.json for miniscope v4

For decoding NEC protocol I've used slightly modified nec-decoder library by Malte Pöggel. Timings generated by my $1 remote were little different than nominal (e.g. 10 ms burst) and original code was not accepting this.

NEC standard codes sent by remote have address = 0x00 and following command values (hexadecimal):
NEC remote controller codes

I've received very similar remote controller with popular USB DVB-T RTL2832U tuner:
DVB-T RTL2832U remote
It has identical size to "Car mp3" controller, NEC address code is identical (0x00) but command codes assigned to buttons are different. You can change mapping from NEC command code to function (ON/OFF, VOLUME UP/DOWN, SEEK UP/DOWN, ...) in ir_remote_s1_car_mp3.c file.

Another popular remote controllers that are using NEC codes are ones from DVB-T tuners based on MSTAR chip: WIWA DVB-T remote

PAM8403

PAM8403 is a D-class stereo audio amplifier running from 5V power supply. For $1.50 I've received 3 modules:
PAM8403 modules

RDA5807M documentation

Poor documentation might be main issue of RDA5807M. There are two datasheet version on the Internet, 1.0 and 1.1, both from 2011 and they are (surprisingly) written in close to correct English, but it seems that either chip was updated or documentation was incomplete and this cost me few solid hours. In "TEA5767 mode" chip had decent reception, but once I switched to native mode it was practically unusable, barely receiving any stations through the noise when placed near the window. Comparing initialization procedure with few other project initially gave no clues. Then small arduino test code got interesting as reception was good with it. Upon closer inspection that code works because it actually doesn't do anything - it sets software reset bit in R2 and apparently this causes all changes to base configuration to be ignored. With try and error R5 writing was proven to cause problem and this is register that also controls volume, so skipping writing to it was not a good option. Reading this register to get default value does not seem to be possible. Accidentally I've looked into python on RPi code - there is 0x0080 bit set in R5 (in datasheet described as "Resvered" with default value = 0) and this works. This bit deserves to be named "R5_UNDOCUMENTED_SHIT" in source code below.

As I've ordered and received my RDA5807M modules in 2018 and this undocumented bit is not set in most existing projects, my theory is that RDA5807M received some update while related datasheet version is still not available.

Schematic and Kicad files

RDA5807M AVR radio
Schematic - pdf
RDA5807M + PAM8403 FM radio PCB
RDA5807M + PAM8403 FM radio PCB
RDA5807M + PAM8403 FM radio PCB
mega328_FM_Kicad.zip
Notes:

Case

I've finally used plain, cheap Z-34B enclosure with few cut outs for connectors (speaker goldpin header, mini-jack for LINE IN, IR sensor, front LED and antenna).
RDA5807M radio
These flat speakers are salvaged from scrapped LCD TVs (LG, e.g. 42LB5610, part numberss EAB62972101, EAB62972102). Their nominal power is 5W (sometimes 10W), these bass-reflex enclosures are relatively big for TV speakers and I guess they are good match for small FM radio.
RDA5807M radio - bookshelf

Firmware

Built with WinAVR-20100110 (latest version).