Can't add blank line to a SD file

Uno minima R4

Arduino IDE 2.3.8

Adafruit RTC+ SDcard shield (it employs the PFC8523 RTC chip)

Apologies to the weary: this is not rocket science. But I have been bashing my head on this stone for too many hours now, approaching it from multiple angles etc etc. to no avail.

I am working on a simple strain-gauge datalogger, writing a {RTC timestamp + gauge data} as CSV data on a SD card. The data is collected in groups of a few measurements at ~ 1 second intervals, then written to the SD file.

I want to add "blank" lines at the end of each "run", so that I can grab the data and graph it, without having to squint at the values of the RTC to manually group the data. In the example below, I have tried to add the word "end" on each of two lines. But this does not work. I have also tried myFile.println() and other variants on that them.....none of them are working.

Here is my code, which does collect the data and writes it to the SD card ....the problem is solely the addition of the delineating "blank" lines:

#include "HX711.h"
#include "SD.h"
#include "RTClib.h"
RTC_PCF8523 rtc;
#define SD_FAT_TYPE 3
#define PIN_SPI_CS 4
File myFile;
#define FILE_NAME "UTEST.TXT"
HX711 scale;
const int chipSelect = 10;
uint8_t dataPin = 2;
uint8_t clockPin = 3;
float f;
int i;
int j;
int k;

// code for setting up SD and checking RTC

void setup() {
  Serial.begin(57600);
  i =j = k = 1;

  if (!rtc.begin()) {  // SETUP RTC MODULE
    Serial.println(F("Couldn't find RTC"));
    while (1)
      ;
  }

  // Setup SD card
  if (!SD.begin(chipSelect)) {
    Serial.println(F("SD CARD FAILED, OR NOT PRESENT!"));
    while (1)
      ;  // don't do anything more:
  }

  Serial.println(F("SD CARD INITIALIZED."));
  Serial.println(F("--------------------"));
  // setup the HX711 module

  scale.begin(dataPin, clockPin);
  scale.set_scale(2340);  // output from the 1 kg beam is now in Gram units
  scale.tare();
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (1) delay(10);
  }
}

void loop() {
  // open file for writing
 if (i <= 5) {
    myFile = SD.open(FILE_NAME, FILE_WRITE);
    if (myFile) {
    } else {
      Serial.print(F("SD Card: error on opening file "));
      Serial.println(FILE_NAME);
    }
    Serial.println(F("Writing log to SD Card"));
    DateTime now = rtc.now();
    Serial.println("write date stamp");
    myFile.print(now.year(), DEC);  // write timestamp
    myFile.print('-');
    myFile.print(now.month(), DEC);
    myFile.print('-');
    myFile.print(now.day(), DEC);
    myFile.print(' ');
    myFile.print(now.hour(), DEC);
    myFile.print(':');
    myFile.print(now.minute(), DEC);
    myFile.print(':');
    myFile.print(now.second(), DEC);
    myFile.print(" , ");  // CSV delimiter between timestamp and data
    Serial.println("read & write scale");
    f = scale.get_units(5);
    myFile.print(f);
    myFile.write("\n");  // new line
    delay(1000);
    i = (i + 1);
    }
    else {
      myFile.println("end");
      myFile.println("end");
    }
    myFile.close();
  }
 


void loop() {
   // open file for writing
   if( i <= 5 ) {
      myFile = SD.open(FILE_NAME, FILE_WRITE);
      if( myFile ) {
      } else {
         Serial.print(F("SD Card: error on opening file "));
         Serial.println(FILE_NAME);
      }
      // FILE IS NOW OPEN FOR WRITING
      .
      .
      .
   } else {
      // FILE IS NOT OPEN WHEN YOU GET TO THIS PART OF YOUR CODE
      // BECAUSE YOU NEVER OPENED IT THIS TIME THROUGH THE LOOP
      myFile.println("end");
      myFile.println("end");
   }
   myFile.close();
   // FILE IS NOW CLOSED
}

Not looking at your problem but it's not sensible to write to a file when opening did not succeed.

True! I did not see that.

That part of the code is copied from the SD-card library offered by Rob Tillaart

It is a Serial.print command, so no it does not go to the SD-card but instead is displayed in the IDE panel. So I assume that Rob Tillart was awake too late one night (much as I am right now)

Thanks for the comment.....sharp eyes!

M

Hi, VDD:

Thanks for the suggestion.

I went into my code and added the line myFile = SD.open(FILE_NAME, FILE_WRITE); after the else { , which I believe is what you intended.

However, it did not work.

It is all very counterintuitive (to me, at least) because there is the myFile.close() command and one would think that, until the Close command is issued, the SD communication remains Open

I will try to help, but my immediate thought is that adding blank lines is not going to be a good solution. Why do you need to squint at the RTC timestamps in order to graph the data? Why do you need to manually group the data?

If you want people to continue to help you, never say that. It's frustrating for those doing their best to help you. They need to know exactly what happened or what changed, which may be nothing, and how that's different to what you wanted or expected to happen.

I think a better solution could be to add a group number to group the data together. There are a couple of ways to do that:

  1. If you are analysing the data in Excel you could add a column with a formula that spots the gaps in the timestamps for you and adds a group number column to the data.
  2. You could add a group number to the Arduino code and write it to the SD card file.

Who better to ask than @robtillaart ?

Rather than allowing sketch to proceed after a failure...

    if (myFile) {
    } else {
      Serial.print(F("SD Card: error on opening file "));
      Serial.println(FILE_NAME);
    }

Try halting progress and printing a warning.

    if (!myFile) {
      Serial.print(F("SD Card: error on opening file "));
      Serial.println(FILE_NAME);
      while(1){};
    }

I could not find the SD library you mentioned: Rob Tillaart - Arduino Libraries

That must be one from the future as I do not recall writing such library (yet).
(check....)
No SD-card lib, the closest is an SD2405 RTC library.

Happens daily :smile:

@polarmolar
That said, where did you copy that code from?
It might be a bug in one of the examples near my libraries.

Adding a blank line ... have you tried myFile.print("\r\n\r\n");

What did not work? How did it not work? No one here read minds and if you don't show us what you're writing about, we don't know. Surely that's obvious?

Since you haven't shown us what you did, I'll just suggest replacing your loop() function with the following and thinking about what you see on the serial monitor when you upload and run this. All it does is add a few debugging statements, something you should have done on your own.

void loop() {
   // open file for writing
   if( i <= 5 ) {
      myFile = SD.open(FILE_NAME, FILE_WRITE);
      if( myFile ) {
         Serial.println(F("File is open"));
      } else {
         Serial.print(F("SD Card: error on opening file "));
         Serial.println(FILE_NAME);
      }
      Serial.println(F("Writing log to SD Card"));
      DateTime now = rtc.now();
      Serial.println("write date stamp");
      myFile.print(now.year(), DEC);  // write timestamp
      myFile.print('-');
      myFile.print(now.month(), DEC);
      myFile.print('-');
      myFile.print(now.day(), DEC);
      myFile.print(' ');
      myFile.print(now.hour(), DEC);
      myFile.print(':');
      myFile.print(now.minute(), DEC);
      myFile.print(':');
      myFile.print(now.second(), DEC);
      myFile.print(" , ");  // CSV delimiter between timestamp and data
      Serial.println("read & write scale");
      f = scale.get_units(5);
      myFile.print(f);
      myFile.write("\n");  // new line
      delay(1000);
      i = (i + 1);
   } else {
      Serial.println(F("Now you're trying to write to the file: is it open or closed?"));
      myFile.println("end");
      myFile.println("end");
   }
   myFile.close();
   Serial.println(F("File is closed"));
}

You are absolutely correct re: "it did not work", which I hesitated to send but I wanted to indicate that I was actively working on the suggestion made. It was late, and with a foggy brain it seemed to me that I was creating more problems and so I did not add further details about "why" and "how" it did not work

Hi, Rob!

Honest mistake on my part. I started creating the sketch with an example from your HX7111 library, then modified it. It still had the "credit" tag at the top, referencing you as the author. So I apologize for the mistaken accreditation!

Thanks again for your patience.

I applied your suggestion, with the debugging lines added. That did help to resolve part of the problem, and I am now able to force the program to write a line to the SD file with the text "end", and also with blank lines.

Here is my code presently; I added serial-print lines to make sure that the HX711 and the RTC are both being interrogated (they are) for each of the 5 loop-throughs.

The present problem is this:

  1. the final SD-write operation (add blank lines or text) does function, but now...
  2. the myFile.print of the data (read from the RTC, and the HX711) does not write any data to the SD !

I added another SD.open command right before the loop of myFile.print commands, but that's not solving the problem...

I cannot see what the bug is, can you take a look, please?

#include "HX711.h"
#include "SD.h"
#include "RTClib.h"
// include <Sleep_n0m1.h>
RTC_PCF8523 rtc;
#define SD_FAT_TYPE 3
#define PIN_SPI_CS 4
File myFile;
#define FILE_NAME "udata.csv"
HX711 scale;
const int chipSelect = 10;
uint8_t dataPin = 2;
uint8_t clockPin = 3;
float f;
int i;
int j;
int k;

// code for setting up SD and checking RTC

void setup() {
  Serial.begin(57600);
  i = j = k = 1;

  if (!rtc.begin()) {  // SETUP RTC MODULE
    Serial.println(F("Couldn't find RTC"));
    while (1)
      ;
  }

  // Setup SD card
  if (!SD.begin(chipSelect)) {
    Serial.println(F("SD CARD FAILED, OR NOT PRESENT!"));
    while (1)
      ;  // don't do anything more:
  }

  Serial.println(F("SD CARD INITIALIZED."));
  Serial.println(F("--------------------"));
  // setup the HX711 module

  scale.begin(dataPin, clockPin);
  scale.set_scale(2340);  // output from the 1 kg beam is now in Gram units
  scale.tare();
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (1) delay(10);
  }
}

void loop() {


  if (i <= 5) {
    myFile = SD.open(FILE_NAME, FILE_WRITE);

    if (myFile) {
      Serial.println(F("File is open"));
    } else {
      Serial.print(F("SD Card: error on opening file "));
      Serial.println(FILE_NAME);
    }
    Serial.println(F("Writing log to SD Card"));
    DateTime now = rtc.now();
    Serial.println("write date stamp");
    myFile = SD.open(FILE_NAME, FILE_WRITE);
    myFile.print("record");
    Serial.print(now.year(), DEC);
    myFile.print(now.year(), DEC);  // write timestamp
    myFile.print('-');
    myFile.print(now.month(), DEC);
    myFile.print('-');
    myFile.print(now.day(), DEC);
    myFile.print(' ');
    myFile.print(now.hour(), DEC);
    myFile.print(':');
    myFile.print(now.minute(), DEC);
    myFile.print(':');
    myFile.print(now.second(), DEC);
    myFile.print(" , ");  // CSV delimiter between timestamp and data
    Serial.println("read & write scale");
    f = scale.get_units(5);
    Serial.print(f);
    myFile = SD.open(FILE_NAME, FILE_WRITE);
    myFile.print(f);
    myFile.write("\n");  // new line
    delay(1000);

  } else if (i = 6) {
    myFile = SD.open(FILE_NAME, FILE_WRITE);
    Serial.println(F("Now you're trying to write to the file: is it open or closed?"));
    myFile.println("end-of-test");
    myFile.write("\n");
    myFile.close();
    Serial.println(F("File is closed"));
    exit(0);
  }
  i = (i + 1);
}

Here is what one pass of output to the serial monitor looks like:

11:44:57.875 -> 0.00File is open

11:44:58.904 -> Writing log to SD Card

11:44:58.904 -> write date stamp

11:44:58.936 -> 2026read & write scale

11:44:59.257 -> -0.00File is open

11:45:00.285 -> Writing log to SD Card

11:45:00.285 -> write date stamp

11:45:00.285 -> 2026read & write scale

11:45:00.605 -> 0.01File is open

11:45:01.632 -> Writing log to SD Card

11:45:01.632 -> write date stamp

11:45:01.665 -> 2026read & write scale

11:45:01.984 -> -0.00File is open

11:45:03.011 -> Writing log to SD Card

11:45:03.011 -> write date stamp

11:45:03.011 -> 2026read & write scale

11:45:03.332 -> -0.01Now you're trying to write to the file: is it open or closed?

11:45:04.392 -> File is closed

and here is the contents of the udata.csv file looks like, after the same run:

  1. end-of-test

Your suggestions are really excellent.

I will probably incorporate them into a "Mark II" version next week. But for now I simply need to get a clunky version running so that I can start capturing the data from a real-world problem that is not waiting patiently!

Thanks very much for your input. I will write back soon

void loop() {

  if (i <= 5) {
    myFile = SD.open(FILE_NAME, FILE_WRITE);

    if (myFile) {
      Serial.println(F("File is open"));
    } else {
      Serial.print(F("SD Card: error on opening file "));
      Serial.println(FILE_NAME);
    }
    Serial.println(F("Writing log to SD Card"));
    DateTime now = rtc.now();
    Serial.println("write date stamp");
    myFile = SD.open(FILE_NAME, FILE_WRITE);
    myFile.print("record");

You are opening the file, testing to see if the file is open, then regardless of whether the file is open or not, you are opening the same file a second time and then writing the data.

I would do something similar to the following, although it would be much more efficient to collect all five data points first, then only open the file once and write everything at one time.

  myFile = SD.open(FILE_NAME, FILE_WRITE);
  if (myFile) {
    Serial.println(F("File is open"));
    if (i <= 5) {
      //write data to SD card
    } else if (i == 6) {
      //write end of file text to SD card
    }
    myFile.close();
    Serial.println(F("File is closed"));
  } else {
    Serial.print(F("SD Card: error on opening file "));
    Serial.println(FILE_NAME);
  }

I have re-worked the code, including some diagnostic lines that diagnose whether the SD card file is "open" or not.

The serial output (to the monitor) indicates that the SD file is opening and closing as required.

But, the weird thing is that after the looping through of multiple iterations, only the FINAL reading is recorded on the SD card file, along with the text "end-of-test" which I have applied to delineate one set of iterations from the next set.

I like the idea of creating a datastring and writing the entire loop's worth of data at once. But I feel that I need to solve this SD-writing problem, first.

Here is the code:


#include "HX711.h"
#include "SD.h"
#include "RTClib.h"
RTC_PCF8523 rtc;
#define SD_FAT_TYPE 3
#define PIN_SPI_CS 4
#define FILE_NAME "udata.csv"
HX711 scale;
const int chipSelect = 10;
File myFile;
uint8_t dataPin = 2;
uint8_t clockPin = 3;
float f;
int i;  // iteration counter
int j;  // pre-set, total number of iterations to perform


// code for setting up SD and checking RTC

void setup() {
  Serial.begin(115200);
  delay(3000);
  while (!Serial) { ; }  // wait for serial port to connect.
  Serial.println("Initializing Serial Port.");

  i = 1;
  j = 5;  // number of iterations to perform

  // SETUP RTC MODULE
  if (!rtc.begin()) {
    Serial.println(F("Couldn't find RTC"));
    while (1)
      ;
  }

  // Setup SD card
  if (!SD.begin(chipSelect)) {
    Serial.println("SD Card initialization failed, or not present!");
    while (1)
      ;
  }
  Serial.println("SD Card initialization done.");

  // setup the HX711 module
  scale.begin(dataPin, clockPin);
  scale.set_scale(2340);  // scale output from the 1 kg beam so that output is in Gram units
  scale.tare();

  // setup the RTC
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (1) delay(10);
  }
  // check the SD card
  if (myFile) {
    Serial.println(F("File is open"));
  } else {
    Serial.print(F("SD Card: error on opening file "));
    Serial.println(FILE_NAME);
  }
}




void loop() {
  if (i <= j) {
    Serial.println(F("Collect data & writing log to SD Card"));
    Serial.println(F("iteration:"));
    myFile = SD.open(FILE_NAME, FILE_WRITE);
          if (myFile) {
            Serial.println(F("File is open"));
          } else {
            Serial.print(F("SD Card: error on opening file "));
            Serial.println(FILE_NAME);
          }
    DateTime now = rtc.now();
    Serial.println("writing date stamp");
    myFile.print("record");         // it is bizarre that this is not written to the SD, as it directly follows the 'SD.open' command
    myFile.print(now.year(), DEC);  // write timestamp year
    myFile.print('-');
             if (myFile) {
            Serial.println(F("File is open"));
          } else {
            Serial.print(F("SD Card: error on opening file "));
            Serial.println(FILE_NAME);
          }
    myFile.print(now.month(), DEC);
    myFile.print('-');
    myFile.print(now.day(), DEC);
    myFile.print(' ');
    myFile.print(now.hour(), DEC);
    myFile.print(':');
    myFile.print(now.minute(), DEC);
    myFile.print(':');
    myFile.print(now.second(), DEC);
    Serial.println(now.second(), DEC);  // visible on the monitor and incrementing so RTC works properly
    myFile.print(" , ");                // CSV delimiter between timestamp and data
    Serial.println("reading & writing weight");
    f = scale.get_units(5);
    Serial.println(f);
    myFile.print(f);     // this is also NOT written to the SD card!
    myFile.write("\n");  // new line
    delay(1000);

  } else if (i > j) {
    Serial.println(F("Trying to write blanks to the file: is it open or closed?"));
             if (myFile) {
            Serial.println(F("File is open"));
          } else {
            Serial.print(F("SD Card: error on opening file "));
            Serial.println(FILE_NAME);
          }
    myFile.print("end-of-test");
    myFile.write("\n");
    myFile.close();
             if (myFile) {
            Serial.println(F("File is open"));
          } else {
            Serial.print(F("SD Card: error on opening file "));
            Serial.println(FILE_NAME);
          }
    Serial.println(F("File is closed"));
    exit(0);
  }
  i = (i + 1);
}


the Serial (monitor) output is

Initializing Serial Port.
SD Card initialization done.
SD Card: error on opening file udata.csv
Collect data & writing log to SD Card
iteration:
File is open
writing date stamp
File is open
58
reading & writing weight
-0.00
Collect data & writing log to SD Card
iteration:
File is open
writing date stamp
File is open
59
reading & writing weight
-0.00
Collect data & writing log to SD Card
iteration:
File is open
writing date stamp
File is open
1
reading & writing weight
-0.00
Collect data & writing log to SD Card
iteration:
File is open
writing date stamp
File is open
2
reading & writing weight
-0.02
Collect data & writing log to SD Card
iteration:
File is open
writing date stamp
File is open
3
reading & writing weight
-0.01
Trying to write blanks to the file: is it open or closed?
File is open
SD Card: error on opening file udata.csv
File is closed

And, the record in the SD-card file is just these 2 lines:

record2026-4-17 20:2:3 , -0.01

end-of-test

Not solving your problem

You should take action if the file was NOT successfully opened. If it does not open for whatever reason you should not attempt to write to the file. Printing an error message is insufficient.
Currently you don't do that.

The easy way out is using a return statement as shown below.

    myFile = SD.open(FILE_NAME, FILE_WRITE);
          if (myFile) {
            Serial.println(F("File is open"));
          } else {
            Serial.print(F("SD Card: error on opening file "));
            Serial.println(FILE_NAME);
            return;
          }

The other (neater) way is to test again if the file is open before writing

    // if the file is open we can write
    if(myFile)
    {
      myFile.print("record");         // it is bizarre that this is not written to the SD, as it directly follows the 'SD.open' command
      myFile.print(now.year(), DEC);  // write timestamp year
      myFile.print('-');
      ...
      ...

See post #3 and post #16.

Thank you.

I have implemented both of your suggestions to my code. Those changes prove that the SD card is "open" and capable of being written to.

The problem now is (so it seems to me...) that each new data-pair is being written to the same line in the SD-card file...such that the latest data-pair over-writes the previous one.

When the loop is run subsequently, another record is created...but that next record consists of only one pair of data points (not, say, the 5 data-pairs I am currently collecting with each if-loop)

Seems to me that I must be ignorant of the method to force each data-pair onto a separate line as the record is being written. Any suggestions?

In the near future (once this basic version is running and collecting data), I will return to the programming to see if I can implement an Array, and write that Array to the SD card only after the if-loop is completed, so that a single write operation will suffice....

I've not been following this topic in detail because I think you are trying the wrong solution, as I said previously. But I'll answer this question.

A CSV file is just a text file. A text file is composed of lines of text. Each line is separated by a new-line ASCII control character. A CSV file additionally has commas to separate the values on each line.

So to force each value pair onto a separate line, print a new-line character between each value pair, but never between any pair of values.

You can print a new-line character by simply using .println(xyz) instead of .print(xyz), just as you do when you are printing to serial monitor. The new-line character will be written after whatever xyz is, if you use .println() but not if you use .print()

You don't need to use .write() or .print("\n") for this.