Help with my first Library

Hi, I apologise in advance if any of the terminology is incorrect.

I’m creating my first library that stores all the functions that I will ever need for manipulating a Micro SD card. I’ve been on this all day but when calling one of the functions it fails to open the file on the card. It’s the main one I will use countless times.

In the library I have included SD.h and FS.h. In the sketch I call a function called setupSDCard(). This calls multiple functions and does the following:

  • Check that the card mounts successfully
  • Gets the card type
  • Lists the directory and removes hidden files produced by my MacBook
  • Creates a Log.txt
  • Appends text to the log file
  • Relists the directory to make sure the hidden Mac files have been removed and the log file has been created.
void Lexi_SD_Helpers::setupSDCard() {
    /* ---------------------------------------------------
     Setup SD Card and files in readiness
     ---------------------------------------------------*/
    SPI.begin(sck, miso, mosi, cs);
    if (!SD.begin(cs)) {
        Serial.println("Card mount failed");
        return;
    } else {
        Serial.println("Card Mounted successfully");
    }
    
    uint8_t cardType = SD.cardType();
    
    if (cardType == CARD_NONE) {
        Serial.println("No SD card attached");
        return;
    } else {
        Serial.println("Card detected");
    }
    
    Serial.print("Card type: ");
    Serial.println(cardType);
    
    //********** List directory and remove Mac hidden files **************
    listDir(SD, "/", 0);
    //********** Create Log file if it doesn't exist ***************
    createFile(SD, "/Log.txt");
    //********** Write to log **************
    appendFile(SD, "/Log.txt", "\nWriting to the /Log.txt file");
    //********** Relist directory **************
    listDir(SD, "/", 0);
    //******************** Test all the functions ************************
    //testAllFunctions();
}

Now this works as expected. Creating the file, closing it and then reopening the file and adding text to the file. So the functions being called work within the library.

Card Mounted successfully
Card detected
Card type: 2
Card Mounted successfully
Card detected
Card type: 2
Listing directory: /
  DIR : .Spotlight-V100
  DIR : Logs
  FILE: Log.txt  SIZE: 291
  DIR : .fseventsd
  DIR : .Trashes
Creating Dir: /Logs
Dir created
Creating file: /Log.txt
File created
Appending to file: /Log.txt
Message appended
Listing directory: /
  DIR : .Spotlight-V100
  DIR : Logs
  FILE: Log.txt  SIZE: 320
  DIR : .fseventsd
  DIR : .Trashes

This is where it starts going wrong and is confusing the hell out of me. If I call this:


void Lexi_SD_Helpers::appendFile(fs::FS &fs, const char *path, const char *message) {
    Serial.printf("Appending to file: %s\n", path);
    
    File file = fs.open(path, FILE_APPEND);
    if (!file) {
        Serial.println("Failed to open file for appending");
        file.close();
        return;
    }
    if (file.print(message)) {
        Serial.println("Message appended");
    } else {
        Serial.println("Append failed");
    }
    file.close();
}

using the following from outside of the library, within my sketch, and it fails to open the file……. even though it worked using the setupSDCard() function within the library. I had to include the SD.h within the sketch because it was complaining about it.

mySD.appendFile(SD, "/Log.txt", "Test");

I get this error. This tells me that I am inside of the function in the library :

Appending to file: Log.txt
Failed to open file for appending

Now, I’ve tried multiple variations of the function. I tried this which also failed,

void Lexi_SD_Helpers::appendFile(const char *path, const char *message) {
    Serial.printf("Appending to file: %s\n", path);
    
    File file = SD.open(path, FILE_APPEND);
    if (!file) {
        Serial.println("Failed to open file for appending");
        file.close();
        return;
    }
    if (file.print(message)) {
        Serial.println("Message appended");
    } else {
        Serial.println("Append failed");
    }
    file.close();
}

I’m confused as to why the append function works within the library but fails when called from outside of the library.

Please bear in mind that this is my first attempt at doing this so be gentle with me. If it’s something really basic that I am missing then feel free to mock me :rofl:

Any help would be appreciated.

Open the file.
Write to file until no more data.
Close the file.
Terminate the program.

Rapid, repeated open/write/close seems to cause problems.

Hi xfpd, the rapid open/write/close worked when I had all the functions in my sketch. When I added them to the library the function fails. I tried writing 1 single time and it failed. So it must not have anything to do with the speed of writes because it failed on a single write.

Post the complete code for the library as well as a complete, simple, short user program that demonstrates the problem. And post the previous "all in one code" that you said works.

It may "work", but it's highly inefficient. The overhead required to open / close the file becomes significant relative to the time spent actually writing data to the file.

1 Like

For something like a log file, you should keep it open all the time and use File::flush after writing instead of closing and re-opening.

Separately, it's inconsistent API for setupSDCard to just use SD, but then have other methods require passing it in as an argument. Also, the path argument in the call shown starts with a /, but the message does not?

When the file fails to open -- as tested with if (!file) -- it is generally pointless, but probably not harmful, to then try to close it. The few examples I looked at don't do this.

I’ve cut it down to next to nothing now, just a few functions but it still doesn't work. Here’s what I’ve got left:

Lexi_SD_Helpers.h

#ifndef LEXI_SD_HELPERS_H
#define LEXI_SD_HELPERS_H

#include "Arduino.h"

class Lexi_SD_Helpers {
public:
    Lexi_SD_Helpers(int SD_CLK, int SD_MISO, int SD_MOSI, int SD_CS);
    void begin();
    void appendFile();
    
    
private:
    int sck;
    int miso;
    int mosi;
    int cs;
    
};

#endif

Lexi_SD_Helpers.cpp

/*  Created : Alex
 Version : 1.0
 Dated   : 07/11/2025
 
 Updated :
 
 */

#include <SD.h>
#include <SPI.h>
#include <Arduino.h>
#include "Lexi_SD_Helpers.h"

File file;

Lexi_SD_Helpers::Lexi_SD_Helpers(int SD_CLK, int SD_MISO, int SD_MOSI, int SD_CS)
{

    sck = SD_CLK;       //ESP32-C3 4
    miso = SD_MISO;     //ESP32-C3 5
    mosi = SD_MOSI;     //ESP32-C3 6
    cs = SD_CS;         //ESP32-C3 10
}

void Lexi_SD_Helpers::begin()
{
    Serial.begin(115200);
    delay(2000);
    Serial.println("Assigning SPI pins");
    SPI.begin(sck, miso, mosi, cs);
    
    if (!SD.begin(cs)) {
        Serial.println("Card mount failed");
        return;
    } else {
        Serial.println("Card Mounted successfully");
    }
    
    uint8_t cardType = SD.cardType();
    
    if (cardType == CARD_NONE) {
        Serial.println("No SD card attached");
        return;
    } else {
        Serial.println("Card detected");
    }
    
    Serial.print("Card type: ");
    Serial.println(cardType);
}

void Lexi_SD_Helpers::appendFile(){
    if (!SD.exists("/Log.txt")){
        Serial.println("File doesn't exist");
    }else{
        Serial.println("File exists");
    }
}

in the appendFile() function I’ve taken everything out. I just want it to recognise the file which it doesn’t even do that.

I set up the SPI using this

Lexi_SD_Helpers mySD(18, 19, 23, 5);

In the setup I do this:


  mySD.begin();

Please be aware that I’ve gutted the whole library to get one single function working. Once I find out what I’m doing wrong I’ll start building it again.

Before I gutted it I had FS.h included and a listDir function which listed the files. The file is on the SD card but the above will not recognise it:

Card detected
Card type: 2
Listing directory: /
  DIR : .Spotlight-V100
  FILE: Log.txt  SIZE: 262
  DIR : .Trashes

Can someone please tell me where I am going wrong?

As already requested, please post a complete sample code that uses the library. And, as already requested, please post the original all-in-one code that you say works.

Also, post the output from this trimmed down version.

Also, exactly which Arduino board are you using?

Are you certain that the file name is exactly correct, all ASCII with no unicode characters, no extra spaces anywhere, case correct? The function seems to work here for finding a file on the SD card using a Wemos R1 D32 board.

That is because you put the #include for SD.h in the cpp file, not the header file, so including the header does not include SD.h in the main sketch.

Thanks David,

All sorted. I did have the SD.h in the .cpp instead of the .h file. I have remedied this and the Library is now working. I made another huge mistake. I gutted the library files and just left a few functions. I then saved it without keeping a copy of the old files. I’ll have to go back now and recreate them. :roll_eyes:

This is how it looks now which works:

Lexi_SD_Helpers.h

#ifndef LEXI_SD_HELPERS_H
#define LEXI_SD_HELPERS_H

#include <SD.h>
#include <SPI.h>
#include <Arduino.h>

class Lexi_SD_Helpers {
public:
    Lexi_SD_Helpers(int CLK, int MISO, int MOSI, int CS);
    void begin();
    void appendFile(const char *path, const char *message);

private:
    int sck;
    int miso;
    int mosi;
    int cs;
    
};

#endif

Lexi_SD_Helpers.cpp

//#include <SD.h>
//#include <SPI.h>
#include "Lexi_SD_Helpers.h"

Lexi_SD_Helpers::Lexi_SD_Helpers(int CLK, int MISO, int MOSI, int CS)

{
    sck = CLK;       //ESP32-C3 4   CYD 18
    miso = MISO;     //ESP32-C3 5   CYD 19
    mosi = MOSI;     //ESP32-C3 6   CYD 21
    cs = CS;         //ESP32-C3 10  CYD 5
}

void Lexi_SD_Helpers::begin() {
  Serial.begin(115200);
  delay(500);
  Serial.println("Assigning SPI pins");
  SPI.begin(sck, miso, mosi, cs);

  if (!SD.begin(cs)) {
    Serial.println("Card mount failed");
    return;
  } else {
    Serial.println("Card Mounted successfully");
  }

  uint8_t cardType = SD.cardType();

  if (cardType == CARD_NONE) {
    Serial.println("No SD card attached");
    return;
  } else {
    Serial.println("Card detected");
  }

  Serial.print("Card type: ");
  Serial.println(cardType);
}

void Lexi_SD_Helpers::appendFile(const char *path, const char *message) {
  //Serial.printf("Appending to file: %s\n", path);
  File file = SD.open(path, FILE_APPEND);
  if (!file) {
    Serial.println("Failed to open file for appending");
    file.close();
    return;
  }
  if (file.println(message)) {
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

Library_Tessting.ino

#include <Lexi_SD_Helpers.h>

File file;
uint32_t time_now = 0;
uint32_t period = 4000;

//CYD board
Lexi_SD_Helpers mySD(18, 19, 23, 5);

void setup() {
  Serial.begin(115200);
  time_now = millis();
  mySD.begin();
}

void loop() {
  while (millis() > time_now + period) {
    mySD.appendFile("/Log.txt", "TESTING");
    time_now = millis();
  }
}

Serial Monitor

Message appended
Message appended
Message appended
Message appended
Message appended
Message appended

Log.txt

I also had multiple versions of SD.h which the Arduino IDE had picked up from various locations on my MacBook. These have since been removed.

Thanks everyone for your help, especially you David.

Alex