Weather Station project: LCD is preventing data storage to the SD card

I am working on a weather station. I am using a UNO, with an Adafruit Data Logging shield for Arduino Product ID: 1141, an Adafruit BME280 I2C or SPI Temperature Humidity Pressure product ID: 2652, and a 2004 LCD with I2C interface. I have looked an many examples and used them for inspiration to compile this code. This issue is when I activate the LCD the data stops logging. The code compiles with no errors. I done understand where the conflict is between the LCD and the SD logging function.
here is the code

/*Arduino weather station 
Thomas T Rush 19JUN2023
R-01 getting the RTC to function
01-Date and time functions using a PCF8523 RTC connected via I2C and Wire lib
02-SD card loging the Date ant Time to a file
03-Non-interrupt timer for writing data to SD card
04-removing rem-ed statments used for development
05-Setting up the BME280 temperature, pressure, humidity sensor
05A- adding "logfile.flush();" and a delay inbetween data wrights to the SD card
06A- Adding the LCD a 2004 and making a custom character for the degree symbol
*/
//Libraries
#include "RTClib.h"//to access the Real Time Clock
#include <SPI.h> //Used to communicate with the SD card
#include <SD.h> //SD card command set
#include <Adafruit_Sensor.h>//used by the BME280
#include <Adafruit_BME280.h>//used by the BME280
#include <Wire.h>//used for I2C comminications, The BME 280
#include <LiquidCrystal_I2C.h>//used by the LCD display

//Setting up the RTC clock
RTC_PCF8523 rtc; // define the Real Time Clock object
unsigned long CurrentTime;// used to measure elapsed time agenst the StartTime
unsigned long StartTime;// for storing the unixtime to manage the time interval

// SD card configuration
const int chipSelect = 10;// for the data logging shield, we use digital pin 10 for the SD cs line
File logfile;// the logging file

// Setting up the BME280 temperature, pressure, humidity sensor
Adafruit_BME280 bme; // the i2c address is 0x77.

// Setting up the LCD desplay to function on I2C
LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x27 for a 20 chars and 4 line display
// making a custom Degree symbol:
byte Degree[] = {
  B00111,
  B00101,
  B00111,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};

void setup() {
  Serial.begin(9600);//establishing serial monitor feedback
  
  #ifndef ESP8266
    while (!Serial); // wait for serial port to connect. Needed for native USB
  #endif  
    Serial.println("");
    Serial.println("testing RTC");
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (1) delay(10);
  }
  if (! rtc.initialized() || rtc.lostPower()) {
    Serial.println("RTC is NOT initialized, let's set the time!");
    // When time needs to be set on a new device, or after a power loss, 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));
    //
    // Note: allow 2 seconds after inserting battery or applying external power
    // without battery before calling adjust(). This gives the PCF8523's
    // crystal oscillator time to stabilize. If you call adjust() very quickly
    // after the RTC is powered, lostPower() may still return true.
  }
    // 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(2023, 6, 25, 19, 4, 10));

    // When the RTC was stopped and stays connected to the battery, it has
    // to be restarted by clearing the STOP bit. Let's do this to ensure
    // the RTC is running.
    rtc.start();

    // The PCF8523 can be calibrated for:
    //        - Aging adjustment
    //        - Temperature compensation
    //        - Accuracy tuning
    // The offset mode to use, once every two hours or once every minute.
    // The offset Offset value from -64 to +63. See the Application Note for calculation of offset values.
    // https://www.nxp.com/docs/en/application-note/AN11247.pdf
    // The deviation in parts per million can be calculated over a period of observation. Both the drift (which can be negative)
    // and the observation period must be in seconds. For accuracy the variation should be observed over about 1 week.
    // Note: any previous calibration should cancelled prior to any new observation period.
    // Example - RTC gaining 43 seconds in 1 week
    float drift = 43; // seconds plus or minus over oservation period - set to 0 to cancel previous calibration.
    float period_sec = (7 * 86400);  // total obsevation period in seconds (86400 = seconds in 1 day:  7 days = (7 * 86400) seconds )
    float deviation_ppm = (drift / period_sec * 1000000); //  deviation in parts per million (μs)
    float drift_unit = 4.34; // use with offset mode PCF8523_TwoHours
    // float drift_unit = 4.069; //For corrections every min the drift_unit is 4.069 ppm (use with offset mode PCF8523_OneMinute)
    int offset = round(deviation_ppm / drift_unit);
    // rtc.calibrate(PCF8523_TwoHours, offset); // Un-comment to perform calibration once drift (seconds) and observation period (seconds) are correct
    // rtc.calibrate(PCF8523_TwoHours, 0); // Un-comment to cancel previous calibration

    Serial.print("Offset is "); 
    Serial.println(offset); // Print to control offset
    DateTime now = rtc.now();
    StartTime = now.unixtime();
    Serial.print("Unit StartTime ");
    Serial.println(StartTime);
    Serial.println("RTC test done");
    Serial.println();
    
    // SD card configuration
    Serial.println("testing SD card");
    // see if the card is present and can be initialized:
    if (!SD.begin(chipSelect)) {
      Serial.println("Card failed, or not present");
    }
    Serial.println("card initialized.");
    // create a new file
    char filename[] = "LOGGER00.CSV";
    for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE); 
      break;  // leave the loop!
    }
  }
    if (! logfile) {
      Serial.println("couldnt create file");
  }
    Serial.println("file created");
    Serial.println("SD card and file test done");
    Serial.println("");
    
    //BME280 sensor configuration and test
    Serial.println("Testing the BME280");
    Serial.println(F("BME280 test"));
    bool status;
    // default settings
    // (you can also pass in a Wire library object like &Wire2)
    status = bme.begin();  
    if (!status) {
      Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
    Serial.println("BME280 test done");
    Serial.println("");
  
    //configuring the LCD
    Serial.println("initialize the lcd and setting the backlight");
    lcd.init();  //initialize the lcd
    lcd.backlight();  //open the backlight
    Serial.println("LCD setup done");
    Serial.println("");
    // Create a custom character:
    lcd.createChar(0, Degree);
  
}

void loop() {
    //checking the current time
    DateTime now = rtc.now();
    CurrentTime = now.unixtime();//setting "CurrentTime" used for the non-interupt timer for the SD card write
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" ");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.println(now.second(), DEC);
    Serial.print("Unix Time ");
    Serial.print(now.unixtime());
    Serial.print("s");
    Serial.println();
    Serial.println();
    
    //Checking the BME280 sensor
    Serial.print("Temperature = ");
    Serial.print(bme.readTemperature());
    Serial.println(" *C");
    Serial.print("Pressure = ");
    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");
    Serial.print("Humidity = ");
    Serial.print(bme.readHumidity());
    Serial.println(" %");
    Serial.println("");
   
    
   
    // log time
    // The Data will be stored on the SD card every interval. The interval is the seconds added to "StartTime"
    // check in the elapsed time to record data to the SD card the interval is 5 minints
    if (CurrentTime >= StartTime + 30){
      StartTime=CurrentTime;//resetting the StartTime for the next interval
      Serial.println("interval compleat, saving data to SD Card:)");
      logfile.print(now.year(), DEC);
      logfile.print("/");
      logfile.print(now.month(), DEC);
      logfile.print("/");
      logfile.print(now.day(), DEC);
      logfile.print(",");
      logfile.print(now.hour(), DEC);
      logfile.print(":");
      logfile.print(now.minute(), DEC);
      logfile.print(":");
      logfile.print(now.second(), DEC);
      logfile.print(','); 
      logfile.flush();
      delay(50);
      //loging the BME280 data
      logfile.print(bme.readTemperature());
      logfile.print(",*C,");
      logfile.flush();
      delay(50);
      logfile.print(bme.readPressure() / 100.0F);
      logfile.print(",hPa,");
      logfile.flush();
      delay(50);
      logfile.print(bme.readHumidity());
      logfile.println(",%");
      logfile.flush();
      delay(50);
    }
   
   //Printing  data to the LDC
    //lcd.clear();  //Clears the LCD screen and positions the cursor in the upper-left corner.
    // Print the date and Time to the LCD;
    lcd.setCursor(0, 0);
    lcd.print(now.year(), DEC);
    lcd.print('/');
    lcd.print(now.month(), DEC);
    lcd.print('/');
    lcd.print(now.day(), DEC);
    lcd.print(" ");
    lcd.print(now.hour(), DEC);
    lcd.print(':');
    lcd.print(now.minute(), DEC);
    lcd.print(':');
    lcd.print(now.second(), DEC);
    lcd.print("   ");
    
    // Print the Temperature to the LCD;    
    lcd.setCursor(0, 1);
    lcd.print("Temperature:");
    lcd.print(bme.readTemperature());
    lcd.write(0); // print the custom character
    lcd.print("C");  
    
    // Print the Pressure to the LCD;    
    lcd.setCursor(0, 2);
    lcd.print("Pressure:");
    lcd.print(bme.readPressure() / 100.0F);
    lcd.print(" hPa");    
    
    // Print the Humidity to the LCD;    
    lcd.setCursor(0, 3);
    lcd.print("Humidity:");
    lcd.print(bme.readHumidity());
    lcd.print(" %");     

    // wait a quarter second between readings;
    delay(250); 
      
    
}

the remarks for the non-delay timer states its for 5 minutes [300 seconds] I reduced it for trouble shooting the SD not recording the data issue.

Not enough information. How do you know the card "stops logging"?

Please explain what you expect to happen and what happens instead. If you haven't already, put in print statements to track progress, and to determine where the hangup is.

Is this the display you are using? "LiquidCrystal Arduino library for the DFRobot I2C LCD displays ".

I am expecting the project to

LCD display to update every .25 seconds

  • date time
  • Temp in C
  • Pressure
  • humidity

SD card to store the above data every 5 minutes.

To verify the SD saved data I power down the project and remove the SD card, then I check the contents on my card reader. When the LCD is deactivated, I get data that looks like this

6/29/2023,17:18:35,26.37,*C,986,hPa,33.94,%

6/29/2023,17:19:05,26.38,*C,985.95,hPa,34.7,%

when the LCD is running I get 0 byte files. The file name will increment with each power cycle, and each subsequent file is 0 byte.

The serial monitor is returning all of the relevant data.

and the LCD operates as expected. and updates every .25 seconds

I am using the cloud editor. It has in included LCD library. I don't know how to tell if its the one you are asking about.

Sorry, still not enough information, There is nothing obviously wrong with the code, except that you never close the file -- a serious mistake.

What does that mean? What did you do to deactivate it?

Check that bytes are actually written, which is the return value from logfile.print().

For trouble shooting to disable the LCD I remark out using // or /* */ all of the LCD commands from the top section, void setup, and void loop.

I was under the impression that logfile.flush(); also closed the file.

In the serial monitor I get Serial.println("interval complete, saving data to SD Card:)"); every 30 seconds when this if statement is activated.

No, it just writes out the current buffer. You always lose data if you don't close the file.

The program needs a button input or other means to stop logging in an orderly way, and to close the file, using logfile.close();

You can read up on the functions you are using here: SD - Arduino Reference

I have
logfile = SD.open(filename, FILE_WRITE);
in void setup

in void loop
if I place
logfile.close();
at the end of my SD write if statment I will I need to add
logfile = SD.open(filename, FILE_WRITE);
at the top of the If statement?

Open the file once in setup() and close it once, when you are all done collecting data.

Hint: the keyword "void" before a function name means that the function does not return a value.

My expectation is to collect date once every 5 minutes for many days/weeks.
So should I open and close the file in the loop portion of the program?

No. That greatly increases the power draw and the error rate of the SD card, and significantly slows down the operation.

Your first challenge is to fix the present problem, which may have to do with the fact that you never close the file. Or it is a hardware/wiring problem.

I am expecting the project to

use an 2004 LCD display to update every .25 seconds it will show
date time
Temp in C
Pressure
humidity

And every 5 minutes the data is written to a CSV file on the SD card. I am expecting the date to be appended to the bottom of the file. as displayed below
6/29/2023,17:18:35,26.37,*C,986,hPa,33.94,%
6/29/2023,17:19:05,26.38,*C,985.95,hPa,34.7,%

When all of the code for the LCD is rem-statement out. The data collects and appends to the bottom of the file and functions correctly for days. When the LCD is included in the code it displays perfectly, but the SD card will not record any data.

So it seams to me that the addition of the LCD code is some how interfering with the SD card saving the data.

So if the issue is the file is never closed and you stated this was "a serious mistake."
when should the file be opened to accept data, and when should it be closed?

See post #12. Good luck with your project.

Temperature and humidity sensors seem to only have data ready in intervals of 1 or 2 seconds.

... edit ... and the "degree" symbol is 0xB0. No need for a custom character.

Static text should be printed only once, in setup().
Values should only be updated when they change, to minimise flickering.
Leo..

I'm curious. If .flush() has written any remaining data in the buffer to the card, what is lost if .close() is not used? The Arduino docs you linked to don't mention that anything gets lost. It does say that .close() also flushes the buffer, and that .flush() saves any data to the card.

Your question is of course impossible to answer, so I'll pose another: under your assumptions, what is the point of the close operation?

In practice, many things can go wrong if a file on a device is not properly closed, including corruption of the file system tables, to the point that the card might even need to be reformatted. If my own troubles with it are typical, the SD library is not very reliable.