?? «Хочу реализовать вывод обложки» , 👉 «Хочу добавить громкость через энкодер» , 👉 «Хочу реализовать визуализацию звука» , 👉 «Хочу пример сборки схемы»,

Qwen3-235B-A22B1:49 PM
Отлично! Я подготовлю для тебя четыре части в соответствии с твоими запросами:
📦 1. Вывод обложки на TFT ST7735 с microSD
🔧 Требуется:
- BMP-изображение размером 128x160 пикселей , 24 бита (RGB), без сжатия.
- Файлы: cover1.bmp, cover2.bmp и т.д. — должны лежать на SD.
✅ Пример кода: вывод обложки
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SdFat.h>
#define SD_CS D8
SdFat SD;
// === Инициализация экрана ===
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
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) {
Serial.println("Не могу открыть файл");
return;
}
if (!bmpFile.read(sdbuffer, 54)) {
Serial.println("Ошибка чтения заголовка");
return;
}
// Проверяем сигнатуру BMP
if (sdbuffer[0] != 'B' || sdbuffer[1] != 'M') {
Serial.println("Это не BMP");
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();
}
2. Регулировка громкости через энкодер KY-040
✅ Пример кода: изменение громкости
#define ENCODER_CLK D5
#define ENCODER_DT D6
Encoder myEncoder(ENCODER_CLK, ENCODER_DT);
long lastPosition = -999;
uint8_t volume = 127; // Громкость от 0 до 255
void IRAM_ATTR isr_volume() {
long newPosition = myEncoder.read();
if (newPosition != lastPosition) {
if (newPosition > lastPosition) {
volume = min(volume + 5, 255);
} else {
volume = max(volume - 5, 0);
}
lastPosition = newPosition;
setVolume(volume);
}
}
void setVolume(uint8_t vol) {
// Если используешь PCM5102 DAC, можно управлять через I²C
// Либо регулировать через PWM амплитуду, если выход через динамик
Serial.print("Громкость: ");
Serial.println(vol);
}
4. Пример сборки схемы (подключение компонентов)
КОМПОНЕНТ |
ПИН ESP8266 (NODEMCU) |
KY-040 CLK |
D5 (GPIO14) |
KY-040 DT |
D6 (GPIO12) |
KY-040 SW |
D7 (GPIO13) |
PCM5102 BCK |
D5 (GPIO14) |
PCM5102 LRC |
D8 (GPIO15) |
PCM5102 DIN |
TX (GPIO1) |
FM24CL64B SDA |
D2 (GPIO4) |
FM24CL64B SCL |
D1 (GPIO5) |
ST7735 CS |
D0 (GPIO16) |
ST7735 DC |
D3 (GPIO0) |
ST7735 RST |
D4 (GPIO2) |
microSD CS |
D8 (GPIO15) |
microSD MOSI |
D7 (GPIO13) |
microSD MISO |
D6 (GPIO12) |
microSD SCK |
D5 (GPIO14) |
⚠️ Убедитесь, что питание всех модулей 3.3 В . Используйте стабилизатор LM1117-3.3 или AMS1117-3.3.
📋 Сводная таблица пинов
НАЗНАЧЕНИЕ |
NODEMCU |
GPIO |
Энкодер CLK |
D5 |
GPIO14 |
Энкодер DT |
D6 |
GPIO12 |
Энкодер SW |
D7 |
GPIO13 |
ST7735 CS |
D0 |
GPIO16 |
ST7735 DC |
D3 |
GPIO0 |
ST7735 RST |
D4 |
GPIO2 |
I²S BCK |
D5 |
GPIO14 |
I²S LRC |
D8 |
GPIO15 |
I²S DIN |
TX |
GPIO1 |
FM24CL64B SDA |
D2 |
GPIO4 |
FM24CL64B SCL |
D1 |
GPIO5 |
microSD CS |
D8 |
GPIO15 |
microSD MOSI |
D7 |
GPIO13 |
microSD MISO |
D6 |
GPIO12 |
microSD SCK |
D5 |
GPIO14 |
🛠 Следующие шаги
Теперь ты можешь объединить всё в один скетч. Если хочешь, я могу подготовить полный пример проекта , который включает:
- Подключение к Wi-Fi
- Переключение станций энкодером
- Сохранение станции в FRAM
- Логирование событий
- Отображение информации на ST7735
- Вывод обложки с SD
- Изменение громкости
- Визуализацию звука
- 1. Web-конфигуратор (веб-интерфейс для настройки станций и Wi-Fi)
- ✅ Возможности:
- Настройка Wi-Fi без перепрошивки.
- Добавление/редактирование списка радиостанций.
- Изменение громкости, темы экрана и др.
- 🔧 Пример кода: простой Web-сервер с формой
#include <ESP8266WebServer.h>
ESP8266WebServer server(80);
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();
}
2. OTA-обновления прошивки (по воздуху)
✅ Возможности:
Обновление прошивки без проводов.
Поддержка отладки через Serial по Wi-Fi.
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
void setupOTA() {
ArduinoOTA.setPort(8266); // порт для обновлений
ArduinoOTA.setPassword("your_ota_password");
ArduinoOTA.onStart([]() {
Serial.println("Начало обновления...");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nОбновление завершено.");
});
ArduinoOTA.begin();
}
void loop() {
ArduinoOTA.handle();
}
3. Использование EEPROM как резервное хранение
✅ Где использовать:
Резервное хранение текущей станции.
Резервная копия логов при отсутствии FRAM.
Сохранение настроек, если FRAM выйдет из строя.
#include <EEPROM.h>
#define EEPROM_SIZE 512
#define STATION_ADDR 0
void saveToEEPROM() {
EEPROM.begin(EEPROM_SIZE);
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.begin(EEPROM_SIZE);
EEPROM.get(STATION_ADDR, currentStation);
for (int i = 0; i < 3; i++) {
int offset = 4 + i * 64;
stations[i] = String((char*)EEPROM.getDataPtr() + offset);
}
}
4. Сохранение истории прослушивания
✅ Возможности:
Хранение последних N треков.
Отображение истории на экране или через веб-интерфейс.
#define HISTORY_MAX 10
#define HISTORY_OFFSET 0x100
void logTrack(const char* station, const char* track) {
static uint16_t pos = 0;
char buffer[64];
snprintf(buffer, sizeof(buffer), "[%s] %s", station, track);
for (int i = 0; buffer[i]; i++) {
writeByte(FRAM_ADDR_2, HISTORY_OFFSET + pos++, buffer[i]);
}
writeByte(FRAM_ADDR_2, HISTORY_OFFSET + pos++, '\n');
if (pos > 1024) pos = 0; // циклическая запись
}
void printHistory() {
char c;
Serial.println("История воспроизведения:");
for (uint16_t i = HISTORY_OFFSET; i < HISTORY_OFFSET + 1024; i++) {
c = readByte(FRAM_ADDR_2, i);
if (c == 0) break;
Serial.print((char)c);
}
}