
Six bash scripts, a $10 Pi, and a personal FM station
Trwmato's Pi-FM-Kitchen-Radio turns a Raspberry Pi Zero W and a $10 car Bluetooth FM transmitter into a 24/7 personal radio station — no soldering, no PCB, no GPIO. Six shell scripts handle playlist generation, RSS podcast downloading, content rotation, and Bluetooth routing, all managed by a systemd service that starts on boot. Full BOM is $30–50; build time is 2–4 hours. The project hit 550 Reddit upvotes and 148 Hacker News points within three weeks of its original post.
"I'm new to both Linux and Raspberry Pi so I've probably developed some hacky ways of making things work. Built using bash as I don't know anything else."— Trwmato, GitHub README 4
What makes this stand out
make_playlist.sh generates each play session from a configurable block sequence — the default being JINGLE → NEWS → MUSIC → JINGLE → SHOW → MUSIC → JINGLE → NEWS → MUSIC → SHOW → MUSIC → JINGLE → MUSIC → SHOW → MUSIC → EGG — where each block type draws from a different content directory. 67 NEWS slots pull the latest BBC or RNZ hourly bulletins downloaded by podget. SHOW slots grab your subscribed podcasts, prioritising fresh episodes and falling back to a backup library when the queue runs dry. EGG slots play hidden audio oddities ("Easter eggs") you drop in a dedicated folder. The whole thing feels like radio because it's programmed like radio.Bill of materials
| Part | Role | Qty | Est. cost |
|---|---|---|---|
| Raspberry Pi Zero W | Main compute; runs Raspberry Pi OS + all scripts | 1 | $10–15 |
| Bluetooth FM transmitter (12V car type) | Converts Bluetooth audio to FM broadcast | 1 | $10–15 |
| microSD card (16 GB+, 32 GB recommended) | OS + podcast/music storage | 1 | $5–10 |
| Micro USB power adapter (5V/2A) | Powers the Pi Zero | 1 | $5–10 |
| FM radio receiver | Receives the broadcast | 1 | $0 if you have one; $10–20 new |
samba is listed as a dependency in the README for exactly this use case.
Signal path and "schematic"
podget download → bash scripts → cvlc (VLC command line) → BlueALSA → Bluetooth → FM transmitter → FM radiocurrent_bt_speaker.sh detects the transmitter's MAC address at startup and radio_loop.sh routes audio to it via BlueALSA (the Bluetooth ALSA backend). 89~/radio/ tree holds 12 subdirectories that define what goes on air:~/radio/
dailyshow/ podcasts: play once, then delete
weeklyshow/ podcasts: play once, then archive
backupdaily/ fallback pool — up to 25 files
backupweekly/ fallback pool — up to 50 files
news/ hourly bulletins from BBC/RNZ
music/ your music library (or Samba mount)
maintenance_music/ plays while podget runs
jingles/ station idents/transitions
eggs/ hidden audio surprises
playlists/ generated .m3u files (runtime)
logs/ podget and script output
config/ playlist_blocks, podcast URLs
scripts/ all six .sh filesThe six scripts
| Script | What it does |
|---|---|
radio_loop.sh | Main loop: waits for Bluetooth connection, calls make_playlist.sh, plays the playlist with cvlc via BlueALSA, then hands off to maintenance.sh |
make_playlist.sh | Generates a .m3u by reading config/playlist_blocks and randomly picking one file from each block's content directory, avoiding repeats within a session |
maintenance.sh | Runs podget to download new RSS episodes, calls curl_retry_failed_podget.sh for any failures, cleans up old files, calls cleanup_show.sh, then returns to radio_loop.sh |
cleanup_show.sh | Enforces content quotas: daily shows deleted after play (max 5/feed kept); weekly shows moved to backupweekly (max 8/feed kept); backup caps: 25 daily, 50 weekly |
curl_retry_failed_podget.sh | Reads podget's error log, re-fetches failed URLs with a browser User-Agent; handles mp3, m4a, aac, ogg, opus |
current_bt_speaker.sh | Scans for a trusted, connected Bluetooth device and returns its MAC address for the audio routing commands |
systemd service file (radio.service, Type=simple, Restart=always) ensures the whole thing starts on boot and restarts after any crash without manual intervention. 10podget sometimes fails on BBC feeds, particularly when they use generic filenames like media.mp3. The curl_retry mechanism usually catches these, but the author notes that occasional duplicate episodes slip through — "not enough to be annoying in my experience." 4Reproduction difficulty
- BlueALSA configuration. Getting Bluetooth audio output reliably routed through ALSA on Raspberry Pi OS is the most common sticking point in Pi audio projects, and the author is candid about it: "Getting Bluetooth to work is very annoying — I'd consider removing all the bluetooth functionality and outputting to a USB DAC." 4 Budget an hour for this; search "bluealsa raspberry pi" for current community solutions before starting.
- Systemd service setup. Copying a
.servicefile and runningsystemctl enableis straightforward once you've done it once. First-timers should budget 20–30 minutes to understand the unit file syntax. - RSS feed curation. The
podgetconfiguration expects a specific format for feed URLs. Most public podcasts and BBC/RNZ bulletins work without issue, but private or Patreon feeds require token URLs.
Community reception
Build resources
- GitHub repository: trwmato/pi-fm-kitchen-radio — all six scripts, systemd service file, and directory structure documentation
- Hackaday writeup: Custom FM Radio Station Powered By Shell Scripts
- Original Reddit post (includes author Q&A in comments): r/digitalminimalism
- BlueALSA on Raspberry Pi: the
bluealsapackage in the Raspberry Pi OS repos is the current path; search the Raspberry Pi forums for "bluealsa setup" for the most current pairing commands
References
- 1
- 2
- 3Hacker News — I replaced Spotify with a homemade FM radio station
news.ycombinator.com
- 4GitHub — trwmato/pi-fm-kitchen-radio
github.com
- 5
- 6scripts/make_playlist.sh — GitHub
raw.githubusercontent.com
- 7config/playlist_blocks — GitHub
raw.githubusercontent.com
- 8scripts/current_bt_speaker.sh — GitHub
raw.githubusercontent.com
- 9scripts/radio_loop.sh — GitHub
raw.githubusercontent.com
- 10systemd/radio.service — GitHub
raw.githubusercontent.com
- 11HN comment #48448364 — dredmorbius
news.ycombinator.com
- 12HN comment #48448232 — tombert
news.ycombinator.com
- 13HN comment #48447100 — EvanAnderson
news.ycombinator.com
This story was produced automatically by a channel. One sentence is all it takes for Neodrop to keep producing for you.
Related content
- Sign in to comment.
