Превратите свой ESP32 в полнофункциональное портативное интернет-радио! Транслируйте прямые трансляции станций из любой точки мира без необходимости использования традиционных радиочастот. Независимо от того, являетесь ли вы любителем или энтузиастом технологий, этот проект — увлекательный и практичный способ изучить технологии Интернета вещей (IoT), радиоволны и технологию Wi-Fi. Идеально подходит для обучения, экспериментов и создания чего-то, что вы действительно будете использовать!
Также на нашем сайте вы можете посмотреть и другие аналогичные проекты:
Необходимые компоненты
- Модуль ESP32 (купить на AliExpress).
- Усилитель PAM8403 + регулятор громкости (купить на AliExpress).
- Декодер MP3 VS1053 (купить на AliExpress).
- Микродинамик 2 Вт.
- OLED дисплей 128х32 I2C 0,91'' (купить на AliExpress).
- TP4056 - микросхема зарядного устройства для литий-ионных аккумуляторов — x1 (купить на AliExpress).
- Аккумулятор LiPo 3,7 В (купить на AliExpress).
- Ручка потенциометра.
- Кнопки.
- Аудиоразъем 3,5.
- Микропереключатель.
- Черная ткань (для решетки динамика).
- Прототип печатной платы 6х8 см.
- Деревянный корпус, изготовленный с помощью лазерного резака (можно использовать свой любой доступный корпус, в том числе и напечатанный на 3D принтере).
- 8-омный громкоговоритель (динамик).
- Соединительные провода.
- PLA-нить.
- Термоусадочные трубки, разные размеры.
- Лист фанеры.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Обзор проекта
В отличие от традиционных радио, которые принимают радиоволны из воздуха, интернет-радио транслирует радиостанции через веб-сайты. Оно подключается к интернету через микроконтроллер, получает данные с потоковых веб-сайтов, декодирует их в аудиосигналы с помощью декодера MP3, а затем воспроизводит их через динамик.
Внешний вид собранной конструкции данного проекта показан на следующем рисунке.
Как это работает
Микроконтроллер ESP32 подключается к интернету через WiFi. Он получает доступ к потоковым веб-сайтам, на которые я указываю ему зайти на хост радиостанции, захватывает аудиоданные и отправляет их на декодер MP3 VS1053 для декодирования аудиосигнала. Декодированный аудиосигнал усиливается с помощью усилителя PAM8403 и воспроизводится через динамик мощностью 2 Вт. Экран OLED 128х32 используется для отображения текущей информации о станции и статусе подключения.
Структурная схема работы интернет радио на основе ESP32 показана на следующем рисунке.
Назначение компонентов данной схемы:
- ESP32: управляет подключением к интернету и потоковой передачей данных.
- VS1053: декодирует аудиосигналы.
- PAM8403: усиливает декодированный звук для динамика.
- TP4056: управляет зарядкой аккумулятора.
- Кнопки: используются для навигации между радиостанциями.
- Микропереключатель: для включения/выключения устройства.
Схема проекта
Схема интернет-радио на основе микроконтроллера ESP32 представлена на следующем рисунке.
После того как вы убедились что все работает на макетной плате, вы можете спаять все компоненты вместе. Будьте осторожны при пайке поскольку контакты ESP32 маленькие и плату легко повредить.
Спаянная конструкция проекта показана на следующем рисунке.
Корпус и окончательная сборка
Я использовал лазерный резак для создания деревянного корпуса для проекта интернет радио. Или вы можете использовать корпус, напечатанный на 3D принтере.
Окончательное тестирование
После сборки проверьте устройство, чтобы убедится что все работает как положено. Проверьте что:
- радио транслируется плавно, без прерываний;
- кнопки переключают радиостанции правильно;
- аккумулятор правильно заряжается и питает устройство.
Исходный код программы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
#include <WiFi.h> // Include WiFi library for ESP32's WiFi functionality #include <VS1053.h> // Include library to control the VS1053 MP3 decoder #include <U8g2lib.h> // Include library for controlling the OLED display // Define the VS1053 MP3 decoder pins #define VS1053_CS 32 // Chip Select for VS1053 #define VS1053_DCS 33 // Data Command Select for VS1053 #define VS1053_DREQ 35 // Data Request pin for VS1053 // Button pins to switch between radio stations #define BUTTON_NEXT 13 // Pin for the 'next station' button #define BUTTON_PREV 12 // Pin for the 'previous station' button // OLED Display setup with I2C communication U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // Create OLED display object // WiFi settings: replace with your own network credentials const char *ssid = "Your SSID"; // Your WiFi network name const char *password = "Your Wifi Password"; // Your WiFi network password // Radio station details const char* stationNames[] = {"ERT Kosmos", "FFH Lounge", "FFH Summer"}; // Array of station names const char* stationHosts[] = {"radiostreaming.ert.gr", "mp3.ffh.de", "mp3.ffh.de"}; // Host URLs for the stations const char* stationPaths[] = {"/ert-kosmos", "/ffhchannels/hqlounge.mp3", "/ffhchannels/hqsummerfeeling.mp3"}; // Paths to the radio streams int currentStation = 0; // Index of the currently playing station const int totalStations = sizeof(stationNames) / sizeof(stationNames[0]); // Calculate the number of available stations // VS1053 MP3 player object VS1053 player(VS1053_CS, VS1053_DCS, VS1053_DREQ); // Create VS1053 object to control MP3 playback WiFiClient client; // WiFi client object to connect to the radio stream // Variables for scrolling text on the OLED display int textPosition = 128; // Initial text position for scrolling unsigned long previousMillis = 0; // Store the last time the display was updated const long interval = 50; // Time interval for updating the display (50 ms) void setup() { Serial.begin(115200); // Start the serial monitor for debugging // Wait for VS1053 and PAM8403 amplifier to power up delay(3000); u8g2.begin(); // Initialize the OLED display u8g2.setFlipMode(1); // Flip the display 180 degrees u8g2.setFont(u8g2_font_ncenB08_tr); // Set font for OLED display // Display startup messages on OLED u8g2.clearBuffer(); u8g2.drawStr(0, 16, "Starting Radio..."); // Initial message u8g2.sendBuffer(); delay(2000); u8g2.clearBuffer(); u8g2.drawStr(0, 16, "Starting Engine..."); // Second message u8g2.sendBuffer(); delay(2000); u8g2.clearBuffer(); u8g2.drawStr(0, 16, "Connecting to WiFi..."); // WiFi connection message u8g2.sendBuffer(); Serial.println("\n\nSimple Radio Node WiFi Radio"); // Debug message in the serial monitor SPI.begin(); // Initialize SPI communication for VS1053 player.begin(); // Start the VS1053 decoder if (player.getChipVersion() == 4) { // Check for correct version of VS1053 player.loadDefaultVs1053Patches(); // Load patches for MP3 decoding if needed } player.switchToMp3Mode(); // Switch VS1053 to MP3 decoding mode player.setVolume(100); // Set the volume (range: 0-100) Serial.print("Connecting to SSID "); Serial.println(ssid); // Debug message: attempting WiFi connection WiFi.begin(ssid, password); // Start WiFi connection // Disable WiFi power saving mode for a more stable connection WiFi.setSleep(false); // Attempt to connect to WiFi with retries int attempts = 0; while (WiFi.status() != WL_CONNECTED && attempts < 20) { delay(500); Serial.print("."); // Print dots to indicate connection progress attempts++; } // Check if WiFi connection is successful if (WiFi.status() == WL_CONNECTED) { Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); // Print the assigned IP address // Display success message on OLED u8g2.clearBuffer(); u8g2.drawStr(0, 16, "Connected Yay!"); u8g2.sendBuffer(); delay(2000); } else { // Display failure message on OLED if WiFi connection fails Serial.println("WiFi not connected"); u8g2.clearBuffer(); u8g2.drawStr(0, 16, "Not Connected"); u8g2.sendBuffer(); delay(2000); } // Initialize button pins with pull-up resistors pinMode(BUTTON_NEXT, INPUT_PULLUP); pinMode(BUTTON_PREV, INPUT_PULLUP); // Set font for displaying station names u8g2.setFont(u8g2_font_profont17_mr); displayStation(); // Display the initial station name on the OLED connectToHost(); // Connect to the radio station stream } void loop() { // Reconnect if the WiFi client is disconnected if (!client.connected()) { Serial.println("Reconnecting..."); connectToHost(); // Attempt to reconnect to the stream } // Read data from the radio stream and send it to the VS1053 decoder for playback if (client.available() > 0) { uint8_t buffer[32]; size_t bytesRead = client.readBytes(buffer, sizeof(buffer)); // Read data from stream player.playChunk(buffer, bytesRead); // Play the received audio data } handleButtons(); // Check if buttons are pressed and switch stations accordingly scrollText(); // Scroll the station name on the OLED display } void connectToHost() { // Connect to the current radio station's server Serial.print("Connecting to "); Serial.println(stationHosts[currentStation]); if (!client.connect(stationHosts[currentStation], 80)) { Serial.println("Connection failed"); // Display error if connection fails return; } // Send HTTP request to the server to get the radio stream Serial.print("Requesting stream: "); Serial.println(stationPaths[currentStation]); client.print(String("GET ") + stationPaths[currentStation] + " HTTP/1.1\r\n" + "Host: " + stationHosts[currentStation] + "\r\n" + "Connection: close\r\n\r\n"); // Skip the HTTP headers in the response while (client.connected()) { String line = client.readStringUntil('\n'); if (line == "\r") { break; // End of headers } } Serial.println("Headers received"); // Debug message: headers successfully received } void handleButtons() { static bool lastButtonNextState = HIGH; // Track the previous state of the 'next' button static bool lastButtonPrevState = HIGH; // Track the previous state of the 'previous' button bool currentButtonNextState = digitalRead(BUTTON_NEXT); // Read current state of 'next' button bool currentButtonPrevState = digitalRead(BUTTON_PREV); // Read current state of 'previous' button // If 'next' button is pressed (LOW), switch to the next station if (lastButtonNextState == HIGH && currentButtonNextState == LOW) { nextStation(); } // If 'previous' button is pressed (LOW), switch to the previous station if (lastButtonPrevState == HIGH && currentButtonPrevState == LOW) { previousStation(); } lastButtonNextState = currentButtonNextState; // Update the last state for the 'next' button lastButtonPrevState = currentButtonPrevState; // Update the last state for the 'previous' button } void nextStation() { currentStation = (currentStation + 1) % totalStations; // Move to the next station (wrap around) displayStation(); // Update the OLED display with the new station name connectToHost(); // Connect to the new station } void nextStation() { currentStation = (currentStation + 1) % totalStations; displayStation(); connectToHost(); } void previousStation() { currentStation = (currentStation - 1 + totalStations) % totalStations; displayStation(); connectToHost(); } void displayStation() { textPosition = 128; // Reset text position to start from the right u8g2.clearBuffer(); u8g2.drawLine(0, 0, 127, 0); u8g2.drawLine(0, 31, 127, 31); u8g2.setCursor(textPosition, 22); u8g2.print(stationNames[currentStation]); u8g2.sendBuffer(); } void scrollText() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; textPosition--; // Move text to the left if (textPosition < -u8g2.getUTF8Width(stationNames[currentStation])) { textPosition = 128; // Reset position to start from the right again } u8g2.clearBuffer(); u8g2.drawLine(0, 0, 127, 0); u8g2.drawLine(0, 31, 127, 31); u8g2.setCursor(textPosition, 22); u8g2.print(stationNames[currentStation]); u8g2.sendBuffer(); } } |