Webrádio - ESP32 / ESP32-S3
Napsal: 21 lis 2025, 02:01
Dnes vám prinášam hotový projekt webrádia, ktoré dokáže prehrávať audio streamy .mp3 - aj bitrate 320 kbps, .ogg (Vorbis), alebo .aac (LC - Low Complexity, HE - High Efficiency, HEv2 - High Efficiency v2) z webových zdrojov (napr. Icecast / SHOUTcast rádiá). Okrem internetových streamov môžete prehrávať stream aj z LAN siete, ak máte nainštalovaný Icecast a vytvorený mountpoint.
Projekt je možné spustiť na ESP32-WROOM-32 / ESP32-S, alebo na ESP32-S3, teda dual-core verzie. Je dôležité poznamenať, že pre funkčnost projektu potrebujete ESP32 dosku s PSRAM! V opačnom prípade kompilácia prejde, aj program sa nahrá, ale budete mať výpadky, po ktorých sa ESP32 bude reštartovať, nakoľko nedokáže v real-time dekódovať audio. Ten výpadok prichádza náhodne, môže to prísť za pár sekúnd, alebo minút.
Projekt využíva webserver bežiaci na ESP32, skrz ktorého rozhranie je možné vyberať jednu z piatich obľúbených staníc a tiež umožňuje reguláciu hlasitosti v plnom rozsahu. Formulár na webovom rozhraní umožňuje zadať aj ľubovoľný URL streamu. Webserver je možné navštíviť aj cez mDNS meno v LAN sieti - audio.local, nakoľko štandardne môže mať ESP32 akúkoľvek IP adresu z DHCP servera na LAN sieti, ak nemáte vyhradenú adresu a nemusíte tak hľadať v rozsahu IP adries, kde ESP32 aktuálne je. Pre dynamickú zmenu hlasitosti je možné použiť slider v rozhraní. Program tiež podporuje OTA (network port pre upload firmvéru priamo z Arduino IDE po kompilácii), čo sa hodí najmä u ESP32-CAM, kde sa zložito nahráva program a musíte všetko zakaždým odpájať. Spotreba hardvéru 60 až 150 mA pri 5V. Bez ohľadu na ESP32, ktoré použijete, zvoľte si minimal SPIFFS tabuľku partícii, nakoľko program sa do bežnej partície nezmestí a podporuje OTA, bola by škoda to nevyužiť a zamedziť si aktualizáciu firmvéru, ak by ste použili napríklad Huge App bez OTA.

Pre linky webrádii odporúčam FMSTREAM, veľká, celosvetová databáza: https://fmstream.org/
Projekt bol testovaný pod ESP32-CAM (ESP32-S) a pod ESP32-Loud s ESP32-S3, pričom v oboch prípadoch sa používal DAC prevodník MAX98357. Dokáže prehrávať aj stereo zvuk s ľavým a pravým kanálom, ak máte 2 DAC prevodníky, inak hraje mono. Ukážkový hardvér zapojíme podľa schémy nižšie. ESP32-CAM je prakticky najdostupnejšou doskou so vstavanou PSRAM.

Programová implementácia využíva knižnicu ESP32-audioI2S (hlavičkový súbor Audio.h). V programe definujeme dátové piny I2S zbernice pre komunikáciu s DAC prevodníkom. Nakoľko ESP32-CAM nemá vyvedené HW I2S piny, sú emulované softvérovo. Ako vidíme, GAIN pin nie je nikde pripojený a je v stave FLOAT, týmto je definovaný zisk prevodníka na 9 dB. Zisk je možné navýšiť na 12 dB pripojením pinu na GND, prípadne skrz 100 kohm pulldown rezistor získame maximálny zisk 15 dB. Knižnica umožňuje regulovať hlasitosť v rozsahu 0 až 21. Použitý reproduktor mal výkon 2W, impedanciu 8 ohm. Testoval som aj s 3W, 4 ohm reproduktorom fungovalo. V oboch prípadoch som bežne rádio prevádzkoval na hlasitosti okolo 8 na bežné počúvanie rádia.
#define I2S_DOUT 12
#define I2S_BCLK 13
#define I2S_LRC 15

Programová implementácia:
- predvolená hlasitosť 8 z 21 možných
- prvé rádio sa automaticky spustí po pripojení ESP32 na WiFi
Preddefinované rádiá:

Video: https://www.youtube.com/watch?v=2jp0x7-ULao
Ak je takáto prepájanie pre vás otravné môžete skúsiť aj dostupné audio-streaming dosky s ESP32, kde je všetko na doske a potrebujete len pripojiť reprák, štandardne do svorkovnice. Okrem WIfi konektivity existujú aj s Ethernetom. U ESP32-S3 PHY Ethernet nehľadajte, nakoľko nemá RMII rozhranie ako štandardný ESP32-WROOM-32. U ESP32-S3 je to potom najčastejšie Wiznet skrz štandardnú SPI zbernicu. Osobne som testoval iba Loud-ESP32 od Sonocotta, kupoval som z Tindie (bez Ethernetu) s ESP32-S3. Loud-ESP32 má prevedenie DPS ako Rpi 3/4 a je kompatibilné aj s jeho krabičkami. Oproti pôvodnej programovej implementácii je tu jeden rozdiel a to, že vyžaduje na EN pin DAC prevodníka HIGH úroveň GPIO. Okrem spomenutého Loud-ESP32 sú dostupné aj menšie dosky, napríklad YB-ESP32-S3-AMP V2 / YB-ESP32-S3-AMP V3. Všetky tieto spomenuté dosky majú stereo audio a PSRAM. Na Loud-ESP32 dosku môžete okrem iného pridať aj IR prijímač (TSOP1738, alebo TSOP1838), čím si môžete webrádio doplniť aj o ovládanie hlasitosti, či zmeny rozhlasovích staníc (streamov) diaľkovým ovládačom a webserver tak úplne vynechať.
Na Tindie je k tejto doske aj návod na prehrávanie hudby zo Spotify cez Home Assistant, čo by tiež mohlo niektorých z vás zaujať.

Video: https://www.youtube.com/watch?v=TvZHcJclqnU
Programová implementácia pre ESP32-S3 (referencia Loud-ESP32)
- funkčnosťou identické s prvým programom pre ESP32-WROOM-32 / ESP32-S, iné GPIO pre DAC
Krabičku pre rádio si už musíte vymyslieť svoju, či už hotovú, tlačenú, alebo hardvér osadíte do starého, vykuchaného rádia. Verím, že tento programový základ viete využiť aj pre vlastné samostatné verzie projektu a budete na nich vedieť stavať ďalej.
Projekt je možné spustiť na ESP32-WROOM-32 / ESP32-S, alebo na ESP32-S3, teda dual-core verzie. Je dôležité poznamenať, že pre funkčnost projektu potrebujete ESP32 dosku s PSRAM! V opačnom prípade kompilácia prejde, aj program sa nahrá, ale budete mať výpadky, po ktorých sa ESP32 bude reštartovať, nakoľko nedokáže v real-time dekódovať audio. Ten výpadok prichádza náhodne, môže to prísť za pár sekúnd, alebo minút.
Projekt využíva webserver bežiaci na ESP32, skrz ktorého rozhranie je možné vyberať jednu z piatich obľúbených staníc a tiež umožňuje reguláciu hlasitosti v plnom rozsahu. Formulár na webovom rozhraní umožňuje zadať aj ľubovoľný URL streamu. Webserver je možné navštíviť aj cez mDNS meno v LAN sieti - audio.local, nakoľko štandardne môže mať ESP32 akúkoľvek IP adresu z DHCP servera na LAN sieti, ak nemáte vyhradenú adresu a nemusíte tak hľadať v rozsahu IP adries, kde ESP32 aktuálne je. Pre dynamickú zmenu hlasitosti je možné použiť slider v rozhraní. Program tiež podporuje OTA (network port pre upload firmvéru priamo z Arduino IDE po kompilácii), čo sa hodí najmä u ESP32-CAM, kde sa zložito nahráva program a musíte všetko zakaždým odpájať. Spotreba hardvéru 60 až 150 mA pri 5V. Bez ohľadu na ESP32, ktoré použijete, zvoľte si minimal SPIFFS tabuľku partícii, nakoľko program sa do bežnej partície nezmestí a podporuje OTA, bola by škoda to nevyužiť a zamedziť si aktualizáciu firmvéru, ak by ste použili napríklad Huge App bez OTA.

Pre linky webrádii odporúčam FMSTREAM, veľká, celosvetová databáza: https://fmstream.org/
Projekt bol testovaný pod ESP32-CAM (ESP32-S) a pod ESP32-Loud s ESP32-S3, pričom v oboch prípadoch sa používal DAC prevodník MAX98357. Dokáže prehrávať aj stereo zvuk s ľavým a pravým kanálom, ak máte 2 DAC prevodníky, inak hraje mono. Ukážkový hardvér zapojíme podľa schémy nižšie. ESP32-CAM je prakticky najdostupnejšou doskou so vstavanou PSRAM.

Programová implementácia využíva knižnicu ESP32-audioI2S (hlavičkový súbor Audio.h). V programe definujeme dátové piny I2S zbernice pre komunikáciu s DAC prevodníkom. Nakoľko ESP32-CAM nemá vyvedené HW I2S piny, sú emulované softvérovo. Ako vidíme, GAIN pin nie je nikde pripojený a je v stave FLOAT, týmto je definovaný zisk prevodníka na 9 dB. Zisk je možné navýšiť na 12 dB pripojením pinu na GND, prípadne skrz 100 kohm pulldown rezistor získame maximálny zisk 15 dB. Knižnica umožňuje regulovať hlasitosť v rozsahu 0 až 21. Použitý reproduktor mal výkon 2W, impedanciu 8 ohm. Testoval som aj s 3W, 4 ohm reproduktorom fungovalo. V oboch prípadoch som bežne rádio prevádzkoval na hlasitosti okolo 8 na bežné počúvanie rádia.
#define I2S_DOUT 12
#define I2S_BCLK 13
#define I2S_LRC 15

Programová implementácia:
- predvolená hlasitosť 8 z 21 možných
- prvé rádio sa automaticky spustí po pripojení ESP32 na WiFi
Preddefinované rádiá:
- Rádio Slovensko
- Európa 2
- Rádio Devín
- Rádio Vlna
- Jemné Melódie
- Rádio Viva
Kód: Vybrat vše
/*|-----------------------------------------------------------|*/
/*|Webrádio - ESP32-CAM (ESP32-S / ESP32-WROOM-32 compatible) |*/
/*|Autor: Martin Chlebovec - Your-IoT (martinius96) |*/
/*|-----------------------------------------------------------|*/
#include "Arduino.h"
#include "WiFi.h"
#include "Audio.h"
#include <ESPmDNS.h>
#include <WebServer.h>
#include <NetworkUdp.h>
#include <ArduinoOTA.h>
#define I2S_DOUT 12
#define I2S_BCLK 13
#define I2S_LRC 15
Audio audio;
WebServer server(80);
String ssid = "WIFI_MENO";
String password = "WIFI_HESLO";
int volume = 8;
String currentStream = "http://icecast.stv.livebox.sk/slovensko_128.mp3";
void handleRoot() {
String html = "<!DOCTYPE html><html lang='en'>";
html += "<head><meta charset='UTF-8'><title>ESP Audio Streamer</title>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css' rel='stylesheet'></head>";
html += "<body class='bg-light d-flex justify-content-center align-items-center vh-100'>";
html += "<div class='card shadow p-4' style='max-width: 400px; width: 100%;'>";
html += "<h4 class='card-title text-center mb-3'>🎵 ESP Audio Streamer</h4>";
// Current Stream Info
html += "<div class='mb-3'>";
html += "<p class='mb-1'><strong>Aktuálny stream:</strong></p>";
html += "<div class='text-break small text-muted'>" + currentStream + "</div>";
html += "</div>";
// Stream Selection
html += "<form action='/seturl' method='get' class='mb-3'>";
html += "<div class='input-group'>";
html += "<select name='url' class='form-select'>";
html += "<option value='' disabled selected>Zvoľte prednastavený stream</option>";
html += "<option value='http://icecast.stv.livebox.sk/slovensko_128.mp3'>Rádio Slovensko</option>";
html += "<option value='https://stream.bauermedia.sk/europa2.mp3'>Európa 2</option>";
html += "<option value='https://icecast.stv.livebox.sk/devin_128.mp3'>Rádio Devín</option>";
html += "<option value='https://stream.radiovlna.sk/vlna-hi.mp3'>Rádio Vlna</option>";
html += "<option value='https://stream.bauermedia.sk/melody-hi.mp3'>Jemné Melódie</option>";
html += "<option value='https://stream.sepia.sk/viva128.mp3'>Rádio Viva</option>";
html += "</select>";
html += "<input type='text' name='url_custom' class='form-control' placeholder='alebo vložte vlastný URL streamu'>";
html += "<button class='btn btn-primary' type='submit'>Prehraj</button>";
html += "</div>";
html += "</form>";
// Volume Control - slider
html += "<div class='mb-3 text-center'>";
html += "<p class='mb-1'>🔊 <strong>Hlasitosť:</strong> <span id='volval'>" + String(volume) + "</span></p>";
html += "<input type='range' class='form-range' min='0' max='21' value='" + String(volume) + "' id='volumeSlider'>";
html += "</div>";
html += "<script>";
html += "const slider = document.getElementById('volumeSlider');";
html += "const volVal = document.getElementById('volval');";
html += "slider.addEventListener('input', () => {";
html += " volVal.textContent = slider.value;";
html += " fetch(`/setvolume?val=${slider.value}`);";
html += "});";
html += "</script>";
html += "</div>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void handleVolume() {
// Ponechané pre spätnú kompatibilitu, už sa nepoužíva
server.sendHeader("Location", "/");
server.send(303);
}
void handleSetVolume() {
if (server.hasArg("val")) {
int newVol = server.arg("val").toInt();
if (newVol >= 0 && newVol <= 21) {
volume = newVol;
audio.setVolume(volume);
Serial.printf("Volume set to: %d\n", volume);
}
}
server.send(200, "text/plain", "OK");
}
void handleSetURL() {
String newUrl = server.arg("url");
String customUrl = server.arg("url_custom");
if (customUrl.length() > 0) {
newUrl = customUrl;
}
if (newUrl.startsWith("http")) {
audio.stopSong();
audio.connecttohost(newUrl.c_str());
currentStream = newUrl;
}
server.sendHeader("Location", "/");
server.send(303);
}
void setup() {
Serial.begin(115200);
WiFi.disconnect();
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), password.c_str());
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.println(F("Wifi connected with IP:"));
Serial.println(WiFi.localIP());
if (MDNS.begin("audio")) {
MDNS.addService("http", "tcp", 80);
Serial.println("MDNS responder started");
Serial.println("You can now connect to http://audio.local");
}
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(volume);
audio.connecttohost(currentStream.c_str());
server.on("/", handleRoot);
server.on("/volume", handleVolume); // deprecated
server.on("/setvolume", handleSetVolume);
server.on("/seturl", handleSetURL);
server.begin();
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
}
void loop() {
audio.loop();
server.handleClient();
ArduinoOTA.handle();
}

Video: https://www.youtube.com/watch?v=2jp0x7-ULao
Ak je takáto prepájanie pre vás otravné môžete skúsiť aj dostupné audio-streaming dosky s ESP32, kde je všetko na doske a potrebujete len pripojiť reprák, štandardne do svorkovnice. Okrem WIfi konektivity existujú aj s Ethernetom. U ESP32-S3 PHY Ethernet nehľadajte, nakoľko nemá RMII rozhranie ako štandardný ESP32-WROOM-32. U ESP32-S3 je to potom najčastejšie Wiznet skrz štandardnú SPI zbernicu. Osobne som testoval iba Loud-ESP32 od Sonocotta, kupoval som z Tindie (bez Ethernetu) s ESP32-S3. Loud-ESP32 má prevedenie DPS ako Rpi 3/4 a je kompatibilné aj s jeho krabičkami. Oproti pôvodnej programovej implementácii je tu jeden rozdiel a to, že vyžaduje na EN pin DAC prevodníka HIGH úroveň GPIO. Okrem spomenutého Loud-ESP32 sú dostupné aj menšie dosky, napríklad YB-ESP32-S3-AMP V2 / YB-ESP32-S3-AMP V3. Všetky tieto spomenuté dosky majú stereo audio a PSRAM. Na Loud-ESP32 dosku môžete okrem iného pridať aj IR prijímač (TSOP1738, alebo TSOP1838), čím si môžete webrádio doplniť aj o ovládanie hlasitosti, či zmeny rozhlasovích staníc (streamov) diaľkovým ovládačom a webserver tak úplne vynechať.
Na Tindie je k tejto doske aj návod na prehrávanie hudby zo Spotify cez Home Assistant, čo by tiež mohlo niektorých z vás zaujať.

Video: https://www.youtube.com/watch?v=TvZHcJclqnU
Programová implementácia pre ESP32-S3 (referencia Loud-ESP32)
- funkčnosťou identické s prvým programom pre ESP32-WROOM-32 / ESP32-S, iné GPIO pre DAC
Kód: Vybrat vše
/*|-------------------------------------------------|*/
/*|Webrádio - ESP32-S3 - Loud-ESP32 @ Sonocotta |*/
/*|Autor: Martin Chlebovec - Your-IoT (martinius96) |*/
/*|-------------------------------------------------|*/
#include "Arduino.h"
#include "WiFi.h"
#include "Audio.h"
#include <ESPmDNS.h>
#include <WebServer.h>
#include <NetworkUdp.h>
#include <ArduinoOTA.h>
#define I2S_DOUT 16 // I2S DATA
#define I2S_BCLK 14 // I2S CLK
#define I2S_LRC 15 // I2S WS (Word Select)
#define DAC_EN 8 // Enable DAC
Audio audio;
WebServer server(80);
String ssid = "WIFI_MENO";
String password = "WIFI_HESLO";
int volume = 8;
String currentStream = "http://icecast.stv.livebox.sk/slovensko_128.mp3";
unsigned long timer = 0;
void handleRoot() {
String html = "<!DOCTYPE html><html lang='en'>";
html += "<head><meta charset='UTF-8'><title>ESP Audio Streamer</title>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<link href='https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css' rel='stylesheet'></head>";
html += "<body class='bg-light d-flex justify-content-center align-items-center vh-100'>";
html += "<div class='card shadow p-4' style='max-width: 400px; width: 100%;'>";
html += "<h4 class='card-title text-center mb-3'>🎵 ESP Audio Streamer</h4>";
// Current Stream Info
html += "<div class='mb-3'>";
html += "<p class='mb-1'><strong>Aktuálny stream:</strong></p>";
html += "<div class='text-break small text-muted'>" + currentStream + "</div>";
html += "</div>";
// Stream Selection
html += "<form action='/seturl' method='get' class='mb-3'>";
html += "<div class='input-group'>";
html += "<select name='url' class='form-select'>";
html += "<option value='' disabled selected>Zvoľte prednastavený stream</option>";
html += "<option value='http://icecast.stv.livebox.sk/slovensko_128.mp3'>Rádio Slovensko</option>";
html += "<option value='https://stream.bauermedia.sk/europa2.mp3'>Európa 2</option>";
html += "<option value='https://icecast.stv.livebox.sk/devin_128.mp3'>Rádio Devín</option>";
html += "<option value='https://stream.radiovlna.sk/vlna-hi.mp3'>Rádio Vlna</option>";
html += "<option value='https://stream.bauermedia.sk/melody-hi.mp3'>Jemné Melódie</option>";
html += "<option value='https://stream.sepia.sk/viva128.mp3'>Rádio Viva</option>";
html += "</select>";
html += "<input type='text' name='url_custom' class='form-control' placeholder='alebo vložte vlastný URL streamu'>";
html += "<button class='btn btn-primary' type='submit'>Prehraj</button>";
html += "</div>";
html += "</form>";
// Volume Control - slider
html += "<div class='mb-3 text-center'>";
html += "<p class='mb-1'>🔊 <strong>Hlasitosť:</strong> <span id='volval'>" + String(volume) + "</span></p>";
html += "<input type='range' class='form-range' min='0' max='21' value='" + String(volume) + "' id='volumeSlider'>";
html += "</div>";
html += "<script>";
html += "const slider = document.getElementById('volumeSlider');";
html += "const volVal = document.getElementById('volval');";
html += "slider.addEventListener('input', () => {";
html += " volVal.textContent = slider.value;";
html += " fetch(`/setvolume?val=${slider.value}`);";
html += "});";
html += "</script>";
html += "</div>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void handleVolume() {
// Ponechané pre spätnú kompatibilitu, už sa nepoužíva
server.sendHeader("Location", "/");
server.send(303);
}
void handleSetVolume() {
if (server.hasArg("val")) {
int newVol = server.arg("val").toInt();
if (newVol >= 0 && newVol <= 21) {
volume = newVol;
audio.setVolume(volume);
Serial.printf("Volume set to: %d\n", volume);
}
}
server.send(200, "text/plain", "OK");
}
void handleSetURL() {
String newUrl = server.arg("url");
String customUrl = server.arg("url_custom");
if (customUrl.length() > 0) {
newUrl = customUrl;
}
if (newUrl.startsWith("http")) {
audio.stopSong();
audio.connecttohost(newUrl.c_str());
currentStream = newUrl;
}
server.sendHeader("Location", "/");
server.send(303);
}
void setup() {
Serial.begin(115200);
WiFi.disconnect();
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), password.c_str());
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.println(F("Wifi connected with IP:"));
Serial.println(WiFi.localIP());
if (MDNS.begin("audio")) {
MDNS.addService("http", "tcp", 80);
Serial.println("MDNS responder started");
Serial.println("You can now connect to http://audio.local");
}
pinMode(DAC_EN, OUTPUT);
digitalWrite(DAC_EN, HIGH); // Power on the DAC
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
audio.setVolume(volume);
audio.connecttohost(currentStream.c_str());
server.on("/", handleRoot);
server.on("/volume", handleVolume); // deprecated
server.on("/setvolume", handleSetVolume);
server.on("/seturl", handleSetURL);
server.begin();
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
}
void loop() {
if ((millis() - timer) >= 15000 || timer == 0) {
timer = millis();
Serial.println("You can now connect to http://audio.local");
Serial.print("Or via http://");
Serial.println(WiFi.localIP());
}
audio.loop();
server.handleClient();
ArduinoOTA.handle();
}