ESP32 — проигрывание mp3 файлов с SD через декодер PCM5102A

ESP32 — проигрывание mp3 файлов с SD через декодер PCM5102A

Целью было, как упомянуто в заголовке, последовательно проигрывать аудиофайлы в формате mp3, записанные на SD карту, через плату декодера интерфейса I2S PCM5102A с помощью ESP32-WROOM.

Про железо.

Плата декодера интерфейса I2S PCM5102A

Взято из LabKit :: WiFi интернет-радиоприёмник «Волчонок» для начинающих

На модуле PCM5102A могут быть или могут отсутствовать перемычки. Перемычки нужны!

Перемычки, смонтированные в поставке от продавца, смотрятся убого.
Если перемычки отсутствуют или не нравятся, напаиваем капельки припоя.
Перемычка H1L в положение L.
Перемычка H2L в положение L.
Перемычка H3L в положение H.
Перемычка H4L в положение L.

Модуль адаптер карты Micro SD

Estardyn ESP32 38pin

Еще раз про соединения. Обозначения контактов ESP32 относятся к переходной плате.

Таблица соединений на https://conntest.ru/program/esp32-proigryvanie-mp3-fajlov-s-sd-cherez-dekoder-pcm5102a

Как видно из скетча, нет явных назначений пинов, все назначения пинов используются по умолчанию. Подключение SD и декодера можно определить из следующих картинок и функций, которые в данном скетче не используются, за ненадобностью.

source = new AudioFileSourceSD();

SPI.begin(18, 19, 23, 5);
А, именно, sck – P18, miso – P19, mosi – P23, ss (CS) – P5.

out = new AudioOutputI2S();

out->SetPinout(26, 25, 22);
А, именно, bclkPin (BCK) – P26, wclkPin (LRCK) – P25, doutPin (DIN) – P22, SCK – GND

Дополнительно использовал для удобства загрузки скетча выносные кнопки , побольше чем на самом ESP32. RST замыкает на GND пин EN, BOOT замыкает на GND пин P0.

Добавленная библиотека:

GitHub - earlephilhower/ESP8266Audio: Arduino library to play MOD, WAV, FLAC, MIDI, RTTTL, MP3, and AAC files on I2S DACs or with a software emulated delta-sigma DAC on the ESP8266 and ESP32 and Pico

Пример скетча:

#include «SD.h»
#include «SPI.h»
#include <WiFi.h>
#include «AudioFileSourceSD.h»
#include «AudioGeneratorMP3.h»
#include «AudioOutputI2S.h»

AudioGeneratorMP3 *mp3 = nullptr;
AudioFileSourceSD *source = nullptr;
AudioOutputI2S *out = nullptr;

std::vector<String> fileList;
size_t currentFileIndex = 0;

void listDir(fs::FS &fs, const char *dirname) {
  Serial.printf(«Listing directory: %s», dirname);
  Serial.println();
  File root = fs.open(dirname);
  if (!root || !root.isDirectory()) {
    Serial.println(«Failed to open directory or not a directory»);
    return;
  }

  File file = root.openNextFile();
  while (file) {
    if (!file.isDirectory()) {
      fileList.push_back(file.path());
      Serial.print(»  FILE: «);
      Serial.print(file.name());
      Serial.print(»  SIZE: «);
      Serial.println(file.size());
    }
    file = root.openNextFile();
  }
}

void playNextFile() {
  if (currentFileIndex < fileList.size()) {
    Serial.println();
    Serial.printf(«Playing: %s», fileList[currentFileIndex].c_str());
    Serial.println();
    Serial.printf(«Playing: %s», fileList[currentFileIndex].c_str());
    Serial.println();
    Serial.printf(«Current File Index: %d», currentFileIndex);
    Serial.println();
    Serial.printf(«Current File Index: %d», currentFileIndex);
   
    Serial.println();
   
    // Создаем новый источник и генератор
    source = new AudioFileSourceSD(fileList[currentFileIndex].c_str());
    out = new AudioOutputI2S();
    out->SetGain(0.2);
    mp3 = new AudioGeneratorMP3();
    Serial.println(«04»);
    delay(100); // 100 миллисекунд пауза
    if (!mp3->begin(source, out)) {
      Serial.println(«Failed to start MP3 generator. Attempting to clean up…»);
      delete source;
      delete out;
      source = nullptr;
      out = nullptr;
      currentFileIndex++; // Перейти к следующему файлу даже если текущий не удалось воспроизвести
      delay(1000); // 1000 миллисекунд пауза
      playNextFile();
      return;
    }
   
    currentFileIndex++;
  } else {
    Serial.println(«No more files to play.»);
    delay(1000000);
  }
}


void setup() {
  WiFi.mode(WIFI_OFF);
  Serial.begin(115200);
  delay(1000);

  if (!SD.begin()) {
    Serial.println(«Card Mount Failed»);
    return;
  }

  listDir(SD, «/»);

  uint8_t cardType = SD.cardType();
  if (cardType == CARD_NONE) {
    Serial.println(«No SD card attached»);
    return;
  }
  Serial.println();
  Serial.print(«SD Card Type: «);
  if (cardType == CARD_MMC) {
    Serial.println(«MMC»);
  } else if (cardType == CARD_SD) {
    Serial.println(«SDSC»);
  } else if (cardType == CARD_SDHC) {
    Serial.println(«SDHC»);
  } else {
    Serial.println(«UNKNOWN»);
  }
 
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf(«SD Card Size: %lluMB», cardSize);

  Serial.println();
  Serial.println();
  Serial.printf(«Total space: %lluMB», SD.totalBytes() / (1024 * 1024));
  Serial.println();
  Serial.printf(«Used space: %lluMB», SD.usedBytes() / (1024 * 1024));
  Serial.println();

  playNextFile(); // Начинаем воспроизведение первого файла
}

void loop() {
  if (mp3 && mp3->isRunning()) {
    if (!mp3->loop()) {
      mp3->stop();
    }
  } else {
    if (mp3) {
      mp3->stop();
      delete mp3;
      mp3 = nullptr;
      //delay(100); // 100 миллисекунд пауза
      Serial.println(«05»);
    }
      if (source) {
      delete source;
      source = nullptr;
      Serial.println(«06»);
    }
    if (out) {
      delete out;
      out = nullptr;
      Serial.println(«07»);
    }
      delay(1000);
      playNextFile(); // Переходим к следующему файлу
  }

Не знаю почему, иногда, после перезаписи этого скетча нужно передернуть питание для обнаружения SD. Кажется, дело в плохом контакте CS.

Скачать SD_read_MP3_write_PCM5202A_04.ino:

SD_read_MP3_write_PCM5202A_04.ino (15 Загрузок)

Хотя вывод в монитор порта интересен только для отладки (а, может быть, и не только), но, с другой стороны, выявил проблему, правда, не совсем соотносящуюся с задачей. В Монитор порта Arduino IDE при выводе Serial.printf(«Playing: %s», fileList[currentFileIndex].c_str()), порой, выводятся следующие кракозябры, затирая часть сообщения.

�����������������������������������������������������������������

Решил использовать дублирования этого вывода. В любом случае, второй вывод не затирался, так и оставил. Оставляю все отладочные Serial.println(«xx»). Так как позже добавил для отладки Serial.printf(«Current File Index: %d», currentFileIndex), то получал кракозябры и после них, поэтому добавил, как и для Serial.printf(«Playing: %s», fileList[currentFileIndex].c_str()) повтор.

Кстати, не помогли следующие ухищрения:

— delay(1000);

— if (Serial.available()) {
// Игнорируем входящие данные
while (Serial.available()) {
Serial.read();
}
— fileList.clear();
fileList.resize(0); // Очищает все элементы и освобождает память

Проверял на своем альбоме OWT (One Way Ticket), 103 песни. Проигрывались первые 18 или 80 файлов, для оставшихся, после Serial.printf(«Playing: %s», fileList[currentFileIndex].c_str()) и Serial.printf(«Current File Index: %d», currentFileIndex)

выводилось “Failed to start MP3 generator. Attempting to clean up…”, и так до “No more files to play.”. 80 файлов проигрывалось гораздо реже (один раз).

Проверил вывод не через встроенный в ESP32 COM порт, а через электронный пульт-имитатор, в котором мост USB-UART обеспечивается микросхемой FT232RL. Никаких кракозябр. Оставил первые 18 файлов, а остальные переместил в папку END на этой же SD и получил корректный результат.

При подключении через пульт, заново запаял делитель для RX и, не проверив, подключил. Получились те же 5В, только через резистор 2,4 кОм. Вывод в монитор удался, а вот запись скетча — увы. Сильно обрадовало, что все работает по встроенному COM порту. Третий раз проигрываю 18 файлов — кракозябр пока не видно, завершение по плану. Питание и связь по встроенному COM порту. Может быть этому способствовало вырубание RX на внешнем контакте, или, его нужно было просто подтянуть к 3,3 В через резистор. Не понятно, как будто ESP32 думает, и, довольно глупо. Не будем уподобляться Нине Леонидовне, которая говорила: — Я все правильно делала, а IBM ошибалась.

Все это получилось с SD 4 GB, а с 32 GB – сбой, не видит карту. Причину пока не установил. Кажется, установил. Видимо, тот же CS, но тогда не додумался передернуть питание. Сейчас гоняет 103 файла на 32 GB, утром посмотрим. Утра ждать не пришлось, проиграл 16 файлов и, как обычно. Можно, конечно, сделать JOIN, но, лучше подумать. Ведь, почти все работает, правда, библиотеки чужие. И не от Microsoft, Borland или IAR. Хотя, и на том спасибо. Запустил снова, отыграл до конца, без кракозябр. Добавил слитый файл ( JOIN всех 103-х), переименовал на SD с 0000_ в начале, но почему-то в перечислении он оказался последним. Полезли кракозябры, кстати, они вылезают и на Serial.println(«xx»), не важно. Время проигрывания немного больше 6 часов. Еще 6 часов, если дойдет до слитого, но его проигрывать, вроде бы, не имеет смысла. На сей раз не прошло, на 26-м сломался. ХЗЧ. Нет, граждане, имею заметить, что уже несколько часов играет слитый. Добавлю о конце. Проиграл безупречно, все. Если есть вопросы, то не ко мне, а к ESP32, видимо, он сам решает.

Мы поняли… хватит, хватит…

1 лайк

Я здесь - новичок, не понял.

А схема соединений модулей то где? Или как у Осипова по цветным проводочкам собирать ваш проект?

Не хочется перерисовывать таблицу. Она здесь: https://conntest.ru/program/esp32-proigryvanie-mp3-fajlov-s-sd-cherez-dekoder-pcm5102a и еще (добавлен INMP441) здесь: https://conntest.ru/program/peredacha-zvuka-s-inmp441-na-dekoder-pcm5102a-s-pomoshhyu-esp32

дать бы тебе премию, за твои белые выделения, и за общую убогость материала.

1 лайк

Чёта не понял, кто в доме хозяин-то?

Может, тогда лучше его попросить материал подготовить? Может, у него вразумительнее получится? И опять же вопросы автору можно будет задать.

1 лайк

ну великий Я, знаю что есть тут вроде как профи, как минимум 2, один говорит что у него опыта больше и может проект делать за 200к, другой который готов пилить код за еду, и говорит что точно сделал бы, но вот кода от них не увижу наверное, хотя почитал бы… кстати вашу реализацию я бы тоже посмотрел бы, запилите статью ?)))) https://forum.arduino.ru/t/pleer-interaktivnogo-stenda/17278/3 как я понимаю это попытка сделать этот заказ, и посмотрел бы реализацию других)))

Gluteus Maximus?

Это что сейчас было?

Мне нравится, что вы трете. go on.

что то новое для вас?
по моему он в своем репертуаре.

Да, нет, вроде, раньше он всё про катушку, про жопу я как-то раньше не замечал

А мне – нет. Я задал Вам вопрос, но ответа, почему-то, не получил.

Или не барское это дело, всяким смердам отвечать? Путь довольствуются тем, что барин сами сказать изволили?

Но ведь не всякий поймёт всякое…

главный принцип “НЕ В ТЕМУ”, соблюден, а про жопу ли или про катушку, неважно.

Парни, я не в теме, только вчера оформил 3 публикации. Мне эти решения интересны (два). Зарегистрировался неделю назад, но та публикация почему-то не прошла, пришлось повторить. Насчет шрифта BOLD разберусь, может быть. SUUM CUIQUE.

Когда разберетесь, тогда это и кто-нибудь и прочтет. Может быть.

Пока других интересантов здесь вы вряд ли найдете.

Могу поспорить, что найдутся. Из-за того, что это реально работает. Месяцы, потраченные на поиск работающего решения в нете оказались потраченными зря. Но, если говорить честно, не совсем.

это не решения, это поделки уровня “мама, я сделяль”