# Converting an old Tivoli radio to an Internet Radio ## AKA, importing good Norwegian taste in music. By Nisse Bergman (nb222qr) **Estimated complete time 1-2 hours.** ![](https://i.imgur.com/YTu8mAV.jpg "Titel") This tutorial will demonstrate how to bring your old radio into the future. During my time abroad, I noticed that no country could meet up to the standards of norwegian radio broadcasting. Our neighbours to the west seem to have nailed down the formula of playing just the right kind of music (mind you, in my opinion). Looking at the channel NRK P13 for example, the music played throughout the day will be a mix of 70's rock, 80's disco, 90's grunge and the best indie from the 00's. What's more to ask from radio? As the norwegian FM signals do not reach us here in this blue/yellow country, and I'm too cheap to buy a digital radio. Why not just build one? ## Objective The objective of this project was to convert a standard FM-radio into a WIFI-connected digital radio, keeping some of the radio functionality for changing stations, turning on and off and changing volume. I hoped to gain insights into connecting multiple parts and boards together and integrating them into a "non IoT-device". ## Material For this project I chose the Espressif ESP32-WROOM-32 module, see image below. I chose this since the ESP32 is a cheap, easy to use and well documented board, boasting the kind of connectivity needed for this project. ![Bilden](https://i.imgur.com/U8a31cZ.jpg) To encode and output audio-data fetched from the ESP32, an external MP3 codec board was used. The VS1053/1003 board is shown in the image below.![](https://i.imgur.com/jSehd5W.jpg) To display the current radiostation, an SSD1306 OLED display was used. See image below. ![](https://i.imgur.com/nYQQcLG.jpg) | Material | Description | Price | Vendor | | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Espressif ESP32 Wroom | A powerful, generic Wi-Fi, Bluetooth, Bluetooth LE MCU module that targets a wide variety of applications. | 10€ | [Amazon](https://www.amazon.se/-/en/dp/B086ZQRB9H/ref=sr\_1\_11?crid=2OZW8I8VWQVFO&keywords=esp32+wroom&qid=1656413321&sprefix=esp32+wroom%2Caps%2C93&sr=8-11) | | LC Technology VS1003/1053 MP3 Codec | A board containing a 3,5mm output and a versatile MP3 decoder chip. Capable of decoding all major audio formats (AAC, MP3, WAV, FLAC). | 15€ | [Amazon](https://www.amazon.se/-/en/dp/B07DJZ8HCL/ref=sr\_1\_6?crid=261IOBBIGGJYJ&keywords=vs1053&qid=1656413386&sprefix=vs1053%2Caps%2C83&sr=8-6) | | SSD1306 128x64 OLED Display | Basic monochromatic high contrast OLED display module. Does not need backlighting. | 7€ | [Amazon](https://www.amazon.se/-/en/dp/B07J2QWF43/ref=sr\_1\_5?crid=2MI7WNJDIMV98&keywords=ssd1306&qid=1656413679&sprefix=ssd1306%2Caps%2C80&sr=8-5) | | 3,5mm audio cable | Basic audio cable to connect to MP3 codec board | 7€ | [Kjell o Co](https://www.kjell.com/se/produkter/ljud-bild/kablar-adaptrar/35-mm/35-mm-kablar/ljudkabel-med-35-mm-kontakt-05-m-p95444) | | Micro-USB cable | For connecting to and powering ESP32 board | 9€ | [Kjell o Co](https://www.kjell.com/se/produkter/mobilt/ladda-koppla/kablar-adaptrar/micro-usb-kablar/micro-usb-kabel-1-m-p68687) | | Dupont wires (120pcs) | For connecting the devices | 8€ | [Amazon](https://www.amazon.se/-/en/gp/product/B01EV70C78/ref=ppx\_yo\_dt\_b\_asin\_title\_o02\_s00?ie=UTF8&psc=1) | | Breadboards | Simplifies connections and acts as "housing" for the components | 10€ | [Amazon](https://www.amazon.se/gp/product/B07VFK5CRP/ref=ppx\_yo\_dt\_b\_asin\_title\_o02\_s01?ie=UTF8&psc=1) | | 10 kΩ linear potentiometer | To attach to radio-band selector knob | 1€ | [electro:kit](https://www.electrokit.com/produkt/pot-10kohm-lin-b10k/) | | Old radio | About to go through the makeover of a lifetime | Priceless | | | | Total: | 67€ | ## Computer setup For the software part of this project, several alternatives were considered. Initially, MicroPython (using CircuitPython, PyMakr or other similar libraries) were tested. As it turns out, using the VS1053/1003 chip to stream audio data puts a high demand on the processing speed. So getting this project up and running with Python would be tricky. See [Adafruits comments on this](https://docs.circuitpython.org/projects/vs1053/en/latest/). The decision was then taken to flash the board using the Arduino IDE and the ESP32 board configuration library. See a tutorial on how to do this [here](https://randomnerdtutorials.com/installing-the-esp32-board-in-arduino-ide-windows-instructions/). After some time in the Arduino IDE (which I have used for projects in the past), I felt that I wanted to try something new and fresh for this course. Because that's why we do this, right? I then stumbled upon [PlatformIO](https://platformio.org/). It is an IDE solution available as an extension for VSCode, enabling compilation and uploading of Arduino code to microdevices. This would then be my driver for this course. ### Device and IDE setup These are the steps to use PlatformIO for compiling and uploading code to the Espressif ESP32 microdevice. *Note: To communicate with the ESP32-board over USB using COM-interface (Which is what is used with Arduino), the* [CP210x USB to UART Bridge VCP Drivers](https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers) *are needed.* *Note: When flashing/uploading data to the Espressif ESP32-WROOM-32, I encountered an issue where the boot button on the board had to be held in order for communication to be successful. An 1μf capacitor between the EN(reset)-pin and GND(ground) solves this issue.* 1. Download [VSCode](https://code.visualstudio.com/) 2. Install the PlatformIO extension from the menu to the left 3. Select + New project in the PlatformIO window. 4. Select the name, board and framework. See upper image below. 5. Wait while PlatformIO downloads the board libraries, and configures the project 6. Start coding 7. Select the "PlatformIO: Upload" option from the VSCode command palette (or from the bottom bar, see bottom image below) to flash the board (first time) and upload the code to the device. ![](https://i.imgur.com/7qC5IdI.png "Fig. 2") ![](https://i.imgur.com/DTrWqyJ.png "Fig. 3") ### Folder structure For PlatformIO projects the following structure is used. ``` My Project /include - Put project header files here /lib - Put external libraries here /src - Put main code here main.cpp platformio.ini - Edit to configure project and device settings ``` The main difference to Arduino IDE is that the code in PlatformIO is read from top-to-bottom in real C/C++ fashion. This means that you will NEED to define functions in the correct order. I chose to externalize all functions into a headers.h file and simply include them in the beginning of the main.cpp file. ### Libraries PlatformIO has it's own library management system. Installing libraries and drivers is as easy as going to the *libraries* tab of the PlatformIO page from within VSCode, searching for a library and selecting *Add to Project*. See image below. ![](https://i.imgur.com/ujO8rJH.png) ## Putting everything together As the ESP32 device is connected via the micro-USB cable, this will power all of the devices on the breadboard. * The OLED is powered by the 3.3v output from the ESP32. * The MP3 codec is powered by the input/output VIN connector from the ESP32. The final product should be connected as described in the circuit diagram below. This depicts how the ESP32 is connected to the VS1053/1003 MP3 codec, the SD1306 OLED display and the potentiometer. ![](https://i.imgur.com/fUsrn55.png) ### Detailing specific wirings #### SD1306 OLED | OLED Pin | Description | ESP32 Pin | |----------|--------------|------------| | GND | Ground | GND | | VDD | Power | 3.3V | | SCK | Clock signal | D22 | | SDA | Data signal | D21 | #### VS1053/1003 MP3 Codec | VS1053/1003 Pin | Description | ESP32 Pin | |-----------------|----------------------|------------| | XDCS | Chip select pin | D33 | | XCS | Chip select pin | D32 | | RST | Reset | EN | | DREQ | Data Request | D35 | | SCK | Serial Clock | D18 | | MOSI | Main out, subnode in | D23 | | MISO | Main in, subnode out | D19 | | GND | Ground | GND | #### 10k potentiometer | Potentiometer Pin | Description | ESP32 Pin | |-------------------|--------------|------------| | Right pin | Power | 3.3V | | Middle pin | Data control | VP | | Left pin | Ground | GND | ### Connecting to the radio The connection to the radio was done through outputting the signal from the VS1053/1003 board via the 3.5mm output into an AUX input on the radio itself, as shown in the red circles in the image below. ![](https://i.imgur.com/2EmYLnO.jpg) I removed the analog FM station selector module from the radio and attached the 10k potentiometer to the knob. I used a dremel on the potentiometer fitting to adapt the size. See images below. ![](https://i.imgur.com/qwuIItM.jpg =300x) ![](https://i.imgur.com/TzFvm6H.jpg =300x) ![](https://i.imgur.com/tJXmTUF.jpg =300x) ![](https://i.imgur.com/kpMnzVQ.jpg =300x) The radio could then be powered by it's own 12V input, allowing ON/OFF and volume functionality to still be functional. ## IoT Platform As this project does not include sensor data or other cloud specific functionalities, I did not connect the device to a platform. I chose to put focus on making the internet radio as user friendly and accessible as possible. If I would integrate a platform into this project, I would have to use a custom solution as I'm not using a PYCOM board or even coding in MicroPython. My choice of platform would then be using a MQTT broker like [EMQX](https://www.emqx.io/) cloud using the [MQTT X](https://mqttx.app/) application to monitor this. The integration would be done using the [PubSubClient](https://www.arduino.cc/reference/en/libraries/pubsubclient/) library for Arduino to enable MQTT messaging for the ESP32 device. The device can then be connected to the cloud through the code. ## The code Below the code and libraries for the project is presented. ### Libraries [SPI](https://docs.arduino.cc/learn/communication/spi) - Serial Peripheral Interface (SPI) for Arduino. Used for the VS1053/1003 board. [Wire](https://reference.arduino.cc/reference/en/language/functions/communication/wire/) - Allows communication with I2C devices. Used for the SD1306 OLED. Adafruit_SSD1306 - Driver for the SD1306 OLED. [VS1053](https://github.com/baldram/ESP_VS1053_Library) - Driver for the VS1053/1003 board. Written by [Marcin Szałomski](https://github.com/baldram) [WifiClient](https://www.arduino.cc/reference/en/libraries/wifi/) - Allows connecting the ESP32 to WIFI [HTTPClient](https://www.arduino.cc/reference/en/libraries/httpclient/) - Allows connecting the ESP32 to an HTTP address. ## Code The code for each functionality is presented below. The entire code will not be pasted but different parts related to each of the functionalities will be presented. ### WIFI Functionality from the Arduino library is used to connect the device to WIFI. ``` #include <WiFi.h> char ssid[] = "XXXXX"; char pass[] = "XXXXX"; WiFiClient client; void setup () { connectToWIFI(); } void connectToWIFI() { WiFi.mode(WIFI_STA); WiFi.begin(ssid, pass); while (WiFi.waitForConnectResult() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("WiFi connected"); } ``` ### VS1053/1003 music streaming Driver library for the board and HTTP functionality is used to fetch and play streams from the MP3 codec board. ``` #include "VS1053.h" #include <HTTPClient.h> uint8_t mp3buff[32]; // vs1053 likes 32 bytes at a time VS1053 player(VS1053_CS, VS1053_DCS, VS1053_DREQ); // Station endpoints String stations[] = {"/nrk_radio_p13_mp3_l", "/nrk_radio_jazz_mp3_l", "/nrk_radio_folkemusikk_mp3_l", "/nrk_radio_klassisk_mp3_l" }; int nrOfStations = sizeof(stations)/sizeof(stations[0]); void setup{ initMP3Decoder(); } void initMP3Decoder() { player.begin(); player.loadDefaultVs1053Patches(); player.switchToMp3Mode(); // optional, some boards require this player.setVolume(VOLUME); } void loop() { // Map values between 0 and nr of stations from potentiometer, use as selector for what radiostation to connect to potentiometer = floor(map(analogRead(36), 0, 2100, 0, nrOfStations-1)); radioStation = potentiometer; station_connect(radioStation); // Read from client object. Send to player in chunks uint8_t bytesread = client.read(mp3buff, 32); player.playChunk(mp3buff, bytesread); } // Connect to station with selected endpoint and attach client-object void station_connect (int station_no ) { if (client.connect("lyd.nrk.no",80)) { Serial.println("Connected now to" + String(stations[station_no])); } client.print(String("GET ") + stations[station_no] + " HTTP/1.1\r\n" + "Host: " + "lyd.nrk.no" + "\r\n" + "Connection: close\r\n\r\n"); } ``` ### OLED screen ``` #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void loop{ potentiometer = floor(map(analogRead(36), 0, 2100, 0, nrOfStations-1)); radioStation = potentiometer; displayStation(radioStation); } void displayStation(int station) { display.clearDisplay(); String stationName = ""; switch (station) { case 0: displayText("NRK P13"); break; case 1: displayText("NRK JAZZ"); break; case 2: displayText("NRK \nFOLKEMUSIKK"); break; case 3: displayText("NRK \nKLASSISK"); break; default: break; } } // Function for centering and displaying text void displayText(String text) { int16_t x1; int16_t y1; uint16_t width; uint16_t height; display.getTextBounds(text, 0, 0, &x1, &y1, &width, &height); // display on horizontal and vertical center display.clearDisplay(); // clear display display.setCursor((SCREEN_WIDTH - width) / 2, (SCREEN_HEIGHT - height) / 2); display.println(text); // text to display display.display(); } ``` ## Transmitting the data / connectivity WIFI is used for connecting to the webradio server. The audio is fetched in chunks through the *HTTP* library for Arduino. Each loop() cycle, the audio-data is sent in 32bit chunks to the VS1053/1003 board. This process is NOT asynchronous but blocks the current processing flow. As the data transferred is so small and the board runs at a reasonably fast pace, this is not noticed. ## Presenting the data As mentioned before, no platform was used for this IoT project as focus relies on user interaction with the radio. ## Finalizing the design ### Summary All in all, this was a very fun project to do! The combination of new and old, digital and analog hardware posed challenges in several ways. ### Issues and challenges As I relied on several libraries and drivers, getting everything to play nice together was not easy. Examples using the VS1053 to stream audio were a bit outdated and I had to track down endpoints to specific radiostations that I then could access. I wished to completely integrate the solution into the radio but the space was a bit too tight to fit. Furthermore, I wanted the ESP32 to be powered by the same 12v that powered the radio. Using a HW-131 DC converter board, I could get 5v to the power rails but this posed a problem when connecting the device to WIFI and streaming audio as apparently this causes a small surge which the 5V input could not keep up with. The WIFI issue could be solved adding a large (>470μf) capacitor to the VIN of the board, but the audio signal was still scrambled and jittery. In the end, I decided to power the device via USB, attach the breadboard to the side of the radio and embrace the wire-armageddon. ## Result ### [Link to video demonstrating the radio in action!](https://photos.google.com/share/AF1QipM7Q3Ve0StdHJszwvmqQwTrbTXUxPyUMtVAlNGwQdZNiKwxiLQd17m0juX0ekTi3Q/photo/AF1QipO5Avr_eywpIIczXqzLGXp67W1vSxWik5VW3VGB?key=Yk00RzE2MWlYek11S2hacVdDQ3RKUnNGRm1mSXNB) (OLED flickering because of video frame rate, not noticed IRL) ![](https://i.imgur.com/x6XLxHH.jpg)