FM Radio

Published: January 16, 2026

This project is an Arduino-powered FM radio that uses the TEA5767 radio module, an LCD1602 display, a rotary encoder for tuning, and a PAM8403 audio amplifier to drive a speaker. With this build, you can manually tune stations with the rotary knob, auto-seek the next available station with a button press, and listen through a connected speaker. It’s a compact DIY radio project that combines digital control with classic FM listening.


📸 Preview

FM Radio Project Preview

⚙️ How It Works:


🧰 Parts & Materials Used

ComponentNotes
Arduino UNOMain microcontroller board
TEA5767 FM Radio ModuleFM receiver, communicates via I²C
LCD1602 Display (16-pin)Shows frequency and status
Rotary Encoder with Push ButtonManual tuning + auto-seek button
PAM8403 Audio AmplifierDrives speaker output
3W 8Ω SpeakerAudio output
10k PotentiometerLCD contrast control
3.5mm Male-to-Bare Wire CableConnects TEA5767 audio to amp
Breadboard + WiresPrototyping
Power SourceUSB or 5V battery bank

🔌 Wiring Instructions

TEA5767 Module → Arduino

TEA5767 PinArduino PinNotes
VCC5VModule is 5V-tolerant
GNDGNDCommon ground
SDAA4I²C data
SCLA5I²C clock

LCD1602 Display → Arduino

LCD PinArduino PinNotes
VSSGNDGround
VDD5VPower
VOMiddle of PotentiometerContrast control
RSD7Register select
RWGNDAlways LOW (write mode)
ED8Enable
D4D9Data
D5D10Data
D6D11Data
D7D12Data
A5VBacklight +
KGNDBacklight –

Rotary Encoder → Arduino

Encoder PinArduino Pin
CLKD2
DTD3
SWD4
+V5V
GNDGND

PAM8403 Audio Amplifier

PAM PinConnection
Power +Arduino 5V
Power –Arduino GND
GBlack aux wire
B (R-in)Red aux wire
Speaker +/-To speaker terminals

🧾 Arduino Code

Language: C++

#include <Wire.h>
#include <LiquidCrystal.h>

// ------ LCD pins (RS, E, D4, D5, D6, D7) ------
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

// ------ Rotary encoder ------
const uint8_t ENC_A = 2;   // CLK
const uint8_t ENC_B = 3;   // DT
const uint8_t ENC_SW = 4;  // push button
volatile int8_t encDelta = 0;
volatile uint8_t lastAB = 0;

// ------ TEA5767 ------
const uint8_t TEA5767_ADDR = 0x60;      // I2C address
float freqMHz = 101.1;                   // start frequency
const float F_MIN = 87.0, F_MAX = 108.0;
const float STEP  = 0.1;                 // MHz
const uint8_t RSSI_THRESHOLD = 8;        // 0..15 (raise if it stops on noise)

// ---- helpers ----
void tea5767SetFrequency(float mhz) {
  uint16_t pll = (uint16_t)((4.0 * (mhz * 1000000.0 + 225000.0)) / 32768.0);
  uint8_t data[5];
  data[0] = (pll >> 8) & 0x3F;           // MUTE=0, SM=0
  data[1] = pll & 0xFF;
  data[2] = 0xB0;                        // HLSI=1, SSL mid, stereo
  data[3] = 0x10;                        // XTAL=1 (32.768kHz)
  data[4] = 0x08;                        // 50µs de-emphasis (UK/EU)
  Wire.beginTransmission(TEA5767_ADDR);
  Wire.write(data, 5);
  Wire.endTransmission();
}

bool tea5767Read(uint8_t buf[5]) {
  Wire.requestFrom((uint8_t)TEA5767_ADDR, (uint8_t)5); // explicit cast
  for (uint8_t i = 0; i < 5 && Wire.available(); i++) buf[i] = Wire.read();
  return true;
}

// RSSI (signal level) is in byte3[7:4] -> 0..15
uint8_t tea5767SignalLevel() {
  uint8_t r[5]; tea5767Read(r);
  return (r[3] >> 4) & 0x0F;
}

// optional “ready” flag: byte0 bit7
bool tea5767Ready() {
  uint8_t r[5]; tea5767Read(r);
  return (r[0] & 0x80) != 0;
}

void updateLCD() {
  lcd.setCursor(0, 0);
  lcd.print("Tuned: ");
  lcd.print(freqMHz, 1);
  lcd.print(" MHz   ");
}

void encISR() {
  uint8_t a = digitalRead(ENC_A);
  uint8_t b = digitalRead(ENC_B);
  uint8_t ab = (a << 1) | b;
  static const int8_t table[16] = {0,-1,+1,0,+1,0,0,-1,-1,0,0,+1,0,+1,-1,0};
  encDelta += table[(lastAB << 2) | ab];
  lastAB = ab;
}

void setup() {
  Wire.begin();
  pinMode(ENC_A, INPUT_PULLUP);
  pinMode(ENC_B, INPUT_PULLUP);
  pinMode(ENC_SW, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(ENC_A), encISR, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENC_B), encISR, CHANGE);

  lcd.begin(16, 2);
  lcd.print("Initializing...");
  delay(200);

  tea5767SetFrequency(freqMHz);
  lcd.clear(); updateLCD();
  lcd.setCursor(0, 1); lcd.print("Rotate: tune   ");
}

void loop() {
  // rotary tuning
  noInterrupts(); int8_t d = encDelta; encDelta = 0; interrupts();
  if (d != 0) {
    freqMHz += d * STEP;
    if (freqMHz < F_MIN) freqMHz = F_MIN;
    if (freqMHz > F_MAX) freqMHz = F_MAX;
    tea5767SetFrequency(freqMHz);
    updateLCD();
  }

  // button: auto-seek forward
  if (digitalRead(ENC_SW) == LOW) {
    lcd.setCursor(0, 1); lcd.print("Auto-seeking...");

    float start = freqMHz;
    while (true) {
      // step forward & wrap
      freqMHz += STEP;
      if (freqMHz > F_MAX) freqMHz = F_MIN;
      tea5767SetFrequency(freqMHz);
      delay(50);                   // small settle time

      // check signal strength
      uint8_t lvl = tea5767SignalLevel();
      if (lvl >= RSSI_THRESHOLD && tea5767Ready()) {
        updateLCD();
        lcd.setCursor(0,1);
        lcd.print("Signal ");
        lcd.print(lvl);
        lcd.print("         ");
        break;
      }

      // safety: stop if we wrapped around without finding anything
      if (fabs(freqMHz - start) < 0.001f) {
        lcd.setCursor(0,1); lcd.print("No station     ");
        break;
      }
    }
    delay(250); // debounce
  }
}

📝 Notes

💻 Upload the Code

  1. Connect your Arduino UNO via USB.
  2. Open Arduino IDE and select your board + port.
  3. Paste the full FM Radio code into the IDE.
  4. Click Upload.
  5. Once uploaded, the LCD will show the frequency. Tune and listen!

← Back to Projects