Adafruit VS1053B MP3 Decoder (Model 1381 V4) not working

This is my first post here as a Particle newbie so please forgive and correct any issues with my post.
I am attempting to get an Adafruit VS1053B MP3 Decoder (Model 1381 V4) working with limited success. Sine wave and volume functions work fine, but my MP3’s are stopping randomly midstream and the led status goes to a solid cyan. They play for random lengths of time, anywhere from 5 seconds to their full length. Sometimes it will play 5-6 songs in a row and then die. Sometimes it dies on the first song. If I play the same song five times in a row it will die at different times and sometimes play to completion. During the time they do play the quality is excellent.
Here are things I’ve tried, all with no improvement:
Various bitrates from 64, 128, 225 and 256. A total of 30+ MP3s tried.
Two brand new Adafruit VS1053B boards.
Three different Sandisk Ultra HC class 10 cards.
De-spiking caps.
10K pullups on the SPI data pins.
Re-routing my wiring.
Various code mods.

I am using ScruffR’s VS1053B cpp and header and have tried a number of changes to the player_simple code. I have butchered things up a bit experimenting. I think I have my SPI pins and connections set up correctly, but haven’t tried the alternate set of SPI pins yet. I think my choice for DREQ pin is OK. I don’t know if set the "musicPlayer.useInterrupt(5)’ up properly because I had problems with the VS1053_FILEPLAYER_PIN_INT (but it said the value was 5 so I used that?)
Any suggestion would be much appreciated.

This is the INO

#include "application.h"
# define PREFER_SDFAT_LIBRARY 1
//# define ARDUINO_STM32_FEATHER 1
# define PARTICLE 1
// ********************************************************************** /
// This is a library for the Adafruit VS1053 Codec Breakout
//
// Designed specifically to work with the Adafruit VS1053 Codec Breakout 
// ---->adafruitcom_products_1381 ....
// 
// Adafruit invests time and resources providing this open source code, 
// please support Adafruit and open-source hardware by purchasing 
// products from Adafruit!
//// Written by Limor Fried/Ladyada for Adafruit Industries.  
// BSD license, all text above must be included in any redistribution
// 
// Original library: githubcom_adafruit_Adafruit_VS1053_Library
// 
// Ported for Particle by ScruffR
// Forked and ported: githubcom_ScruffR_Adafruit_VS1053_Library
// ********************************************************************** /
//
// To run this example prepare a micro SD card with a file hierarchy like
// SD:
// ????01
//        001.mp3
//        002.mp3
// optionally up to ...  
//        998.mp3
//        999.mp3
//
// ********************************************************************** /

SYSTEM_THREAD(ENABLED)
#include "SdFat.h"
#include "Adafruit_VS1053.h"

//SerialLogHandler traceLog(LOG_LEVEL_WARN, { { "app", LOG_LEVEL_INFO } });

SdFat SD;

#define CLK     A3//13       // SPI Clock, shared with SD card BM "SCLK"
#define MISO    A4//12       // Input data, from VS1053/SD card BM "MISO"
#define MOSI    A5//11       // Output data, to VS1053/SD card BM "MOSI"



// These are the pins used for the music maker FeatherWing
const int  MP3_RESET        = D1;                 // VS1053 reset pin BM "RST"
const int  SD_CS            = A2;                 // SD Card chip select pin BM "SDCS"
const int  MP3_CS           = D3;                 // VS1053 chip select pin (output) BM "CS"
const int  DREQ             = D4;                 // VS1053 Data request, ideally an Interrupt pin BM "DREQ"
const int  MP3_DCS          = D5;                 // VS1053 Data/command select pin (output) BM "XDCS"

const char *fileNamePattern = "%03d.mp3";         // file name pattern to insert track number

Adafruit_VS1053_FilePlayer musicPlayer(MP3_RESET, MP3_CS, MP3_DCS, DREQ, SD_CS); 

int        trackNumber      = 0;
bool       needStart        = false;
//bool needs_restart = false;

int led_debug = D6;
void setup() {
pinMode(A3, OUTPUT);
pinMode(A4, INPUT);
pinMode(A5, OUTPUT);
  pinMode(D6, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(D1, OUTPUT);
  pinMode(D7, OUTPUT);
     //led_blink_fast(10);
  Particle.function("playSine", playSine);
  Particle.function("playTrack", playTrack);
  Particle.function("setVolume", setVolume);
  Particle.function("resetVS1053", resetVS1053);
 
  if (!SD.begin(SD_CS)) {
    //Log.error("SD failed, or not present");
    while(1) yield();                             // don't do anything more
  }
    led_blink_slow(5);
  Serial.println("SD OK!");
  //Log.info("Adafruit VS1053 Library Test");
  // initialise the music player
  
  if (!musicPlayer.begin()) {                     // initialise the music player
    Log.error("Couldn't find VS1053, do you have the right pins defined?");
     while(1) yield();
  }
  //Log.info("VS1053 found");
    led_blink_fast(10);

  // Make a tone to indicate VS1053 is working
   musicPlayer.sineTest(0x44, 200);

  // set current working directory
  SD.chdir("/01", true);
  // list files
  //SD.ls(&Serial, LS_R);

  // Set volume for left, right channels. lower numbers == louder volume!
  musicPlayer.setVolume(11,11);

  // ***** Two interrupt options! ***** 
  // This option uses timer0, this means timer1 & t2 are not required
  // (so you can use 'em for Servos, etc) BUT millis() can lose time
  // since we're hitchhiking on top of the millis() tracker
  //musicPlayer.useInterrupt(VS1053_FILEPLAYER_TIMER0_INT);
  
  // This option uses a pin interrupt. No timers required! But DREQ
  // must be on an interrupt pin. For Uno/Duemilanove/Diecimilla
  // that's Digital #2 or #3
  // See http://arduino.cc/en/Reference/attachInterrupt for other pins
  // *** This method is preferred ***

  if (musicPlayer.useInterrupt(5))//DREQ
  {
    digitalWrite(D7, HIGH);
    musicPlayer.setIsrCallback(blink);
  }
  else
    Log.info("DREQ pin is not an interrupt pin");

  // Alternatively just play an entire file at once
  // This doesn't happen in the background, instead, the entire
  // file is played and the program will continue when it's done!
  // musicPlayer.sineTest(0x44, 200);

//musicPlayer.playFullFile("001.mp3");
}

void loop() {
  static uint32_t msPlayStarted = 0;
  static uint32_t msLastAction = 0;

  if (needStart && trackNumber) {
    char fileName[32];
    char msg[128];
    uint32_t us = micros();

    // Start playing a file, then we can do stuff while waiting for it to finish
    snprintf(fileName, sizeof(fileName), fileNamePattern, trackNumber);
    //Log.trace("Starting: %lu", micros() - us); us = micros();
    if (musicPlayer.startPlayingFile(fileName)) {
      //Log.trace("Started: %lu", micros() - us); 
      us = micros();
      snprintf(msg, sizeof(msg), "Started playing '%s'",fileName);
      msPlayStarted = millis();
    }
    else {
      //Log.trace("Not started: %lu", micros() - us); 
      us = micros();
      snprintf(msg, sizeof(msg), "Could not open file '%s'",fileName);
    }
    //Log.info(msg);
    needStart = false;
  }

  if (millis() - msLastAction >= 1000) {
    uint32_t sec = (millis() - msPlayStarted) / 1000.0;
    //led_blink_fast(1);
    // file is now playing in the 'background' so now's a good time
    // to do something else like handling LEDs or buttons :)
    msLastAction = millis();
    //Serial.printf("\r%02lu:%02lu %s  ", sec / 60, sec % 60, musicPlayer.playingMusic ? "playing" : "stopped");
  }
}

void led_blink_fast(int reps)
{
  for (size_t i = 0; i < reps; i++)
  {
    digitalWrite(led_debug, HIGH);
    delay(10);
    digitalWrite(led_debug, LOW);
    delay(10); /* code */
  }
}

void led_blink_slow(int reps)
{
  for (size_t i = 0; i < reps; i++)
  {
    digitalWrite(led_debug, HIGH);
    delay(500);
    digitalWrite(led_debug, LOW);
    delay(500); /* code */
  }
}

int playTrack(const char* arg) {
  int n = atoi(arg);

  if (n > 0) {
    trackNumber = n;
    if (musicPlayer.playingMusic) {
      musicPlayer.stopPlaying();
    }
    needStart = true;
  }
  return trackNumber;
}

int setVolume(const char* arg) {
  int vol = atoi(arg);
  musicPlayer.setVolume(vol, vol);
  return vol;
}

int playSine(const char* arg) {
  int freq = atoi(arg);
  musicPlayer.sineTest(freq, 1000);            
  musicPlayer.reset();    
  return freq;
}

int resetVS1053(const char* arg){
int freq = atoi(arg);
musicPlayer.reset();
musicPlayer.begin();
return 1;
}
void blink(void) {
    //digitalWriteFast(D7, !pinReadFast(D7));
}  

Sorry for the late response, but I don’t know in how far your board is comparable with the Adafruit Music Maker FeatherWing but I wasn’t able to reproduce your issue (yet - although my test MP3s all used the same bitrate and I tried with an Argon - which I had considered less stable than the Photon :wink: ).

I’d need to do more tests to see whether I can reproduce it with a setup as close to yours as possible.

Just some questions:

  • are all your MP3s stored in the SD’s root directory?
  • can you provide some info about the typical naming of the files? (do all files match the format %03d.mp3)?
  • what size is your SD card?
  • have you tried different SDs?
  • what are your log outputs? (don’t comment logs - if you don’t want to see them once the code works, you’d just comment the declaration of the log handler or set the log level to only report errors)
  • have you tried without calling musicPlayer.useInterrupt(5)?

I realized that my ino file was much different from the player_simple.ino in your examples directory. (I’ve tried nearly everything under the sun including parts from other sketches) I also have a suspicion that although I have both your original Adafruit_VS1053.cpp and Adafruit_VS1053.h in a ScruffR_VS_1053\lib\Adafruit_VS1053\src\ folder that they are not what is being compiled in the cloud. I figured that because my MP3 files had been in /01/ under the SD root directory and when I moved them to the root directory and removed the SD.chdir("/01", true); line from my code, the files did not play at all. So I figured I would start from scratch with a new project (ScruffR_VS1053_ORIG) with the code from your player_simple.ino and your original Adafruit_VS1053.cpp and Adafruit_VS1053.h in a ScruffR_VS1053_ORIG\lib\Adafruit_VS1053\src\ folder as well as your library.properties in ScruffR_VS1053_ORIG\lib\Adafruit_VS1053.

Now I get the following errors when attempting a cloud compile:
cannot open source file “SD.h”

#include "application.h"
# define PREFER_SDFAT_LIBRARY 1
# define PARTICLE 1
// include SPI, MP3 and SD libraries
#include <SPI.h>
#include <;Adafruit_VS1053.h>
#include <SD.h>

I’m not sure where SD.h is supposed to be located? It’s not supposed to be “SdFat.h” is it?

Thanks,
Bob

I have not adapted the original library examples but only added the ParticleTest example.
You should try that
https://github.com/ScruffR/Adafruit_VS1053_Library/blob/master/examples/ParticleTest/ParticleTest.ino

I ended up having to do a particle doctor after I flashed the Particle Test code. Not sure if it was the pin defs not matching my setup or not. I then changed my pins to match your code SD_CS= D2 (had been A2) and disconnected the MP3_RESET pin. After the Particle doctor restore the status reported the device version as 1.2.1 when it had been 1.1.1 for at least the last week or so. Since then, the OTA flashing sequence has been much different than before. I get a few quick magenta flashes and then a very slow on and off (not breathing) for perhaps 15 - 25 times (3 to 5 minutes total) and then it goes to breathing cyan. This happens every time I OTA flash! Now I get the messages

0000004686 [app] INFO: Adafruit VS1053 Library Test
0000004987 [app] ERROR: Couldn't find VS1053, do you have the right pins defined?

My SD cards are all Sandisk Ultra HC-1 (two are 32GB is 16GB.
Since your INO code has SD.chdir("/01", true); I assume they need to be in folder /01/ under the root and that where I put them. their names are “001.MP3” etc.
I tried without calling musicPlayer.useInterrupt(5) Gave the same Couldn’t find VS1053 message as above.
Any other things I might try?
Thanks,
Bellatrix

Well, magically the OTA Flash began working normally after an hour during which something must have changed! I got back to the point where I can play partial, sometimes full tracks using the Particle Test code with my pins configured as such:

const int  MP3_RESET        = D1;                 // VS1053 reset pin (unused!)
const int  SD_CS            = D2;                 // SD Card chip select pin
const int  MP3_CS           = D3;                 // VS1053 chip select pin (output)
const int  DREQ             = D4;                 // VS1053 Data request, ideally an Interrupt pin
const int  MP3_DCS          = D5;                 // VS1053 Data/command select pin (output)
const char *fileNamePattern = "%03d.mp3";         // file name pattern to insert track number
Adafruit_VS1053_FilePlayer musicPlayer(MP3_RESET, MP3_CS, MP3_DCS, DREQ, SD_CS);

The following code is coming back with an IRQ of 4

if (type == VS1053_FILEPLAYER_PIN_INT) {
    int8_t irq = digitalPinToInterrupt(_dreq);
    Serial.print("Using Bob IRQ "); Serial.println(irq);

The following code only prints "0 bytes read, must be end of track " if the file finishes playing normally. Neither message is printed if the file ends prematurely.

  // Feed the hungry buffer! :)
  while (readyForData()) {
    // Read some audio data from the SD card file
    int bytesread = currentTrack.read(mp3buffer, VS1053_DATABUFFERLEN);
    
    if (bytesread == 0) {
      // must be at the end of the file, wrap it up!
      Serial.print("0 bytes read, must be end of track ");
      playingMusic = false;
      currentTrack.close();
      break;
    }
    if (bytesread == -1) {
      // this means an I/O Error I believe!
      Serial.print("-1 bytes read, I/O Error ");
      playingMusic = false;
      currentTrack.close();
      break;
    }
    playData(mp3buffer, bytesread);
  }
}

I tried changing

 #define VS1053_DATA_SPI_SETTING     SPISettings(8000000, MSBFIRST, SPI_MODE0)

to this without any difference

 #define VS1053_DATA_SPI_SETTING     SPISettings(4000000, MSBFIRST, SPI_MODE0)

About time to hit the sack!

That's fine since on Particle deviced the pin number is also the interrupt source and since D4 == 4 that's the expected result for your const int DREQ = D4;.

I wondered if there might be an issue with my wiring so I jiggled every wire, pressed on and tried to wiggle and rock my photon and breakout board while playing tracks and was unable to stop the track. I have already tried a new breakout board. So I guess it’s about time for a different photon. I also wonder, due to the highly random freezing if by any chance there is an interrupt from an external source that could be the source of the problem. Can anyone tell me what if any requests might be an issue? I guess it might be possible to disconnect it from the wifi and have it play all 15 mp3s and see what happens. Any thoughts?

Well, it isn’t the photon as I tried a brand new one. I wanted to try and play five tracks in a row in setup while blocking all non-essential interruptions but so far I haven’t had success with that. I was wondering if anyone out there has had success with this particular breakout board (Adafruit VS1053B MP3 Decoder (Model 1381 V4)) on a Photon. Also I am wondering if one can play MP3s on this board by sending it a command on the UART? If so that may be a way to eliminate the Photon interrupt as part of equation.
Thanks,
Bob

I haven’t found the time to test yet, but if you only want to play a static set of MP3s I’d go with the way cheaper DFRobot Mini MP3 module.
You can also store your MP3s on an SD and just talk to the module via UART.

Thanks for getting back to me. I'm going to stick with the Adafruit VS1053B for now as I have two brand new ones and hate to see the money go to waste. Plus, I looked over the data sheet and there are ALOT of features theVS1053B is capable of that I'd like to explore. Can't seem to find the processor used in the DFRobot Mini MP3 module.
I poked around a bit more and it looks like the VS1053 is handling whatever is in the 32 byte buffer fine. It looks like the stream from the SD card is stopping at random points durring the track. That led me to think about the SdFat library. The one I am currently using is SdFat 1.0.16 which is what I get by default when I did a library search for SdFat. But I looked back at your comments in library for your VS1053 library:

url=GitHub - ScruffR/Adafruit_VS1053_Library: This is a library for the Adafruit VS1053 Codec Breakout and Music Maker Shields
repository=GitHub - ScruffR/Adafruit_VS1053_Library: This is a library for the Adafruit VS1053 Codec Breakout and Music Maker Shields
architectures=*
dependencies.SdFat=0.0.7
dependencies.SdFat=1.0.15

Soooo, I wonder if that SdFat version 1.0.16 could be the source of my issues?? And if so where is SdFat 1.0.15?
Thanks,
Bob

Huh? Ther should not be two entries for the same library - let me check why that is.

BTW, when you want to import an other version of a library than the one you get suggested by default, you can do that in Web IDE by clicking on the (i) icon below the name
image
and select the version you want by just clicking it in this dialog

However, I’m pretty sure 1.0.16 is the one I should also be using in that library :wink:

I don’t think you have two entries. That was part of a change. The 0.0.7 has a light pink background and the 1.0.15 has a light green background.

1 Like

And thanks for the tip on the library version icon!

It appears the program flow never never returns from the line

int bytesread = currentTrack.read(mp3buffer, VS1053_DATABUFFERLEN);

in "Adafruit_VS1053.cpp" when the track stops playing. The function "readyForData" is checked just before this call which means I think, the DRQ pin is high signaling the mp3buffer is ready for data. I check the files is_open, position and available just before the read and all are fine. However, when I attempt a peek before the read it never returns. So I'm wondering what is changing or not being caught between the time currentTrack.read(mp3buffer, VS1053_DATABUFFERLEN) is called and the actual block is read? Perhaps an interrupt?

I would like to have my modified version of "FatFile.cpp" used in the cloud compile so I could drop in a few print statements but apparently it is no doing so right now. Any feedback suggestion would be much appreciated.

In order to prevent cloud compile from using the cloud version of a library and rather use your local copy you just need to comment/remove the dependencies.SdFat line in your project.properties file and the libraries' library.properties file(s).

I got it to do a local compile using SdFat 1.0.15 and still had the same issues as before. So I tried using WiFi.off() before playing files and then set up the loop() to play all fifteen files. It played all 15 without a hitch and did so three times in a row. The fourth time it stopped playing after the eleventh file but the staus led continued breathing white. That constitutes a HUGE improvement and I believe the hitch on the 11th file may be due to my own coding problem. I will hopefully fix that and then go for four reps of a full set of 30 tracks with bitrates ranging from 128k to 320k. If they play without a hitch then I will need to look into what events are being generated and hopefully find the one that is causing the lockup. I looked at issues for the standard Adafruit 1053 library and it seems that have also encountered some issues, so I will look into their possible solutions. One mentioned increasing the buffer size.

2 Likes

Hi @Bellatrix I seem to have run into the same problems as you. Let me know if you find any workaround…cheers

I haven’t gone beyond using wifi.off() before playing files. In fact I have been able to loop through a set of 30 mp3s (160 to 320 kb bitrate) for 8 to 15 hours without a single hang many times. So perhaps there are periodic messages that are taking too long to process, perhaps when the wifi connection is slow? It would be nice to turn off just the system diagnostic message to see if that is the offender. Hmm … maybe I could do this by calling Particle.publishVitals(0) to disable it? And to be able to list all the inbound and outbound messages, but I haven’t really explored that. I vaguely recall the response time in the system diagnostic dialog typically being in the 70 to 100 ms range but occaisionally seeing it jump up into the 300+ range. I haven’t tried changing the buffer size yet. An interesting note is that when I had wifi on, I was able to use the change track and set volume functions repeatedly without triggering a hang. PlaySine however hung it immediately.