Skip to content

Releases

v3.4.0 — real https, and dropouts that fall silent

Choose a tag to compare

@halbeshuhn halbeshuhn released this 22 Aug 11:16

https stations play. Really play.

Not rewritten to http and hoped for. Not skipped. A real TLS handshake, a
real encrypted stream.

The previous release did what every Cardputer radio does: it took an https://
address, changed it to http://, and tried its luck. That works for most German
stations and for very few Swiss ones. Everything the directory lists as
https-only was simply unreachable — the entire SRG family among them. Radio SRF 3
plays now. In AAC+.

Fitting TLS into a device with no PSRAM was the whole job:

free heap largest block
at boot 250,260 196,596
running, http station 129–134 KB 86,004
with the TLS context held open 96–101 KB 55,284
running, https station 13.8–28.5 KB 7,668–9,716

The lowest free heap seen during an https stream was 5,480 bytes. Nothing
crashed. Nothing is left over either.

Three things got it there: rebuilt ESP-IDF libraries with an asymmetric mbedTLS
buffer (IN 16384, OUT 2048 — measured across 44 real https stations, none of
which ever sent more than 1,568 bytes outbound), one TLS context opened at boot
and never closed, and a patch to the audio library, which opens its own HTTP
client internally and had no way to be handed a TLS one.

A dropout is silence now, not a rattle

You know the sound: the network stutters and the speaker starts rattling — the
same fragment of audio, over and over, until it recovers.

It was never the decoder. The I2S output repeats its last DMA block when nobody
feeds it — 26 ms in a loop — and nobody had ever told it to fall silent instead.
One flag in the channel configuration, left at its default since forever.

On top of that the decoder now pauses below 8 KB of buffer instead of eating
every byte the moment it arrives, and resumes at 24 KB with a real head start.
Before, a single dropout threw the whole 56 KB ring away and the radio spent the
next seconds in a loop of fetch a scrap, play it, run dry.

That second half is Heotsan's idea, described
in issue #3. Thank
you — it was the piece I had looked past twice.

If nothing comes back for eight seconds the footer says Stream stopped - no data and the radio reconnects on its own.

The volume control finally behaves

1.9 dB per press, from the quietest step to full.

It used to be wildly lopsided — measured, not guessed: the first audible press
changed the level by 6.02 dB, the last one by 0.42 dB, and four presses out of
twenty-six did nothing at all
.

The cause sat in the audio library, which keeps its gain in a single uint8_t:
64 steps between silence and full, spaced evenly in amplitude while the ear
hears in decibels. Below −36 dB there was nothing but silence, and the last step
before it was a 6 dB cliff. No curve fits into that. The radio no longer uses it
and scales the samples itself.

Everything else

  • Charge Mode — a screen for a device on a charger. Stream, watchdog and the
    Wi-Fi radio go off, the clock drops to 80 MHz, the charge level stands in
    large digits, and the display goes dark after five seconds.
  • Keyboard shortcuts v i e w l o s for the meters, info screen, equalizer,
    Wi-Fi, local list, online directory and sleep timer.
  • No flicker. Menus and overlays used to clear their area and then redraw it;
    the panel has no back buffer, so that gap was visible. Everything paints opaque
    over its own background now.
  • Long names scroll — station name, stream title, and the full stream address
    on the info screen, with scheme and path.
  • No thump at station start. 500 ms of silence, then a two-second fade-in.
  • The pre-buffer is 56 KB, requested first and filled before the first sample
    is played.
  • An escape hatch for streams the decoder cannot read. An AAC stream that
    reached the MP3 decoder used to lock the device up completely.
  • The battery shows a number, and the system menu scrolls by the pixel.

Fixed: logo downloads waited out their full eight-second deadline every single
time (the data was there after 75 ms), .ico logos were downloaded and then
thrown away, and VU meters drew into open lists.

Smaller, despite all of it

v3.3.0 v3.4.0
program 2,919,043 2,892,091
global variables 102,164 90,960
free under M5Launcher 29 KB 56 KB

Before you build it yourself

Flashing the images below needs nothing special. Compiling the source now needs
four patches to ESP8266Audio and rebuilt ESP-IDF libraries
, all documented with
before/after copies in
v3.4.0/library-patch/.

Only one of them is enforced by the compiler. Skip the ESP-IDF rebuild and your
build will run, play http stations, and fail every https handshake — the largest
free block will not be big enough. That is the one to read first.

Honest limits

HLS stations are recognised and skipped rather than played as noise. AAC+ plays
its base layer, because SBR wants memory this device does not have. The output
is mono — the Cardputer Adv has a mono codec, at the speaker and at the
headphone jack alike. Arabic, Hebrew and Thai need a text engine that is not
there.

Two things are measured and still unexplained, and are written down rather than
glossed over: why an https open() sometimes takes 43 KB instead of the expected
19–21, and why the memory ceiling drops by about 24 KB once per session and
stays down.


M5Launcher or SD card: Cardputer-WebRadio_v3.4.0.bin
M5Burner or a web flasher: Cardputer-WebRadio_v3.4.0_merged.bin, written to 0x0

v3.3.0 — station logos, a sleep timer, and 59 KB that were being wasted

Choose a tag to compare

@halbeshuhn halbeshuhn released this 17 Aug 12:37

Station logos, a sleep timer, and 59 KB that were being wasted

The radio now shows the station's logo. That sounds like decoration, and it
started as one — but it only became possible after something else was fixed, and
that fix is the real content of this release.

SBR was being compiled into the AAC decoder and never used. SBR is the “+”
in AAC+; this device has played the base layer only since day one, because SBR's
own state wants another 50 KB that do not exist here. Compiling it in anyway
cost 59 KB of decoder workspace for nothing. Measured with the device's own
toolchain: 89,428 bytes with it, 26,352 without.

Taking it out changed the machine underneath. During playback there are now
72 to 104 KB of free heap with 59 to 78 KB in one contiguous piece, where it
used to be 20 to 30 KB free and 8 to 18 KB in one piece. That is what pays for
the PNG decoder, the TLS connection that fetches a logo, and the room to keep it
on the SD card.

It needs a one-time patch to the audio library — three lines in two files plus
one condition in a third. It is in library-patch/, and the sketch refuses to
compile
without it, so you cannot get it wrong quietly.

What else is new

  • Station logos. Fetched once, kept in /logos on the card, shown on the
    new info screen. Fetching happens while the sound is off — the one moment the
    heap is clean.
  • An info screen. F past the spectrum analyzer: codec, bitrate, sample
    rate, channels, buffer fill, largest free block, and the host the audio really
    comes from — the resolved one, not the one in your list.
  • A sleep timer. Sleeptime in the menu, HH:MM up to twelve hours, digit
    by digit. The remaining time counts down in the footer in yellow; at zero the
    stream stops and the screen goes dark. Any key wakes it, restores the
    brightness and reconnects.
  • Playlists are resolved instead of refused. .pls and plain .m3u carry
    the real stream address, so the radio reads it and connects. HLS keeps its own
    message — a segment list has no stream address in it to find.
  • https stations play, by rewriting the address to http and trying. Of the
    80 most-clicked German stations the directory files as https, 80 of 80
    serve the same audio over plain http.

Fixed

  • Wi-Fi failed for some users under M5Launcher. Three reports, same cause:
    M5Launcher up to 2.8.0 lays out a smaller shared NVS area than usual
    (0x4000 instead of 0x5000), and the Wi-Fi driver writes its configuration
    there while connecting. The radio now calls WiFi.persistent(false) before
    its first Wi-Fi call — the driver keeps its configuration in RAM and never
    touches that area. Credentials are ours to store, and we store them ourselves.
  • A sixth failure message, for the case where the Wi-Fi driver refuses to
    start at all. It used to wait 15 seconds and then blame the network.
  • Stations whose address redirects to https did not play. Reported for NTS
    Radio: a plain-http request is answered with a redirect to an https host,
    which redirects again to a plain-http node that serves the audio. The address
    was never the problem, the redirect was. Those are now rewritten and followed.
  • The first play of a new station could show a false https not work, with
    a short break in the sound a few seconds later. The stream watchdog's
    five-second clock was started before the logo was fetched, so a slow logo
    download used up the budget meant for connecting. The clock now starts after
    the logo is in.
  • Quoted playlist entries (File1="http://…") were rejected. Found by
    testing real stations, not by reading code.

Still not there: real https

The radio does not speak TLS for audio, and this release does not change
that.
It side-steps it, and where a station truly insists on TLS it stays
unplayable.

It is worth saying why, because the memory situation did improve and the
obvious next thought is “so do TLS now”. An open TLS connection costs roughly
36 to 40 KB permanently, with a peak near 50 KB during the handshake. The logo
fetch already uses TLS out of the same pot — that is why it refuses to start
below 56 KB in one contiguous block — and the PNG decoder behind it needs 32,768
bytes in one piece. The audio pre-buffer, the decoder workspace and the I2S DMA
buffers are reserved statically on purpose, so they cannot depend on what the
heap happens to look like. Add a permanent 40 KB for a stream connection and the
largest free block drops below what the rest needs: the stream would play while
everything around it began failing quietly.

On top of that the audio library opens its own HTTP client internally and does
not accept a TLS one, so it would take a second library patch.

So it stays on the list. If real TLS streaming lands, it will be because those
numbers were measured again and worked out — not because it sounded achievable.

Size

Sketch:          2,919,043 bytes (92% of 3,145,728)
Global variables:  102,164 bytes (31% of 327,680)

Global variables are down 59 KB against 3.2.0 — that is the SBR workspace.
The M5Launcher gives an application 2880 KB and this build is 2851 KB, so there
are 29 KB left. Anything added from here has to fit in that.

Thanks

To everyone in r/CardPuter who reported
something instead of shrugging. Four of the five fixes above came from there.

v3.2.0 — The world, searchable

Choose a tag to compare

@halbeshuhn halbeshuhn released this 13 Aug 18:44

This is the release that makes the world findable from the device.

The point

Until now this radio knew 42 countries, and finding a station meant paging
through a list. Germany alone has 2,678 of them — 244 pages. A listener in
Taiwan opened issue #1 because his country was not in the list at all, and
because everything he did find was drawn as a row of question marks.

Both are fixed, and the fix went further than the report.

What's new

  • 240 countries — every country radio-browser.info knows a station in,
    instead of the 42 with the most stations.
  • A search field for countries. Type, and the list narrows; three letters
    are usually enough. It appears at the bottom of the screen, and BACKSPACE
    only ever deletes a character — you cannot fall out of the list by holding it
    down.
  • A search field for stations, invisible until you type and gone again when
    the last character is deleted. Its space stays reserved, so the list does not
    jump. The search runs on the server: only eleven names fit in this device at
    a time.
  • Foreign scripts are displayed. Chinese, Japanese, Korean, Greek and
    Cyrillic station names and stream titles now arrive as UTF-8 and are drawn
    with the matching font from M5GFX. Numeric HTML references — some stations
    send 百 instead of the character — are resolved.
  • A 5-band equalizer, ±6 dB at 100 Hz, 350 Hz, 1 kHz, 3.5 kHz and 10 kHz,
    in 1 dB steps, stored on the device and active from the next start. Measured
    before it was built: the filter arithmetic costs nothing the audio buffer
    notices, at 320 kbit/s the buffer stays at 99 %.
  • Playlists are recognised. A station that serves HLS or .m3u says
    Playlist - not supported in the footer instead of feeding the decoder text.
    HLS stations are skipped while the list is read — in Taiwan that is 18 % of
    them, and five of the first eleven.
  • Both channels are averaged before the output. The Cardputer Adv has a
    mono codec; until now it silently took the left channel, and anything a
    station put hard right was lost.

What is gone

  • The regional subdivision. The directory leaves the state empty for 1,443
    of 2,678 German stations and spells the rest inconsistently — "Bayern" next
    to "Bavaria". HR1 was missing from Hessen because of it. A filter that hides
    more than it shows is worse than none.
  • The duplicate check. Same name does not mean same station: 228 of 346
    same-named German entries have different addresses — "Sunshine Live" three
    times with different bitrates, "Rock Antenne" four times. Dropping them by
    name would have thrown away the working one and kept the dead address.

Fixed

  • A crash when opening the system menu while an online station was playing.
    The menu could assemble seven entries into an array of six; the seventh
    landed on the audio object's vtable pointer, and the next call into the
    decoder jumped into nothing. It has been there from the beginning and needed
    two rare conditions at once to show.
  • A fine crackle, audible on headphones, that had nothing to do with
    clipping: the library offers the same sample again when it did not fit into
    the I2S buffer, and the equalizer ran over it a second time.

Known limits

No https. HLS is recognised, not played. AAC+ plays its base layer, because SBR
needs PSRAM this device does not have. The output is mono, at the speaker and
at the 3.5 mm jack alike — that is the codec, not the software. Arabic, Hebrew
and Thai need a text engine that is not there; station names in a legacy
encoding such as Big5 are discarded rather than drawn as boxes.

Flashing

File Use with
Cardputer-WebRadio_v3.2.0.bin SD card / M5Launcher — application only
Cardputer-WebRadio_v3.2.0_merged.bin M5Burner or a web flasher — complete image, write to address 0x0

Board settings are unchanged: Huge APP partition scheme, PSRAM disabled.
The application is now 2.91 MB of the 2880 KB the M5Launcher provides — 29 KB
to spare. Most of the growth is the CJK fonts, which live in flash and cost no
RAM.

v3.1.2 — A new audio layer, and the reboots are gone

Choose a tag to compare

@halbeshuhn halbeshuhn released this 11 Aug 18:33

This is the release that stops the reboots.

What was wrong

Playing a stream took the device down every few minutes. Four crashes, four
different places, one picture: the lwIP connection structures held garbage.
A corrupted free list inside tlsf_malloc. A receive window of impossible
size. A netconn in a state its own close routine rejects. A TCP socket that
dispatched into the UDP branch because its type field no longer said TCP.

None of them was a shortage of memory — the heap was healthy when the last two
happened. Something was writing over memory it did not own, and the network
stack, which allocates constantly, was simply the first to walk into the
damage.

What's new

  • The audio layer runs on ESP8266Audio.
    The sketch still speaks the old interface; AudioCompat.h maps it onto the
    new library. Going back is one include line. Measured after the change: two
    hours of playback and 193 station changes across MP3, AAC and AAC+ with
    no restart, no crash, and no memory lost — free heap at the end was in the
    same band as at the start.
  • Both library patches are obsolete. No more mbedTLS workaround for https
    redirects, no more swapping the AAC decoder back to Helix. ESP8266Audio
    brings its own Helix decoders and never attempts https. If you built earlier
    versions, you can stop reapplying patches after every library update.
  • AAC and AAC+ have a fixed 90,048-byte field instead of asking the heap
    for a contiguous block. Low RAM - codec too big cannot happen any more,
    and it is gone from the code.
  • The bitrate in the footer is measured, not reported. Stations that send
    no icy-br header used to leave it at zero, which made the radio believe
    the stream had failed. It now counts the bytes actually arriving; measured
    against fourteen test streams it lands within two percent.
  • The stream title moved. It used to be a second header line that scrolled
    past once. It now stands still below the red rule, in the empty display
    where there is room for it — reachable with F, the same key that cycles
    the VU meter and the spectrum. Set in Noto Sans at 14 px, wrapped at word
    boundaries, with umlauts and accents. Drawing waits while the input
    buffer is low: the sound has priority over the display.
  • VU meter and spectrum moved up into the space the header gave back, same
    size, re-centred. They now read the decoder's own level, so they no longer
    drop when you turn the volume down.
  • A lost stream comes back by itself. Until now the radio simply went
    silent: no message, no second attempt, the footer still showing the old
    bitrate. It now notices after eight seconds and retries at 5, 10, 20 and
    then every 40 seconds.
  • Wi-Fi reconnects on its own. When the access point disappears the driver
    gives up after one attempt, even with auto-reconnect switched on. A keeper
    now tries again every 15 seconds without blocking playback.

Known limits

AAC+ works, but the decoder field is sized for it — 90,048 bytes are reserved
whether you play AAC or not. That is the price of never failing for want of a
contiguous block on a device with no PSRAM.

The pre-flight request that reads Content-Type costs one extra connection
per station change. It is what makes codec detection reliable instead of
guessed.

https is still not supported, and for the same reason as before: mbedTLS wants
about 32 KB that this device does not have.

Flashing

File Use with
Cardputer-WebRadio_v3.1.2.bin SD card / M5Launcher — application only
Cardputer-WebRadio_v3.1.2_merged.bin M5Burner or a web flasher — complete image, write to address 0x0

Building from source now needs only
ESP8266Audio alongside the
M5 libraries. Board settings are unchanged: Huge APP partition scheme,
PSRAM disabled.

v3.1.1 — Wi-Fi failures are diagnosed, not guessed

Choose a tag to compare

@halbeshuhn halbeshuhn released this 09 Aug 23:24

A small release, and it is about one thing: the radio no longer guesses why
your Wi-Fi did not connect.

The message that sent people the wrong way

Until now, every failed connection ended in "Saved password no longer
matches"
. The radio had no way to tell a wrong password from a network that
was out of range, a router that never handed out an address, or a connection
that simply timed out — so it named the only cause it knew.

That wording cost someone a real evening. A user upgrading from an older
version could not get online, believed the leftover files on his SD card were
to blame, deleted his program and his station list, and then began to suspect
his whole device. None of it was the password.

The driver does know the reason, but it says so exactly once, in the
disconnect event, and WiFi.status() never carries it. The radio now catches
that event and tells you which of these it was:

On screen What it means
Wrong password for this network authentication or the key exchange failed
Network not in range the access point was not there to answer
No IP address from the router you got in, but no address came back — DHCP
No answer from the network nothing happened at all within the timeout

And it only asks you to retype the password when the password is plausibly
the problem. Being asked for a password after a DHCP failure taught you
nothing and pointed at the wrong thing.

Two defects that turned up while building it

Every password was accepted. Success was measured with WiFi.status(),
which still reports WL_CONNECTED from the running connection when you start
an attempt from the system menu — the radio is playing, after all. The check
saw that in its first pass and reported success before the attempt had begun.
A deliberately wrong password went through without a murmur. Success is now
taken from this attempt's own GOT_IP event, the driver's auto-reconnect is
held off while the attempt runs, and an existing connection is dropped and
awaited first.

A typo was saved before it was tried. Credentials went into NVS ahead of
the connection, so a mistyped password stayed in the list and the radio
quietly retried it at every start. They are now written after the connection
stands; a failed attempt leaves an existing entry untouched.

The rest

  • Timeout is 15 s with a second attempt, instead of a single 9 s one. Slow
    DHCP, WPA2/WPA3 transition networks and repeaters need more than nine
    seconds more often than you would think.
  • Channel and BSSID from the scan are passed to WiFi.begin(), so the first
    attempt skips the driver's own hunt across all channels.
  • disconnect(true, true) is gone everywhere except the explicit reset. The
    second flag cleared the driver's stored network in nvs.net80211 — a
    region every program on the device shares, not just this one.
  • The window for the BtnG0 Wi-Fi reset at power-on is 2.5 s instead of 1 s.
    One second was not enough to read the prompt and act on it.

Carried over from the working copy: the footer now names the codec next to
the bitrate (AAC+ 96kBit), and the old L station list has been removed —
if you used L, that key does nothing now. It drew over the header and
the scale, and BtnG0 → Local list has done the same job properly since
3.0.0.

Flashing

File Use with
Cardputer-WebRadio_v3.1.1.bin SD card / M5Launcher — application only
Cardputer-WebRadio_v3.1.1_merged.bin M5Burner or a web flasher — complete image, write to address 0x0

Building from source still requires the library patches in
library-patch/ —
the https one and the AAC decoder swap. Without the second, AAC streams
reboot the device. The prebuilt images above already have both.

Nothing changed about the known limits: no https, AAC+ plays its base layer
only, and the Cardputer Adv has no PSRAM. They are explained with the measured
numbers in the README.

v3.1.0 — AAC works, and it was never the hardware

Choose a tag to compare

@halbeshuhn halbeshuhn released this 09 Aug 14:57

AAC and AAC+ play now. The cause was never the Cardputer.

ESP32-audioI2S used the Helix AAC decoder up to version 3.0.0 and switched to
faad2 afterwards. faad2's channel_pair_element() asks for 22,006 bytes
in one piece, per stereo frame — about 43 times a second — and checks 2 of
its 71 allocations for NULL. Without PSRAM that block is not reliably there, so
every AAC station died on its first frame, 64 kbit/s exactly like 192.

Putting the Helix decoder back fixes it. It allocates 20.6 KB once when the
station opens and keeps it. Fourteen test streams — MP3 64–320, AAC 64–192,
AAC+ 32–96 — then played with zero crashes and nothing audible missing. The
build also shrinks by 62 KB.

The swap is a change to the library, not to the sketch, and it is written up in
library-patch/aac-helix-swap.md.
A library update overwrites it, and AAC will start rebooting the device
again until it is redone.

What follows from it

  • The online directory no longer filters AAC away. That filter was hiding
    42% of the stations in Australia, 33% in Poland, 13% in Germany.
  • "128 kbit/s or lower is the sweet spot" is withdrawn. Measured twice
    over: every MP3 stage up to 320 kbit/s played cleanly, no warning from the
    library, nothing audible. Bitrate was never the limit — the decoder was.
  • A decoder that runs out of memory now says so. The footer shows
    Low RAM - codec too big instead of the device rebooting.
  • station_list_test.txt ships with the fourteen streams used for all of this,
    named 128-mp3, 64-aac, 48-aacp and so on, each verified by measuring
    its real data rate rather than trusting a directory entry.

The one catch

No SBR — the "+" in AAC+. The decoder compiles it in only when
BOARD_HAS_PSRAM is defined, because its state costs another 50,788 bytes.
AAC+ therefore plays its base layer only: 22,050 Hz instead of 44,100, and mono
instead of stereo where parametric stereo is used. It is stable, it just sounds
duller. Plain AAC-LC is unaffected.

https is still out, for the unchanged reason: mbedTLS wants about 32 KB.

Flashing

File Flash to
Cardputer-WebRadio_v3.1.0.bin SD card / M5Launcher — application only
Cardputer-WebRadio_v3.1.0_merged.bin address 0x0 with M5Burner or a web flasher

Building from source needs both library patches, the https one and the AAC
decoder swap.

Credits

Thanks to DrRhesus, whose
radio plays AAC on the same board without PSRAM. Comparing against it is what
pointed at the decoder rather than the hardware. And to
schreibfaul1, whose
library does the work — including the 3.0.0 decoder that makes this release
possible.

v3.0.1 — A new battery, and a way to see the screen without flashing

Choose a tag to compare

@halbeshuhn halbeshuhn released this 09 Aug 12:49

A small release for the radio itself, and a new way of working on it.

On the device

The battery symbol is redrawn in the shape of the one on an iPhone: a rounded
case with the terminal beside it, filling from the left. It takes its two
colours from the volume scale above it — the dim blue for the frame, the
bright one for the charge — and its fill occupies exactly the same rows as the
scale's lit dots, so the top of the screen reads as one band instead of two
unrelated widgets. The low-battery red is gone with it; the scale has no red.

Both tones now live in UI_SCALE_ON and UI_SCALE_GRID, used by the scale and
the icon alike, so they cannot drift apart. And the dB label on the VU scale
had a lowercase b.

That is the whole functional change. Audio, station handling, Wi-Fi and the
memory situation are untouched from v3.0.0.

Off the device

tools/screen_main.py and tools/screen_menu.py render the radio's screens to
a PNG on your computer. Not a mock-up: they read the coordinates out of the
sketch's #define lines, the labels out of the drawString() calls and
Lang.h, the country names out of rbCountries[], and the fonts out of
NokiaFC.h and M5GFX's own glcdfont.h, with the typesetting rules taken from
GFXfont::drawChar. Colours go through RGB565 exactly as the panel does.

So a layout change can be looked at in ten seconds instead of a compile-flash
cycle, and it stays honest — change a #define and the picture follows.
tools/measure.py reports which colour occupies which area, for when the eye
disagrees with the pixels.

The battery symbol above was designed entirely this way and needed no
correction once it reached the device. Every screenshot in the README is
rendered by these tools rather than photographed.

Repository layout

Each release now lives in its own folder — v3.0.1/, v3.0.0/ — complete with
its source, firmware, images and README. Nothing is overwritten when the next
version arrives.

Flashing

File Flash to
Cardputer-WebRadio_v3.0.1.bin SD card / M5Launcher — application only
Cardputer-WebRadio_v3.0.1_merged.bin address 0x0 with M5Burner or a web flasher — complete image

The library patch is unchanged and still required when building from source;
see v3.0.1/library-patch/.

Known limitations

Unchanged from v3.0.0, and all three follow from the Cardputer Adv having no
PSRAM: no https streams, no AAC, and 128 kbit/s or lower is the sweet spot.
The README explains each one with measurements.

v3.0.0 — The station directory moves onto the device

Choose a tag to compare

@halbeshuhn halbeshuhn released this 08 Aug 18:22

The station directory now lives on the device.

Until now, adding a station to a Cardputer radio meant powering it down, pulling
the microSD card, finding a card reader, searching the web for a stream URL that
actually works, and editing a text file. This release removes that entirely.

Press BtnG0 → Online stations and you are browsing
radio-browser.info: 42 countries, with Germany
and the United States broken down by region. ENTER plays a station right away.
Save station writes it into your own list on the SD card — written by the
device, no cable involved.

The same goes for Wi-Fi: scan, pick, type the password. Up to five networks are
remembered and can be replaced or deleted from the menu. No recompiling to move
to a different network.

What else is new in this version

  • Local station list fully editable on the device — play, add, delete
  • Real visualizations: VU meters from the decoder's own level data, and a
    spectrum analyzer running a 512-point radix-2 FFT with a Hann window over the
    actual PCM samples. The code this grew from filled the display with random()
  • German and English, switchable at runtime, remembered across restarts
  • Nokia pixel font in two sizes, generated from the original TTF
  • Stream monitoring — a stream that connects but delivers nothing says so
    instead of failing silently
  • Paging with ← / → on every level of the browser

Flashing

File Use with
Cardputer-WebRadio_v3.0.0.bin SD card / M5Launcher — application only
Cardputer-WebRadio_v3.0.0_merged.bin M5Burner or a web flasher — complete image, write to address 0x0

On first start the device scans for Wi-Fi and asks for a password. The interface
comes up in English; switch under BtnG0 → Language.

Building from source: the library patch is required

ESP32-audioI2S must be patched before you compile, or the device can crash and
lose its stored Wi-Fi credentials. Some stations are listed as http but
redirect to https when you press play; the library then brings up mbedTLS,
which without PSRAM wants about 32 KB on a device that has 13 to 25 KB free.
Measured on hardware, free heap dropped to 996 bytes.

Four lines, in library-patch/ with the full reasoning and the measurements.
Devices with PSRAM are unaffected.

Known limitations

The Cardputer Adv has no PSRAM, and that shapes everything:

  • https streams do not play. The station directory is filtered accordingly
  • AAC does not play — the AAC decoder needs more memory than exists here.
    The directory is filtered to MP3. Note that a station's name is no guide:
    "ABC News Radio MP3" is served as AAC+
  • 128 kbit/s or lower is the sweet spot. The input buffer holds 0.87 s at
    128 kbit/s but only 0.44 s at 256 kbit/s — not enough to ride out network
    jitter

All three are explained with measurements in the README.

Credits

Built on WuSiU/WebRadio_WuSiU_Cardputer_Adv,
which is based on cyberwisk/M5Cardputer_WebRadio.
Audio by schreibfaul1/ESP32-audioI2S.
Station data from radio-browser.info, free and
without registration.