Turn your ESP32 into a fully functional portable Internet radio! Stream live stations from anywhere without the need for traditional radio frequencies. Whether you're a tech hobbyist or enthusiast, this project is a fun and hands-on way to explore IoT, radio waves, and Wi-Fi technology. Perfect for learning, experimenting, and building something you'll actually use!
August 21, 2024
61411 views
8 respects
Devices and components
40 colorful male-female connection wires
Prototype PCB, 6 cm x 8 cm
TP4056 5V 1A Micro USB Li-Ion Battery Charger Module (AZDelivery)
0.91" I2C OLED 128x32
PLA filaments
Push button
Heat shrink tubing, assorted sizes
3.7V 1100mAh rechargeable lithium battery
plywood sheet
Fader potentiometer button
micro switch
PAM8403 + volume control
3.5 audio jack
VS1053 DSP
Materials and tools
Crimping tool (generic)
Welding Station
Soldering wire
Tinkerine Ditto Pro 3D printer
Trotec Speedy 100 laser and engraver
Hot glue gun (generic)
Software and tools
Autodesk Inventor
AutoCAD
Tinkerine Suite 3 (no longer supported; use TInkerine Cloud)
Arduino IDE
Project description
ESP32 Internet Radio
Esp32_Internet_Radio
Inspired by the code from educ8s.tv
1#include <WiFi.h> // Include WiFi library for ESP32's WiFi functionality
2#include <VS1053.h> // Include library to control the VS1053 MP3 decoder
3#include <U8g2lib.h> // Include library for controlling the OLED display
4
5// Define the VS1053 MP3 decoder pins
6#define VS1053_CS 32 // Chip Select for VS1053
7#define VS1053_DCS 33 // Data Command Select for VS1053
8#define VS1053_DREQ 35 // Data Request pin for VS1053
9
10// Button pins to switch between radio stations
11#define BUTTON_NEXT 13 // Pin for the 'next station' button
12#define BUTTON_PREV 12 // Pin for the 'previous station' button
13
14// OLED Display setup with I2C communication
15U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // Create OLED display object
16
17// WiFi settings: replace with your own network credentials
18const char *ssid = "Your SSID"; // Your WiFi network name
19const char *password = "Your Wifi Password"; // Your WiFi network password
20
21// Radio station details
22const char* stationNames[] = {"ERT Kosmos", "FFH Lounge", "FFH Summer"}; // Array of station names
23const char* stationHosts[] = {"radiostreaming.ert.gr", "mp3.ffh.de", "mp3.ffh.de"}; // Host URLs for the stations
24const char* stationPaths[] = {"/ert-kosmos", "/ffhchannels/hqlounge.mp3", "/ffhchannels/hqsummerfeeling.mp3"}; // Paths to the radio streams
25int currentStation = 0; // Index of the currently playing station
26const int totalStations = sizeof(stationNames) / sizeof(stationNames[0]); // Calculate the number of available stations
27
28// VS1053 MP3 player object
29VS1053 player(VS1053_CS, VS1053_DCS, VS1053_DREQ); // Create VS1053 object to control MP3 playback
30WiFiClient client; // WiFi client object to connect to the radio stream
31
32// Variables for scrolling text on the OLED display
33int textPosition = 128; // Initial text position for scrolling
34unsigned long previousMillis = 0; // Store the last time the display was updated
35const long interval = 50; // Time interval for updating the display (50 ms)
36
37void setup() {
38 Serial.begin(115200); // Start the serial monitor for debugging
39
40 // Wait for VS1053 and PAM8403 amplifier to power up
41 delay(3000);
42
43 u8g2.begin(); // Initialize the OLED display
44 u8g2.setFlipMode(1); // Flip the display 180 degrees
45 u8g2.setFont(u8g2_font_ncenB08_tr); // Set font for OLED display
46
47 // Display startup messages on OLED
48 u8g2.clearBuffer();
49 u8g2.drawStr(0, 16, "Starting Radio..."); // Initial message
50 u8g2.sendBuffer();
51 delay(2000);
52
53 u8g2.clearBuffer();
54 u8g2.drawStr(0, 16, "Starting Engine..."); // Second message
55 u8g2.sendBuffer();
56 delay(2000);
57
58 u8g2.clearBuffer();
59 u8g2.drawStr(0, 16, "Connecting to WiFi..."); // WiFi connection message
60 u8g2.sendBuffer();
61
62 Serial.println("\n\nSimple Radio Node WiFi Radio"); // Debug message in the serial monitor
63
64 SPI.begin(); // Initialize SPI communication for VS1053
65
66 player.begin(); // Start the VS1053 decoder
67 if (player.getChipVersion() == 4) { // Check for correct version of VS1053
68 player.loadDefaultVs1053Patches(); // Load patches for MP3 decoding if needed
69 }
70 player.switchToMp3Mode(); // Switch VS1053 to MP3 decoding mode
71 player.setVolume(100); // Set the volume (range: 0-100)
72
73 Serial.print("Connecting to SSID ");
74 Serial.println(ssid); // Debug message: attempting WiFi connection
75 WiFi.begin(ssid, password); // Start WiFi connection
76
77 // Disable WiFi power saving mode for a more stable connection
78 WiFi.setSleep(false);
79
80 // Attempt to connect to WiFi with retries
81 int attempts = 0;
82 while (WiFi.status() != WL_CONNECTED && attempts < 20) {
83 delay(500);
84 Serial.print("."); // Print dots to indicate connection progress
85 attempts++;
86 }
87
88 // Check if WiFi connection is successful
89 if (WiFi.status() == WL_CONNECTED) {
90 Serial.println("WiFi connected");
91 Serial.println("IP address: ");
92 Serial.println(WiFi.localIP()); // Print the assigned IP address
93
94 // Display success message on OLED
95 u8g2.clearBuffer();
96 u8g2.drawStr(0, 16, "Connected Yay!");
97 u8g2.sendBuffer();
98 delay(2000);
99 } else {
100 // Display failure message on OLED if WiFi connection fails
101 Serial.println("WiFi not connected");
102 u8g2.clearBuffer();
103 u8g2.drawStr(0, 16, "Not Connected");
104 u8g2.sendBuffer();
105 delay(2000);
106 }
107
108 // Initialize button pins with pull-up resistors
109 pinMode(BUTTON_NEXT, INPUT_PULLUP);
110 pinMode(BUTTON_PREV, INPUT_PULLUP);
111
112 // Set font for displaying station names
113 u8g2.setFont(u8g2_font_profont17_mr);
114
115 displayStation(); // Display the initial station name on the OLED
116 connectToHost(); // Connect to the radio station stream
117}
118
119void loop() {
120 // Reconnect if the WiFi client is disconnected
121 if (!client.connected()) {
122 Serial.println("Reconnecting...");
123 connectToHost(); // Attempt to reconnect to the stream
124 }
125
126 // Read data from the radio stream and send it to the VS1053 decoder for playback
127 if (client.available() > 0) {
128 uint8_t buffer[32];
129 size_t bytesRead = client.readBytes(buffer, sizeof(buffer)); // Read data from stream
130 player.playChunk(buffer, bytesRead); // Play the received audio data
131 }
132
133 handleButtons(); // Check if buttons are pressed and switch stations accordingly
134 scrollText(); // Scroll the station name on the OLED display
135}
136
137void connectToHost() {
138 // Connect to the current radio station's server
139 Serial.print("Connecting to ");
140 Serial.println(stationHosts[currentStation]);
141
142 if (!client.connect(stationHosts[currentStation], 80)) {
143 Serial.println("Connection failed"); // Display error if connection fails
144 return;
145 }
146
147 // Send HTTP request to the server to get the radio stream
148 Serial.print("Requesting stream: ");
149 Serial.println(stationPaths[currentStation]);
150
151 client.print(String("GET ") + stationPaths[currentStation] + " HTTP/1.1\r\n" +
152 "Host: " + stationHosts[currentStation] + "\r\n" +
153 "Connection: close\r\n\r\n");
154
155 // Skip the HTTP headers in the response
156 while (client.connected()) {
157 String line = client.readStringUntil('\n');
158 if (line == "\r") {
159 break; // End of headers
160 }
161 }
162
163 Serial.println("Headers received"); // Debug message: headers successfully received
164}
165
166void handleButtons() {
167 static bool lastButtonNextState = HIGH; // Track the previous state of the 'next' button
168 static bool lastButtonPrevState = HIGH; // Track the previous state of the 'previous' button
169
170 bool currentButtonNextState = digitalRead(BUTTON_NEXT); // Read current state of 'next' button
171 bool currentButtonPrevState = digitalRead(BUTTON_PREV); // Read current state of 'previous' button
172
173 // If 'next' button is pressed (LOW), switch to the next station
174 if (lastButtonNextState == HIGH && currentButtonNextState == LOW) {
175 nextStation();
176 }
177 // If 'previous' button is pressed (LOW), switch to the previous station
178 if (lastButtonPrevState == HIGH && currentButtonPrevState == LOW) {
179 previousStation();
180 }
181
182 lastButtonNextState = currentButtonNextState; // Update the last state for the 'next' button
183 lastButtonPrevState = currentButtonPrevState; // Update the last state for the 'previous' button
184}
185
186void nextStation() {
187 currentStation = (currentStation + 1) % totalStations; // Move to the next station (wrap around)
188 displayStation(); // Update the OLED display with the new station name
189 connectToHost(); // Connect to the new station
190}
191
192
193void nextStation() {
194 currentStation = (currentStation + 1) % totalStations;
195 displayStation();
196 connectToHost();
197}
198
199void previousStation() {
200 currentStation = (currentStation - 1 + totalStations) % totalStations;
201 displayStation();
202 connectToHost();
203}
204
205void displayStation() {
206 textPosition = 128; // Reset text position to start from the right
207 u8g2.clearBuffer();
208 u8g2.drawLine(0, 0, 127, 0);
209 u8g2.drawLine(0, 31, 127, 31);
210 u8g2.setCursor(textPosition, 22);
211 u8g2.print(stationNames[currentStation]);
212 u8g2.sendBuffer();
213}
214
215void scrollText() {
216 unsigned long currentMillis = millis();
217
218 if (currentMillis - previousMillis >= interval) {
219 previousMillis = currentMillis;
220 textPosition--; // Move text to the left
221 if (textPosition < -u8g2.getUTF8Width(stationNames[currentStation])) {
222 textPosition = 128; // Reset position to start from the right again
223 }
224
225 u8g2.clearBuffer();
226 u8g2.drawLine(0, 0, 127, 0);
227 u8g2.drawLine(0, 31, 127, 31);
228 u8g2.setCursor(textPosition, 22);
229 u8g2.print(stationNames[currentStation]);
230 u8g2.sendBuffer();
231 }
232}
Downloadable files
KeyCap_for_Button
feel free to create a better one.
ButtonKeyCap.stl
Navigate to the DWG file of the wooden enclosure.
I can't download the document here, here is my GitHub
https://github.com/ZETRO2/Esp32-Internet-Radio
Documentation
Schematic
Internet radio schematic Esp32.png
Button_Enclosure
Picture of how to install the button housing
Button_Inclosure_back.png
Button_Enclosure_Front
Another picture of how to install the button housing
Button_Inclosure_front.png
solder Esp32 to decoder
How to solder Esp32 to MP3 decoder
Esp32_soldered_with_Decoder.png
Breadboard scene
don't forget to test everything on a mockup first
BreadBoard_Stage.png
Pins on the decoder
how to solder pins of Mp3 decoder
How_to_solder_Decoder_2.png
Solder decoder
how to solder pins of Mp3 decoder
How_to_solder_Decoder.png
Project structure
How the project works (from educ8s.tv)
Project_Structure.png
RadioBack
Rear view of the radio
RadioBack.png
Front radio
Front view of the radio
RadioFrontSide.png
Indoor radio
Interior view of the radio
RadioInside.png
Radio Left
View from the left side of the radio
RadioGauche.png
Right radio
View from the right side of the radio
RadioRight.png
Open radio
Left and right side view of the radio when opened
RadioOuverte.png
Stripped audio jack
I needed to strip an audio jack to fit
Stripped_AudioJack.png
Note: Content and images are from: https://projecthub.arduino.cc/, with some modifications.
If you want it removed due to copyright reasons, please leave a comment. Thank you.
I want to share this article more widely so that everyone knows about Arduino and your project.