Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“» ESP32 SI4703 FM Radio

A fully-featured FM radio receiver built on an ESP32 microcontroller and the SI4703 FM tuner IC, with an SSD1306 OLED display, rotary encoder tuning, hardware seek buttons, RDS text decoding, and persistent NVS storage.


✨ Features

  • FM Tuning β€” 87.5 – 108.0 MHz, 100 kHz steps via rotary encoder with speed-adaptive stepping
  • Hardware Seek β€” Up/Down seek buttons with RSSI-based station validation and mid-seek cancel support
  • Volume Control β€” 0–15 hardware volume levels via dedicated buttons; mute at level 0
  • RDS Decoding β€” Receives and displays PS Name (8-char station name) and RadioText (64-char scrolling info)
  • Istanbul FM Station Lookup β€” Built-in fallback table of ~100 Istanbul FM stations shown when RDS is unavailable
  • OLED Display β€” 128Γ—64 SSD1306; shows frequency, signal bars, stereo/mono indicator, AFC rail flag, volume, and scrolling top-line text
  • Persistent Storage β€” Last tuned frequency and volume level saved to ESP32 NVS (survives power cycles)
  • Stereo Detection β€” Multi-sample stereo lock detection with RSSI threshold
  • AFC Rail Indicator β€” Alerts when the SI4703's AFC is saturated (off-center frequency)

πŸ”§ Hardware

Components

Component Description
ESP32 (DevKit) Main MCU β€” dual-core Xtensa LX6, 240 MHz
SI4703 FM RDS Radio Tuner IC (I2C, 3.3 V)
SSD1306 128Γ—64 OLED display (I2C)
Rotary Encoder Incremental encoder for frequency tuning
4Γ— Tactile Buttons Vol Up, Vol Down, Seek Up, Seek Down

Pin Table

Signal ESP32 GPIO Notes
I2C SDA 21 Shared between SI4703 and SSD1306
I2C SCL 22 Shared between SI4703 and SSD1306
SI4703 RESET 27 Active-low reset, driven by firmware
Encoder CLK (A) 18 Hardware interrupt
Encoder DT (B) 19 Hardware interrupt
Vol Down Button 32 INPUT_PULLUP, active-low
Vol Up Button 33 INPUT_PULLUP, active-low
Seek Down Button 25 INPUT_PULLUP, active-low
Seek Up Button 26 INPUT_PULLUP, active-low

Note: The SI4703 requires a specific boot sequence β€” SDA must be pulled LOW before releasing RESET. This is handled automatically in SI4703Radio::begin(). Do not add an external pull-up on SDA before the ESP32 has booted.

Wiring Diagram (Text)

ESP32            SI4703
─────            ──────
GPIO21 (SDA) ──► SDA
GPIO22 (SCL) ──► SCL
GPIO27       ──► RESET
3.3V         ──► VCC
GND          ──► GND

ESP32            SSD1306
─────            ───────
GPIO21 (SDA) ──► SDA
GPIO22 (SCL) ──► SCL
3.3V         ──► VCC
GND          ──► GND

πŸ›  PlatformIO Configuration

[env:esp32dev]
platform  = espressif32
board     = esp32dev
framework = arduino

monitor_speed = 115200

lib_deps =
    olikraus/U8g2 @ ^2.35.7
    igorantolic/Ai Esp32 Rotary Encoder @ ^1.6

No extra board manager URLs are required. All dependencies are available through the PlatformIO registry.

Build Notes

  • Framework: Arduino (via ESP-IDF under the hood)
  • Flash partition: Default β€” NVS namespace "radio" is used for persistence
  • Wire clock is set to 100 kHz for SI4703 stability; do not increase to 400 kHz
  • The ISR for the rotary encoder is placed in IRAM (IRAM_ATTR) to avoid cache miss crashes

πŸ“ SI4703 β€” Technical Overview

The Si4703 (Silicon Laboratories) is a single-chip FM radio receiver with:

  • Integrated LNA, mixer, IF filter, and ADC
  • I2C interface (address 0x10)
  • RDS/RBDS decoder (groups 0A for PS Name, 2A/2B for RadioText)
  • Seek/Tune engine with STC (Seek/Tune Complete) interrupt flag
  • RSSI register (0–75 dBΒ΅V range) for signal strength
  • Stereo blend and AFC (Automatic Frequency Control)
  • Register map: 16 Γ— 16-bit registers; reads start from 0x0A, writes start from 0x02

Key Registers Used

Register Address Purpose
POWERCFG 0x02 Enable, seek direction, mute
CHANNEL 0x03 Channel set + TUNE bit
SYSCONFIG1 0x04 RDS enable, GPIO
SYSCONFIG2 0x05 Volume, band, spacing, SEEKTH
SYSCONFIG3 0x06 SKSNR, SKCNT thresholds
TEST1 0x07 Crystal oscillator enable (0x8100)
STATUSRSSI 0x0A RDSR, STC, SFBL, AFCRL, ST, RSSI
READCHAN 0x0B Current channel + BLERs
RDSA–RDSD 0x0C–0x0F RDS block data

Seek Quality Thresholds

Configured in SYSCONFIG2 and SYSCONFIG3:

  • SEEKTH = 8 (register field, ~26 dBΒ΅V equivalent) β€” minimum RSSI to stop seek
  • SKSNR = 2 β€” SNR threshold
  • SKCNT = 4 β€” impulse noise threshold
  • Post-seek RSSI re-validation: stations with RSSI < 10 are skipped recursively

🧩 Code Structure

Classes

SI4703Radio

Core driver class for the SI4703 chip.

Method Description
begin() Boot sequence: reset pulse β†’ crystal enable β†’ power up β†’ config
tuneToKHz(freq) Tune to a specific frequency with STC polling
seek(up, cancelled) Hardware seek with cancel-on-button support
setVolume(vol) Set hardware volume (0–15)
setMute(mute) Toggle DMUTE bit in POWERCFG
pollRDS() Parse one RDS frame; updates PS Name and RadioText buffers
getBestRdsText() Returns RadioText if available, falls back to PS Name
getFrequencyKHz() Reads READCHAN register and converts to kHz
getRSSI() Returns raw RSSI byte from STATUSRSSI
isStereoLocked() Multi-sample stereo detection with RSSI guard
isAfcRail() Checks AFCRL bit

Button

Software debounce class (25 ms debounce window).

Method Description
begin(pin) Configures pin as INPUT_PULLUP
update() Must be called every loop; updates stable state
fell() Returns true once on falling edge (press event)
isPressed() Returns true while button is held

Key Global Functions

Function Description
handleRotaryTuning() Reads encoder delta, applies speed-adaptive step size
doSeek(up) Wraps radio.seek() with mute/unmute and UI status updates
tuneAbsolute(freq) Tune to exact frequency with mute, status, and NVS scheduling
updateRadioInfo() Polls RSSI, stereo, AFC, RDS every 120 ms
drawScreen() Full U8G2 page render: top line, frequency, bottom bar
servicePersist() Deferred NVS write β€” saves freq/vol after debounce delay
lookupStationName(freq) Binary-friendly table lookup for Istanbul FM stations

Rotary Encoder Speed Adaptation

Turning the encoder faster increases the tuning step size:

Turn interval Step size
≀ 18 ms 1000 kHz (1 MHz)
≀ 35 ms 500 kHz
≀ 70 ms 200 kHz
> 70 ms 100 kHz (1 step)

πŸ’Ύ Persistent Storage (NVS)

Uses the ESP32 Preferences library under the namespace "radio".

Key Type Default Description
freq_khz uint32 87500 Last tuned frequency in kHz
volume uint8 12 Last set volume level

Writes are deferred (1200 ms for frequency, 800 ms for volume) to avoid excessive flash wear during rapid tuning.


πŸ–₯ Display Layout

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Metro FM                   β”‚  ← RDS / station name (scrolls if long)
│────────────────────────────│
β”‚                            β”‚
β”‚        98.8  MHz           β”‚  ← Large frequency display
β”‚                            β”‚
│────────────────────────────│
β”‚ SIG ▐▐▐▐░  ST  SEEK   V:12 β”‚  ← Signal bars, stereo, status, volume
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • Top line priority: RadioText β†’ PS Name β†’ Station table β†’ raw frequency
  • Signal bars: 5 levels (0, 8, 18, 28, 40, 50 RSSI thresholds)
  • ST/MO: Stereo lock indicator
  • AFC: Shown when AFCRL bit is set (tuner is off-center)
  • Status area: Shows transient messages (seek, tune, vol) or clears when idle

πŸš€ Getting Started

  1. Wire up the hardware per the pin table above
  2. Clone this repo and open in PlatformIO (VS Code extension or CLI)
  3. Install dependencies: pio lib install
  4. Build and upload: pio run -t upload
  5. Open Serial Monitor at 115200 baud for debug output
  6. The radio boots to the last saved frequency (default: 87.5 MHz on first run)

πŸ“„ License

MIT License β€” free to use, modify, and distribute with attribution.


πŸ™ Acknowledgements

  • U8g2 β€” universal graphics library for embedded displays
  • Ai Esp32 Rotary Encoder β€” interrupt-driven encoder library
  • Silicon Laboratories SI4703 datasheet and AN230 application note

About

πŸ“» ESP32-based FM radio receiver with SI4703 tuner, SSD1306 OLED display, RDS decoding, rotary encoder tuning, and NVS persistent storage β€” built with Arduino/PlatformIO.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages