2020-12-12
md
ESP32 Internet Radio
ESP32 Internet Radio II-> <-Internet Radio Player based on MOC

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

  1. In the Beginning
  2. Hardware
  3. Software
  4. Initial Problems and Success
  5. The DOIT ESP32 devkit v1

In the Beginning toc

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.

(*)
Sorry for the familiarity; I have never met these two gentlemen. However, I regularly invited them into my living room with the help of YouTube, of course, and hence feel that they are somehow part of my circle. Must be hard to be a star ;-).

Hardware toc

Here is an exhaustive list of parts for a minimalist Internet radio.

Here is the wiring diagram, which is copied from the LibreOffice ESP32-radio.odt documentation in the project repository.

ESP32devVS1053Signal
GPIO32pin 1XDCS
GPIO5pin 2XCS
ENpin 3XRST
GPIO4pin 4DREQ
GPIO18pin 5SCK
GPIO23pin 6MOSI
GPIO19pin 7MISO
GNDpin 8GND
5Vpin 95 V

Software toc

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.

; PlatformIO Project Configuration File ; ; Build options: build flags, source filter ; Upload options: custom upload port, speed and extra flags ; Library options: dependencies, extra library storages ; Advanced options: extra scripting ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [platformio] default_envs = esp32dev [extra] baud = 115200 [env] platform = espressif32 framework = arduino monitor_speed = ${extra.baud} [env:esp32dev] board = esp32dev build_flags = -D LED_BUILTIN=2 -D BAUD=${extra.baud} [env:esp32doit-devkit-v1] board = esp32doit-devkit-v1 monitor_speed = ${extra.baud} build_flags = -D BAUD=${extra.baud}

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.

michel@hp:~/Documents/PlatformIO/Projects/esp32/esp32_radio$ wget https://github.com/Edzelf/ESP32-Radio/archive/master.zip --2020-12-04 17:09:54-- https://github.com/Edzelf/ESP32-Radio/archive/master.zip ... master.zip [ <=> ] 7,36M 11,4MB/s ds 0,6s 2020-12-04 17:09:55 (11,4 MB/s) - «master.zip» enregistré [7718518] michel@hp:~/Documents/PlatformIO/Projects/esp32/esp32_radio$ unzip -j master ESP32-Radio-master/Esp32_radio/* -d src Archive: master.zip cac92801382b67c0ba14378fa94804a65714a662 inflating: src/CH376.h inflating: src/Dummytft.h ... inflating: src/radio_css.h

In the next steps, the default main.cpp source created by PlatfomIO in src was deleted and the Arduino sketch was renamed.

michel@hp:~/Documents/PlatformIO/Projects/esp32/esp32_radio$ ls src about_html.h config_html.h favicon_ico.h LCD2004.h oled.h bluetft.h defaultprefs.h ILI9341.h main.cpp radio_css.h bluetft_ucg.h Dummytft.h index_html.h mp3play_html.h SDcard.h CH376.h Esp32_radio.ino LCD1602.h NEXTION.h SSD1306.h michel@hp:~/Documents/PlatformIO/Projects/esp32/esp32_radio$ rm src/main.cpp michel@hp:~/Documents/PlatformIO/Projects/esp32/esp32_radio$ mv src/Esp32_radio.ino src/Esp32_radio.cpp michel@hp:~/Documents/PlatformIO/Projects/esp32/esp32_radio$ ls src about_html.h config_html.h favicon_ico.h LCD2004.h radio_css.h bluetft.h defaultprefs.h ILI9341.h mp3play_html.h SDcard.h bluetft_ucg.h Dummytft.h index_html.h NEXTION.h SSD1306.h CH376.h Esp32_radio.cpp LCD1602.h oled.h

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:

// Define type of local filesystem(s). See documentation. //#define CH376 // For CXH376 support (reading files from USB stick) //#define SDCARD // For SD card support (reading files from SD card) // Define (just one) type of display. See documentation. //#define BLUETFT // Works also for RED TFT 128x160 //#define OLED // 64x128 I2C OLED #define DUMMYTFT // Dummy display //#define LCD1602I2C // LCD 1602 display with I2C backpack //#define LCD2004I2C // LCD 2004 display with I2C backpack //#define ILI9341 // ILI9341 240*320 //#define NEXTION // Nextion display. Uses UART 2 (pin 16 and 1

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.

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

// forward declaration void scan_content_length ( const char* metalinebf );

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 toc

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.

# Example configuration # Programmable input pins: gpio_00 = uppreset = 1 gpio_12 = upvolume = 2 gpio_13 = downvolume = 2 gpio_14 = stop gpio_17 = resume (will not appear if correcitons to defaultprefs.h done) gpio_34 = station = icecast.omroep.nl:80/radio1-bb-mp3 # # MQTT settings mqttbroker = none mqttport = 1883 mqttuser = none mqttpasswd = none mqqprefix = none (sic - will be mqttprefix if the corrections to defaultsprefs.h) # Enter your WiFi network specs here: wifi_00 = SSID1/PASSWD1 wifi_01 = SSID2/PASSWD2 # volume = 72 toneha = 0 tonehf = 0 tonela = 0 tonelf = 0 # preset = 6 # Some preset examples preset_00 = 109.206.96.34:8100 # 0 - NAXI LOVE RADIO, Belgrade, Serbia preset_01 = airspectrum.cdnstream1.com:8114/1648_128 # 1 - Easy Hits Florida 128k preset_02 = us2.Internet-radio.com:8050 # 2 - CLASSIC ROCK MIAMI 256k preset_03 = airspectrum.cdnstream1.com:8000/1261_192 # 3 - Magic Oldies Florida preset_04 = airspectrum.cdnstream1.com:8008/1604_128 # 4 - Magic 60s Florida 60s Classic Rock preset_05 = us1.Internet-radio.com:8105 # 5 - Classic Rock Florida - SHE Radio preset_06 = icecast.omroep.nl:80/radio1-bb-mp3 # 6 - Radio 1, NL preset_07 = 205.164.62.15:10032 # 7 - 1.FM - GAIA, 64k preset_08 = skonto.ls.lv:8002/mp3 # 8 - Skonto 128k preset_09 = 94.23.66.155:8106 # 9 - *ILR CHILL and GROOVE preset_10 = ihr/IHR_IEDM # 10 - iHeartRadio IHR_IEDM preset_11 = ihr/IHR_TRAN # 11 - iHeartRadio IHR_TRAN # # Clock offset and daylight saving time clk_server = pool.ntp.org # Time server to be used clk_offset = 1 # Offset with respect to UTC in hours clk_dst = 1 # Offset during daylight saving time (hours) # Some IR codes ir_40BF = upvolume = 2 ir_C03F = downvolume = 2 # GPIO pinnings pin_ir = 35 # GPIO Pin number for IR receiver VS1838B 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" # pin_tft_cs = 15 # GPIO Pin number for TFT "CS" pin_tft_dc = 2 # GPIO Pin number for TFT "DC" # pin_sd_cs = 21 # GPIO Pin number for SD card "CS" # pin_vs_cs = 5 # GPIO Pin number for VS1053 "CS" pin_vs_dcs = 32 # GPIO Pin number for VS1053 "DCS" pin_vs_dreq = 4 # GPIO Pin number for VS1053 "DREQ"

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.

# MQTT settings mqttbroker = aaa.bbb.yyy.zzz mqttport = 1883 mqttuser = none mqttpasswd = none mqttprefix = ESP32Radio # Enter your WiFi network specs here: wifi_00 = MY-SSID1/my-password # volume = 72 # preset = 6 # Some preset examples preset_00 = 109.206.96.34:8100 # 0 - NAXI LOVE RADIO, Belgrade, Serbia preset_01 = airspectrum.cdnstream1.com:8114/1648_128 # 1 - Easy Hits Florida 128k preset_02 = us2.Internet-radio.com:8050 # 2 - CLASSIC ROCK MIAMI 256k preset_03 = airspectrum.cdnstream1.com:8000/1261_192 # 3 - Magic Oldies Florida preset_04 = airspectrum.cdnstream1.com:8008/1604_128 # 4 - Magic 60s Florida 60s Classic Rock preset_05 = us1.Internet-radio.com:8105 # 5 - Classic Rock Florida - SHE Radio preset_06 = icecast.omroep.nl:80/radio1-bb-mp3 # 6 - Radio 1, NL preset_07 = 205.164.62.15:10032 # 7 - 1.FM - GAIA, 64k preset_08 = skonto.ls.lv:8002/mp3 # 8 - Skonto 128k preset_09 = 94.23.66.155:8106 # 9 - *ILR CHILL and GROOVE preset_10 = ihr/IHR_IEDM # 10 - iHeartRadio IHR_IEDM preset_11 = ihr/IHR_TRAN # 11 - iHeartRadio IHR_TRAN #

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.

michel@hp:~$ avahi-browse -d local _arduino._tcp --resolve + enp4s0 IPv4 ESP32Radio _arduino._tcp local = enp4s0 IPv4 ESP32Radio _arduino._tcp local hostname = [ESP32Radio.local] address = [192.168.1.140] port = [3232] txt = ["board=doitESP32devkitV1" "tcp_check=no" "ssh_upload=no" "auth_upload=no"]

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.

michel@hp:~$ avahi-browse -d local _arduino._tcp --resolve + enp4s0 IPv4 ESP32Radio-2 _arduino._tcp local + enp4s0 IPv4 ESP32Radio _arduino._tcp local = enp4s0 IPv4 ESP32Radio-2 _arduino._tcp local hostname = [ESP32Radio-2.local] address = [192.168.1.138] port = [3232] txt = ["board=esp32" "tcp_check=no" "ssh_upload=no" "auth_upload=no"] = enp4s0 IPv4 ESP32Radio _arduino._tcp local hostname = [ESP32Radio.local] address = [192.168.1.140] port = [3232] txt = ["board=doitESP32devkitV1" "tcp_check=no" "ssh_upload=no" "auth_upload=no"]

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.

michel@hp:~$ mosquitto_sub -h aaa.bbb.yyy.zzz -t "ESP32Radio/#" 192.168.1.140

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 toc

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.

#include "soc/soc.h" #include "soc/rtc_cntl_reg.h"

Then at the start of the bool connectwifi() function at around line 2120 of Esp32_radio.cpp add the lines:

WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector delay(100);

Enable brownout detector just before exiting the function.

WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector return ( localAP == false ) ; // Return result of connection }

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.

ESP32 Internet Radio II-> <-Internet Radio Player based on MOC