The original plan was to build an Internet radio player using a Raspberry Pi or another single board computer and to pipe its audio output into the AUX input of an older compact stereo receiver in the living room. I may very well do that, but right now a minimalist Internet radio is in place and it took all of three hours to set up. And that includes the time to debug a self inflicted problem and the time needed to hot glue a cardboard chassis inside a random card box to house the project. It's not pretty, but it's functional and will be a great testing bed for a permanent solution.
Table of contents
In the Beginning
Back in April, Andreas Spiess The Guy with the Swiss Accent published a video #195 DIY Internet Radio using an ESP32 (Arduino IDE) which made me think that perhaps I could try that approach. However at the time, I did not have an ESP32 on hand. About a month ago, Ralph S Bacon put up a video, #204 TFT Touch Screen ILI9341 SPI for ESP32 (Internet Radio Research), on the same subject. This time, there was a 30 pin DOIT ESP32 devkit v1, a 38 pin generic ESP32 development board, and a couple of TTGO T-Display boards in my parts bin. So I ordered a VS1053 MP3 decoder and player. Actually, I ordered a couple of them from different vendors, because getting parts from China remais a hit-or-miss thing. Amazingly, the decoder arrived in the mail two days ago. I don't recall ever getting a small item from China in less than a month.
A GitHub project ESP32-Radio by Ed Smallenburg (Edzelf) is the starting point for both Andreas and Ralph (*), but true to their nature, they have adopted very different approaches to the software side of the project. Andreas, ever the rational engineer, just implemented the Ed Smallenburg project showing how he solved a few problems he ran into. Ralph, on the other hand, wanted to learn something and accordingly decided to implement the firmware from the ground up. Normally, I would imitate Ralph and strike out on my own. However, a month later, he is still publishing videos on the subject. While I find the videos valuable, I don't really have the time to spend on that project at this point. So I decided to go the easy route and to just implement a bare-bone ESP32-Radio-based project.
Hardware
Here is an exhaustive list of parts for a minimalist Internet radio.
- Generic 38 pin ESP32 (do not use a single-core ESP32s). A 30 pin ESP32 board, such as the DOIT ESP32 devkit v1, would be just as good, although you may want to read The DOIT ESP32 devkit v1.
- VS1053 MP3 Module. Search for that term on AliExpress, Banggood, eBay and Amazon etc. Make sure it is a VS1053 and not a VS1003. Mine has a label "VS1003/1053 MP3 CODEC" on the bottom but the chip is a VS1053.
- 9 female to female Dupont wires.
- An appropriate audio cable to connect the MP3 module to an audio amplifier. A 3.5mm male to male audio stero cable was needed for the Yamaha receiver, but for testing purposes I used wired 3.5mm earbuds directly connected to the VS1053 module without recourse to an external amplifier.
- A 5V DC power supply with the appropriate USB connector (it's a micro USB on the generic ESP32, but some ESP32 boards use a USB-C connector). A small 1A aftermarket USB charger worked fine. In a more permanent installation, I would consider getting one with an On/Off switch or perhaps plugging the power supply into a Wi-Fi switch.
- A case. I made do with a cardboard box. Optional!
Here is the wiring diagram, which is copied from the LibreOffice ESP32-radio.odt
documentation in the project repository.
ESP32dev | VS1053 | Signal |
---|---|---|
GPIO32 | pin 1 | XDCS |
GPIO5 | pin 2 | XCS |
EN | pin 3 | XRST |
GPIO4 | pin 4 | DREQ |
GPIO18 | pin 5 | SCK |
GPIO23 | pin 6 | MOSI |
GPIO19 | pin 7 | MISO |
GND | pin 8 | GND |
5V | pin 9 | 5 V |
Software
I created an empty PlatformIO project named esp32_radio
for a DOIT ESP32 DEVKIT V1 board. Then I modified the platformio.ini
configuration file as follows.
This is my "standard" configuration file for ESP32 projects which quickly allows me to switch between the two types of ESP32 boards that I have. Note how defaul_envs
in the [platformio]
section is set to esp32dev
because I am using the generic development board. After opening a terminal in the directory of the project, I downloaded the ESP32-Radio package from the repository.
In the next steps, the default main.cpp
source created by PlatfomIO in src
was deleted and the Arduino sketch was renamed.
Actually I didn't use the command line to do these things preferring to work with the Double Commander file manager which is very useful for tasks such as these. Next step was to modify the source code a little bit. First, since an SD card and USB thumb drive are not used, the types of local file systems are commented out. Second, the dummy TFT display is select as again that hardware is not part of the minimalist setup. All this is near line 171 of Esp32_radio.cpp
:
I also changed the Serial.begin(115200)
line (approx. line 3239) in the setup()
to Serial.begin(BAUD)
just to be coherent with the configuration file. Crossing my fingers, I tried to compile the program and got an error: a function was used before being declared. This is not uncommon when porting Arduino projects to PlatformIO. There are 3 ways to handle the problem.
- Move the offending function up in the source code to a position before it is first called.
- Do a forward declaration of the file near the beginning of the source code.
- Create a header file with all forward declarations and include the header file.
I chose the third option thinking there might be many such problems in the 5,500+ lines of code. Actually, there was only one. So the quickest solution would have been to add
after the series of #include
directives at around line 199 of the Esp32_radio.cpp
. The source then compiled and platformIO had no problem uploading the firmware to the ESP32.
You could leave well enough alone at this point, but I corrected a typo and changed "mqqprefix" to "mqttprefix" in line 19 of file defaultprefs.h
. In the same file, I removed line 11 "gpio_17 = resume" because the resume
command was dropped in 2018 as far as I can tell. The stop
command is actually a toggle command, sort of a stop/resume
command or as shown in the ESP32Radio Web interface an (un)STOP
command.
Initial Problems and Success
After pressing the reset button of the ESP32, I had to figure out how to connect the ESP to the local Wi-Fi network. As is often the case with ESP chips that cannot connect to a Wi-Fi network, it created a Wi-Fi access point. In this case the name of the network and its password is ESP32Radio
. Once the desktop was connected to that network, the ESP32 Radio web server was reached at the usual URL: 192.168.4.1
.
After clicking on Config
in the top menu, and finding out that preferences had not been found, I clicked on the Default
button as suggested. The following rather long list of settings was displayed in the window.
I suggest that you make a copy of this file for future reference, although it can always be obtained by pressing the Default
button later. Here is the configuration that I used, except for the "secret" or local data which will be different in every situation.
If using an MQTT broker, I suggest setting the mqttprefix
to something other than "none" because, in the latter case, the software will create a topic based on the MAC address of the ESP. Be careful, that topic must be unique. I kept the preset examples, it is a relatively simple matter to change those later.
Note that all the programmable pin definitions were removed, especially the three pertaining to the rotary encoder used a volume control. In my first test run, I used the proposed default configuration and only changed the MQTT and Wi-Fi settings. That caused a perplexing problem because leaving these definitions in the configuration file
pin_enc_clk = 25 # GPIO Pin number for rotary encoder "CLK" pin_enc_dt = 26 # GPIO Pin number for rotary encoder "DT" pin_enc_sw = 27 # GPIO Pin number for rotary encoder "SW"meant that interrupts were attached to the three GPIO pins. The floating unconnected pins regularly triggered a CLK or DT interrupt which was interpreted as a turning down of the volume. Rather quickly the volume would be driven down to 0 which lead me to erroneously believe that the project did not work. Stupid mistake on my part, which became obvious once I looked at the debug output on the terminal while the ESP was still connected to the desktop.
To use this new configuration, click on the Save
button and then the Restart
button. After a while and got the confirmation "Config saved"
After clicking on Restart
, the "Command accepted" confirmation was written to the screen and then the access point was closed by the Internet radio. The device managed to connect on the local Wi-Fi network and it was easy to connect to it from the Linux desktop on which avahi
is installed. All I had to do is open URL esp32radio.local
on a web browser.
One could resort to browsing the local network for published ZeroConf
services.
I know that looks silly. After all if avahi-browse
is available on the system, it should be possible to go directly to esp32radio.local
. However, that is a useful trick if more than one EsP32Radio is installed. As is, the hostname cannot be configured, so all ESP32Radio devices will advertise as ESP32Radio which creates a conflict. The Zeroconf
work around is to add a numerical suffix to distinguish additional devices with the same hostname. Below I have used that browser to find the two instances of ESP32Radio concurrently running on the local network.
If the desktop does not have ZeroConf
services installed, but there is a MQTT broker available and if ESP32Radio was configured to use it, then just subscribe to all messages from the radio. It will display the IP address at regular intervals.
Here one can set the prefix in the configuration, so if more than one instance of ESP32Radio is to be used, make sure that "mqttprefix" is different.
If none of the above worked, then traditional methods for searching for IP clients on the network will have to be used such as looking at connected devices on the Wi-Fi router.
Unfortunately it may be necessary to guess at which IP is the correct one as shown above. Utilities such as AngryBird Scanner or nmap could be used.
Finally, one piece of advice for those that are new to Internet Radio. Be patient when tuning in a station. It can take up to a full minute, maybe even more before the station starts to play. Impatient presses of the PREV or NEXT button in the Web interface will result in nothing good.
The DOIT ESP32 devkit v1
The ESP32Radio has been running very well for a few hours.


As can be seen, I was not kidding about the cardboard box. Don't worry it's much prettier from the front where there's no label. There's plenty of room for additional hardware.
In writing this post, I used a DOIT ESP32 devkit v1 board while listening to radio on the first Internet radio shown above which is built around a generic ESP32 board. I ran into problems with the DOIT board which seem to have afflicted others as well. First of all, some versions of esptool.py
have problems automatically detecting the chip type. That's why I added the command line option --chip esp32
and modified the platformio.ini
file. Even then, I sometimes have to try more than once to upload firmware to the device.
Often the serial log of ESP32-radio reported brownouts when the DOIT was connected to the desktop USB even when the VS1053 was not connected to the board and the USB connection was through a powered hub. The ESP would reboot and sometimes would engage in a boot cycle. I did a little search on the web and found that this was, not surprisingly, associated with the WiFi.begin()
function. Some reported an improvement by disabling brownout detection (Disabling the ESP32 Brownout detector and Brownout detector was triggered #168). I have implemented this and also observed better behaviour. To do the same, include two libraries near the beginning of the sketch at around line 199.
Then at the start of the bool connectwifi()
function at around line 2120 of Esp32_radio.cpp
add the lines:
Enable brownout detector just before exiting the function.
Even with these changes, sometimes the DOIT cannot manage to connect to the local wifi network when the router is less than 10 feed away. Perhaps things will be better with a dedicated power supply. I'll have to wait until a second VS1053 MP3 module arrives before I can test more thoroughly. It's interesting to note that Ed Smallenburg proposed a hardware solution: a big capacitor across ground and Vcc on the DOIT board.