Trouble storing data in a txt file on a SD card

Hi all,

I've been having trouble with storing the results of my loop in a txt file on a SD Card.

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <SPI.h>
#include <SD.h>

File myFile;

const int chipSelect = 10;

String time;
tmElements_t tm;

float counter;
float counter2;

boolean counted = false;
boolean counted2 = false;

float import;
float exported; 


void setup() {
    
  Serial.begin(9600);
  pinMode(7, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);

  while (!Serial) ; // wait for serial
  delay(200);
  Serial.println("Starting up the system");
  Serial.println("-------------------");
  Serial.println("Flower Turbine DataLogger ");
  pinMode(SS, OUTPUT);

  if (!SD.begin(chipSelect)) {
    Serial.println("SD Card initialization failed!");
    return;  
  }
  Serial.println("SD Card OK.");
  delay(200);

  ReadText();
}

void ReadText(){
  myFile = SD.open("DataFile.txt");
  if (myFile) {
      Serial.println("DataFile.txt");

      while (myFile.available()) {
        Serial.write(myFile.read());
      }
      myFile.close();
  }
  else {
    Serial.println("Error opening the DataFile.txt file");
  }
}

void WriteText(String txt){
  myFile = SD.open("11_energy.txt", FILE_WRITE);
  if (myFile) {
    myFile.println(txt);
    myFile.close();
  }
  else {
    Serial.println("Error opening the 11_energy file");
  }
}




void loop() {
    myFile = SD.open("11_energy.txt", FILE_WRITE);
  if(digitalRead(7) == 1 && counted == false){
    counter++;
    counted = true;
    import = counter / 1000; 
    myFile.print(Now() + ", ");
    myFile.print(counter);
    myFile.print(", ");
    myFile.print(millis()/1000);
    myFile.print(", vermogen import:, ");
    myFile.print(import);
    myFile.println(",kWh"); 
    myFile.close();
  } 
    else {if (digitalRead(7) == 0){
    counted = false;
    }
  }
    myFile = SD.open("11_energy.txt", FILE_WRITE);
  if (digitalRead(6) == 1 && counted2 == false){ 
    counter2++;
    counted2 = true;
    exported = counter2/1000;
    myFile.print(Now() + ", ");
    myFile.print(counter2);
    myFile.print(", ");
    myFile.print(millis()/1000);
    myFile.print(", vermogen exported:, ");
    myFile.print(exported);
    myFile.println(",kWh");
    myFile.close();
  } 
    else {if (digitalRead(6) == 0){
    counted2 = false;
  }
}
}


String Now(){
  String time = "";
  if (RTC.read(tm)) {
    //    time = String(tm.Hour+":"+tm.Minute+":"+tm.Secnd+" DAY : "+tm.Day+"/"+tm.Month+"/"+tmYearToCalendar(tm.Year));
    time+=tm.Hour;
    time+=":";

    time+=tm.Minute;
    time+=":";

    time+=tm.Second;
    time+=", ";

    time+=tm.Day;
    time+=",";

    time+=tm.Month;
    time+=",";

    time+=tmYearToCalendar(tm.Year);
  } 
  else {
    time = "NO";
    if (RTC.chipPresent()) {
      Serial.println("The DS1307 is stopped.  Please run the SetTime");
      Serial.println("example to initialize the time and begin running.");
      Serial.println();
    } 
    else {
      Serial.println("DS1307 read error!  Please check the circuitry.");
      Serial.println();
    }
  }
  return time;

}

The script as it currenty is runs, but the txt file is completely empy. I also tried to print it in a CSV file, but it still returns empty. If I just Serial.print it, the results show up nicely in the console.

I am quite puzzled on why the file returns empty. Could anyone maybe steer me in the right direction..

Thank you guys in advance.

Kind regards,

Jerom

You open the file unconditionally, but only write anything if certain conditions are met. Add some serial prints to find out if the writing code is ever called.

Das hier ist mein Code der Funktioniert (etwas anderer anwendungsfall)

void Logger::Write(long now, int State, float alltitude, float rotY, float rotZ, float accelX, float accelY, float accelZ, float uy, float uz, String filePath) {
    // CreateOpen file 
    logger_file = SD.open(filePath, FILE_WRITE);
    // if the file opened okay, write to it:
    if (logger_file) {
        String data = String(now) + ";" + String(State) + ";" + String(alltitude) + ";" +String(rotY) + ";" + String(rotZ) + ";" + String(accelX) + ";" + String(accelY) + ";" + String(accelZ) + ";" + String(uy) + ";" + String(uz);
        logger_file.println(data);
        logger_file.close(); // close the file
    }
    // if the file didn't open, print an error:
    else {
      digitalWrite(LEDG, LOW);
    }
}

In the testing phase everything was Serial printed. The script worked perfectly and upon every pulse (of the energy meter which it is connected to) it printed the counted pulse values and the rest.

These are the results whenever I serial print everything.

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <SPI.h>
#include <SD.h>

File myFile;

const int chipSelect = 10;

String time;
tmElements_t tm;

float counter;
float counter2;

boolean counted = false;
boolean counted2 = false;

float import;
float exported; 


void setup() {
    
  Serial.begin(9600);
  pinMode(7, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);

  while (!Serial) ; // wait for serial
  delay(200);
  Serial.println("Starting up the system");
  Serial.println("-------------------");
  Serial.println("Flower Turbine DataLogger ");
  pinMode(SS, OUTPUT);

  if (!SD.begin(chipSelect)) {
    Serial.println("SD Card initialization failed!");
    return;  
  }
  Serial.println("SD Card OK.");
  delay(200);
  
  Serial.println("Setting the time of RTC");
  delay(200);
  Serial.println("Setting time succesful");
  ReadText();
}

void ReadText(){
  myFile = SD.open("DataFile.txt");
  if (myFile) {
      Serial.println("DataFile.txt");

      while (myFile.available()) {
        Serial.write(myFile.read());
      }
      myFile.close();
  }
  else {
    Serial.println("Error opening the DataFile.txt file");
  }
}

void WriteText(String txt){
  myFile = SD.open("11_energy.txt", FILE_WRITE);
  if (myFile) {
    myFile.println(txt);
    myFile.close();
  }
  else {
    Serial.println("Error opening the 11_import file");
  }
}




void loop() {
  if(digitalRead(7) == 1 && counted == false){
    counter++;
    counted = true;
    import = counter / 1000; 
    Serial.print(Now() + ", ");
    Serial.print(counter);
    Serial.print(", ");
    Serial.print(millis()/1000);
    Serial.print(", vermogen import:, ");
    Serial.print(import);
    Serial.println(",kWh"); 

  } 
    else {if (digitalRead(7) == 0){
    counted = false;
    }
  }
  if (digitalRead(6) == 1 && counted2 == false){ 
    counter2++;
    counted2 = true;
    exported = counter2/1000;
    Serial.print(Now() + ", ");
    Serial.print(counter2);
    Serial.print(", ");
    Serial.print(millis()/1000);
    Serial.print(", vermogen exported:, ");
    Serial.print(exported);
    Serial.println(",kWh");

  } 
    else {if (digitalRead(6) == 0){
    counted2 = false;
  }
}
}


String Now(){
  String time = "";
  if (RTC.read(tm)) {
    //    time = String(tm.Hour+":"+tm.Minute+":"+tm.Secnd+" DAY : "+tm.Day+"/"+tm.Month+"/"+tmYearToCalendar(tm.Year));
    time+=tm.Hour;
    time+=":";

    time+=tm.Minute;
    time+=":";

    time+=tm.Second;
    time+=", ";

    time+=tm.Day;
    time+=",";

    time+=tm.Month;
    time+=",";

    time+=tmYearToCalendar(tm.Year);
  } 
  else {
    time = "NO";
    if (RTC.chipPresent()) {
      Serial.println("The DS1307 is stopped.  Please run the SetTime");
      Serial.println("example to initialize the time and begin running.");
      Serial.println();
    } 
    else {
      Serial.println("DS1307 read error!  Please check the circuitry.");
      Serial.println();
    }
  }
  return time;

}

I would like the following lines to be saved in a txt/csv file everytime a pulse is observed.

16:22:13, 7,11,2021, 1.00, 0, vermogen import:, 0.00,kWh
16:22:13, 7,11,2021, 1.00, 0, vermogen exported:, 0.00,kWh

Which arduino are you using? The SD card will need space in RAM for a 512 byte buffer.

In loop, you are opening the file, but when the if statements are false you are never closing the file.

The filename "11_energy.txt" is too long, the file name is in 8.3 format, 11_energy is 9 letters.

It is a serious mistake to open and close the file every time you want to write a line. This vastly increases the read/write time, the power draw and the error rate, and decreases the lifetime of the SD card.

Open the file once and close it when you are completely finished writing data.

Arduino Uno + A logging shield with a 16gb SD Card

Thank you! This makes sense. But where would I close the file, since the measurement is ongoing and never stops untill I decide to stop it?

Hopefully a compliant 8.3 name will fix it.

Another concern is the 512 byte buffer - it's allocated dynamically and since you open the file potentially many times, you could quickly run out of RAM. How much does the IDE tell you you've got free?

That is when you close the file.

I changed the name, but it still does not work. With this script I've around 25% free.

Whenever I decide to stop it I will physically unplug the arduino from the set-up, remove the SD card and read the file. When I place everything back, it should start measuring and logging again untill I physically stop the process to read the data. I don't know where and when I should write the stop code in the script.

There's your problem. When the file open code runs, it's either failing or consuming almost all of your remaining RAM.

Most people would add a button to the system that starts and stops recording, and the required tiny amount of code to the loop function.

Also, if you have a limited amount of memory (like an AVR-based Arduino), use of the String class generally leads to memory problems and program crashes at random times.

Code like this needs to be revised to use C-strings (zero terminated character arrays):

String Now(){
  String time = "";

It is a problem, but even if I reduce the code it still does not print the output in the txt file.

Thanks! I've physically added a button for opening the file, and closing the file. But it still does not work...

I suggest that you write a minimal program that just writes to a file to test the hardware. Then add your remaining code back until it fails.

Did you add any code to read the button state, and perform appropriate actions?

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <SPI.h>
#include <SD.h>

File myFile;

const int chipSelect = 10;

String time;
tmElements_t tm;

float counter;
float counter2;

boolean counted = false;
boolean counted2 = false;

float import;
float exported; 

const int buttonPin = 2;
const int buttonPin2 = 3;
int buttonState = 0;
int buttonState2 = 0;

void setup() {
    
  Serial.begin(9600);
  pinMode(7, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);

  while (!Serial) ; // wait for serial
  delay(200);
  Serial.println("Starting up the system");
  Serial.println("-------------------");
  Serial.println("Flower Turbine DataLogger ");
  pinMode(SS, OUTPUT);

  if (!SD.begin(chipSelect)) {
    Serial.println("SD Card initialization failed!");
    return;  
  }
  Serial.println("SD Card OK.");
  delay(200);
  
  Serial.println("Setting the time of RTC");
  delay(200);
  Serial.println("Setting time succesful");
  ReadText();
}

void ReadText(){
  myFile = SD.open("DataFile.txt");
  if (myFile) {
      Serial.println("DataFile.txt");

      while (myFile.available()) {
        Serial.write(myFile.read());
      }
      myFile.close();
  }
  else {
    Serial.println("Error opening the DataFile.txt file");
  }
}

void WriteText(String txt){
  myFile = SD.open("11FT.txt", FILE_WRITE);
  if (myFile) {
    myFile.println(txt);
        myFile.close();
  }
  else {
    Serial.println("Error opening the 11_import file");
  }
}


void loop() {
    buttonState2 = digitalRead(buttonPin2);
      if (buttonState2 == HIGH) {
      File myFile = SD.open("11FT.txt", FILE_WRITE);
      Serial.println("File succesfully opened");
      myFile.print("TestTestTest");
  } else {
  }
  
  if(digitalRead(7) == 1 && counted == false){
    counter++;
    counted = true;
    import = counter / 1000; 
    myFile.print(Now() + ", ");
    myFile.print(counter);
    myFile.print(", ");
    myFile.print(millis()/1000);
    myFile.print(", vermogen import:, ");
    myFile.print(import);
    myFile.println(",kWh"); 
  } 
    else {if (digitalRead(7) == 0){
    counted = false;
    }
  }
  if (digitalRead(6) == 1 && counted2 == false){ 
    counter2++;
    counted2 = true;
    exported = counter2/1000;
    myFile.print(Now() + ", ");
    myFile.print(counter2);
    myFile.print(", ");
    myFile.print(millis()/1000);
    myFile.print(", vermogen exported:, ");
    myFile.print(exported);
    myFile.println(",kWh");
  } 
    else {if (digitalRead(6) == 0){
    counted2 = false;
  }
}
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {
    myFile.close();
    Serial.println("File succesfully saved");
  } else {
  }
}


String Now(){
  String time = "";
  if (RTC.read(tm)) {
    //    time = String(tm.Hour+":"+tm.Minute+":"+tm.Secnd+" DAY : "+tm.Day+"/"+tm.Month+"/"+tmYearToCalendar(tm.Year));
    time+=tm.Hour;
    time+=":";

    time+=tm.Minute;
    time+=":";

    time+=tm.Second;
    time+=", ";

    time+=tm.Day;
    time+=",";

    time+=tm.Month;
    time+=",";

    time+=tmYearToCalendar(tm.Year);
  } 
  else {
    time = "NO";
    if (RTC.chipPresent()) {
      Serial.println("The DS1307 is stopped.  Please run the SetTime");
      Serial.println("example to initialize the time and begin running.");
      Serial.println();
    } 
    else {
      Serial.println("DS1307 read error!  Please check the circuitry.");
      Serial.println();
    }
  }
  return time;

}

This is my current code. I added two buttons for opening and closing the file.