Ethernet + MP3/SD

I have a SainSmart Ethernet shield and a Sparkfun MP3 shield plugged into an UNO. Trying to run SDInfo from the SDFat library keeps telling me that card initialization has failed with error code 0X1. Which I'm assuming, in this case, means that there is interference in the SPI select pins. Correct? (And for those that ask which SD card slot is in use, its the one on the MP3 shield. The other one has nothing in it.)

The MP3 shield uses pin 9 (SD) and 6 (MP3), I believe, while the Ethernet uses pin 10.

Am I getting interference between the two shields? I did test the MP3 shield only and everything worked perfectly, And the MP3 Player shield library example file worked perfectly as well, with the Ethernet shield still connected.

I'm kind of at a loss here. Its the first time I've messed with shields, and only second Arduino project.
Thanks

Before you try initializing the SD, you should disable all other SPI devices, like the w5100 and MP3.

// disable w5100 SPI
pinMode(10, OUTPUT);
digitalWrite(10, HIGH);
// disable MP3 SPI
pinMode(6, OUTPUT);
digitalWrite(6, HIGH);
// now do SD setup

The Sparkfun page mentions modifying the SdFat library to change the SS pin. Did you do that?
https://www.sparkfun.com/tutorials/295

The file they say to modify I don’t even have. I just made sure that I had the most up to date libraries for SDFat and SPFMP3Shield.

This is the sketch I’m trying to use, including the code from above. The SdChipSelect I’ve tried changing values (8, 9, SS), and 8 worked at one point but now none of them do, either with or without the code form above.

/*
 * This sketch attempts to initialize an SD card and analyze its structure.
 */
#include <SdFat.h>
/*
 * SD chip select pin.  Common values are:
 *
 * Arduino Ethernet shield, pin 4.
 * SparkFun SD shield, pin 8.
 * Adafruit SD shields and modules, pin 10.
 * Default SD chip select is the SPI SS pin.
 */
const uint8_t SdChipSelect = 8;

Sd2Card card;
SdVolume vol;

// serial output steam
ArduinoOutStream cout(Serial);

// global for card size
uint32_t cardSize;

// global for card erase size
uint32_t eraseSize;
//------------------------------------------------------------------------------
// store error strings in flash
#define sdErrorMsg(msg) sdErrorMsg_P(PSTR(msg));
void sdErrorMsg_P(const char* str) {
  cout << pgm(str) << endl;
  if (card.errorCode()) {
    cout << pstr("SD errorCode: ");
    cout << hex << int(card.errorCode()) << endl;
    cout << pstr("SD errorData: ");
    cout << int(card.errorData()) << dec << endl;
  }
}
//------------------------------------------------------------------------------
uint8_t cidDmp() {
  cid_t cid;
  if (!card.readCID(&cid)) {
    sdErrorMsg("readCID failed");
    return false;
  }
  cout << pstr("\nManufacturer ID: ");
  cout << hex << int(cid.mid) << dec << endl;
  cout << pstr("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
  cout << pstr("Product: ");
  for (uint8_t i = 0; i < 5; i++) {
    cout << cid.pnm[i];
  }
  cout << pstr("\nVersion: ");
  cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
  cout << pstr("Serial number: ") << cid.psn << endl;
  cout << pstr("Manufacturing date: ");
  cout << int(cid.mdt_month) << '/';
  cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
  cout << endl;
  return true;
}
//------------------------------------------------------------------------------
uint8_t csdDmp() {
  csd_t csd;
  uint8_t eraseSingleBlock;
  if (!card.readCSD(&csd)) {
    sdErrorMsg("readCSD failed");
    return false;
  }
  if (csd.v1.csd_ver == 0) {
    eraseSingleBlock = csd.v1.erase_blk_en;
    eraseSize = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
  } else if (csd.v2.csd_ver == 1) {
    eraseSingleBlock = csd.v2.erase_blk_en;
    eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low;
  } else {
    cout << pstr("csd version error\n");
    return false;
  }
  eraseSize++;
  cout << pstr("cardSize: ") << 0.000512*cardSize;
  cout << pstr(" MB (MB = 1,000,000 bytes)\n");

  cout << pstr("flashEraseSize: ") << int(eraseSize) << pstr(" blocks\n");
  cout << pstr("eraseSingleBlock: ");
  if (eraseSingleBlock) {
    cout << pstr("true\n");
  } else {
    cout << pstr("false\n");
  }
  return true;
}
//------------------------------------------------------------------------------
// print partition table
uint8_t partDmp() {
  cache_t *p = vol.cacheClear();
  if (!p) {
    sdErrorMsg("cacheClear failed");
    return false;
  }
  if (!card.readBlock(0, p->data)) {
      sdErrorMsg("read MBR failed");
      return false;
  }
  cout << pstr("\nSD Partition Table\n");
  cout << pstr("part,boot,type,start,length\n");
  for (uint8_t ip = 1; ip < 5; ip++) {
    part_t *pt = &p->mbr.part[ip - 1];
    cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type);
    cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl;
  }
  return true;
}
//------------------------------------------------------------------------------
void volDmp() {
  cout << pstr("\nVolume is FAT") << int(vol.fatType()) << endl;
  cout << pstr("blocksPerCluster: ") << int(vol.blocksPerCluster()) << endl;
  cout << pstr("clusterCount: ") << vol.clusterCount() << endl;
  uint32_t volFree = vol.freeClusterCount();
  cout << pstr("freeClusters: ") <<  volFree << endl;
  float fs = 0.000512*volFree*vol.blocksPerCluster();
  cout << pstr("freeSpace: ") << fs << pstr(" MB (MB = 1,000,000 bytes)\n");
  cout << pstr("fatStartBlock: ") << vol.fatStartBlock() << endl;
  cout << pstr("fatCount: ") << int(vol.fatCount()) << endl;
  cout << pstr("blocksPerFat: ") << vol.blocksPerFat() << endl;
  cout << pstr("rootDirStart: ") << vol.rootDirStart() << endl;
  cout << pstr("dataStartBlock: ") << vol.dataStartBlock() << endl;
  if (vol.dataStartBlock() % eraseSize) {
    cout << pstr("Data area is not aligned on flash erase boundaries!\n");
    cout << pstr("Download and use formatter from www.sdcard.org/consumer!\n");
  }
}
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  while(!Serial) {}  // wait for Leonardo

  // use uppercase in hex and use 0X base prefix
  cout << uppercase << showbase << endl;

  // pstr stores strings in flash to save RAM
  cout << pstr("SdFat version: ") << SD_FAT_VERSION << endl;
  
//*******************************************************
  // disable w5100 SPI
pinMode(10, OUTPUT);
digitalWrite(10, HIGH);
// disable MP3 SPI
pinMode(6, OUTPUT);
digitalWrite(6, HIGH);
// now do SD setup
//******************************************************
}
//------------------------------------------------------------------------------
void loop() {
  // read any existing Serial data
  while (Serial.read() >= 0) {}

  // pstr stores strings in flash to save RAM
  cout << pstr("\ntype any character to start\n");
  while (Serial.read() <= 0) {}
  delay(400);  // catch Due reset problem
  
  uint32_t t = millis();
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  if (!card.init(SPI_HALF_SPEED, SdChipSelect)) {
    sdErrorMsg("\ncard.init failed");
    return;
  }
  t = millis() - t;
  
  cardSize = card.cardSize();
  if (cardSize == 0) {
    sdErrorMsg("cardSize failed");
    return;
  }
  cout << pstr("\ninit time: ") << t << " ms" << endl;
  cout << pstr("\nCard type: ");
  switch (card.type()) {
    case SD_CARD_TYPE_SD1:
      cout << pstr("SD1\n");
      break;

    case SD_CARD_TYPE_SD2:
      cout << pstr("SD2\n");
      break;

    case SD_CARD_TYPE_SDHC:
      if (cardSize < 70000000) {
        cout << pstr("SDHC\n");
      } else {
        cout << pstr("SDXC\n");
      }
      break;

    default:
      cout << pstr("Unknown\n");
  }
  if (!cidDmp()) return;
  if (!csdDmp()) return;
  if (!partDmp()) return;
  if (!vol.init(&card)) {
    sdErrorMsg("\nvol.init failed");
    return;
  }
  volDmp();
}

"Set it, and forget it!". Call this once in setup just after where it says "// now do SD setup".

  if (!card.init(SPI_HALF_SPEED, SdChipSelect)) {
    sdErrorMsg("\ncard.init failed");
    return;
  }

And the datasheet shows D9 is the SS for the SD.

const uint8_t SdChipSelect = 9;

Has anyone solved this SD card issue yet for the Sainsmart Ethernet shield + Sparkfun MP3 shield combo???

I have been working on this same issue for 2 days now and have tried all kinds of solutions. My system uses a Mega + Sainsmart Ethernet shield + Sparkfun MP3 shield on the hardware side. I am using the awesome SFEMP3Shield library for the MP3 shield + the standard Ethernet library for the Ethernet shield. Like every other post I have found on the internet, the Mega +MP3 shield works just great; the Mega plus Ethernet Shield works just great; the Mega + ethernet + MP3 causes all kinds of SD card issues (mainly SD errorCode: 0X1,0X0).

After two days on this I am now considering rewriting my entire project and going to a Raspberry Pi for everything or resorting to master/slave setup with two Arduinos. The fixes listed above also didn't work.

I think I may have figured it out. I am going to leave this build running overnight to see if it stays stable. Here is what is working currently for me.

Hardware
Instead of using the Sparkfun shield for MP3 playback, I am using another MP3 breakout board ( http://amzn.com/B009Z620PI ) so I can jumper the connections on the Mega easily. So following the instructions in the SFEMP3Shield library Read Me, I jumped pins 13, 12, and 11 to pins 50, 51, and 52 on the Mega. I loaded a 4GB SD with MP3s into the MP3 breakout board. I am using the SainSmart Ethernet Shield with this breakout board ( http://amzn.com/B006J4FZTW ).

Software
I am using the following code in the initialization for the Ethernet shield, MP3 shield, and its on board SD card reader. The big change was in the setup loop. I changed to sd.begin(SD_SEL, SPI_HALF_SPEED); to sd.begin(SD_SEL, SPI_FULL_SPEED); and both shields started to magically work together. Note: you may not need the webserver part below – I am using that to pass data back to my web app via GET.


// INITIALIZE
#define WEBDUINO_FAIL_MESSAGE “

Request Failed

” //For the webserver
#include <SPI.h>
#include “avr/pgmspace.h”
#include “Ethernet.h”
#include “WebServer.h” //For the webserver
#define VERSION_STRING “0.1”
//Add the SdFat Libraries
#include “SdFat.h”
#include “SdFatUtil.h”
//and the MP3 Shield Library
#include “SFEMP3Shield.h”
SdFat sd;
SFEMP3Shield MP3player;

Then in the Setup loop I use.


/* Start Ethernet Shield and webserver /
Ethernet.begin(mac, ip); // initialize the Ethernet adapter
webserver.setDefaultCommand(&helloCmd); // setup our default command that will be run when the user accesses
webserver.setFailureCommand(&my_failCmd); // setup our default command that will be run when the user accesses
webserver.addCommand(“index.html”, &helloCmd); // run the same command if you try to load /index.html
webserver.begin(); // start the webserver
/
Start MP3 Shield */
sd.begin(SD_SEL, SPI_FULL_SPEED);
MP3player.begin();

Then in my functions/loop I have to tell the Arduino to wait until the MP3 is finished playing before moving on to the next line of code. Otherwise it will hang up.


MP3player.playTrack(1);
while (MP3player.isPlaying());

Here is that same code in context of a full command from my controlling web app…


if (*commands == ‘D’) { //------> Zone 01 lighting OFF command from web app
MP3player.playTrack(1);
digitalWrite(relayPin01, LOW); //Relay for zone 01’s lighting
while (MP3player.isPlaying());
*commands = ‘X’; //Resets the commands var. Used X for exit.
}
if (*commands == ‘C’) { //------> Zone 01 lighting ON command from web app
MP3player.playTrack(2);
digitalWrite(relayPin01, HIGH); //Relay for zone 01’s lighting
while (MP3player.isPlaying());
*commands = ‘X’; //Resets the commands var. Used X for exit.
}