ESP32 MP3 Player
Hardware- 11 Devlogs
- 62 Total hours
Breadboard MP3 player based on ESP32
Breadboard MP3 player based on ESP32
The main program came together pretty quick since I went through the gauntlet of writing classes for each major component.
Quick summary of the MP3 player:
Load up a microSD with songs/albums in MP3 format. Then navigate the file browser using the rotary encoder to scroll through and button to select. You can play/pause, skip to next song, and toggle between playback and browser mode at any time.
I would love to continue improving this, like right now the ESP32 has trouble registering double button clicks if it’s also streaming audio at the same time. That’s why you see me pause songs before double clicking in the video LOL. Unfortunately with school starting soon I decided to wrap it up here. Still super proud of how far I’ve come considering I was only getting blinky on ESP32 one month ago.
This is also meant to be a breadboard proof of concept for a larger PCB project I’ll be tackling, and I’m definitely glad I learned some lessons here and not while working on the PCB. I doubt finishing the PCB is in scope with a month left of stardance but stay tuned haha
File system in progress
Approaching 10 hrs so had to devlog lol
Will be back once file system is finished
Display class for the LCD is now complete, ready to be integrated with rest of program.
Learnings:
Day 7 - debugging, oop, state machines
The only new functionality added today was updating my button state machine for double click and long press detection. Intended behavior: While a song is playing, double click to play the next song or long press to return to the file navigator / song selector. Demo test in video
The more interesting part to me was getting experience with two real big potato things: debugging & oop in c++.
I got a taste of debugging low-level errors using toolchains. In this case it was the Xtensa addr2line tool used to convert ESP32 memory address logs into the exact file and line number involved in the error. Shoutout RoboService on youtube with 10 subscribers for making a banger tutorial.
I also kinda went headfirst into c++ classes. My main arduino ide sketch was getting long enough to the point where partitioning it into classes became the smart move. I got the hang of it faster than expected. The new part is learning c++ specific syntax, but the fundamental concepts are the same across all oop languages.
Day 6 - song progress timer complete with the help of codex
Didn’t realize how complicated calculating song duration from an MP3 would be. You would think duration is stored in metadata but you actually need to do some reverse engineering & check for a bunch of different compression formats yada yada
Anyways now a basic timer is working. Also replaced the headphone jack with speakers like the engineers of the MAX98357 amp modules intended. Still lots more to implement like file navigation and song selection
Day 5 - hello world LCD display
I2C protocol learnings: SDA and SCL lines are held HIGH by external pull-up resistors. Connecting to GPIO pins with internal pull-down resistors will interfere with the pull-ups and cause a serial error. I was stuck on that for a hot minute lol
Looking forward, I’ll be implementing song title/artist, timer, progress bar, and hopefully file navigation functionality
Day 3 & 4 - Audio is now playing! Found a library called arduino-audio-tools that conveniently abstracts the MP3 -> PCM -> I2S pipeline, so no need to reinvent the wheel. This library provides functions like setVolume(), play(), and stop(). All I had to do was integrate these functions into my rotary encoder code from Day 1, and voila there’s volume control and pause/resume. The next session will likely be debugging the static & flicker coming out of the TRRS headphone jack
Day 2 - Researched SD card reading on the Arduino platform. This one took waayyyy too long - once I realized the native arduino SD library doesn’t support my >32GB exFAT microSD I spent the entire day looking into and testing various libraries.. turns out it was a hardware problem. I was using custom pins for SPI instead of the standard ESP32 VSPI pins and assumed it would work the same. Guess I chose a bad pin?
Day 1 - Prototyped volume control, scrolling, and button press on a KY-040 rotary encoder. Button press was done with a timer based debounce, but nailing down debounce logic for volume/scrolling was more tricky… The solution I landed on was making a state transition table, which maps changes in the encoder’s quadrature signal to a physical turn, CW or CCW. In the screenshot you can see debounce in action - button press is registered 3 times but the actual output only changes once!