Nine Polish stations
News, music and Polish affairs over Wi-Fi — on your Bluetooth speaker or the built-in one. You switch it on and it plays; the cube worries about the rest.
My family and friends abroad like having Polish radio on in the background. Until now that meant paid apps or pricey dedicated receivers, so I built a cube that plays nine Polish radio stations over Wi-Fi and Bluetooth. There's also a white, pink and brown noise generator: it soothes, calms, and needs no internet. The system image is free — you can build your own.
yoRadio is a great DIY workhorse: you pick the display and the DAC, solder, compile in the Arduino IDE and play over a wire. Respect. We're building a new platform from scratch with a different philosophy: Bluetooth instead of wires, mobility, convenience and lightness.
And if tinkering is your love language: yoRadio has 65,000 station slots, MQTT and Home Assistant — and it's great at that. We're building the radio for your mom. Or maybe a new habit for any age: a small cube that gives your day a backdrop — music or noise — so living and working simply feel better.
Polish stations by day. A noise generator that calms the house by night. One cube.
News, music and Polish affairs over Wi-Fi — on your Bluetooth speaker or the built-in one. You switch it on and it plays; the cube worries about the rest.
One tap turns the radio into a calming machine: baby sleep, naps, meditation, deep-work focus. The noise is generated inside the cube, so it works with no internet at all — in a hotel, a car, a plane.
Millions of Poles live abroad. This cube costs about as much as two pizzas and gives you something no platform sells: Polish mornings, Polish news and Polish music in Dublin, Chicago or Oslo.
No subscriptions, no account, no terms of service that change tomorrow. There is no OTA and no cloud, so there is no button anyone could press to remotely silence your radio.
Hotel Wi-Fi is enough for the radio, and the noise plays with no network at all. Up to two hours on the built-in battery, any power bank after that.
The station streams are public and the noise is born inside the cube. There is nothing to pay monthly.
The cube is not a service. There is no server that can die and no company that can change its mind. It's yours — like a radio on a shelf, except it plays from the internet.
Open Radio started from one premise: buy ready-made all-in-one hardware and write the entire system for it. We froze the feature set on purpose.
Nine of Poland's biggest broadcasters — from Antyradio's rock to the club mixes on RMF MAXX Club. The cube plays their official, public MP3 streams straight from the broadcaster: no retransmission, no recording, no middlemen. As far as the station can tell, it's just one more tick on the listener counter, only in a nicer box. The links go to the official sites.
Because it's the only path to €50 without soldering. Screen, touch, speaker, battery, enclosure and both radios are all included — you pull it out of the box and flash it.
You pay for it in one currency: Wi-Fi and Bluetooth share a single 2.4 GHz radio and have to time-share it. Most of the entries in the log below trace straight back to that one purchasing decision.
The Core2 is one piece of a bigger M5Stack ecosystem: display-equipped Core cubes, smaller Atom and StickC units, and dozens of stackable modules — all built on ESP32, all with public documentation. We didn't add anything to this project: one cube, straight out of the box.




Selected entries from the engineering log. Every number comes from telemetry dumped off the device during multi-minute test runs.
Wi-Fi and Bluetooth share one 2.4 GHz radio on the ESP32 and time-slice the same antenna. When A2DP is transmitting, the TCP window (~5.7 KB — an Arduino LWIP compile-time constant) multiplied by an RTT stretched to 350–500 ms gives you 12–14 KB/s. A 128 kbps MP3 stream needs 16. The deficit isn't a bug, it's arithmetic.
Classic forum advice for speeding up Wi-Fi on the ESP32. With Bluetooth active, the controller hits abort() and the cube boot-loops every 26 seconds. Modem-sleep stays; the coex layer requires it.
The CDN hands over the first few seconds of a stream in a fast burst, then settles into exactly 1× realtime. Any buffer threshold set above the equilibrium point will never be reached — the radio waits forever to "finish buffering." Every reserve threshold had to come down below it.
Tripling the decoder's work per loop pass didn't add a single audio frame (the deficit lives in the network), but it stretched the pass to 1.6 s and killed touch response. Under a network deficit, the decoder budget only governs UI responsiveness. We set it back to 8192 frames per pass.
The stream's death paths never cleared the active endpoint, so "candidate rotation" had been reopening the same dead URL over and over. Symptom: 81 restarts in 3 minutes. The fix came down to clearing the endpoint in three places.
A TLS handshake needs ~40 KB of free DRAM. With Bluetooth playing, only 25–30 KB is left, so switching to a station that needs HTTPS discovery mid-playback is physically impossible. The fix: pinned plain-HTTP fallbacks per station, plus a cache of the last source that worked.
After a power cycle, the speaker doesn't politely wait for a scan — it calls the cube itself. And the cube was rejecting strangers. A standoff. Fix: tapping "scan" opens a window where any matching incoming speaker gets adopted and remembered.
Two meters from the access point, the audio drops out in waves; one room over, it plays flawlessly. The router's Wi-Fi transmitter is drowning out the cube's Bluetooth receiver. Sometimes the best firmware fix is moving the device three meters.
The design rule: plug in + power = it plays. The radio has to survive every failure on its own, no phone and no human required.
The system is built entirely by a pair of AI agents working on the same machine, cross-auditing each other's work. The human is left with the decisions and the ears.
Architecture, firmware, a pixel-parity GUI simulator (the browser draws the exact same RGB565 pixels as the device), bilingual documentation.
Crisis takeovers: audio path physics, rotation and TLS hotfixes, QC gates, cross-auditing the other engine's reports.
Sets the product boundaries, listens to the results live, and stops any flash that hasn't passed the test protocol. The only part of the system that can hear.
Telemetry hands down the PASS/FAIL verdict. Every firmware candidate runs a multi-minute logged test: starvation Δ0 · watchdog Δ0 · injected silence 0.0 s / 300 s · queues full. Only then does the human listen.
The repo holds over 100 numbered engineering documents, each in Polish and English. Yes, for a radio.
Some of them, anyway.
My wife likes seven of them. The other two added themselves by having a stable stream. Engineering can be simple. ;)
Because the router shouts Wi-Fi straight into the cube's Bluetooth ear. We measured it. Three meters of distance beat many a firmware fix, see LOG-08.
They do, some of them paid. Except a phone rings, notifies, and leaves the kitchen with you. The cube stays and plays.
About €50 in hardware. Plus 2.75 billion AI tokens that we are not converting to euros, because this was supposed to be a hobby.
We flipped the usual release order on purpose: instead of dropping raw source first, we're shipping a ready-to-flash image first — one esptool command and the cube plays. The code goes up on GitHub once it's cleaned up, has survived long-run testing, and the licensing gates are closed.
Declared licenses: GPL-3.0-or-later (code) · CC BY-SA 4.0 (documentation) · CC0 (station catalog) · no retransmission or recording of broadcasts · station logos only with broadcaster consent