я могу выдать вам такой код
// ============================================================================
// ESP32 + VS1003 MP3 Decoder с WiFi и Web-управлением
// + Воспроизведение MP3-потока с URL (интернет-радио)
// ИСПРАВЛЕННАЯ ВЕРСИЯ
// ============================================================================
#include <WiFi.h>
#include <WebServer.h>
#include <SPI.h>
#include <SD.h>
#include <HTTPClient.h>
#include <WiFiClient.h>
// ==================== НАСТРОЙКИ WiFi ====================
const char* AP_SSID = "ESP32-MP3-Player";
const char* AP_PASSWORD = "mp3player123";
const char* STA_SSID = "YOUR_WIFI_SSID";
const char* STA_PASSWORD = "YOUR_WIFI_PASSWORD";
// ==================== ПИНЫ VS1003 ====================
#define VS_XRESET 4
#define VS_XCS 5
#define VS_XDCS 16
#define VS_DREQ 19
#define VS_MOSI 23
#define VS_MISO 19
#define VS_SCK 18
// ==================== ПИН SD-КАРТЫ ====================
#define SD_CS 21
// ==================== РЕГИСТРЫ VS1003 ====================
#define VS_WRITE_COMMAND 0x02
#define VS_READ_COMMAND 0x03
#define VS_MODE_REG 0x00
#define VS_STATUS_REG 0x01
#define VS_BASS_REG 0x02
#define VS_CLOCKF_REG 0x03
#define VS_DECODE_TIME_REG 0x04
#define VS_AUDATA_REG 0x05
#define VS_WRAM_REG 0x06
#define VS_WRAMADDR_REG 0x07
#define VS_HDAT0_REG 0x08
#define VS_HDAT1_REG 0x09
#define VS_AIADDR_REG 0x0A
#define VS_VOL_REG 0x0B
#define VS_AICTRL0_REG 0x0C
#define VS_AICTRL1_REG 0x0D
#define VS_AICTRL2_REG 0x0E
#define VS_AICTRL3_REG 0x0F
// ==================== ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ====================
WebServer server(80);
File currentFile;
WiFiClient streamClient;
HTTPClient httpClient;
bool isPlaying = false;
bool isPaused = false;
bool isStreaming = false;
String currentTrackName = "Нет трека";
String currentFileName = "";
String currentStreamURL = "";
int currentVolume = 50;
float currentBass = 0;
float currentTreble = 0;
unsigned long streamBytesReceived = 0;
unsigned long lastStreamBitrateUpdate = 0;
unsigned long streamBitrate = 0;
unsigned long currentFilePosition = 0;
unsigned long currentFileSize = 0;
TaskHandle_t playTaskHandle = NULL;
// ==================== HTML ИНТЕРФЕЙС ====================
const char* HTML_PAGE = R"rawliteral(
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESP32 MP3 Player & Radio</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
min-height: 100vh;
color: #eee;
padding: 20px;
}
.container {
max-width: 600px;
margin: 0 auto;
}
h1 {
text-align: center;
margin-bottom: 30px;
color: #00d4ff;
text-shadow: 0 0 20px rgba(0,212,255,0.3);
}
.player-card {
background: rgba(255,255,255,0.05);
border-radius: 20px;
padding: 25px;
margin-bottom: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.1);
}
.now-playing {
text-align: center;
margin-bottom: 20px;
}
.track-name {
font-size: 1.3em;
color: #00d4ff;
margin: 10px 0;
word-break: break-all;
}
.status {
color: #888;
font-size: 0.9em;
}
.stream-badge {
display: inline-block;
background: linear-gradient(135deg, #ff4757, #cc0033);
color: white;
padding: 3px 12px;
border-radius: 12px;
font-size: 0.75em;
margin-left: 8px;
vertical-align: middle;
}
.file-badge {
display: inline-block;
background: linear-gradient(135deg, #2ed573, #1e90ff);
color: white;
padding: 3px 12px;
border-radius: 12px;
font-size: 0.75em;
margin-left: 8px;
vertical-align: middle;
}
.controls {
display: flex;
justify-content: center;
gap: 15px;
margin: 20px 0;
}
.btn {
width: 60px;
height: 60px;
border-radius: 50%;
border: none;
cursor: pointer;
font-size: 24px;
transition: all 0.3s;
display: flex;
align-items: center;
justify-content: center;
}
.btn-play {
background: linear-gradient(135deg, #00d4ff, #0099cc);
color: white;
width: 70px;
height: 70px;
font-size: 28px;
}
.btn-play:hover { transform: scale(1.1); box-shadow: 0 0 30px rgba(0,212,255,0.5); }
.btn-stop {
background: linear-gradient(135deg, #ff4757, #cc0033);
color: white;
}
.btn-stop:hover { transform: scale(1.1); box-shadow: 0 0 30px rgba(255,71,87,0.5); }
.btn-pause {
background: linear-gradient(135deg, #ffa502, #ff7f00);
color: white;
}
.btn-pause:hover { transform: scale(1.1); box-shadow: 0 0 30px rgba(255,165,2,0.5); }
.btn-next, .btn-prev {
background: rgba(255,255,255,0.1);
color: #00d4ff;
border: 2px solid #00d4ff;
}
.btn-next:hover, .btn-prev:hover {
background: #00d4ff;
color: #1a1a2e;
}
.slider-container {
margin: 20px 0;
}
.slider-label {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
color: #aaa;
}
input[type="range"] {
width: 100%;
height: 8px;
border-radius: 4px;
background: rgba(255,255,255,0.1);
outline: none;
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: #00d4ff;
cursor: pointer;
box-shadow: 0 0 15px rgba(0,212,255,0.5);
}
.file-list {
max-height: 250px;
overflow-y: auto;
}
.file-item {
padding: 12px 15px;
margin: 5px 0;
background: rgba(255,255,255,0.05);
border-radius: 10px;
cursor: pointer;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 10px;
}
.file-item:hover {
background: rgba(0,212,255,0.2);
transform: translateX(5px);
}
.file-item.playing {
background: rgba(0,212,255,0.3);
border-left: 4px solid #00d4ff;
}
.file-icon { font-size: 1.2em; }
.wifi-info {
text-align: center;
color: #666;
font-size: 0.85em;
margin-top: 20px;
}
.progress-bar {
width: 100%;
height: 6px;
background: rgba(255,255,255,0.1);
border-radius: 3px;
margin: 15px 0;
overflow: hidden;
}
.progress-fill {
height: 100%;
width: 0%;
background: linear-gradient(90deg, #00d4ff, #0099cc);
border-radius: 3px;
transition: width 0.5s;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.stream-section {
margin-top: 20px;
}
.stream-input {
width: 100%;
padding: 12px 15px;
border-radius: 10px;
border: 1px solid rgba(0,212,255,0.3);
background: rgba(255,255,255,0.05);
color: #eee;
font-size: 1em;
margin-bottom: 10px;
}
.stream-input::placeholder { color: #666; }
.stream-input:focus {
outline: none;
border-color: #00d4ff;
box-shadow: 0 0 10px rgba(0,212,255,0.2);
}
.btn-stream {
width: 100%;
padding: 12px;
border-radius: 10px;
border: none;
background: linear-gradient(135deg, #ff4757, #cc0033);
color: white;
font-size: 1em;
cursor: pointer;
transition: all 0.3s;
}
.btn-stream:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(255,71,87,0.4);
}
.preset-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.preset-btn {
padding: 8px 14px;
border-radius: 20px;
border: 1px solid rgba(0,212,255,0.3);
background: rgba(255,255,255,0.05);
color: #00d4ff;
cursor: pointer;
font-size: 0.85em;
transition: all 0.3s;
}
.preset-btn:hover {
background: rgba(0,212,255,0.2);
border-color: #00d4ff;
}
.bitrate-info {
text-align: center;
color: #888;
font-size: 0.85em;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="container">
<h1>🎵 ESP32 MP3 Player & Radio</h1>
<div class="player-card">
<div class="now-playing">
<div class="status" id="status">Готов</div>
<div class="track-name" id="trackName">Нет трека</div>
<div class="bitrate-info" id="bitrateInfo"></div>
<div class="progress-bar">
<div class="progress-fill" id="progressBar"></div>
</div>
</div>
<div class="controls">
<button class="btn btn-prev" onclick="sendCmd('prev')">⏮</button>
<button class="btn btn-play" id="playBtn" onclick="togglePlay()">▶</button>
<button class="btn btn-pause" onclick="sendCmd('pause')">⏸</button>
<button class="btn btn-stop" onclick="sendCmd('stop')">⏹</button>
<button class="btn btn-next" onclick="sendCmd('next')">⏭</button>
</div>
<div class="slider-container">
<div class="slider-label">
<span>🔊 Громкость</span>
<span id="volValue">50%</span>
</div>
<input type="range" min="0" max="100" value="50" id="volumeSlider" oninput="setVolume(this.value)">
</div>
<div class="slider-container">
<div class="slider-label">
<span>🎸 Bass</span>
<span id="bassValue">0</span>
</div>
<input type="range" min="0" max="15" value="0" id="bassSlider" oninput="setBass(this.value)">
</div>
<div class="slider-container">
<div class="slider-label">
<span>🎺 Treble</span>
<span id="trebleValue">0</span>
</div>
<input type="range" min="-8" max="7" value="0" id="trebleSlider" oninput="setTreble(this.value)">
</div>
</div>
<div class="player-card">
<h3 style="margin-bottom: 15px; color: #ff4757;">📡 Интернет-радио / Поток</h3>
<input type="text" class="stream-input" id="streamUrl"
placeholder="Введите URL MP3-потока..."
value="http://stream.radioparadise.com/mp3-192">
<button class="btn-stream" onclick="playStream()">▶ Воспроизвести поток</button>
<div style="margin-top: 15px; color: #888; font-size: 0.85em;">Быстрые ссылки:</div>
<div class="preset-list">
<button class="preset-btn" onclick="setStreamAndPlay('http://stream.radioparadise.com/mp3-192')">Radio Paradise</button>
<button class="preset-btn" onclick="setStreamAndPlay('http://ice1.somafm.com/groovesalad-128-mp3')">SomaFM Groove</button>
<button class="preset-btn" onclick="setStreamAndPlay('http://icecast.err.ee/vikerraadio.mp3')">Vikerraadio</button>
<button class="preset-btn" onclick="setStreamAndPlay('http://stream.live.vc.bbcmedia.co.uk/bbc_radio_one')">BBC Radio 1</button>
<button class="preset-btn" onclick="setStreamAndPlay('http://icecast.omroep.nl/radio1-bb-mp3')">NPO Radio 1</button>
<button class="preset-btn" onclick="setStreamAndPlay('http://direct.fipradio.fr/live/fip-midfi.mp3')">FIP Radio</button>
</div>
</div>
<div class="player-card">
<h3 style="margin-bottom: 15px; color: #00d4ff;">📁 Плейлист (SD-карта)</h3>
<div class="file-list" id="fileList">
<div class="file-item">Загрузка...</div>
</div>
</div>
<div class="wifi-info" id="wifiInfo"></div>
</div>
<script>
let playerState = {
playing: false,
streaming: false,
track: ''
};
function sendCmd(cmd, value) {
let url = '/cmd?action=' + cmd;
if (value) url += '&value=' + value;
fetch(url).then(r => r.json()).then(data => updateUI(data));
}
function sendCmdWithFile(cmd, file) {
fetch('/cmd?action=' + cmd + '&file=' + encodeURIComponent(file))
.then(r => r.json())
.then(data => updateUI(data));
}
function sendCmdWithUrl(cmd, url) {
fetch('/cmd?action=' + cmd + '&url=' + encodeURIComponent(url))
.then(r => r.json())
.then(data => updateUI(data));
}
function togglePlay() {
if (playerState.playing) {
sendCmd('pause');
} else {
sendCmd('play');
}
}
function setVolume(val) {
document.getElementById('volValue').textContent = val + '%';
sendCmd('volume', val);
}
function setBass(val) {
document.getElementById('bassValue').textContent = val;
sendCmd('bass', val);
}
function setTreble(val) {
document.getElementById('trebleValue').textContent = val;
sendCmd('treble', val);
}
function playFile(filename) {
sendCmdWithFile('playfile', filename);
}
function setStreamAndPlay(url) {
document.getElementById('streamUrl').value = url;
playStream();
}
function playStream() {
const url = document.getElementById('streamUrl').value.trim();
if (!url) return;
sendCmdWithUrl('stream', url);
}
function updateUI(data) {
playerState.playing = data.playing;
playerState.streaming = data.streaming;
playerState.track = data.track;
document.getElementById('trackName').innerHTML = (data.track || 'Нет трека') +
(data.streaming ? '<span class="stream-badge">STREAM</span>' :
(data.playing && !data.streaming ? '<span class="file-badge">FILE</span>' : ''));
document.getElementById('status').textContent = data.status;
document.getElementById('playBtn').textContent = (data.playing && !data.paused) ? '⏸' : '▶';
document.getElementById('progressBar').style.width = data.progress + '%';
document.getElementById('bitrateInfo').textContent = data.bitrate > 0 ?
'Bitrate: ' + Math.round(data.bitrate / 1024) + ' KB/s' : '';
document.querySelectorAll('.file-item').forEach(item => {
item.classList.toggle('playing', item.dataset.file === data.fileName);
});
}
function loadFileList() {
fetch('/files')
.then(r => r.json())
.then(files => {
const list = document.getElementById('fileList');
list.innerHTML = files.map(f =>
`<div class="file-item" data-file="${f}" onclick="playFile('${f}')">
<span class="file-icon">🎵</span>
<span>${f}</span>
</div>`
).join('') || '<div class="file-item">Нет MP3 файлов</div>';
});
}
function updateStatus() {
fetch('/status')
.then(r => r.json())
.then(data => updateUI(data));
}
loadFileList();
fetch('/wifi').then(r => r.text()).then(t => document.getElementById('wifiInfo').innerHTML = t);
setInterval(updateStatus, 1000);
setInterval(loadFileList, 10000);
</script>
</body>
</html>
)rawliteral";
// ==================== ФУНКЦИИ VS1003 ====================
void vs1003_writeRegister(uint8_t reg, uint16_t value) {
while (!digitalRead(VS_DREQ)) delay(1);
digitalWrite(VS_XCS, LOW);
SPI.transfer(VS_WRITE_COMMAND);
SPI.transfer(reg);
SPI.transfer(value >> 8);
SPI.transfer(value & 0xFF);
digitalWrite(VS_XCS, HIGH);
while (!digitalRead(VS_DREQ)) delay(1);
}
uint16_t vs1003_readRegister(uint8_t reg) {
while (!digitalRead(VS_DREQ)) delay(1);
digitalWrite(VS_XCS, LOW);
SPI.transfer(VS_READ_COMMAND);
SPI.transfer(reg);
uint16_t result = SPI.transfer(0xFF) << 8;
result |= SPI.transfer(0xFF);
digitalWrite(VS_XCS, HIGH);
while (!digitalRead(VS_DREQ)) delay(1);
return result;
}
void vs1003_sendData(const uint8_t* data, size_t len) {
while (len > 0) {
while (!digitalRead(VS_DREQ));
digitalWrite(VS_XDCS, LOW);
size_t chunk = min(len, (size_t)32);
SPI.writeBytes(data, chunk);
digitalWrite(VS_XDCS, HIGH);
data += chunk;
len -= chunk;
}
}
void vs1003_setVolume(int vol) {
if (vol < 0) vol = 0;
if (vol > 100) vol = 100;
int attenuation = map(vol, 0, 100, 0xFE, 0x00);
uint16_t volValue = (attenuation << 8) | attenuation;
vs1003_writeRegister(VS_VOL_REG, volValue);
currentVolume = vol;
}
void vs1003_setBass(int bass) {
uint16_t bassReg = vs1003_readRegister(VS_BASS_REG);
bassReg &= 0xFF0F;
bassReg |= (bass & 0x0F) << 4;
vs1003_writeRegister(VS_BASS_REG, bassReg);
currentBass = bass;
}
void vs1003_setTreble(int treble) {
uint16_t bassReg = vs1003_readRegister(VS_BASS_REG);
bassReg &= 0xF0FF;
bassReg |= (treble & 0x0F) << 8;
vs1003_writeRegister(VS_BASS_REG, bassReg);
currentTreble = treble;
}
void vs1003_softReset() {
vs1003_writeRegister(VS_MODE_REG, 0x0804);
delay(100);
while (!digitalRead(VS_DREQ));
}
void vs1003_cancel() {
vs1003_writeRegister(VS_MODE_REG, 0x0808);
delay(100);
}
void vs1003_init() {
pinMode(VS_XRESET, OUTPUT);
pinMode(VS_XCS, OUTPUT);
pinMode(VS_XDCS, OUTPUT);
pinMode(VS_DREQ, INPUT);
digitalWrite(VS_XRESET, LOW);
digitalWrite(VS_XCS, HIGH);
digitalWrite(VS_XDCS, HIGH);
delay(10);
digitalWrite(VS_XRESET, HIGH);
delay(10);
SPI.begin(VS_SCK, VS_MISO, VS_MOSI);
SPI.setFrequency(1000000);
vs1003_writeRegister(VS_MODE_REG, 0x0804);
delay(100);
while (!digitalRead(VS_DREQ)) delay(1);
vs1003_writeRegister(VS_CLOCKF_REG, 0x8800);
delay(10);
vs1003_setVolume(50);
vs1003_writeRegister(VS_BASS_REG, 0x0000);
SPI.setFrequency(4000000);
}
// ==================== ОСТАНОВКА ВОСПРОИЗВЕДЕНИЯ ====================
void stopPlayback() {
isPlaying = false;
isPaused = false;
isStreaming = false;
if (currentFile) {
currentFile.close();
}
if (streamClient.connected()) {
streamClient.stop();
}
if (httpClient.connected()) {
httpClient.end();
}
vs1003_softReset();
currentTrackName = "Нет трека";
currentStreamURL = "";
currentFileName = "";
streamBytesReceived = 0;
streamBitrate = 0;
currentFilePosition = 0;
currentFileSize = 0;
if (playTaskHandle != NULL) {
vTaskDelete(playTaskHandle);
playTaskHandle = NULL;
}
}
// ==================== ВОСПРОИЗВЕДЕНИЕ ФАЙЛА С SD ====================
void playFileTask(void* parameter) {
const size_t BUFFER_SIZE = 512;
uint8_t buffer[BUFFER_SIZE];
currentFileSize = currentFile.size();
while (isPlaying && currentFile.available()) {
if (isPaused) {
vTaskDelay(100 / portTICK_PERIOD_MS);
continue;
}
currentFilePosition = currentFile.position();
size_t bytesRead = currentFile.read(buffer, BUFFER_SIZE);
if (bytesRead > 0) {
vs1003_sendData(buffer, bytesRead);
}
vTaskDelay(1 / portTICK_PERIOD_MS);
}
if (currentFile) {
currentFile.close();
}
isPlaying = false;
isPaused = false;
playTaskHandle = NULL;
vTaskDelete(NULL);
}
void playMP3File(const char* filename) {
String fname = String(filename);
if (fname.startsWith("http://") || fname.startsWith("https://")) {
Serial.println("Ошибка: попытка воспроизвести URL как файл");
return;
}
stopPlayback();
currentFile = SD.open(filename);
if (!currentFile) {
Serial.println("Ошибка открытия файла: " + String(filename));
currentTrackName = "Ошибка: файл не найден";
return;
}
currentFileName = String(filename);
currentTrackName = String(filename);
isPlaying = true;
isPaused = false;
isStreaming = false;
currentFilePosition = 0;
currentFileSize = currentFile.size();
Serial.println("Воспроизведение файла: " + String(filename));
xTaskCreatePinnedToCore(
playFileTask,
"PlayFileTask",
8192,
NULL,
1,
&playTaskHandle,
1
);
}
// ==================== ВОСПРОИЗВЕДЕНИЕ ПОТОКА С URL ====================
void playStreamTask(void* parameter) {
const size_t BUFFER_SIZE = 2048;
uint8_t buffer[BUFFER_SIZE];
unsigned long lastDataTime = millis();
unsigned long reconnectDelay = 2000;
int reconnectAttempts = 0;
const int MAX_RECONNECTS = 10;
while (isPlaying && isStreaming) {
if (isPaused) {
vTaskDelay(100 / portTICK_PERIOD_MS);
continue;
}
if (!streamClient.connected()) {
httpClient.end();
httpClient.setTimeout(10000);
httpClient.setReuse(true);
if (!httpClient.begin(streamClient, currentStreamURL)) {
Serial.println("Не удалось начать HTTP-запрос");
vTaskDelay(reconnectDelay / portTICK_PERIOD_MS);
reconnectAttempts++;
if (reconnectAttempts >= MAX_RECONNECTS) {
Serial.println("Превышено количество попыток переподключения");
break;
}
continue;
}
httpClient.addHeader("Icy-MetaData", "1");
httpClient.addHeader("User-Agent", "ESP32-MP3-Player/1.0");
int httpCode = httpClient.GET();
if (httpCode != HTTP_CODE_OK && httpCode != 200) {
Serial.println("HTTP ошибка: " + String(httpCode));
httpClient.end();
vTaskDelay(reconnectDelay / portTICK_PERIOD_MS);
reconnectAttempts++;
if (reconnectAttempts >= MAX_RECONNECTS) {
break;
}
continue;
}
reconnectAttempts = 0;
Serial.println("Подключено к потоку, HTTP: " + String(httpCode));
lastDataTime = millis();
}
int available = streamClient.available();
if (available > 0) {
int toRead = min(available, (int)BUFFER_SIZE);
int bytesRead = streamClient.read(buffer, toRead);
if (bytesRead > 0) {
vs1003_sendData(buffer, bytesRead);
streamBytesReceived += bytesRead;
lastDataTime = millis();
unsigned long now = millis();
if (now - lastStreamBitrateUpdate >= 1000) {
streamBitrate = streamBytesReceived;
streamBytesReceived = 0;
lastStreamBitrateUpdate = now;
}
}
} else {
if (millis() - lastDataTime > 30000) {
Serial.println("Таймаут потока, переподключение...");
streamClient.stop();
httpClient.end();
vTaskDelay(1000 / portTICK_PERIOD_MS);
reconnectAttempts++;
if (reconnectAttempts >= MAX_RECONNECTS) {
break;
}
}
vTaskDelay(5 / portTICK_PERIOD_MS);
}
}
streamClient.stop();
httpClient.end();
isPlaying = false;
isStreaming = false;
playTaskHandle = NULL;
Serial.println("Поток завершён");
vTaskDelete(NULL);
}
void playMP3Stream(const char* url) {
stopPlayback();
currentStreamURL = String(url);
currentTrackName = String(url);
currentFileName = "";
isPlaying = true;
isPaused = false;
isStreaming = true;
streamBytesReceived = 0;
streamBitrate = 0;
lastStreamBitrateUpdate = millis();
Serial.println("Воспроизведение потока: " + String(url));
xTaskCreatePinnedToCore(
playStreamTask,
"PlayStreamTask",
12288,
NULL,
1,
&playTaskHandle,
1
);
}
// ==================== WEB-ОБРАБОТЧИКИ ====================
void sendStatus() {
int progress = 0;
String statusText = "Готов";
if (isPlaying && !isPaused) {
statusText = isStreaming ? "Воспроизведение потока ▶" : "Воспроизведение ▶";
} else if (isPlaying && isPaused) {
statusText = "Пауза ⏸";
} else {
statusText = "Готов";
}
if (!isStreaming && currentFileSize > 0 && currentFilePosition > 0) {
progress = (int)(currentFilePosition * 100 / currentFileSize);
}
String json = "{";
json += "\"playing\":" + String(isPlaying && !isPaused ? "true" : "false") + ",";
json += "\"paused\":" + String(isPaused ? "true" : "false") + ",";
json += "\"streaming\":" + String(isStreaming ? "true" : "false") + ",";
json += "\"track\":\"" + currentTrackName + "\",";
json += "\"fileName\":\"" + currentFileName + "\",";
json += "\"status\":\"" + statusText + "\",";
json += "\"volume\":" + String(currentVolume) + ",";
json += "\"bitrate\":" + String(streamBitrate) + ",";
json += "\"progress\":" + String(progress);
json += "}";
server.send(200, "application/json", json);
}
void handleStatus() {
sendStatus();
}
void handleCommand() {
String action = server.arg("action");
String value = server.arg("value");
String file = server.arg("file");
String url = server.arg("url");
if (action == "play") {
if (isPlaying && isPaused) {
isPaused = false;
} else if (!isPlaying) {
if (isStreaming && currentStreamURL.length() > 0) {
playMP3Stream(currentStreamURL.c_str());
} else if (!isStreaming && currentFileName.length() > 0) {
playMP3File(currentFileName.c_str());
}
}
}
else if (action == "pause") {
if (isPlaying) {
isPaused = !isPaused;
}
}
else if (action == "stop") {
stopPlayback();
}
else if (action == "playfile") {
playMP3File(file.c_str());
}
else if (action == "stream") {
if (url.length() > 0) {
playMP3Stream(url.c_str());
}
}
else if (action == "volume") {
vs1003_setVolume(value.toInt());
}
else if (action == "bass") {
vs1003_setBass(value.toInt());
}
else if (action == "treble") {
vs1003_setTreble(value.toInt());
}
else if (action == "next") {
Serial.println("Next - не реализовано");
}
else if (action == "prev") {
Serial.println("Prev - не реализовано");
}
sendStatus();
}
void handleFiles() {
String json = "[";
File root = SD.open("/");
bool first = true;
while (true) {
File entry = root.openNextFile();
if (!entry) break;
String name = entry.name();
if (name.endsWith(".mp3") || name.endsWith(".MP3")) {
if (!first) json += ",";
json += "\"" + name + "\"";
first = false;
}
entry.close();
}
root.close();
json += "]";
server.send(200, "application/json", json);
}
void handleWiFi() {
String info = "IP: " + WiFi.localIP().toString();
if (WiFi.getMode() & WIFI_AP) {
info += " | AP: " + WiFi.softAPIP().toString();
}
info += " | MAC: " + WiFi.macAddress();
server.send(200, "text/plain", info);
}
void handleRoot() {
server.send(200, "text/html", HTML_PAGE);
}
// ==================== SETUP ====================
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n=== ESP32 MP3 Player & Radio (ИСПРАВЛЕННАЯ ВЕРСИЯ) ===");
vs1003_init();
Serial.println("VS1003 инициализирован");
if (!SD.begin(SD_CS)) {
Serial.println("Ошибка инициализации SD-карты!");
} else {
Serial.println("SD-карта инициализирована");
}
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(AP_SSID, AP_PASSWORD);
Serial.println("AP запущен: " + String(AP_SSID));
Serial.println("AP IP: " + WiFi.softAPIP().toString());
if (strlen(STA_SSID) > 0 && String(STA_SSID) != "YOUR_WIFI_SSID") {
WiFi.begin(STA_SSID, STA_PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nПодключено к WiFi: " + WiFi.localIP().toString());
} else {
Serial.println("\nНе удалось подключиться к WiFi");
}
}
server.on("/", HTTP_GET, handleRoot);
server.on("/cmd", HTTP_GET, handleCommand);
server.on("/status", HTTP_GET, handleStatus);
server.on("/files", HTTP_GET, handleFiles);
server.on("/wifi", HTTP_GET, handleWiFi);
server.begin();
Serial.println("Web-сервер запущен на порту 80");
Serial.println("Откройте браузер: http://" + WiFi.softAPIP().toString());
}
// ==================== LOOP ====================
void loop() {
server.handleClient();
delay(1);
}
вы можете его скормить какому то ии…
затем спросить какие ошибки у вас могли быть, и какие исправленны, затем проверить ошибки на своем коде, и если они есть, заплатить мне, столько на сколько хотели поспорить))) при этом если сумма нормальная, а могу скинуть реквизиты в телеграмме, для получения награды)))
подходит ?)))