Corrupt? files on SD card

Ran the following code over the weekend that captures temp, optical density and pH of a cellular culture. (As a side note, I’ve ordered a RTC to alleviate the labor intensive runtime “timestamp”.) My issue is when I check the memory card this morning I have multiple new files with names such as " ╩調`pⁿb " in addition to the destination file. The destination file data starts 29hrs into the runtime.

Is the data prior to 29hrs lost or any chance it is recoverable?
Additionally, is this a fluke? I assume it isn’t but what is the cause of it/what should I look to modify to avoid this happening again?

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

File myFile;

#define TEMP_INTERVAL             360000ul   // 6min      
#define OD_INTERVAL               360000ul   // 6min      
#define PH_INTERVAL               360000ul   // 6min      
#define PH_CO2_ADD_INTERVAL       60000ul     // 1min  
#define PH_CO2_CHECK_INTERVAL     600000ul    // 10min


// * * * * pH ADDED * * * *
unsigned long int avgValue;   //Store the average value of the sensor feedback
float b;
int buf[10], temp;

// Optical Density Sensor
const byte OD_Probe          = A5;   //
const byte Temp_Probe        = A0;   //
const byte pH_Probe          = A4;   //
const byte CO2_GateValve     = 3;
const byte pH_HIGH_THRESHOLD = 16.00;

unsigned long timeOD;
unsigned long timepH;
unsigned long timeTemp;
unsigned long timeCo2Add;
unsigned long timeCo2Chk;
unsigned long timeNow;

enum { ST_INIT, ST_ADD, ST_CHK };
int state;


// -----------------------------------------------------------------------------

void setup () {
  Serial.begin (9600);

  Serial.print("Initializing SD card...");
  pinMode(10, OUTPUT);
  if (!SD.begin(4)) {
    Serial.println("Initialization failed!");
    return;
  }
  Serial.println("Initialization done.");
  myFile = SD.open("08122020.txt", FILE_WRITE);

  if (myFile) {
    Serial.print("Writing to 08122020.txt...");
    // myFile.println("testing 1, 2, 3.");
    myFile.close();
    Serial.println("done.");
  } else {
    Serial.println("error opening 08122020.txt");
  }

  myFile = SD.open("08122020.txt");
  if (myFile) {
    Serial.println("08122020.txt");
    while (myFile.available()) {
      Serial.write(myFile.read());
    }
    myFile.close();
  } else {
    Serial.println("error opening 08122020.txt");
  }

  pinMode (OD_Probe, INPUT);
  pinMode (CO2_GateValve, OUTPUT);
  pinMode (Temp_Probe, INPUT);
  pinMode (pH_Probe, INPUT);

  timeNow  = millis ();
  timeOD   = timeNow;
  timepH   = timeNow;
  timeTemp = timeNow;

}


void loop ()
{
  timeNow  = millis ();
  // -----------------------------------------------------------------------------

  // * * * * * Temp INTERVAL * * * * *

  if ((timeNow - timeTemp) >= TEMP_INTERVAL)
  {
    timeTemp = timeNow;
    int temp_Val = analogRead (Temp_Probe);

    myFile = SD.open("08122020.txt", FILE_WRITE);
    myFile.print("RT Min: ");
    unsigned long Now = millis() / 60000;
    myFile.print(Now);
    myFile.print(". Temp: ");
    myFile.println(((temp_Val) - 500) / 10);
    Serial.print("Temp: ");
    Serial.println(((temp_Val) - 500) / 10);
    myFile.close();
  }

  // -----------------------------------------------------------------------------

  // * * * * * OD INTERVAL * * * * *

  if ((timeNow - timeOD) >= OD_INTERVAL)
  {
    timeOD     = timeNow;
    int od_Val = analogRead (OD_Probe);
    myFile = SD.open("08122020.txt", FILE_WRITE);
    myFile.print("RT Min: ");
    unsigned long Now = millis() / 60000;
    myFile.print(Now);
    myFile.print(". OD: ");
    myFile.println(od_Val);
    Serial.print("OD: ");
    Serial.println(od_Val);
    myFile.close();
  }

  // -----------------------------------------------------------------------------

  // * * * * * pH INTERVAL * * * * *

  if ((timeNow - timepH) >= PH_INTERVAL)
  {
    int pH_Val;

    switch (state)  {
      case ST_INIT:

        for (int i = 0; i < 10; i++) //Get 10 sample value from the sensor for smooth the value
        {
          buf[i] = analogRead(pH_Probe);
          delay(10);
        }
        for (int i = 0; i < 9; i++) //sort the analog from small to large
        {
          for (int j = i + 1; j < 10; j++)
          {
            if (buf[i] > buf[j])
            {
              temp = buf[i];
              buf[i] = buf[j];
              buf[j] = temp;
            }
          }
        }
        avgValue = 0;
        for (int i = 2; i < 8; i++)               //take the average value of 6 center sample
          avgValue += buf[i];
        float pH_Val = (float)avgValue * 5.0 / 1024 / 6; //convert the analog into millivolt
        pH_Val = 3.5 * pH_Val;                  //convert the millivolt into pH value


        myFile = SD.open("08122020.txt", FILE_WRITE);
        myFile.print("RT Min: ");
        unsigned long Now = millis() / 60000;
        myFile.print(Now);
        myFile.print(". pH: ");
        myFile.println(pH_Val);
        Serial.print("pH: ");
        Serial.println(pH_Val);
        myFile.close();

        if (pH_Val >= pH_HIGH_THRESHOLD)  {
          state = ST_ADD;
          timeCo2Add = timeNow;
          timeCo2Chk = timeNow;

          digitalWrite (CO2_GateValve, LOW);
        }
        else
          timepH = timeNow;
        break;

      case ST_ADD:
        if ((timeNow - timeCo2Add) >= PH_CO2_ADD_INTERVAL)  {
          state = ST_CHK;
          digitalWrite (CO2_GateValve, HIGH);
        }
        break;


      case ST_CHK:
      default:
        if ((timeNow - timeCo2Chk) >= PH_CO2_CHECK_INTERVAL)  {
          state  = ST_INIT;
          timepH = timeNow;
        }
        break;
    }
  }
}

It is a very bad idea to open the file, write one or two values, then close it again, over and over.

This requires the library code to read, erase and rewrite the same data block on the SD card many, many times and vastly increases the chance of error, as well as the power consumption (if the latter is an issue).

Open the file once for writing in setup(), and don't close it again until it is time to do so. If the power fails, at most you lose only the last, unwritten data block.

Opening a new file every day and closing the previous day's file would be another option.

jremington:
It is a very bad idea to open the file, write one or two values, then close it again, over and over.

This requires the library code to read, erase and rewrite the same data block on the SD card many, many times and vastly increases the chance of error,

Karma. I have asked this question before, and I never got an answer.

jremington:
It is a very bad idea to open the file, write one or two values, then close it again, over and over.

This requires the library code to read, erase and rewrite the same data block on the SD card many, many times and vastly increases the chance of error, as well as the power consumption (if the latter is an issue).

Open the file once for writing in setup(), and don't close it again until it is time to do so. If the power fails, at most you lose only the last, unwritten data block.

Opening a new file every day and closing the previous day's file would be another option.

Thank you! For this set up, the amount of power draw is not of concern since it will be plugged into an outlet 24/7.

A follow up Q: would it make more sense to have individual files for each temp, pH & OD? Open all three in setup(), leave open & write to relative file when called upon or is that more risky than having a single file open? What I'm working on has the potential to be running for ~2-4 weeks.

Thank you again!

would it make more sense to have individual files for each temp, pH & OD

Not at all, for several reasons.

It is always a pain to merge data files later, especially if measurement times are important, and each open file consumes a large amount of precious RAM for the file block buffer, 512 bytes/file IIRC.

Another excellent option is to log all data through the serial port and store it using the Sparkfun Openlog. Very cheap, reliable and relieves your Arduino of everything to do with the SD card. I've used it to log data for months at a run.

The Openlog input serial port is 3.3V only, so use some sort of level shifting when logging from a 5V Arduino (I use just a 3.3K resistor in series between 5V TX and 3.3V RX).

jremington:
It is a very bad idea to open the file, write one or two values, then close it again, over and over.

This requires the library code to read, erase and rewrite the same data block on the SD card many, many times and vastly increases the chance of error, as well as the power consumption (if the latter is an issue).

Open the file once for writing in setup(), and don't close it again until it is time to do so. If the power fails, at most you lose only the last, unwritten data block.

Opening a new file every day and closing the previous day's file would be another option.

The last couple days (admittedly out of fear of more lost data) I ran it with each parameter having it's own file but will rectify that shortly. Until the RTC arrives, how would I close the file less frequently than immediately after data is written? Sounds like leaving it open for an unspecified length of time is a bit like playing with fire.

You initialize your SD drive. But, what is it mounted on? Typically they come on displays. If this is the case with yours, you need to initialize the display as well. UN-initilize displays can do random stuff on the SPI bus messing up your SD drive pretty badly.

-jim lee

Sounds like leaving it open for an unspecified length of time is a bit like playing with fire.

I leave files open for a month or more at the time. As I stated, you lose at most the last, unwritten 512 byte data block. However, some people write daily files. At midnight, close the open one, and open a new with labeled with the day's date.

You have already seen what can happen when you open and close a file for every single write. THAT is playing with fire!

A good alternative would be to call flush() at reasonable intervals, like every 10 minutes.