Adafruit_VS1053_Library
Adafruit_VS1053_Library copied to clipboard
Does not work on ESP32
The VS1053 library does not work with ESP32's.
- Arduino board: Adafruit ESP32-S3 NO_PSRAM
- Adafruit hardware: Breakout https://www.adafruit.com/product/1381
- Arduino IDE version: 2.0.3 & 2.1.0
Steps to reproduce:
- Open the "player_simple.ino" example after installing the library
- Set pins for BREAKOUT_RESET 9, BREAKOUT_CS 10, BREAKOUT_CS 6, CARDCS 4, DREQ 3
- Connect according to the above
- Compile & upload
This will cause a hardware crash on the ESP32, causing it to reboot at a regular interval. Changing from this constructor:
Adafruit_VS1053_FilePlayer(BREAKOUT_RESET, BREAKOUT_CS, BREAKOUT_DCS, DREQ, CARDCS);
... to the software SPI version:
Adafruit_VS1053_FilePlayer(MOSI, MISO, SCK, BREAKOUT_RESET, BREAKOUT_CS, BREAKOUT_DCS, DREQ, CARDCS);
... does stop the crashing, but the device still does not play audio files. Printing out MOSI, MISO and SCK lists the correct pins (35, 37, 36) so it does not make much sense that calling this makes a difference (but it does).
This library contains working code (as a workaround), but I'd at least update the Adafruit guide for the VS1053 products and point out that this library does not work with ESP32 for now..
It looks like this issue has been raised in issue #45 but it was closed as a "cannot reproduce". Trying the sample file with an ESP32 S3 with no psram will make it 100% reproducible.
The approach in the @eziya fork is to essentially not use interrupts
The issue is with interrupt playback. That currently is not working for ESP32 platforms. In the player_simple.ino example, comment out this line:
musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT); // DREQ int
and then it should work for the non-interrupt playback call to:
musicPlayer.playFullFile("/track001.mp3");
The subsequent interrupt based playback call(s) will not work however:
musicPlayer.startPlayingFile("/track002.mp3");
Do not have the breakout version of this to test. However, using a Feather ESP32-S3 (PID 5323) and commenting out the DREQ setup in feather_player.ino works.
I can confirm that removing the interrupt does work. As suggested, playFullFile does play the full file. Using startPlayingFile also works if you make sure to fill the buffer in your loop:
void loop() {
musicPlayer.feedBuffer();
// more stuff here
}
This works as long as the loop time is less than 150ms so the buffer stays full. It would be quite easy to add a timer that can just keep the buffer filled. I might make use this approach. I can do a PR on this if it's useful?
I dont think a timer will work, because thise time depends in the bitrate of the file and probably even more factors. DREQ pin is specifically there to tell you when it is time to send more data, so „watching“ it, with or without interrupt, is the best approach here.
Spent some more time on this. What is crashing is reading from SD inside an interrupt. If I just set a flag in the interrupt and then check this flag in the loop, it works fine. Here's an example that is using interrupts, but just not to fill the buffer: https://pastebin.com/qwcJVs8E
It is basically the same as calling the feedBuffer routine from the main loop, but it will only do it when required. I would like to get this to work with interrupts, but I'm unsure where to go next. Any hints helpful!
sidenote: I've tried using the SDFat option present in the library, but it looks like that's not been maintained in a while? Setting PREFER_SDFAT_LIBRARY yields compilation errors. Tried using adafruit/SdFat - Adafruit Fork@^1.5.1 as well as 2.2.1. Slightly different output
Turns out that it's not hard to get this working by using FreeRtos Functions. Adding the following code to Adafruit_VS1053.cpp will use a FreeRTOS Task to poll the feedBuffer routine.
I can deliver a PR if you think this is a good way to solve it. I can also test it. My current fork of the library allows me to play files directly from SPIFFS or any other files system.
static void feedEsp32( void * pvParameters ){
for(;;){
myself->feedBuffer();
delay(10);
}
}
boolean Adafruit_VS1053_FilePlayer::useInterrupt(uint8_t type) {
myself = this; // oy vey
if (type == VS1053_FILEPLAYER_TIMER0_INT) {
#if defined(ARDUINO_ARCH_AVR)
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
return true;
#elif defined(ESP32)
TaskHandle_t Task1;
xTaskCreatePinnedToCore(
feedEsp32, /* Task function. */
"Task1", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
&Task1, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
#elif defined(__arm__) && defined(CORE_TEENSY)
Here's my current code https://github.com/adafruit/Adafruit_VS1053_Library/compare/master...jenschr:Adafruit_VS1053_Library:master
I don't think it's possible to get the non-timer method to ever work on ESP32 (ref: Calling long ISR's will crash ), so in my codebase, I'm printing out a warning on Serial if this is used.
Thanks a lot @jenschr that fixed it for me!