Try SdFat, a Library for SD Cards

Click on SDFat and scroll to the bottom and you will see the examples.

Yup!
Niceā€¦ That is what Iā€™m looking for.

Thank you @kennethlimcp and thank you all.:smiley:

Sorry for any inconvenience questions.:grin:

Hi looking for a solution to all of this, Has anyone gotten the SD to work with the electron? Read all the post and just keep getting lost. Any help would be great just want to use the SD to store string of data.

Hello,
Iā€™m using SDFAT, and I think itā€™s a great library for the particle devices. Big thanks to whg.
I am using it to log low frequency data (1 point per second or 1 point per 5 seconds). I would like to be able to allow my logger to run for several weeks before retrieving the SD card.

I have it working, except that my logger stops writing data to the file after some time. Last night it stopped at about 1:30 AM. This morning, I checked my serial monitor and everything seemed fine, except the file is not getting any additional data ā€œputā€ into it.

Every 5 seconds Iā€™m opening the file, writing one line of data, and then closing the file.

It stopped after 376,446 bytes.

Iā€™m concerned that I need to do something more complex with the ā€œblocksā€ in order to write files that are very large. I have glanced over the LowLatencyLogger.cpp example, but it seems overly complicated for what I need. (but maybe notā€¦)

Does anyone have any experience with this type of problem or can point me in the correct direction for appending data to very large files?

Here is the code that Iā€™m using to append my sensor data to the file:

void writeDataSD()
{
  if (!myFile.open(curLogFile, O_RDWR | O_CREAT | O_AT_END)) {
    //sd.errorHalt("opening test.txt for write failed");
    Serial.println("...opening Logfile: <" + curLogFile + "> for write at end FAILED");
  }
  else
  {
    // if the file opened okay, write to it:
    //Serial.print("Writing to test.txt...");
    myFile.printlnf("%d,%d,%d", Time.now(), global_var1, global_var2);
  }
  // close the file:
  myFile.close();
}

curLogFile is just a string containing the name of the *.csv to log to. Example:
LogFile_20171024_1612.csv

As you can see, I just ripped the File write code straight out of one of the examples.

Thanks for your help.

So Iā€™ve been looking in many place, and I think this is the best thread.

Iā€™m working on a project with a SD card and the electron is going to deep sleep regularly. The SD card is supposed to use 100ĀµA when idle, but it looks more like 6.6mA actually. Not sure why, but this seems to be the same value as mentioned by @peekay123 last yearā€¦ Any explication or change I should make?

For info, Iā€™m using the SD photon compatible Library and the card is using SPI (hardware) on A2-A5 (electron)

Help, anyone?

Cheers
Phil.

@peergum, I find some microSD cards donā€™t sleep as expected. Iā€™ve had to use high-side power control to the SD was necessary using a MOSFET. I bias the fate of the FET with a pullup or down resistor so the SD remains off during deep sleep.

2 Likes

Hello All.
Need help here! I have an SD card hooked up to my Electron. Everything works, except that I never seem to reach end of file.
Below is the relevant part of my code.

Serial.begin(9600);
  while(!Serial.available());

  if (sd.begin(chipSelect, SPI_HALF_SPEED)) {
    Serial.println("SD opened successfully");
  } else     sd.initErrorHalt();

  // re-open the file for reading:
  if (myFile.open("data.dat", O_READ)) {
      Serial.println("File opened successfully");
  } else   sd.errorHalt("opening test.txt for read failed");

  char c;
  String line="";
  int bytes=0;

  while ((c=myFile.read())>=0)
  {
    ++bytes;
    Serial.write(c);
  }
  Serial.println("File read done. Bytes="+bytes);

This loop prints all the data on the card well, but does not terminate.
So I printed the data in HEX format. At first, I saw that the last byte was 0xDA , which is equivalent to ā€œĆšā€ and the while loop did not print anything further nor terminate.
Now, several trials later, it prints a series of "F"s continuously after reaching the end of the file. This happens for 2 micro-sd cards I have. One is 1GB and the other 2GB.
Yes, there is a possibility that these are not reliable SD cards, they were quite cheap (about 2 USD each). They have no issue however on Windows. I can write to them and read from them well. They are formatted to FAT32 (using the default Windows formatter). I also used SD Card Formatter, same story.

I later realized that the TryMeFirst example used int and not char to hold the characters while reading.
Using that approach, the file does reach the end and does NOT continue to print the "F"s. But the code outside the loop doesnā€™t get executed.
So i modified the code to look like below, to print the bytes counted after each new line.

int c;
  String line="";
  int bytes=0;
  while ((c=myFile.read())>=0)
  {
    ++bytes;
    Serial.write(c);
    if(c=='\n'){
      Serial.println("BYTES="+bytes);
    }
  }
  Serial.println("FILE READ DONE. Bytes="+bytes);

The result, in PUTTY, is in the snapshot below.

The ā€œproblem?ā€ and ā€œandlerā€ are NOT part of my file contents and the Serial.println("BYTES="+bytes); statement is not executed as should be.
The Particle Dev monitor prints something slightly different. Look at the snapshot below:

I seem to have extra gibberish.
Any takes? Any ideas?
Worst case scenario, Since I do print all my data, I need to at least detect that there is no more printable data.
I am using SPI Configuration 0 and everything is wired to the hardware SPI and the default SS.
I need to also mention that the TryMeFirst example, unmodified, works perfectly well!
Thanks!

I havenā€™t checked the implementation of SDFat, but the -1 you expect is meant to be an int but your char c can never become -1.

0xFF would be -1 with int8_t but char is unsigned.

Thatā€™s also why TryMeFirst - which uses int c - works.

Do you always get the same ā€œextraā€ words? Or are these random?
Have you double checked whether these are in your file after all?
Maybe your SD write code is putting them there by mistake.

2 Likes

Hi all,

just a note to let people know i have successfully used the sdFat library with an FTDI 813 touch LCD touch screen (ported from github RudolphRiedel/FT800-FT813) running on a Photon. I used the SD card to store all my images for the lcd.

everything seems to be working as expected.

1 Like

If you are using SPI with DMA configuration enabled, when you call

bool result = file.sync();

does file.sync() block until the DMA transfer is complete? If it doesnā€™t block, then the return value would not indicate whether the SD write succeeded or notā€¦

What about if you are not doing anything with the return value, as in:

file.sync();

In this case, does the DMA write operation block?

I have no definet answer to the first part - although I'd assume the sync() call would be a synchronous DMA call (like SPI.transfer() with a NULL pointer for callback) - but I haven't checked.

However to answer the second part, the function itself has no way of knowing whether or not the calling routine will use the return value or not, so whether you use it or not doesn't matter for the called function itself.

1 Like

OK that makes sense. I thought I remembered something about certain lower-level functions blocking if you attempt to do something with the result, and not blocking otherwise. I think I must be thinking of functions that have overloaded function signatures i.e. void func() vs bool func() where the bool func() version blocks but the void func() version is fire-and-forget.

I think this Particle version of the SdFat library does support asynchronous DMA transfers, as can be seen in this screenshot of search results for the SPI transfer function in the library:

And if I'm not mistaken you can control the behaviour of the two lines you marked there by either having an actual callback routine assigned to SD_SPI_DMA_transfereComplete_callback (async DMA) or setting it NULL (sync DMA).

BTW, AFAIK multiple overloads are not allowed to just differ in return type since it is perfectly fine not to catch a return value even when the function is declared non-void. If it was allowed you'd always get an ambiguity error message when you tried to call the void version as the compiler couldn't tell whether you actually want to call the void version or just not care about the return value. But it also can't force you to not want the latter.

SdFat calls are synchronous to the user.

I used an asynchronous SPI DMA call as a workaround for a bug in the SPI library.

When I ported SdFat to Particle, I discovered that SPI calls returned before all data had been transferred to memory. It was a DMA FIFO problem.

I calculated an extra delay before accessing data. Here is an example routine with the delay.

  uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
    SPI_DMA_TransferCompleted = false;
    spiPtr[m_spiIf]->transfer(0, buf, n, SD_SPI_DMA_TransferComplete_Callback);
    while (!SPI_DMA_TransferCompleted) {}
    if (bugDelay) {
      delayMicroseconds(bugDelay);
    }
    return 0;
  }

Here is the callback.

void SD_SPI_DMA_TransferComplete_Callback(void) {
    SPI_DMA_TransferCompleted = true;
}

If the bug has been fixed, the SPI calls could be made synchronous.

@whg, had you logged a firmware repo issue for this bug?

I did log the issue but never followed up to check for a fix since I became very busy with other non Particle projects.

@whg, looks like a fix was merged into the firmware at the time so it is most likely fixed.

@whg, Are you William Greiman who authored the SdFat library? If you are then I would like to ask a question about a problem I have with using System.Sleep() [not deep sleep] and SdFat. I have a setup where for power management reasons before calling System.sleep() I am turning off power to peripherals including 2 devices on the SPI bus, a TFT screen controller and an SD card controller. The photon remains powered from a back-up battery. I am finding that to get the Sleeping power consumption down I need to set MOSI pin to an INPUT. This works first time through sleep and then wake after a system reset but then on subsequent cycles 1. the MOSI pin cannot be set as an INPUT and remains at 3V in sleep thereby causing a current drain or 2. worst case, blocks System.sleep() and after a period the [Particle] Application Watchdog kicks in and restarts the system. I have looked in the library files and seen in Syscall.h that there is a thread halt and yield plus Particle.process() - could this be a cause of issue 2. And for issue 1. is it possible that there is a static declared variable which is causing the blocking of the MOSI pinMode change? I cannot call SPI.end() before pinMode(A5, INPUT) as this always causes issue 2. Is there a way to reliably put the SD card controller to sleep like I am doing with the TFT screen controller OR a different way to do SD.begin() after the wake from sleep? I would be grateful for any advice on this.

armor.

halt() is only called by the errorHalt() and initErrorHalt() functions. These are user callable functions not called in the SdFat code.

yield() is also called in one example waiting for serial input but not in the SdFat code.

All access to the SPI controllers is in SdSpiParticle.cpp.

The SPI begin call is here:

void SdSpi::begin(uint8_t chipSelectPin) {
  spiPtr[m_spiIf]->begin(chipSelectPin);
}

You need to call end with the proper SPI interface.

SPI end() does not modify pin modes. Here is a quote from the documentation.

Note: The SPI firmware ONLY initializes the user-specified slave-select pin as an OUTPUT. The user's code must control the slave-select pin with digitalWrite() before and after each SPI transfer for the desired SPI slave device. Calling SPI.end() does NOT reset the pin mode of the SPI pins.

You should be able to power up the SD card and call begin.

The bench example allows a card to be removed and replaced with another card and begin() is called before each test.

@whg, Thanks for clarifying the halt() and yield() usage.

I do understand about SPI.end() not changing the pinModes of the SPI pins. It just so happens that if I call SPI.end() in 0.7.0 it blocks System.sleep() not sure why, 0.8.0-rc.4 works OK.

Since I posted initially I have tried another approach which is to not switch off the power to the SD controller before going into/calling System.sleep(). I still set the pinModes (A2, A3, A5, etc.) to INPUT to stop push/pull on these pins and then do the reverse just after wake. I do not call SD.begin(). This appears to work reliably although SPI bus will remain at 3V3 and backfeeds through the voltage regulator to the switched 5V bus. It does seem to be an issue with 0.7.0 but not something Particle support are willing to pick up.

Thanks for confirming that power to the SD card controller can be removed and then re-applied and sd.begin() called. What is the ā€˜bench exampleā€™?