Arduino FM Radio met RDA5807M – RDS, Presets & OLED Display
Onderdelen en benodigdheden
| Arduino UNO of Nano | 1 |
| RDA5807M FM Radio Module (I2C) | 1 |
| OLED Display 0.96" SSD1306 (I2C) | 1 |
| Rotary Encoder met drukknop | 1 |
| 3.5mm Audio Jack + kleine speaker/versterker | 1 |
| LiPo 3.7V + TP4056 laadcircuit (draagbaar) | 1 |
| 3D-geprinte behuizing | 1 |
Benodigde tools en machines
- 3D-printer
- Soldeerapparatuur
Over dit project
Bouw je eigen FM-radio met de RDA5807M-chip: volledige FM-band (87.5-108 MHz), RDS-ondersteuning (zenderinfo, liedtitel, tijd), presets, volumeregeling, en een mooi OLED-interface. Ideeal als weekend-project of leerproject voor I2C-communicatie en signaalverwerking.
RDA5807M Kenmerken
- I2C-interface (adres 0x10 of 0x11)
- RDS/RBDS decoder (Program Service, RadioText, Clock Time, Alternative Frequencies)
- Programmeerbare de-emphasis (50/75 µs)
- Digitale volumeregeling (0-15)
- Bass boost, mono/stereo detectie
- Laag stroomverbruik (~20mA)
- Directe audio-uitgang (differentieel)
Bediening
- Draaien encoder: Frequentie veranderen (stappen 100 kHz) of menu navigeren
- Korte druk: Menu selecteren / preset opslaan / mute toggle
- Lange druk: Hoofdmenu / zoekmodus / preset laden
Menu-structuur
- Hoofdscherm: Frequentie, zendernaam (RDS), signaalsterkte, batterij, volume
- Presets: 10 opslagplaatsen (lange druk op encoder → opslaan, korte druk → laden)
- RDS-info: Program Service, RadioText, ALT-frequenties, UTC-tijd
- Instellingen: De-emphasis (EU/US), volume, bass boost, mono forceren, auto-scan
- Auto-scan: Zoekt sterke zenders, slaat op als presets
Code
Arduino FM Radio Sketch
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#define OLED_ADDR 0x3C
#define RDA_ADDR 0x10
#define ENC_A 2
#define ENC_B 3
#define ENC_BTN 4
Adafruit_SSD1306 display(128, 64, &Wire, -1);
volatile int encoderPos = 0;
volatile bool encoderChanged = false;
unsigned long lastBtnPress = 0;
bool inMenu = false;
int menuIndex = 0;
uint16_t frequency = 9690; // 96.9 MHz * 100
uint8_t volume = 10;
uint16_t presets[10];
// RDA5807M registers
struct RDA_Regs {
uint16_t regs[16];
} rda;
void setup() {
Wire.begin();
pinMode(ENC_A, INPUT_PULLUP);
pinMode(ENC_B, INPUT_PULLUP);
pinMode(ENC_BTN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENC_A), encoderISR, CHANGE);
attachInterrupt(digitalPinToInterrupt(ENC_B), encoderISR, CHANGE);
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
loadPresets();
rdaInit();
setFrequency(frequency);
setVolume(volume);
drawMainScreen();
}
void loop() {
// Encoder afhandeling
if (encoderChanged) {
encoderChanged = false;
if (inMenu) navigateMenu();
else {
frequency += (encoderPos > 0) ? 10 : -10; // 100kHz stappen
frequency = constrain(frequency, 8750, 10800);
setFrequency(frequency);
drawMainScreen();
}
encoderPos = 0;
}
// Knop afhandeling
bool btn = digitalRead(ENC_BTN) == LOW;
static bool btnPrev = false;
if (btn && !btnPrev) lastBtnPress = millis();
if (!btn && btnPrev) {
unsigned long dt = millis() - lastBtnPress;
if (dt > 1000) longPressAction();
else shortPressAction();
}
btnPrev = btn;
// RDS polling
static unsigned long lastRDS = 0;
if (millis() - lastRDS > 500) {
lastRDS = millis();
readRDS();
drawMainScreen();
}
}
void rdaInit() {
// Reset & enable
writeReg(0x02, 0xC001); // Soft reset, enable
delay(500);
writeReg(0x02, 0xC001 | 0x0002); // Enable, RDS on
writeReg(0x03, 0x0010); // High side LO injection
writeReg(0x04, 0x0400); // 50µs de-emphasis (EU)
writeReg(0x05, 0x8886); // RDS config
}
void setFrequency(uint16_t freq) {
// freq in 10kHz units: 8750 = 87.5 MHz
uint16_t chan = (freq - 870) / 10; // RDA channel spacing
rda.regs[0x03] = (rda.regs[0x03] & 0xFE00) | chan;
writeReg(0x03, rda.regs[0x03]);
}
void setVolume(uint8_t vol) {
rda.regs[0x05] = (rda.regs[0x05] & 0xFFF0) | (vol & 0x0F);
writeReg(0x05, rda.regs[0x05]);
}
void writeReg(uint8_t reg, uint16_t val) {
Wire.beginTransmission(RDA_ADDR);
Wire.write(reg);
Wire.write(val >> 8);
Wire.write(val & 0xFF);
Wire.endTransmission();
rda.regs[reg] = val;
}
void readRDS() {
Wire.requestFrom(RDA_ADDR, 32);
// Parse RDS blocks A, B, C, D
// Extract PI, PTY, PS, RT, CT, AF
// Implementatie vereist zorgvuldige bit-manipulatie
}
void drawMainScreen() {
display.clearDisplay();
display.setCursor(0,0);
display.print("FM ");
display.print(frequency/100); display.print(".");
display.print((frequency%100)/10);
display.print(" MHz");
// Signaalsterkte, RDS naam, volume, batterij...
display.display();
}
// Encoder ISR, menu navigatie, preset functies, etc.
// Volledige code te lang voor hier, beschikbaar op GitHub
Schema
RDA5807M: VCC → 3.3V, GND → GND, SDA → A4, SCL → A5. ROUT/LOUT → audio jack via koppelcondensatoren (100µF). OLED: VCC → 3.3V, GND → GND, SDA → A4, SCL → A5 (gedeelde I2C-bus). Rotary encoder: A→D2, B→D3, BTN→D4 (pull-up). Voeding: LiPo → TP4056 → 3.3V regulator voor RDA/OLED, 5V boost voor Arduino (indien Nano).
Uitbreidingen
- RDS RadioText scrolling op OLED
- Bluetooth audio receiver (CSR8645) voor streaming
- Wecker/slaap-timer functie
- Equalizer via DSP (bijv. ESP32 met VS1053)
- Web-interface via ESP32 voor remote besturing
Productieproces
- Een 433 MHz Slimme Huiscounter Bouwen met Sensorflare en Raspberry Pi
- Slimme Irrigatiecontroller met Windows 10 IoT, Raspberry Pi en XBee Vocht-sensoren
- De PSoC 6 Pioneer Board met IoT Add-On Shield Beheersen: Een Ontwikkelaarsgids
- Bouw een Internet-gecontroleerde Video-streamende Robot met Arduino & Raspberry Pi
- Bouw een Squid Game-thema Interactieve Pop met Arduino UNO – DIY Project
- Web-gestuurd DMX-verlichtingssysteem – Arduino Master Controller
- Bouw een Arduino Real-Time Clock voor Islamitische Gebedstijden
- Unopad: Naadloze Arduino MIDI-controller voor Ableton Live
- Bouw een Android-bestuurd extern voertuig met Raspberry Pi Motor Shield
- 25 kHz 4-pins PWM-ventilatorbesturing met Arduino Uno – Bouw, Code & Test
- Implementaties stroomlijnen met multi-stage container builds