SD stop after reading and writte a few data

Hello everyone,

I'm new on arduino's and I'm using an arduino one to read and writte moisture content with sensors. My code works to read and writte in the SD card, but after a few time recording the data its stop. I being trying modifying the code and change the SD card and SD card reader, but without results.

Ps. I'm also using a RTC clock to have the time of the reading cause the idea is using the arduino with battery and not connected to the computer (it works perfectly reading in the computer).

I leave my code here to see if you can help me with this

Thanks !!

//first calibrate dry sensor value and pure water. We might want to consider calibrating under extremely dry conditions if we want more accuracy in drier values.
const int dry = 560; // value for dry sensor
const int wet = 305; // value for wet sensor
const int chipSelect = 4;

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

RTC_DS3231 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Wednesday", "Thursday", "Friday", "Saturday"};
int moist1;
// SD CARD READER
File myFile;

void setup() {
  Serial.begin(9600);  //com enable
  delay (1000);
  if (!rtc.begin ()) {
    Serial.println ("couldn't find RTC");
    Serial.flush();
    while (1) delay(1000);
  }

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  }
  //wait for serial port to connect.

  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("card failed, or not present");
    // don't do anything more:
    while (1);
    Serial.println("card initialized.");
  }

  // When time needs to be re-set on a previously configured device, the
  // following line sets the RTC to the date & time this sketch was compiled
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

void loop() {
  //make a string for assembling the data to log:
  String dataString = "";

  DateTime now = rtc.now();
  Serial.print (now.year(), DEC);
  Serial.print ("/");
  Serial.print (now.month(), DEC);
  Serial.print ("(");
  Serial.print (daysOfTheWeek[now.dayOfTheWeek()]);
  Serial.print (")");
  Serial.print (now.hour(), DEC);
  Serial.print (":");
  Serial.print (now.minute(), DEC);
  Serial.print (":");
  Serial.print (now.second(), DEC);
  Serial.print (",");
  {
    int sensorValue1 = analogRead(A1);
    int moist1 = analogRead(A1);
    moist1 = analogRead(A1); //take reading 1
    int percentMoisture1 = map(sensorValue1, wet, dry, 100, 0);
    Serial.print(moist1);
    Serial.print(",");
    Serial.print(percentMoisture1);
    Serial.print(",");
    Serial.println("%");
    myFile = SD.open("test.txt", FILE_WRITE);
    if (myFile) {
      myFile.print (now.year(), DEC);
      myFile.print ("/");
      myFile.print (now.month(), DEC);
      myFile.print ("(");
      myFile.print (daysOfTheWeek[now.dayOfTheWeek()]);
      myFile.print (")");
      myFile.print (now.hour(), DEC);
      myFile.print (":");
      myFile.print (now.minute(), DEC);
      myFile.print (":");
      myFile.print (now.second(), DEC);
      myFile.print (",");
      myFile.print(moist1);
      myFile.print(",");
      myFile.print(percentMoisture1);
      myFile.println("%");
      myFile.close(); // close the file
    }
    // if the file didn't open, print an error:
    else {
      Serial.println("error opening test.txt");
    }
  }
  delay(1000);
}

Welcome to the forum.

It is called a "Arduino Uno".
The code seems okay.
The Wire library can halt a sketch if you have a bad I2C bus. Can you show a picture of how the RTC module is wired ? Can you give a link to where you bought the RTC module ? Breadboards can have bad contacts and jumper wires can be broken.

Thanks for the correction and for the welcome! I atach here a photo on how the pieces are connected for the RTC. Do you think that is the problem and not the micro SD card reader?


My RTC is connected:
SCL to A5
SDA to A4
energy to 5V and GND

not very sure about the link where the RTC was bought cause someone give me the pieces (maybe amazon?).

I agree with @Koepel that your sketch itself does not show a reason for your problems.

Anyway, I have managed to make it work on Wokwi so it can be tested in the simulator
(the only thing I added here is a function to print the SD content by sending a 'p' via Serial terminal):

https://wokwi.com/projects/333926180509975124

And I created a modified version that addresses some issues that make it "nicer":

  • Replaced the 1000 msec delay by a millis()-function
  • Changed the printing routine to one that can print to Serial or SD card
  • Created a struct for the moisture data that is a little more "verbose"
  • Added the printing function called by entering the character 'p' in the Serial terminal (this routine takes over control until all lines are printed and may therefore interfere with the one second timing of the measurement.)
  • Removed some minor parts of the sketch which did not seem to be required
//first calibrate dry sensor value and pure water. We might want to consider calibrating under extremely dry conditions if we want more accuracy in drier values.
const int dry = 560; // value for dry sensor
const int wet = 305; // value for wet sensor
const int chipSelect = 4;

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

RTC_DS3231 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Wednesday", "Thursday", "Friday", "Saturday"};

// SD CARD READER
File myFile;
// Date and time
DateTime now;
// Moisture data
struct moisture_type {
    int Percent;
    int Raw;
};

moisture_type Moisture;

void setup() {
  Serial.begin(9600);  //com enable
  delay (1000);
  if (!rtc.begin ()) {
    Serial.println ("couldn't find RTC");
    Serial.flush();
    while (1);
  }

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  }
  
  Serial.print("Initializing SD card...");
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("card failed, or not present");
    // don't do anything more:
    while (1);
  }
  Serial.println("Card initialized.");

  // When time needs to be re-set on a previously configured device, the
  // following line sets the RTC to the date & time this sketch was compiled
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}

void loop() {
  static unsigned long lastMeasurement = 0;
  if (millis()-lastMeasurement >= 1000) {
    lastMeasurement = millis();
    now = rtc.now();
    getMoistureData();
    PrintTo(Serial);
    WriteToSD();
  }
  if (Serial.read() == 'p') PrintSD();
}

void WriteToSD(){
  myFile = SD.open("test.txt", FILE_WRITE);
  if (myFile) {
    PrintTo(myFile);
    myFile.close(); // close the file
  }
    // if the file didn't open, print an error:
  else {
      Serial.println("error opening test.txt");
    }
  }

void getMoistureData(){
  Moisture.Raw     = analogRead(A1);
  Moisture.Percent = map(Moisture.Raw, wet, dry, 100, 0);
}

void PrintTo(Stream &stream){
  stream.print (now.year(), DEC);
  stream.print ("/");
  stream.print (now.month(), DEC);
  stream.print ("(");
  stream.print (daysOfTheWeek[now.dayOfTheWeek()]);
  stream.print (")");
  stream.print (now.hour(), DEC);
  stream.print (":");
  stream.print (now.minute(), DEC);
  stream.print (":");
  stream.print (now.second(), DEC);
  stream.print (",");
  stream.print(Moisture.Raw);
  stream.print(",");
  stream.print(Moisture.Percent);
  stream.print(",");
  stream.println("%");
}

void PrintSD(){
    String s = "-> ";
    myFile = SD.open("test.txt");
    if (myFile) {
      while (myFile.available()) {
            char c = myFile.read();
            if (c != 10) s += c;
            else {
              Serial.println(s);
              s = "-> ";
            }
      }
     myFile.close(); 
    } 
}

This can be found and tested on Wokwi at
https://wokwi.com/projects/333922419329729107

Just feel free to use it (or not :wink: ).

Looks so much nice than mine, thank you very much.

Sadly, I tried the code in the arduino and save the data but after a time I have the same problem and I can see in the monitor serial this message "2022/6(Thursday)14:28:5,587,-10,%
2022/6(Thursday)14:28:6,595,-13,%
error opening test.txt
2022/6(Thursday)14:28:7,594,-13,%
error opening test.txt
2022/6(Thursday)14:28:8,594,-13,%".

Do you know if my micro SD card need some configuration or something?
it is so weird that read/writte and than stop doing.

That seems to be a problem with the SD card or the reader ... Did you change the micro SD card or did you try to re-format it?

As @Koepel already mentioned there seems to be no error in the code so the reason is quite likely a hardware related problem ...

I made some small changes to my sketch:

  • myFile is opened in setup()
  • myFile can be closed via Serial command 's' (stop) that also stops measuring and writing further data
  • Measurement will start and myFile will be opened again with Serial command 'w'
//first calibrate dry sensor value and pure water. We might want to consider calibrating under extremely dry conditions if we want more accuracy in drier values.
const int dry = 560; // value for dry sensor
const int wet = 305; // value for wet sensor
const int chipSelect = 4;

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

RTC_DS3231 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Wednesday", "Thursday", "Friday", "Saturday"};

// SD CARD READER
File myFile;
// Date and time
DateTime now;
// Moisture data
struct moisture_type {
    int Percent;
    int Raw;
};

moisture_type Moisture;
boolean DoWriting = true;

void setup() {
  Serial.begin(9600);  //com enable
  delay (1000);
  if (!rtc.begin ()) {
    Serial.println ("couldn't find RTC");
    Serial.flush();
    while (1);
  }

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  }
  
  Serial.print("Initializing SD card...");
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("card failed, or not present");
    // don't do anything more:
    while (1);
  }
  Serial.println("card initialized.");

  // When time needs to be re-set on a previously configured device, the
  // following line sets the RTC to the date & time this sketch was compiled
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));

   myFile = SD.open("test.txt", FILE_WRITE);
}

void loop() {
  static unsigned long lastMeasurement = 0;
  if (millis()-lastMeasurement >= 1000 && DoWriting) {
    lastMeasurement = millis();
    now = rtc.now();
    getMoistureData();
    PrintTo(Serial);
    WriteToSD();
  }
  handleSerialCommands();
}

void handleSerialCommands(){
  if (Serial.available()){
    char c = Serial.read();
    if (c == 'p') PrintSD();
    if (c == 's') {
        DoWriting = false;
        Serial.println("Stopped Writing to SD");
        myFile.close();
    }    
    if (c == 'w') {
        DoWriting = true;
        Serial.println("Start Writing to SD");
    }    
  }
}

void WriteToSD(){
  if (!myFile) myFile = SD.open("test.txt", FILE_WRITE);
  if (myFile) {
    PrintTo(myFile);
  }
    // if the file didn't open, print an error:
  else {
      Serial.println("error writing to test.txt");
    }
  }

void getMoistureData(){
  Moisture.Raw     = analogRead(A1);
  Moisture.Percent = map(Moisture.Raw, wet, dry, 100, 0);
}

void PrintTo(Stream &stream){
  stream.print (now.year(), DEC);
  stream.print ("/");
  stream.print (now.month(), DEC);
  stream.print ("(");
  stream.print (daysOfTheWeek[now.dayOfTheWeek()]);
  stream.print (")");
  stream.print (now.hour(), DEC);
  stream.print (":");
  stream.print (now.minute(), DEC);
  stream.print (":");
  stream.print (now.second(), DEC);
  stream.print (",");
  stream.print(Moisture.Raw);
  stream.print(",");
  stream.print(Moisture.Percent);
  stream.print(",");
  stream.println("%");
}

void PrintSD(){
    String s = "-> ";
    if (myFile) myFile.close();
    File PrintFile = SD.open("test.txt");
    if (PrintFile) {
      while (PrintFile.available()) {
            char c = PrintFile.read();
            if (c != 10) s += c;
            else {
              Serial.println(s);
              s = "-> ";
            }
      }
     PrintFile.close(); 
    } 
}

This way we avoid opening and closing the file every second (the file has to be closed "manually" before the SD card can be removed and there is a risk that the file may be corrupted in case of unintentional power off).

However for testing whether the behavior is the same or different it might be ok.

On Wokwi at https://wokwi.com/projects/333928877024870996

Again feel free to test it with your hardware ...

I ask only out of curiosity. Do you really need to record humidity every second? How much do you expect to change in 1 second?

1 Like

Yes.... so I'm running experiments with different moisture content in sand and we also use other sensors like thermocouples, and they are used 1 sec of interval so to make them consistent we need the 1 sec interval for the moisture sensor to.

It is a mistake to open the file, write one line, then close it again. That vastly increases the error rate, the power consumption and the time required to access the SD card. Also, breadboards and long, loose wires connecting the various modules increase the error rate significantly.

Open the file once in setup(), write to it, then close it when you are all done collecting data. A "stop data collection" button that closes the file is a good idea.

To make sure that the file pointers are kept up to date, issue this command at reasonable intervals, like hourly, daily, etc.

myFile.flush();

So you recomend me to use myFile.flush() instead of myFile.close ()?. I'm a bit confuse with that.

You must use both. Issue a myFile.flush() command periodically and myFile.close() when you are done collecting data.

If the power fails, you will lose at most those data collected since the last myFile.flush() command.

For reliable data collection, avoid breadboards and long, loose wires.

That RTC module has a design fault. It has a charging circuit which overcharges a rechargeable battery when it is used with 5V.
You have a normal battery, which will be damaged by the charging circuit.
Can you remove the diode to stop the charging ?

afbeelding

This code works perfectly ones I stop manually all the data is one the SD card.

Do you know if it is possible to add physical button to replace that start and stop the Serial command?

Sure:

The sketch:

//first calibrate dry sensor value and pure water. We might want to consider calibrating under extremely dry conditions if we want more accuracy in drier values.
const int dry = 560; // value for dry sensor
const int wet = 305; // value for wet sensor
const int chipSelect = 4;

const byte pinStop  = 2;
const byte pinStart = 3;

const byte ledStop = 5;
const byte ledStart = 6;

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

RTC_DS3231 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Wednesday", "Thursday", "Friday", "Saturday"};

// SD CARD READER
File myFile;
// Date and time
DateTime now;
// Moisture data
struct moisture_type {
    int Percent;
    int Raw;
};

moisture_type Moisture;
boolean DoWriting = true;

void setup() {
  Serial.begin(9600);  //com enable
  pinMode(pinStart, INPUT_PULLUP);
  pinMode(pinStop, INPUT_PULLUP);
  pinMode(ledStop, OUTPUT);
  pinMode(ledStart, OUTPUT);

  delay (1000);
  if (!rtc.begin ()) {
    Serial.println ("couldn't find RTC");
    Serial.flush();
    while (1);
  }

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

  }
  
  Serial.print("Initializing SD card...");
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("card failed, or not present");
    // don't do anything more:
    while (1);
  }
  Serial.println("card initialized.");

  // When time needs to be re-set on a previously configured device, the
  // following line sets the RTC to the date & time this sketch was compiled
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));

   myFile = SD.open("test.txt", FILE_WRITE);
}

void loop() {
  static unsigned long lastMeasurement = 0;
  if (millis()-lastMeasurement >= 1000 && DoWriting) {
    lastMeasurement = millis();
    now = rtc.now();
    getMoistureData();
    PrintTo(Serial);
    WriteToSD();
  }
  handleSerialCommands();
  handleButtons();
  if (myFile) SetWritingLED(true);
         else SetWritingLED(false);
}

void SetWritingLED(boolean state){
  digitalWrite(ledStop, !state);
  digitalWrite(ledStart, state);
}

void handleSerialCommands(){
  if (Serial.available()){
    char c = Serial.read();
    if (c == 'p') PrintSD();
    if (c == 's') {
        DoWriting = false;
        Serial.println("Stopped Writing to SD");
        myFile.close();
    }    
    if (c == 'w') {
        DoWriting = true;
        Serial.println("Start Writing to SD");
    }    
  }
}

void handleButtons(){
   if (StopButtonReleased()) {
        DoWriting = false;
        Serial.println("Stopped Writing to SD");
        myFile.close();
    }    
    if (StartButtonReleased()) {
        DoWriting = true;
        Serial.println("Start Writing to SD");
    }    
}


void WriteToSD(){
  if (!myFile) myFile = SD.open("test.txt", FILE_WRITE);
  if (myFile) {
      PrintTo(myFile);
  }
    // if the file didn't open, print an error:
  else {
      Serial.println("error writing to test.txt");
    }
  }

void getMoistureData(){
  Moisture.Raw     = analogRead(A1);
  Moisture.Percent = map(Moisture.Raw, wet, dry, 100, 0);
}

void PrintTo(Stream &stream){
  stream.print (now.year(), DEC);
  stream.print ("/");
  stream.print (now.month(), DEC);
  stream.print ("(");
  stream.print (daysOfTheWeek[now.dayOfTheWeek()]);
  stream.print (")");
  stream.print (now.hour(), DEC);
  stream.print (":");
  stream.print (now.minute(), DEC);
  stream.print (":");
  stream.print (now.second(), DEC);
  stream.print (",");
  stream.print(Moisture.Raw);
  stream.print(",");
  stream.print(Moisture.Percent);
  stream.print(",");
  stream.println("%");
}

void PrintSD(){
    String s = "-> ";
    if (myFile) myFile.close();
    File PrintFile = SD.open("test.txt");
    if (PrintFile) {
      while (PrintFile.available()) {
            char c = PrintFile.read();
            if (c != 10) s += c;
            else {
              Serial.println(s);
              s = "-> ";
            }
      }
     PrintFile.close(); 
    } 
}

boolean StopButtonReleased(){
  static byte lastState = HIGH;
  static unsigned long lastChange;
  static byte released = false;
  byte state = digitalRead(pinStop);
  if (lastState != state) lastChange = millis();
  if (millis() - lastChange > 30) {
     if (!state) released = true;
    }
  lastState = state;  
  if (released && state)  {
     released = false;
     return true;
  }
  return false;
}

boolean StartButtonReleased(){
  static byte lastState = HIGH;
  static unsigned long lastChange;
  static byte released = false;
  byte state = digitalRead(pinStart);
  if (lastState != state) lastChange = millis();
  if (millis() - lastChange > 30) {
     if (!state) released = true;
    }
  lastState = state;  
  if (released && state)  {
     released = false;
     return true;
  }
  return false;
}

and on Wokwi: https://wokwi.com/projects/333977963866358355

It could be done a little be more efficient by combining the button functions, but due to some time pressure and finally it works ... :wink:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.