Close
0%
0%

Arduino Radio with RDS

I came across a RDA5807FP chip in an old alarm clock and made it into a fully functioning radio with RDS functionality

Similar projects worth following
The RDA5807FP radio chip is controlled by the Arduino over I2C. An LCD and keypad shield is used for the display and radio controls.

Currently the radio has the following features:
* Searching up or down the FM band for a channel by pressing the left or right button.
* Changing volume with the up and down buttons.
* Show the current frequency and signal strength.
* Decode RDS to show the name of the current channel.
* Save the last tuned channel and volume setting into Arduino EEPROM and restore it when the Arduino is powered on.

Still needs a little amp so it does not require external speakers.

A few days ago I was cleaning out a small storage room in my house. I found an alarm clock that I had put in there a few years ago because its display was much too bright. My initial intention with it was to dim the display and put it back into use, but that never happened, it got replaced by a simple cheap alarm clock and it ended up in a closet. So I decided to tear it apart to see if there was anything interesting in there and to maybe harvest some components.

Apart from some components I could harvest I noticed a small PCB dangling off the main board marked 'Radio'. It had a single RDA5807FP chip on it and a few additional components. I googled the part number and found a datasheet for the device. Turns out it's an FM radio in a chip! And it's a quite capable one, controllable over I2C!

It turned into a little surprise project! I got one of my Arduinos out and connected the board and some cheap external speakers with a built-in amp. The datasheet of the 5807 nicely describes the functions of the chip's registers. It still took me quite a while to figure out how to get some sound out of the thing. I wanted it to be a learning experience so I didn't look into any code that may already be available to control the 5807. After lots of trial and error and just random pops from the speaker I finally got it to produce some sweet whit noise!

In the process I blew up the 3.3v regulator on the little board by hooking up the ground to the 5V supply on the Arduino, but luckily the 5807 was unharmed. Hence the jumper that I soldered onto the board :)


Switching on the radio and tuning

The sequence to switch the radio on turned out to be:

  • R2: Enable the radio, Enable soft reset
  • R3: Set up the correct band and channel spacing (EU FM band and 100KHz spacing in this case)
  • R5: Setting the correct antenna port, Signal to noise ratio and a volume
  • The other registers are cleared

Now the radio can be switched on by

  • R2: Disabling soft reset, disabling mute and enabling audio output
  • R3: Enable tuning

Once this is done the rest is quite straight forward. Tuning the radio to a frequency is easy by taking into account the current band and channel spacing setting. In my case frequency to tune to = (frequency - 87) / 0.1. I can reverse this formula to get the frequency display on screen when I read the current frequency form the 5807.

The Arduino is constantly communicating with the 5807 by reading its 6 status registers. With this I can update the current frequency, signal strength, RDS etc.

Decoding the channel name

The 5807 also supports RDS which turned out to be a whole new experience to get working. I had no prior knowledge of RDS so finding out how it works was interesting. I found some good documentation about the different RDS messages here: http://www.g.laroche.free.fr/english/rds/groupes/listeGroupesRDS.htm. The 5807 will set a flag when new RDS data is available. Through some more trial and error I found out that when handling an RDS message you best toggle the RDS enable of the 5807 to reset this data flag, otherwise you may be dealing with an old message which can be problematic...

the four RDS blocks are each stored in a separate register C through F. The 5807 only detects errors on block A and B, not on blocks C and D, which contain the actual data! So to read the channel name I first look if there is any new RDS data and if there is that the error bits of block A and B are all 0. Now I can look for the so called group number in block B. If I detect a group A0 or B0 we're receiving channel tuning data that also contains part of the channel name.

The channel name is 8 characters long and transmitted 2 characters at a time in block D. The last two bits of block B tell the offset of the characters in the channel name. Now since the 5807 doesn't tell me anything about the quality of the data I only accept a part of the channel name if I receive the same characters at the same position twice in a row.

ArduinoRadio_v1.1.zip

Updated version of the Arduino sketch with much better RDS support

Zip Archive - 5.88 kB - 09/18/2016 at 20:50

Download

radio_module.png

Schematic of the radio module. Requires an external amplifier to give a good volume.

Portable Network Graphics (PNG) - 87.44 kB - 07/18/2016 at 20:36

Preview
Download

  • 1 × Arduino Uno
  • 1 × LCD Keypad Shield
  • 1 × RDA5807FP board Comes with a 32KHz crystal and small 10uF cap on the back of the board
  • 1 × Jack plug socket

  • Added better RDS support

    Maarten Janssen09/18/2016 at 20:54 0 comments

    I updated the Arduino sketch to improve RDS support. The following has changed:

    • If the radio received an RDS message during the last update it will poll the radio again to see if there is still more RDS data to process
    • If there is no RDS data then only read registers A and B. Registers C through F are only read when there is new RDS data
    • Don't reset the RDS enable bit when a new message is received
    • Process individual data bytes of the station name instead of pairs of bytes
    • Removed the delay in the program's main loop

    Added the new sketch to the project page: ArduinoRadio_v1.1

View project log

Enjoy this project?

Share

Discussions

hansakg77 wrote 08/22/2021 at 09:45 point

seems I'm having trouble with tuneTo() function as it does not tunes to correct frequency. anyone have any idea the reason?

setup routine this works, but in the loop if i set to certain station which I've save in eeprom (like say ), it goes to like a weird frequency like 112.5.

I was able get this sorted out by incorporating this function copied from https://funprojects.blog/tag/rda5807/

void Radio::tuneTo2(int freq) {
  int freqB;
  byte freqH, freqL;

  freqB = freq - 870; // chip needs to have freq offset from lowest freq (870)
  freqH = freqB>>2; // you need to break the offset freq into 2 parts (hi/low)
  freqL = (freqB&3)<<6; // Shift channel selection for matching register 0x03
   
  Wire.beginTransmission(0x11);
  Wire.write(0x03);
  Wire.write(freqH); // write High freq byte
  Wire.write(freqL + 0x10); // write Low freq byte
  Wire.endTransmission();
}


  Are you sure? yes | no

Nuno Brandão CT1JQJ wrote 07/11/2021 at 17:22 point

I’ve made this project and liked very much of it, perhaps two inprovements for the future: manual tunning, and store some presets. 73’s

  Are you sure? yes | no

mfmradio1 wrote 07/16/2020 at 22:22 point

  Are you sure? yes | no

mfmradio1 wrote 07/16/2020 at 22:21 point

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
Radio radio;

please help me code error  (Radio radio;)

  Are you sure? yes | no

Serega wrote 05/23/2020 at 08:06 point

Time info not decoded ? 

  Are you sure? yes | no

shaheen01 wrote 04/15/2020 at 14:21 point

Please Solve Massages 'Radio' does not name a type

  Are you sure? yes | no

Sergio wrote 06/01/2018 at 14:49 point

i haved some trouble with buttons, only works right button, i have changed, some values in sketch

143 = up (volume +)
336 = down (volume -)
508 = left (seek down)
0 = right (seek up)

I have some trouble with RDS, it shows slowly the text, and lost characters


  Are you sure? yes | no

Eduardo wrote 04/27/2018 at 13:23 point

Hi, I made some modifications to work with lcd I2C and reading the keyboard through the digital ports. I just could not see RDS working.

The lcd I used is 20x4. The layout of the digital inputs was:

#define buttonUp 8
#define buttonDown 9
#define buttonRight 10
#define buttonLeft 11

Follow the complete code:

#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <radio.h>

#define SHOW_STATION 0
#define SHOW_VOLUME  1
#define SHOW_TUNING  2

#define buttonUp 8
#define buttonDown 9
#define buttonRight 10
#define buttonLeft 11

unsigned buttonValue = 0;

byte CHR_ANTENNA[8] = {
  B11111, B10101, B10101, B01110, B00100, B00100, B00100, B00100
};

byte CHR_STRENGTH[8][8] = {{
    B00000, B00000, B00000, B00000, B00000, B00000, B00000, B10000
  }, {
    B00000, B00000, B00000, B00000, B00000, B00000, B11000, B10000
  }, {
    B00000, B00000, B00000, B00000, B00000, B11000, B11000, B10000
  }, {
    B00000, B00000, B00000, B00000, B11100, B11000, B11000, B10000
  }, {
    B00000, B00000, B00000, B11100, B11100, B11000, B11000, B10000
  }, {
    B00000, B00000, B11110, B11100, B11100, B11000, B11000, B10000
  }, {
    B00000, B11110, B11110, B11100, B11100, B11000, B11000, B10000
  }, {
    B11111, B11110, B11110, B11100, B11100, B11000, B11000, B10000
  }
};

byte CHR_BAR[7][8] = {{
    B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111
  }, {
    B11111, B00000, B11000, B11000, B11000, B11000, B00000, B11111
  }, {
    B11111, B00000, B11011, B11011, B11011, B11011, B00000, B11111
  }, {
    B01111, B11000, B10000, B10000, B10000, B10000, B11000, B01111
  }, {
    B11110, B00011, B00001, B00001, B00001, B00001, B00011, B11110
  }
};

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
Radio radio;

byte currentDisplay = SHOW_STATION;
long displayTimeout;
long lastKeyPress;

void setup() {
  lcd.begin(20, 4);

  pinMode(buttonUp, INPUT);
  pinMode(buttonDown, INPUT);
  pinMode(buttonRight, INPUT);
  pinMode(buttonLeft, INPUT);

  digitalWrite(buttonUp, HIGH);
  digitalWrite(buttonDown, HIGH);
  digitalWrite(buttonRight, HIGH);
  digitalWrite(buttonLeft, HIGH);

  lcd.createChar(0, CHR_ANTENNA);
  lcd.createChar(1, CHR_STRENGTH[7]);
  lcd.createChar(2, CHR_BAR[0]);
  lcd.createChar(3, CHR_BAR[1]);
  lcd.createChar(4, CHR_BAR[2]);
  lcd.createChar(5, CHR_BAR[3]);
  lcd.createChar(6, CHR_BAR[4]);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("     Radio?     ");

  if (!radio.init()) {
    lcd.setCursor(0, 1);
    lcd.print("Radio error!    ");
    while (true);
  }
  //     radio.tuneTo(92.5);

  if (EEPROM.read(0) == 'R' && EEPROM.read(1) == 'A' && EEPROM.read(2) == 'D' && EEPROM.read(3) == 'I' && EEPROM.read(4) == 'O') {
    radio.tuneTo(getEepromFreq());
    radio.setVolume(getEepromVolume());
  } else {
    EEPROM.update(0, 'R');
    EEPROM.update(1, 'A');
    EEPROM.update(2, 'D');
    EEPROM.update(3, 'I');
    EEPROM.update(4, 'O');
    setEepromVolume(4);
  }

  radio.setBassBoost(true);
  radio.setMute(false);

  lastKeyPress = millis();
  displayTimeout = 0;
}

void loop() {
  radio.updateStatus();
  if (!radio.state.isTunedToChannel && currentDisplay == SHOW_STATION) {
    currentDisplay = SHOW_TUNING;
    displayTimeout = 0;
  } else if (currentDisplay == SHOW_TUNING && radio.state.isTunedToChannel) {
    currentDisplay = SHOW_STATION;
    setEepromFreq(radio.state.frequency);
  }

  handleKeys();
  updateDisplay();
  delay(10);
}

void handleKeys() {
  if (millis() - lastKeyPress < 100) return;
  
  if (digitalRead(buttonLeft) == LOW) {
    buttonValue = 1;
  }
  else if (digitalRead(buttonUp) == LOW) {
    buttonValue = 4;
  }
  else if (digitalRead(buttonDown) == LOW) {
    buttonValue = 3;
  }
  else if (digitalRead(buttonRight) == LOW) {
    buttonValue = 2;
  }else buttonValue = 0;

  switch (buttonValue) {
    case 1:
      if (radio.state.volume < 15) {
        radio.setVolume(radio.state.volume + 1);
        displayTimeout = millis() + 5000;
        currentDisplay = SHOW_VOLUME;
      }
      break;

    case 2:
      if (radio.state.volume > 0) {
        radio.setVolume(radio.state.volume - 1);
        displayTimeout = millis() + 5000;
        currentDisplay = SHOW_VOLUME;
      }
      break;

    case 3:
      radio.seekDown();
      break;

    case 4:
      radio.seekUp();
      break;
  }

  lastKeyPress = millis();
}

void updateDisplay() {
  if (currentDisplay != SHOW_STATION && displayTimeout != 0 && millis() > displayTimeout) {
    if (currentDisplay == SHOW_VOLUME) {
      setEepromVolume(radio.state.volume);
    }
    lcd.clear();
    currentDisplay = SHOW_STATION;
  }

  drawStatusLine();

  switch (currentDisplay) {
    case SHOW_STATION:
      lcd.setCursor(0, 1);
      if (radio.state.hasStationName) {
        lcd.print("    ");
        lcd.print(radio.state.stationName);
        lcd.print("    ");
      } else {
        lcd.print("                ");
      }
      break;

    case SHOW_VOLUME:
      lcd.setCursor(0, 1);
      lcd.print("Volume ");
      lcd.write(byte(5));
      for (byte i = 0; i < 7; i ++) {
        if (radio.state.volume > i * 2 + 1) {
          lcd.write(byte(4));
        } else if (radio.state.volume > i * 2) {
          lcd.write(byte(3));
        } else {
          lcd.write(byte(2));
        }
      }
      lcd.write(byte(6));
      break;

    case SHOW_TUNING:
      lcd.setCursor(0, 1);
      lcd.print("  Searching...  ");
      break;
  }
}

void drawStatusLine() {
  lcd.setCursor(0, 0);
  lcd.print(radio.state.frequency);
  lcd.print(" MHz   ");

  switch (radio.state.signalStrength) {
    case 0 ... 1:
      lcd.createChar(1, CHR_STRENGTH[0]);
      break;
    case 2 ... 13:
      lcd.createChar(1, CHR_STRENGTH[1]);
      break;
    case 14 ... 24:
      lcd.createChar(1, CHR_STRENGTH[2]);
      break;
    case 25 ... 34:
      lcd.createChar(1, CHR_STRENGTH[3]);
      break;
    case 35 ... 43:
      lcd.createChar(1, CHR_STRENGTH[4]);
      break;
    case 44 ... 51:
      lcd.createChar(1, CHR_STRENGTH[5]);
      break;
    case 52 ... 57:
      lcd.createChar(1, CHR_STRENGTH[6]);
      break;
    case 58 ... 63:
      lcd.createChar(1, CHR_STRENGTH[7]);
      break;
  }
  lcd.setCursor(14, 0);
  lcd.write(byte(0));
  lcd.write(byte(1));
}

float getEepromFreq() {
  float frequency = 0.0f;
  frequency += EEPROM.read(6)  * 100.0f;
  frequency += EEPROM.read(7)  * 10.0f;
  frequency += EEPROM.read(8);
  frequency += EEPROM.read(9)  / 10.0f;
  frequency += EEPROM.read(10) / 100.0f;
  return frequency;
}

byte getEepromVolume() {
  return EEPROM.read(11);
}

void setEepromFreq(float frequency) {
  unsigned short freq = frequency * 100;
  EEPROM.update(6, (freq / 10000));
  EEPROM.update(7, (freq / 1000) % 10);
  EEPROM.update(8, (freq / 100) % 10);
  EEPROM.update(9, (freq / 10) % 10);
  EEPROM.update(10, freq % 10);
}

void setEepromVolume(byte volume) {
  EEPROM.update(11, volume);
}

  Are you sure? yes | no

Zsolt Kovari wrote 12/20/2017 at 22:19 point

Hi Everybody,

First of all, sorry for my bad English :-)

I just like to tell you a few ideas about my project with RDA5807M. Its TDS does not tell everything. It says for BLERA (REG:0BH<3:2>) and BLERB (REG:0BH<1:0>) that "Available only in RDS Verbose mode". But nowhere tells how to switch ON the VERBOSE MODE. I could find, if REG:56H<7:0>=FFH than it works. Furthermore, where is BLERC and BLERD?  I could find at REG:10H<15:14> and REG:10H<13:12>. The other question: How to write REG:56H? (Because, according TDS it is not possible)

It is a simple BASIC sample:

I2C write &H11, 3, &H56, &HAE, &HFF

'WRITE to &H11 adress, 3 bytes, REG adress, HIGH byte, LOW byte (This register is 16bit long)

After reset REG:56H's value is &HAEC0. It should be changed to &HAEFF.

If you use adress &H11 instead &H10 (what you can read in TDS) than you can read/write the registers directly (instead sequentially, like in TDS).

How to read &H56 register directly? (and all ones)

I2C write &H11, 1, &H56 'WRITE to &H11 adress, 1 byte, REG adress

I2C read &H11, 2, (target HIGH byte), (target LOW byte) 'READ from  &H11 adress, 2 bytes, HIGH byte, LOW byte

Maybe it works similarly in 5807FP also.

I hope it is useful for you :-)

  Are you sure? yes | no

RagsRage wrote 11/28/2017 at 15:57 point

Yo quiero Galletas. Tu tienes?

  Are you sure? yes | no

RagsRage wrote 11/28/2017 at 15:55 point

Very good Galletas muy bueno galletas

  Are you sure? yes | no

Victor Makarov wrote 03/28/2017 at 09:56 point

Your project is really cool. Source code is clear and helps a lot in understanding how the chip works. In function tuneTo I found a bug. R3_CHANNEL bits are not cleared before writing a channel number. You should add the line:

regs[RADIO_REG_3] &= (~R3_CHANNEL);
before writing.

  Are you sure? yes | no

Brown wrote 03/01/2017 at 19:26 point

Ok , i figured out the lcd connections , and started to modify the code to use digital pins , but it wont work, code compiles fine , which resistor values did you use for analog input?

  Are you sure? yes | no

Brown wrote 02/26/2017 at 13:42 point

Hey , i'd like to build your radio , but from scratch , can you tell me which atmega pins the switches go , and which pins connected with the lcd which pins , vcc gnd and contrst i know where to connect but the rest not. 

  Are you sure? yes | no

volvo4 wrote 01/14/2017 at 08:23 point

Hello guys, I have a problem during compilation a program. I downloaded program from this site, and trying to get on my arduino but first of all im getting errors on compilation. Or in another project i have errors that i dont have library. Im using the latest version of arduino 1.8.1 and also i installed uptated library radio. Looking for your help and replay. :)



  Are you sure? yes | no

volvo4 wrote 01/14/2017 at 13:34 point

Ok i solved it by downloading older version of IDE, but still have problem with compilation...


  Are you sure? yes | no

Brown wrote 03/01/2017 at 19:28 point

Did you copy the .c and . h files to arduino directory? if its not there , it cant use it to call the commands in the .ino file the ino is the sketch only , the other 2 files contain the data the ino file calls. You need to copy it.

  Are you sure? yes | no

Atalay Kutlay wrote 07/17/2016 at 08:57 point

Hey, thanks for this great project! Is it possible to do it without i2c ? I have RDA5807M chip. I understand the code but I don't know much about i2c. So I couldn't get how to make it work with my chip. I would be very happy if you could help me a little :)

  Are you sure? yes | no

Maarten Janssen wrote 07/17/2016 at 12:46 point

It shouldn't be too hard to get it working with your chip. As far as I know the only difference between my FP chip and your M chip is that mine has some additional GPIO pins, that I'm not using anyway. The chip only supports i2c, so you'll just have to deal with that :). At first glance looking at both chips' datasheets the radio commands look identical. So I think it should be just a matter of hooking up the right pins. I have a schematic of this project on an old harddrive that should show you how to hookup the chip. I'll look it up and add it here tomorrow. Cheers!

  Are you sure? yes | no

Atalay Kutlay wrote 07/18/2016 at 08:47 point

I think I can handle it if you send me the schematic :) Thank you very much !

  Are you sure? yes | no

Maarten Janssen wrote 07/18/2016 at 20:31 point

I added the radio module's schematic. Probably a bit more elaborate than you actually need and it requires and external amplifier. Hope it works for you!

  Are you sure? yes | no

Atalay Kutlay wrote 07/20/2016 at 12:28 point

Man ! Thank you very much. The only problem I have now is I can't get the station name, but I'm working on it.

  Are you sure? yes | no

Maarten Janssen wrote 07/20/2016 at 19:48 point

Glad to hear that it's working so far :). Getting the station name isn't always working for me as well. It depends a lot on the station... For some it just works, for others it takes a really long time and some just give garbage. That's why I only update the station name if I receive the same byte twice in a row. Just as a fail safe (maybe too safe in some cases)

  Are you sure? yes | no

Atalay Kutlay wrote 07/23/2016 at 11:13 point

Yeah, it's so annoying. My old Nokia E52 phone reads the RDS very well with the same station. I watched some russian videos and their chips work very well. I thought it's about the station, but i tried many other stations. I will update if I can solve the problem :)

  Are you sure? yes | no

Maarten Janssen wrote 09/18/2016 at 21:00 point

Hi, I updated the radio code quite a bit. RDS is now working much better :). The chip requires a good signal though. Check it out if you're still interested.

  Are you sure? yes | no

lightcalamar wrote 01/30/2016 at 11:36 point

Thank you for your kind reply!  Finally resolve this code;

// ================================================

// Code read analog pin for value resistors

#include
#include


#define I2C_ADDR    0x3F // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

int a=0;
 
void setup()
{
    lcd.begin (16,2); //  <<----- My LCD was 16x2
    lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
    lcd.setBacklight(HIGH);
    lcd.begin(20, 4);
    // pinMode(A5, INPUT_PULLUP);
}
 
void loop()
{
  a = analogRead(1);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("  analogRead() ");
  lcd.setCursor(0,1);
  lcd.print("  value is :");
  lcd.print(a);
  delay(250);
}

// End

Give me exact code for operated, after change work fine you code.  I have one LCD 20x4 and I2C want to modify your code more content for example " lcd.createChar(1, CHR_STRENGTH[7]); " horizontal bars. .. and more 

I try some codes RDA5807 and very hard working example:http://www.mathertel.de/Arduino/RadioLibrary.aspx , malfunctions, problems with memory and be slow.  I will inform you of my progress. Thanks again. Thomas.

  Are you sure? yes | no

lightcalamar wrote 01/29/2016 at 08:27 point

Nice project!

I have one problem.  Not running or not work my keys manual contruction. This not definition on sketch?

Press any key not work. Help me?

Best Regards! Thanks.

  Are you sure? yes | no

Maarten Janssen wrote 01/29/2016 at 19:51 point

Hi, first off thanks for liking my project :)

So the buttons on the shield I'm using are mapped to analogue values. If you look in the handleKeys function you will find the analogue values used by the keys.

99 = up (volume +)
255 = down (volume -)
408 = left (seek down)
0 = right (seek up)

You can rewrite the switch there to use digital inputs if that's what you're after. The only thing to keep in mind is the debounce code in the if statement above the switch. A keyState of 1023 means that no buttons are pressed, so you'll have to change that to a boolean value for example to determine whether any button is pressed.

Hope this helps you!

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates