Sd card percent full function

I’m using the SD card library found here and I need a function that returns how full the SD card is in percent.

At the moment I am using the following function which works correctly

int16_t SDClass::percentFullSd(String fileName)
{
    //Get total size
    uint32_t volumesize = volume.blocksPerCluster();
    volumesize *= volume.clusterCount();
    volumesize *= 512;

    //Get file size
    File logFile = SD.open(fileName, FILE_WRITE);
    uint32_t filesize = logFile.size();
    logFile.close();

    //Calculate percentage
    return round(((double)filesize / (double)volumesize) * 100);
}

I need a function that returns how full the Sd card is, that will work with multiple files where the fileNames are not known.

Is this possible? or alternatively is there a way to get a list of fileNames on the SD card that I can use to call the function above once for each fileName?

Have you had a look at the Spark-CardInfo sample with this library?

This should show how to traverse the whole file system allowing you to sum up the file sizes.
I’m not sure if there is a function to just look at the FAT entries to get to the same info quicker.

I have, but i’m not sure how to use the root.ls(LS_R | LS_DATE | LS_SIZE); line to get the fileNames as it just prints them out over serial.

Yes, but if you look at the implementation of void SdFile::ls(uint8_t flags, uint8_t indent) [at line 205ff] (which is used for doing so) you can copy it and adapt it to your needs.
https://github.com/head-labs/sd-card-library/blob/master/firmware/sd-file.cpp

Instead of Serial printing the LS_SIZE info, add the number to a usedBytes counter.

1 Like

Looking at file sizes is not a very good way to determine free space.

I am the author of SdFat. I wrote the version that has been ported to Particle in 2009.

Newer versions have a function to scan the FAT and determine the count of free clusters.

I will soon release a port of the latest version of SdFat for particle.

3 Likes

Thanks for your input.

@whg when do you think the port will be released?

I have rewritten the function as follows:

int16_t SDClass::percentFullSd()
{
    //Get total size
    uint32_t volumesize = volume.blocksPerCluster();
    volumesize *= volume.clusterCount();
    volumesize *= 512;

    //Get file size
    uint32_t filesize = 0;
    dir_t* p;
    rewind();
    while ((p = readDirCache()))
    {
        // done if past last used entry
	if (p->name[0] == DIR_NAME_FREE) break;

	// skip deleted entry and entries for . and  ..
	if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue;

	// only list subdirectories and files
	if (!DIR_IS_FILE_OR_SUBDIR(p)) continue;

	// add size
	filesize += p->filesize;
    }

    //Calculate percentage
    return round(((double)filesize / (double)volumesize) * 100);
}

However this gives me the following errors:

too few arguments to function "void rewind(FILE*)"
"readDirCache" was not declared in this scope
"dir_t" has no member named filesize

I think this is because I need to be calling the functions in “sd-fat.h” but I am not sure of the correct way to access them. Any ideas?

Should I be using openNextFile(uint8_t mode) or rewindDirectory(void) in “sd-card-library-photon-compat.cpp” instead?

First you’d need a SDFile object that gives you access to the root folder of your SD card.
Like in the example code

Sd2Card card;
SdVolume volume;
SdFile root;
...
  ...
  root.openRoot(volume);  // card and volume have to be set up too
  ...

And since original ::ls() method was part of the SDFile class it has access to the “internal” functions (like SDFile::rewind()) without explicitly referencing its “owner”.
But as “external” code you’d need to (e.g. root.rewind() or rootPtr->rewind()).

And for filesize += p->fileSize; you have to use be exact - mark the capital S.

I had not realised that the root folder had to be opened.

The function below seems to work, I need to leave my SD logging for a while to double check the percent full returned is correct. I also had to make the readDirCache function public in the sd-file.cpp class. Any way around this?

int16_t SDClass::percentFullSd()
{
    //Get total size
    uint32_t volumesize = volume.blocksPerCluster();
    volumesize *= volume.clusterCount();
    volumesize *= 512;

    //Open root
    root.openRoot(volume);

    //Get file size
    uint32_t filesize = 0;
    dir_t* p;
    root.rewind();
    while ((p = root.readDirCache()))
    {
	// done if past last used entry
	if (p->name[0] == DIR_NAME_FREE) break;

	// skip deleted entry and entries for . and  ..
	if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue;

	// only list subdirectories and files
	if (!DIR_IS_FILE_OR_SUBDIR(p)) continue;

	// add size
	filesize += p->fileSize;
    }

    //Calculate percentage
    return round(((double)filesize / (double)volumesize) * 100);
}

There are a number of problems with your percentage full calculation.

For SD cards larger than 4 GB, you can’t use uint32_t.

You must use the amount of space allocated to the file, not the file size.

You must use a recursive function to account for sub-directories.

Note that entries for directories have zero size so you must trace the allocation chain.

Root on FAT16 volumes is a fixed area, not allocated from clusters so shouldn’t be counted.

The best way to get used/free space is to scan the FAT, File Allocation Table, and determine what fraction of clusters have been allocated.

See the freeClusterCount function in this file:

FatVolume.cpp

2 Likes

Thanks, comments below.

For SD cards larger than 4 GB, you can’t use uint32_t.
I am using a 4GB SD card.

You must use the amount of space allocated to the file, not the file size.
Not sure what this means. How can I get the amount of space allocated to the file?

You must use a recursive function to account for sub-directories.
I don’t have any sub-directories.

Note that entries for directories have zero size so you must trace the allocation chain.
Not sure this is relevant for me - see above.

Root on FAT16 volumes is a fixed area, not allocated from clusters so shouldn’t be counted.
Not sure this is relevant to me - the card is formatted as FAT32.

The best way to get used/free space is to scan the FAT, File Allocation Table, and determine what fraction of clusters have been allocated.
How would I do this?

Thanks

I added a link to the above post.

Ok thanks. Is this function Ok to clear the SD card

void SDClass::clearSD()
{
    root.openRoot(volume);
    root.rmRfStar();
}

I really can’t help you with the old 2009 version of SdFat.

It had lots of bugs that weren’t fixed by the Arduino company.

Arduino made a few mods which introduced new bugs or broke parts they didn’t want users to see.

More mods have been made to port it to Particle.