FusionEdge is an ESP32-S3 internet radio firmware built on the yoRadio codebase. It uses LovyanGFX and LittleFS and adds a 480x320 touch-oriented interface, spectrum and waveform visualizations, optional Bluetooth audio input, SD and DLNA playback, WebUI control, encoders, weather information and theme support.
The project grew from the LovyanGFX/LittleFS-based VTomRadio variant. FusionEdge keeps the original yoRadio playback architecture while substantially extending the display, controls, audio visualization and source handling.
FusionEdge is now feature-complete. Development focuses on confirmed bug fixes, hardware compatibility and selected community contributions.
- Prevents playlist touch scrolling from changing the volume when the gesture begins on the player screen
- Refines the full-screen cover layout, including centered clock/weather content, safe nameday wrapping and artifact-free transparent text rendering
- Uses attributed and HTTP-cached Last.fm metadata while retrieving artwork exclusively from Cover Art Archive via MusicBrainz release identifiers
- Corrects IR previous/next and volume-button navigation direction in the channel list, including held-button repeats
- Clears the RGB LED strip before every deep-sleep path so the final animation frame cannot remain illuminated
The artifact-free cover-screensaver text rendering in this release was contributed by Volodimyr Zagranichniy. Thank you for the continued testing and careful improvements.
- Adds an optional full-screen album-cover screensaver with current track, time, date, nameday and weather information
- Improves multilingual Last.fm matching by normalizing punctuation, Unicode dashes and quotes, Cyrillic case and trailing technical IDs, and by matching any semicolon-separated artist together with the track title
- Raises the accepted cover-image size to 512 KB while retaining PSRAM-backed storage and cooperative network handling
- Adds configurable backlight PWM frequency and Wi-Fi TX power defaults to reduce audio interference and supply-current peaks on sensitive hardware
- Adds optional
BTN_MODEandBTN_RGBcontrols for source switching, LED strip enable/disable and effect selection - Keeps RGB VU animation active during playback screensavers, including a dedicated Bluetooth VU source, and reserves the idle animation for silence
- Makes WEB/SD/DLNA and Bluetooth transitions safer by coordinating decoder, I2S bridge and remote-source pause state
- Corrects FLAC sample-buffer reuse, restores the gen4-ESP32-35CT landscape orientation and synchronizes the supported hardware configuration examples
The full-screen cover screensaver and most non-Bluetooth improvements in this release were contributed or proposed by Volodimyr Zagranichniy. Thank you for the careful development, testing and continued refinement.
- Applies the configurable clock-screensaver brightness to both idle playback states and the While playing clock when the cassette screensaver is not compiled in
- Coordinates clock brightness, ambient-light control and automatic fading;
with
LIGHT_SENSORenabled, the clock setting scales the sensor-controlled brightness instead of replacing it - Refreshes the current station or album-cover image immediately after leaving the clock screensaver
- Improves Last.fm fallback matching by checking more search results while retaining strict artist priority and guarded title-only matching
- Scott Barber's independent community MOD has moved to its own repository, where it can be maintained separately from the main firmware
- Added a configurable
0-100%brightness level for the While not playing clock screensaver - Restores the normal display brightness on wake and keeps the setting compatible with the automatic backlight fade feature
- Migrates existing configurations automatically with a
100%default, so a full flash erase is not required
- Fixed SD playback metadata propagation so embedded artist/title tags can trigger album-cover lookup, with the filename retained as a fallback
- Preserves the last valid SD track across restarts and safely resets the stored index when the card contents no longer contain that track
- Added an experimental classic ESP32-WROVER-E N16R8 compatibility package with target-specific PSRAM allocation, wake-mode and GPIO safeguards
- Kept all WROVER memory changes isolated from the primary ESP32-S3 build
- Stabilized DLNA browser initialization, category loading, playlist import and switching between DLNA and WEB playlists
- Improved HTTPS stream reliability by coordinating CoverArt, DLNA and audio network operations and reducing internal-memory pressure
- Fetches the largest available Last.fm album image directly, with MusicBrainz and Cover Art Archive retained as fallbacks
- Gives stream changes priority over background cover downloads, preventing a new station name from appearing while the previous station keeps playing
- Improved stream-stall and Wi-Fi reconnection recovery while preserving the user's playback intent
- Fixed WebUI file-serving races that could crash
player.htmlor leave the display locked after an interrupted request - Fixed automatic backlight fading, including invalid legacy settings and a zero fade-step value
- Prevented the built-in LED and NeoPixel strip from competing for GPIO48
- Added an optional ESP32-WROVER-E N16R8 PlatformIO configuration template
- Optional current-track album art from Last.fm, MusicBrainz and Cover Art Archive for WEB, SD, DLNA and Bluetooth playback
- A more reliable asynchronous DLNA WebUI browser with configured root-folder support and paged directory listings
- Correct source icons and mode handling when switching between DLNA, WEB and Bluetooth playback
- Serialized, low-priority cover and DLNA network work to preserve audio playback memory and watchdog responsiveness
- ESP32-S3 with PSRAM
- 480x320 ILI9486, ILI9488, ST7796 or AXS15231B display
- LovyanGFX display driver
- XPT2046 resistive or supported I2C capacitive touch controller
- External I2S DAC or amplifier
- Optional QCC5124EL Bluetooth module using UART AT commands and an I2S audio bridge
- Optional SD card, DLNA, MQTT, IR receiver, LED strip and RTC
Hardware pins and optional features are selected in myoptions.h. Bluetooth support is compiled only when USE_BLUETOOTH is defined.
The supplied platformio.ini targets an ESP32-S3 DevKitC-1 N16R8 board using the pioarduino Arduino core.
- Install Visual Studio Code, PlatformIO, or PlatformIO Core.
- Review
myoptions.handmytheme.hfor your hardware. - Copy
data/data/wifi.example.csvtodata/data/wifi.csvand enter your Wi-Fi credentials, or configure Wi-Fi through the radio's access point. - Build and upload the firmware.
- Upload the
datadirectory as a LittleFS filesystem image.
An experimental classic ESP32-WROVER-E N16R8 package is available in
esp32-wrover-n16r8. It contains a matching
platformio.ini, example myoptions.h, installation steps and the known
hardware limitations. The ESP32-S3 remains the recommended target: the WROVER
WebUI can be slow during playback, and HTTPS streams may fail when sufficient
contiguous internal RAM is unavailable for the TLS handshake.
data/data/wifi.csv is intentionally ignored by Git because it contains local
network credentials.
Typical PlatformIO commands:
pio run
pio run --target upload
pio run --target uploadfs
pio device monitor
The serial monitor speed is 460800 baud.
Scott Barber's independent community build is maintained separately in the FusionEdgePlus2 repository. It adds layouts for smaller displays, including ILI9341 support, automatic tape color changes, and selectable screensaver styles including album-cover art. The MOD has its own source tree, configuration and documentation and does not automatically receive changes from the main FusionEdge firmware.
The tested Bluetooth path uses a QCC5124EL module:
-
Select Bundle: HFP and Color: 5124 I2S when ordering; other variants are not the tested FusionEdge configuration
-
UART AT control on the pins configured by
BT_UART_TXandBT_UART_RX -
I2S input on
BT_I2S_BCK,BT_I2S_LRCKandBT_I2S_DATA -
No MCLK connection is required by the tested module configuration
-
Metadata, touch/encoder/WebUI mode switching and spectrum visualization are supported
The working bridge parameters are kept in src/core/bluetooth_config.h.
When USE_DLNA is enabled, set dlnaHost to the media server address and
dlnaIDX to the preferred root object ID in myoptions.h. The DLNA page in the
WebUI opens at that configured root, lists folders and tracks, and can build or
append entries to the playback list.
Directory browsing runs through a background worker instead of the web-server callback. This keeps SOAP requests away from the AsyncTCP task and makes larger or slower media-server libraries considerably more reliable. The WebUI and firmware endpoints are version-dependent, so upload the matching v1.1.1 LittleFS data along with the firmware when upgrading from an earlier release.
Current-track album art can optionally replace the station/source icon in WEB,
SD, DLNA and Bluetooth modes. Create a Last.fm API account, then add the
following to myoptions.h:
#define USE_LASTFM_COVER
#define LASTFM_API_KEY "your_lastfm_api_key"The lookup runs in a low-priority background task and only starts when metadata
contains both an artist and a title in Artist - Title form. FusionEdge uses
Last.fm for metadata matching and an album MusicBrainz ID when available. It
otherwise searches MusicBrainz for a matching release group, then retrieves the
image from Cover Art Archive. Images are kept in PSRAM and are not written to
LittleFS. While a cover is unavailable, downloading or invalid, the normal
station or source icon remains visible.
Last.fm metadata responses use a small PSRAM cache which follows the server's
Cache-Control and Expires instructions. Responses marked no-store or
no-cache are not reused.
Bluetooth metadata receives an additional fallback for video services. If the
QCC artist field contains a channel name and the exact lookup fails, FusionEdge
cleans the video title and resolves it with Last.fm track.search. Local files
still use their exact embedded artist and title metadata first.
Search-result comparison ignores common ASCII and Unicode punctuation and normalizes Cyrillic case. A trailing numeric technical ID after a semicolon is removed before searching, and metadata containing multiple semicolon-separated artists can match when the title and at least one artist agree.
Cover lookup uses plain HTTP and keeps its task stack and JSON allocations in PSRAM. This is intentional: opening another TLS session can consume the contiguous internal RAM required by HTTPS audio streams on the ESP32-S3. Only the Last.fm API key is sent; do not add a Last.fm shared secret to the firmware.
This integration uses metadata from Last.fm and MusicBrainz, and cover art from the Cover Art Archive. The WebUI includes a visible link crediting Last.fm. Review the Last.fm API terms, retain that credit in redistributed builds, and respect the rights of artists and labels when using Cover Art Archive images.
Weather values use metric units by default: Celsius, hPa and km/h. To display
Fahrenheit, inHg and mph, select LANGUAGE EN and define IMPERIALUNIT in
myoptions.h. A build-time check prevents this option from being combined
with locale files that currently contain metric-only weather format strings.
The IMPERIALUNIT weather-unit option was reintroduced by Adam Navrowski.
Define USE_COVER_SCREENSAVER in myoptions.h to use the current album cover
as the non-blank While playing screensaver. It displays the current artist
and track alongside the time, date, nameday and weather. When no downloaded
cover is available, the normal station or source image is used instead.
This option currently requires a 480 x 320 display and PSRAM. It cannot be
enabled together with USE_CASSETTE_SCREENSAVER; select one of the two styles
at build time. The album-cover screensaver was created and contributed by
Volodimyr Zagranichniy.
Define USE_CASSETTE_SCREENSAVER in myoptions.h to enable the animated
vintage cassette. In the WebUI, enable the While playing screensaver, set
its timeout in seconds, and leave its Blank screen option disabled. During
playback the cassette shows the current artist and track title; without stream
metadata it falls back to the station name and a PLAYING label. The lower
label displays the available codec, bitrate, sample-rate and bit-depth
information. The normal idle screensaver remains the large clock, while
Blank screen continues to switch the display off.
The WebUI Clock brightness (%) setting controls the large-clock
screensaver brightness. It always applies to While not playing, and also
to While playing when USE_CASSETTE_SCREENSAVER is not defined. The
cassette screensaver continues to use the normal display brightness.
When LIGHT_SENSOR is configured, this value scales the current automatic
brightness instead: for example, 50% displays the clock at half of the
ambient-light-controlled brightness.
The cassette idea and original animation prototype were contributed by Adam
Navrowski. The FusionEdge implementation redraws only the two reel areas during
animation to keep display traffic low. The supplied background is stored at
/images/screensaver/retro_audio_cassette.png; CASSETTE_PNG_PATH can point
to a replacement image with the same 456 x 291 pixel layout. Blue/pink and
red/cream alternatives are supplied as retro_audio_cassette_blue.png and
retro_audio_cassette_red.png in the same directory.
CASSETTE_FRAME_MS can optionally set the reel refresh interval; the default
is 100 ms. If the PNG or its PSRAM-backed sprite cannot be loaded, FusionEdge
falls back to the code-drawn cassette.
Some ST7796 panels require the Invert display WebUI option for correct colors. This is a panel/controller variation and is currently a known hardware-specific setting.
When an ST7796, XPT2046 and SD card share the same SPI bus, some modules may show display artifacts during SD playback. Other tested display/touch combinations do not exhibit this behavior.
For the Guition JC3248W535 with its integrated AXS15231B QSPI display and touch controller, use the tested configuration in AXS15231B_options. The final AXS15231B driver stabilization and hardware testing were completed by Tibor Botfai.
For the 4D Systems gen4-ESP32-35CT with its integrated ILI9488 display,
FocalTech capacitive touch controller and microSD slot, use the configuration
in gen4-ESP32-35CT_options. It uses the
same ILI9488 driver as regular SPI modules, with board-specific pins and bus
settings supplied through myoptions.h.
To add logos for your own stations, see the custom station icon guide.
- yoRadio by e2002 and contributors
- VTomRadio LovyanGFX/LittleFS yoRadio variant by VTom
- ESP32-audioI2S by schreibfaul1
- Final AXS15231B driver stabilization and hardware testing by Tibor Botfai
- 4D Systems gen4-ESP32-35CT board support by Révész Tamás
- Reintroduction of
IMPERIALUNITweather-unit support by Adam Navrowski - Cassette screensaver concept and original prototype by Adam Navrowski
- Full-screen album-cover screensaver, multilingual cover matching and backlight/Wi-Fi hardware tuning by Volodimyr Zagranichniy
- Smaller-display and selectable-screensaver community MOD by Scott Barber
- FusionEdge development by SimZs and contributors
Bundled and external components retain their own copyrights and licenses. See THIRD_PARTY_NOTICES.md.
FusionEdge is distributed under the GNU General Public License v3.0. See LICENSE.
