SD Card logging with card replacements

So yes, I have searched and read many threads about this; and I have been through the SdFat source files and documentation many times… here is why I am still posting this:

  • I couldn’t find a simple, clear example of how to log data while supporting card removes/inserts. Lots of instructions of how to do it, but no real sample code
  • I was looking for a solution using SdFat only (I am seriously memory constrained)
  • Many threads are old, and I see several posts from Bill Greinman mentioning updates to improve things and I wasn’t sure what applied and what didn’t anymore

… so I’d like your thoughts about the code below, and whether it could be improved? The goal is to keep the program running (no resets) while the card is occasionally removed & re-inserted. I do understand that corruption could happen if the card is removed while logging, I’ll handle that separately.

I have tested removing / re-inserting the card; and starting w/o a card and inserting it later.

A question I still have: I suspect the **Halt methods may be useful but I am not sure what they do exactly? If I call them every time I detect a failure will the next SD.begin still work?

#include <RTClib.h>
#include <SPI.h>
#include <SdFat.h>

#define SS_PIN 10
#define print2(F,V) if (V < 10) F.print('0'); F.print(V, DEC)

// clock
RTC_DS1307 rtc;

// SD Card
SdFat SD;

uint16_t log_id;
bool sdSetup(bool force = false);

void setup()
{
  Serial.begin(9600);
  rtcSetup();
  sdSetup(true);
}

void rtcSetup()
{
  while (! rtc.begin()) {
    Serial.println("RTC not found!");
    delay(1000);
  }
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running, setting the time");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  else {
    Serial.println("RTC is running");
  }
}

bool sdSetup(bool force)
{
  if (force || SD.cardErrorCode()) {
    Serial.println("\nInitializing SD card...");
    SD.begin(SS_PIN);

    if (SD.cardErrorCode()) {
      Serial.print("SD Card init failed! "); SD.errorPrint(&Serial);
    }
  }
  return ! SD.cardErrorCode();
}

void loop () {
  writeLog();
  delay(10000);
}

void writeLog()
{
  SdFile log;
  if (! sdSetup()) {
    return;
  }

  if (! log.open(SD.vwd(), "test.csv", FILE_WRITE)) {
    Serial.print("Could not open file: "); SD.errorPrint(&Serial);
    return;
  }

  DateTime now = rtc.now();

  log.print(log_id); log.print(' ');
  log.print(now.year(), DEC);
  log.print('-'); print2(log, now.month());
  log.print('-'); print2(log, now.day());
  log.print(' '); print2(log, now.hour());
  log.print(':'); print2(log, now.minute());
  log.print(':'); print2(log, now.second());
  log.println();

  bool log_ok = log.close() && ! log.getError();
  if (log_ok) {
    Serial.println("Log written");
    log_id++;
  }
  else {
    Serial.print("Could not write log: "); SD.errorPrint(&Serial);
  }
}