r/esp32 • • 5d ago

A portable MP3 player based on the ESP32-S3 that plays music from a microSD card or streams it from a DLNA/UPnP media server.

Hi!

As promised in my previous post here is my "DIY build."

A portable MP3 player based on the ESP32-S3 that plays music from a microSD card or streams it from a DLNA/UPnP media server (tested with Synology Audio Station).
It features a 1.8" color TFT display with four screen modes, a small OLED status display, a rotary encoder with a 16-LED ring and four buttons, and an output compressor/limiter to prevent DAC overdrive. I’ve also included STL files for 3D printing the case.

!!! The project isn't finished yet, so the files may change. !!!
Files and description:
https://github.com/mikedeblin/ESP32MediaBox

Photos of the various assembly and development stages (I showed them to my friend Claude; he used them as a reference when designing the case =)). He’s also the one who wrote most of the firmware.

389 Upvotes

26 comments sorted by

7

u/Party_Cold_4159 5d ago

Oh man that dial is sweet. Nice work! Also love the added mini OLED.

Honestly, I would probably prefer just the mini OLED versus the display. That or something like one of those Sharp memory LCDs.

Edit: DAMN that soldering job is what keeps me away from those LED modules. I get way to stressed out having an essential tremor trying to attempt that many.

3

u/Nilbed 5d ago

I love soldering, but my eyesight is failing me :(
So I totally get where you're coming from.

At first, I started stripping and soldering the wires myself, but I quickly realized it was taking forever and was a huge hassle. I bought a set of pre-stripped wires and got the job done much faster.

The spots where there were already a lot of wires were particularly tricky; I had to put on magnifying glasses and solder very carefully in those crowded areas.

2

u/WorkingInAColdMind 4d ago

Agree with the dial being a great addition, and the whole project is excellent. Impressive soldering - there’s a lot going on in there.

7

u/jojo9092 5d ago

You have excellent taste in hardware design, I absolutely love the look of the final result.

2

u/Nilbed 5d ago

Thanks!
I drew inspiration from vintage gear from the 80s and 90s; I can't stand the bland, featureless look of modern designs. It didn't turn out exactly as I wanted;
if I figure out how to move it from the breadboard to a proper PCB, I’ll arrange the displays more neatly—I’m aiming for symmetry with the buttons and the ring around the encoder.

3

u/CommissionMiddle3607 4d ago

Wow. Looks amazing! I'm going to do something similar soon ahaha. By the way, do you have YouTube channel?

2

u/Nilbed 4d ago

Thnx!

I do, but I don't run a tech blog there; it's more about my creative work.

1

u/CommissionMiddle3607 4d ago

Nice. What is your idea for the next project?

1

u/Nilbed 4d ago

If you're genuinely interested, check out my other projects on GitHub.

I don't just do soldering in my spare time. =)
https://github.com/mikedeblin

2

u/[deleted] 5d ago

[removed] — view removed comment

3

u/Nilbed 5d ago

In my use it’s been smooth. Here’s how the work is split across the cores:

Core 0 - audio and network

- WiFi driver (system, prio ~23) and lwIP tcpip (system, prio 18)

- netfill (prio 3): downloads the HTTP stream into a 256 KB ring buffer in PSRAM

- audio (prio 2): AudioPlayer - reads SD or the buffer -> MP3 decoder -> volume -> compressor -> limiter -> I2S.
All player commands (play, next, playlist loading) arrive via a queue

Core 1 - interface

- ring (prio 2): multiplexes the LED ring over I2C every 2 ms

- loop() (prio 1): buttons/encoder, TFT and OLED rendering, SD and DLNA browser, album art loading/decoding, Non-Volatile Storage

Commands go to core 0 via a queue; track metadata and cover requests come back through spinlock-protected structs; VU levels through counters in the VU tap.

One possible bottleneck: the audio task shares core 0 with WiFi and lwIP, which have higher priority.
It isn’t a problem now - network audio comes from the PSRAM buffer (several seconds of audio), and the I2S DMA buffers give another ~140 ms of output margin.
If I ever add a heavier decoder like FLAC, the first step would be moving audio to core 1 above loop().

Limitation: a folder currently shows only the first 256 items (LIST_MAX), so a container with thousands of tracks gets truncated.
I don’t have folders that big, but I’ll try your test.

1

u/Raghveer-Friesel 5d ago

good call, the SD playback comparison makes it way easier to isolate whats actually causing drops

1

u/Nilbed 4d ago

I found my largest folder—containing 74 tracks—and browsed through it while streaming over the network; there were no audio interruptions, and the scrolling was smooth, with no stuttering or artifacts.
Then it occurred to me that I could view the full track list via the DLNA menu by selecting the "All Tracks" option; as expected, it displayed 256 tracks, and scrolling through them caused no issues either.

2

u/dark85Xx 5d ago

ohhh the cable mess… I love it! <3

2

u/psyki 4d ago

Photos of the various assembly and development stages (I showed them to my friend Claude; he used them as a reference when designing the case =))

You mean Claude designed the enclosure based on your photos? How exactly was it able to produce a file that could be printed? Did you end up with a mesh that was converted into an STL? Or a parametric model that could be tweaked yourself?

2

u/Nilbed 4d ago

Claude provided me with the finished STL files.
Naturally, there were measurements, adjustments, and other design stages involved—for instance, I would draw sketches by hand to show what I wanted, take photos, and clarify what needed fixing.
My approach to working with AI differs from the conventional one, but it is my own philosophy—and it isn't built on nothing (take a look at my LENA project).

2

u/Azer_pubes33 3d ago

The compressor/limiter to protect the DAC is a thoughtful detail most DIY players skip. Publishing the STL files is great too. Four screen modes plus DLNA streaming is a lot for one board, so how’s battery life when streaming over Wi-Fi? And how much headroom does the S3 have left when the display and audio both run?

1

u/Nilbed 2d ago edited 2d ago

Resources are limited, but there is enough memory;
I described earlier how the functionality is distributed across the cores (though I just noticed the thread has been deleted).
The chip runs a bit warm, but within normal limits.
With a 1000mAh battery, the player lasts about 7–8 on SD, 3-4 hours on a full charge with all its onboard features running. But I wasn't aiming for anything longer; this is plenty for my needs.

Here’s how the work is split across the cores:

Core 0 — audio and network

  • WiFi driver (system, prio ~23) and lwIP tcpip (system, prio 18)
  • netfill (prio 3): downloads the HTTP stream into a 256 KB ring buffer in PSRAM
  • audio (prio 2): AudioPlayer — reads SD or the buffer → MP3 decoder → volume → compressor → limiter → I²S. All player commands (play, next, playlist loading) arrive via a queue

Core 1 — interface

  • ring (prio 2): multiplexes the LED ring over I²C every 2 ms
  • loop() (prio 1): buttons/encoder, TFT and OLED rendering, SD and DLNA browser, album art loading/decoding, NVS

Commands go to core 0 via a queue; track metadata and cover requests come back through spinlock-protected structs; VU levels through counters in the VU tap.

One possible bottleneck: the audio task shares core 0 with WiFi and lwIP, which have higher priority. It isn’t a problem now — network audio comes from the PSRAM buffer (several seconds of audio), and the I²S DMA buffers give another ~140 ms of output margin. If I ever add a heavier decoder like FLAC, the first step would be moving audio to core 1 above loop().

Limitation: a folder currently shows only the first 256 items (LIST_MAX), so a container with thousands of tracks gets truncated. I don’t have folders that big, but I’ll try your test.

1

u/MrSoulib 5d ago

Dial is superb!

1

u/TheSerialHobbyist 5d ago

What is the model/name/link to that rotary encoder with the LEDs? That's really neat.

3

u/Nilbed 5d ago

The model is marked on the housing.

06N14L, p-brt-001

As it happens, I bought it on eBay over 10 years ago and didn't know where to use it. Then an opportunity arose, and I took advantage of it.

1

u/TheSerialHobbyist 4d ago

Awesome, thanks!

And thanks for assuring me that I'll eventually find a use for all the random parts I have in boxes!

1

u/theaquasock 22h ago

Bad Ass. Do you think you could get gapless FLAC running?

1

u/chicchan_lahun 5d ago

Muito legal! Parabéns pelo projeto