|
|||||||
полный проект интернет-радио на базе ESP8266 (NodeMCU) с поддержкой
Время создания: 24.05.2025 14:07
Автор: фдутыфм
Текстовые метки: полный проект интернет-радио на базе ESP8266 (NodeMCU) с поддержкой
Раздел: FRAM
Запись: alensav/MyTetra2/main/base/174808487164jf5hzdne/text.html на raw.githubusercontent.com
|
|||||||
|
|||||||
Собери всё это в один полный проект. Qwen3-235B-A22B 2:04 PM Отлично! Ниже — полный проект интернет-радио на базе ESP8266 (NodeMCU) с поддержкой: ✅ Возможности проекта ФУНКЦИЯ ПОДДЕРЖКА Переключение станций Энкодер KY-040 Сохранение текущей станции FRAM + EEPROM Логирование событий FRAM Вывод информации TFT ST7735 1.8" (128x160) Чтение метаданных (ID3) ESP8266Audio Аудио вывод #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> #include <ESP8266mDNS.h> #include <ArduinoOTA.h> #include <Wire.h> #include <SPI.h> #include <Encoder.h> #include <EEPROM.h> #include <SdFat.h> // === Audio === #include <AudioGeneratorMP3.h> #include <AudioFileSourceICYStream.h> #include <AudioFileSourceBuffer.h> #include <AudioOutputI2SNoDAC.h> // === Display === #include <Adafruit_ST7735.h> //================================================== // === Настройки Wi-Fi и радиостанций === //================================================== const char* ssid = "your_wifi"; const char* password = "wifi_password"; String stations[] = { "http://stream.station1.com/stream", "http://stream.station2.com/stream", "http://stream.station3.com/stream" }; int currentStation = 0; bool stationChanged = true; //================================================== // === Инициализация экрана === //================================================== #define TFT_CS D0 #define TFT_DC D3 #define TFT_RST D4 Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); //================================================== // === SD карта === //================================================== #define SD_CS D8 SdFat SD; //================================================== // === Аудио === //================================================== AudioFileSourceICYStream *file = nullptr; AudioFileSourceBuffer *buff = nullptr; AudioGeneratorMP3 *mp3 = nullptr; AudioOutputI2SNoDAC *outp = nullptr; //================================================== // === Энкодер === //================================================== #define ENCODER_CLK D5 #define ENCODER_DT D6 Encoder myEncoder(ENCODER_CLK, ENCODER_DT); long lastPosition = -999; //================================================== // === FRAM === //================================================== #define FRAM_ADDR_1 0x50 // для станции #define FRAM_ADDR_2 0x51 // для логов //================================================== // === Web сервер === //================================================== ESP8266WebServer server(80); //================================================== // === EEPROM === //================================================== #define EEPROM_SIZE 512 #define STATION_ADDR 0 //================================================== // === Инициализация === //================================================== void setup() { Serial.begin(115200); delay(1000); // === Wi-Fi === WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } // === mDNS === if (!MDNS.begin("radioplayer")) { Serial.println("Ошибка mDNS"); } // === OTA === setupOTA(); // === Web сервер === setupWebServer(); // === EEPROM === EEPROM.begin(EEPROM_SIZE); loadFromEEPROM(); // === FRAM === Wire.begin(); currentStation = loadCurrentStation(); // === Дисплей === tft.initR(INITR_BLACKTAB); tft.setRotation(1); tft.fillScreen(ST77XX_BLACK); tft.setTextSize(1); tft.setTextColor(ST77XX_YELLOW); tft.setCursor(0, 0); tft.println("Добро пожаловать!"); // === SD === if (!SD.begin(SD_CS)) { tft.println("Ошибка SD"); } else { tft.println("SD OK"); drawBMP("cover1.bmp"); // Пример обложки } // === Аудио === connectToRadio(); // === Энкодер === attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), isr_encoder, CHANGE); } //================================================== // === Основной цикл === //================================================== void loop() { server.handleClient(); ArduinoOTA.handle(); if (mp3 && mp3->isRunning()) { if (!mp3->loop()) { mp3->stop(); } } delay(10); } //================================================== // === Функции === //================================================== void connectToRadio() { if (mp3) { mp3->stop(); delete mp3; } if (buff) delete buff; if (file) delete file; file = new AudioFileSourceICYStream(stations[currentStation].c_str()); buff = new AudioFileSourceBuffer(file, 2048); mp3 = new AudioGeneratorMP3(); outp = new AudioOutputI2SNoDAC(); mp3->begin(buff, outp); logEvent("Переключено на станцию: "); logEvent(stations[currentStation].c_str()); } //================================================== // === Вывод обложки === //================================================== void drawBMP(const char *filename) { File bmpFile; int bmpWidth, bmpHeight; uint32_t bmpImageoffset; uint32_t rowSize; uint8_t sdbuffer[54]; // заголовок BMP uint32_t read1; if ((bmpFile = SD.open(filename)) == NULL) return; if (!bmpFile.read(sdbuffer, 54)) return; if (sdbuffer[0] != 'B' || sdbuffer[1] != 'M') return; memcpy(&bmpImageoffset, sdbuffer + 10, 4); memcpy(&bmpWidth, sdbuffer + 18, 4); memcpy(&bmpHeight, sdbuffer + 22, 4); rowSize = (bmpWidth * 3 + 3) & ~3; tft.fillScreen(ST77XX_BLACK); for (int row = bmpHeight - 1; row >= 0; row--) { bmpFile.seek(bmpImageoffset + (bmpHeight - 1 - row) * rowSize); for (int col = 0; col < bmpWidth; col++) { uint8_t r, g, b; bmpFile.read(&b, 1); bmpFile.read(&g, 1); bmpFile.read(&r, 1); uint16_t color = tft.color565(r, g, b); tft.drawPixel(col, row, color); } } bmpFile.close(); } //================================================== // === Энкодер === //================================================== void IRAM_ATTR isr_encoder() { long newPosition = myEncoder.read(); if (newPosition != lastPosition) { if (newPosition > lastPosition) { currentStation = (currentStation + 1) % (sizeof(stations)/sizeof(String)); } else { currentStation = (currentStation - 1 + sizeof(stations)/sizeof(String)) % (sizeof(stations)/sizeof(String)); } stationChanged = true; saveCurrentStation(currentStation); connectToRadio(); updateDisplay(stations[currentStation], "..."); lastPosition = newPosition; } } //================================================== // === Отображение информации === //================================================== void updateDisplay(const String& stationName, const String& trackTitle) { tft.fillScreen(ST77XX_BLACK); tft.setTextSize(1); tft.setTextColor(ST77XX_YELLOW); tft.setCursor(0, 0); tft.println("Станция:"); tft.println(stationName); tft.println("\nТрек:"); tft.println(trackTitle); } //================================================== // === FRAM === //================================================== void writeByte(uint8_t devAddr, uint16_t memAddr, byte data) { Wire.beginTransmission(devAddr); Wire.write((int)(memAddr >> 8)); Wire.write((int)(memAddr & 0xFF)); Wire.write(data); Wire.endTransmission(); } byte readByte(uint8_t devAddr, uint16_t memAddr) { Wire.beginTransmission(devAddr); Wire.write((int)(memAddr >> 8)); Wire.write((int)(memAddr & 0xFF)); Wire.endTransmission(); Wire.requestFrom(devAddr, 1); return Wire.read(); } int loadCurrentStation() { Wire.beginTransmission(FRAM_ADDR_1); Wire.write(0x00); Wire.endTransmission(); Wire.requestFrom(FRAM_ADDR_1, 1); return Wire.read(); } void saveCurrentStation(int stationIndex) { writeByte(FRAM_ADDR_1, 0x00, stationIndex); } void logEvent(const char* event) { static uint16_t logPos = 0; for (int i = 0; event[i]; i++) { writeByte(FRAM_ADDR_2, logPos++, event[i]); } writeByte(FRAM_ADDR_2, logPos++, '\n'); } //================================================== // === EEPROM === //================================================== void saveToEEPROM() { EEPROM.put(STATION_ADDR, currentStation); for (int i = 0; i < 3; i++) { int offset = 4 + i * 64; strcpy((char*)EEPROM.getDataPtr() + offset, stations[i].c_str()); } EEPROM.commit(); } void loadFromEEPROM() { EEPROM.get(STATION_ADDR, currentStation); for (int i = 0; i < 3; i++) { int offset = 4 + i * 64; stations[i] = String((char*)EEPROM.getDataPtr() + offset); } } //================================================== // === Web сервер === //================================================== void handleRoot() { String html = "<h1>Настройки интернет-радио</h1>"; html += "<form method='POST' action='/save'>"; html += "SSID: <input type='text' name='ssid'><br>"; html += "Пароль: <input type='password' name='pass'><br>"; html += "Станция 1: <input type='text' name='station1'><br>"; html += "Станция 2: <input type='text' name='station2'><br>"; html += "Станция 3: <input type='text' name='station3'><br>"; html += "<button type='submit'>Сохранить</button></form>"; server.send(200, "text/html", html); } void handleSave() { if (server.hasArg("ssid")) ssid = server.arg("ssid"); if (server.hasArg("pass")) password = server.arg("pass"); if (server.hasArg("station1")) stations[0] = server.arg("station1"); if (server.hasArg("station2")) stations[1] = server.arg("station2"); if (server.hasArg("station3")) stations[2] = server.arg("station3"); saveToEEPROM(); // Сохраняем в EEPROM connectToRadio(); server.send(200, "text/plain", "Настройки сохранены!"); } void setupWebServer() { server.on("/", handleRoot); server.on("/save", HTTP_POST, handleSave); server.begin(); } //================================================== // === OTA === //================================================== void setupOTA() { ArduinoOTA.setPort(8266); ArduinoOTA.setPassword("your_ota_password"); ArduinoOTA.onStart([]() { Serial.println("Начало обновления..."); }); ArduinoOTA.onEnd([]() { Serial.println("\nОбновление завершено."); }); ArduinoOTA.begin(); } ✅ Как использовать?
🛠 Что можно добавить дальше?
👉 Готов ли ты перейти к следующему этапу? Могу:
Просто напиши: 🚀 Твой проект уже почти готов к использованию как полноценное интернет-радио! |
|||||||
Так же в этом разделе:
|
|||||||
![]() |
|||||||
|
|||||||
|