Code is freezing up after running for an hour or so

Hello World,
I am working on a controller for a greenhouse and have the code working properly but I am running into an issue where the program freezes up after an half an hour and the board needs to be reset. I am not receiving any errors when I compile it and it works properly for a while. I am having to use the <DS1307RTC.h> library in place of the normal DS3231 library as I can't get it to function with the Time library properly. Here is the code...

/**************************************************************************
This is code to run a greenhouse. It uses the SCD30 airsensor and a DS3231 rtc module to take sensor readings and keep the date. It 
has two functions, an automatic mode and a manual control mode. The automatic mode runs the SSR's controlling the various outputs 
depending on the sensor readings. Manual Control allows the SSR's to be controlled by 4 switches along with a 5 to toggle between 
modes.  
 
 This example is for a 128x64 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 
 **************************************************************************/
// Libraries to include
#include <SPI.h>
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "SparkFun_SCD30_Arduino_Library.h" 

// Start the SCD30 air sensor                  
SCD30 airSensor;

#define SCREEN_WIDTH 128                                      // OLED display width, in pixels
#define SCREEN_HEIGHT 64                                      // OLED display height, in pixels

                                                              // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
                                                              // The pins for I2C are defined by the Wire-library. 
                                                              // On an arduino UNO:       A4(SDA), A5(SCL)
                                                              // On an arduino MEGA 2560: 20(SDA), 21(SCL)
                                                              // On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     4                                      // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C                                   //< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Input for the switch contol of SSR
const int lightswitchA = 3;
const int lightswitchB = 5;
const int waterswitchC = 6;
const int fanswitchD = 7;
const int controlswitchE = 12;

// outputs for SSR and control LED
const int outputA = 8;
const int outputB = 9;
const int outputC = 10;
const int outputD = 11;
const int outputE = 13;

// Var to store state of switch
int switchAstate = 0;
int switchBstate = 0;
int switchCstate = 0;
int switchDstate = 0;
int switchEstate = 0;

// interval for cumulating function and timing of screen display
const int interval = 8000;           
unsigned long currentMillis;
unsigned long lastMillis;
int remainder = 0;
 
void setup() {                                                // put your setup code here, to run once: 
  
Serial.begin(9600);
  Serial.println("SCD30 And SD3231 Read and Write"); 
  Wire.begin(); 
  
// Warning of state of RTC
if(timeStatus()!= timeSet) 
     {Serial.println("Unable to sync with the RTC");}
  else
     {Serial.println("RTC has set the system time");}  

// Warning for SCD30 sensor       
 if (airSensor.begin() == false)
  {Serial.println("Air sensor not detected. Please check wiring. Freezing...");
    while (1);}


// Establishes the pinMode inputs
pinMode(lightswitchA, INPUT);   
pinMode(lightswitchB, INPUT);    
pinMode(waterswitchC, INPUT);    
pinMode(fanswitchD, INPUT);    
pinMode(controlswitchE, INPUT);  

// Establishes the pinMode OUTPUT
pinMode(outputA, OUTPUT);  
pinMode(outputB, OUTPUT);  
pinMode(outputC, OUTPUT);  
pinMode(outputD, OUTPUT);
pinMode(outputE, OUTPUT);  

// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
    {Serial.println(F("SSD1306 allocation failed"));
    for(;;);} // Don't proceed, loop forever
  

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(500); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();
}



void loop() {
switchAstate = digitalRead(lightswitchA);
switchBstate = digitalRead(lightswitchB);
switchCstate = digitalRead(waterswitchC);
switchDstate = digitalRead(fanswitchD);
switchEstate = digitalRead(controlswitchE);

int c = airSensor.getCO2();
int t = airSensor.getTemperature();
float h = airSensor.getHumidity();

// alternate method to getting RTC data
//tmElements_t tm;  
//RTC.read(tm);  
//setTime(tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year);  //(hours, minutes, seconds, days, months, years)  

// the function to get the time from the RTC
setSyncProvider(RTC.get); 

currentMillis = millis();
remainder = currentMillis - lastMillis;
  if (remainder >= interval)
  {lastMillis += interval;}

if(switchEstate == HIGH)
{manualControl();
 printData();}
else
{automaticControl();
 printData();}
 

  printData();
//  sensorReading();
}

// This function prints the sensor readings and the date and time to the OLED display
void printData(){
  
  int c = airSensor.getCO2();
  int t = airSensor.getTemperature();
  float h = airSensor.getHumidity();
  
  if (remainder >= 0 && remainder <2000)
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(45, 0);
  display.println(F("CO2"));
  display.setTextSize(3); // Draw 2X-scale text
  display.setCursor(35, 27);
  display.println(c);
  display.display();      // Show initial text
  }

else if (remainder >= 2000 && remainder <4000) 
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(32, 0);
  display.println(F("Temp(F)"));
  display.setTextSize(3); // Draw 2X-scale text
  display.setCursor(35, 27);
  display.println((int)(1.8*t+32));
  display.display();      // Show initial text
  }

else if (remainder >= 4000 && remainder <6000) 
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(22, 0);
  display.println(F("Humidity"));
  display.setTextSize(3); // Draw 2X-scale text
  display.setCursor(35, 27);
  display.println(h, 1);
  display.display();      // Show initial text
  }

else 
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(27, 0);
  display.println(F("Time"));
  display.setTextSize(2); // Draw 2X-scale text
  display.setCursor(12, 27);
  display.print(hourFormat12());
  display.print(":");
  display.print(minute());
  display.print(":");
  display.print(second());
  display.setCursor(5,47);
  display.print(month());
  display.print("-");
  display.print(day());
  display.print("-");
  display.print(year()); 
  display.display();      // Show initial text
  }
}

// This function is for the manual control of the SSR's
void manualControl(){

 digitalWrite(outputE, HIGH);

// send data to the OLED screen
 printData();
//   sensorReading(); 
  
  if(switchAstate == HIGH)
{digitalWrite(outputA, HIGH);}
  else
  {digitalWrite(outputA, LOW);}
if(switchBstate == HIGH)
{digitalWrite(outputB, HIGH);}
  else
  {digitalWrite(outputB, LOW);}
if(switchCstate == HIGH)
{digitalWrite(outputC, HIGH);}
  else
  {digitalWrite(outputC, LOW);}
if(switchDstate == HIGH)
{digitalWrite(outputD, HIGH);}
  else
  {digitalWrite(outputD, LOW);}  
}

// function to print sensor readings to the serial monitor 
void sensorReading(){
  if (remainder == 2000 )
  { 
    Serial.println(switchAstate);
    Serial.println(switchBstate);
    Serial.println(switchCstate);
    Serial.println(switchDstate);
    Serial.println(switchEstate);
    
    Serial.print("co2(ppm):");
    Serial.print(airSensor.getCO2());

    Serial.print(" temp(C):");
    Serial.print(airSensor.getTemperature(), 1);

    Serial.print(" humidity(%):");
    Serial.print(airSensor.getHumidity(), 1);
  } 
}

void automaticControl(){

 digitalWrite(outputE, LOW);
  
  int c = airSensor.getCO2();
  int t = airSensor.getTemperature();
  float h = airSensor.getHumidity();
  int lightsTime = hour();
  int waterTime = minute();
  int tempF = (1.8*t+32);

//Send data to the OLED screen
  printData();  
//sensorReading(); 

// Fan Control based of temp 85F degrees 
  if (tempF >= 85)
  {digitalWrite(outputD, HIGH);}
  else
   {digitalWrite(outputD, LOW);}

// Control of Lights 7am to 7pm on
  if (lightsTime >= 7 && lightsTime <=19)
   {digitalWrite(outputA, HIGH);
    digitalWrite(outputB, HIGH);}
  else
   {digitalWrite(outputA, LOW);
    digitalWrite(outputB, LOW);}

// Control of the watering, at 8am and 1pm for 10 minutes
   if (lightsTime == 8 || lightsTime == 13){
    if (waterTime >= 0 && waterTime <= 10)
    {digitalWrite(outputC, HIGH);}
    else if (waterTime > 10)
    {digitalWrite(outputC, LOW);}
   } 
}

I have a stable working version that doesn't use the DS3231 RTC module and just relies on the millis() function to run and sync the time up but I don't want to run into the overflow issues after 50 days

Hello
Add to each function a Serial.println(func); to see what happens during run time simple .
Have a nice day and enjoy coding in C++.

No overflow issues from what I can see. The overflow of millis does not affect the calculated result. Lets do a test ...

unsigned long remainder = 0;
unsigned long lastMillis = 0;
unsigned long currentMillis = 0xFFFFFFFA;

void setup() {
  Serial.begin(115200);
  delay(3000);
  for (int i = 0; i < 20; i++) {
    lastMillis = currentMillis;
    currentMillis += 1;
    remainder = currentMillis - lastMillis;
    Serial.print(F(" currentMillis: "));  Serial.print(currentMillis);
    Serial.print(F("  lastMillis: "));  Serial.print(lastMillis);
    Serial.print(F("  remainder: "));  Serial.println(remainder);
  }
}

void loop() {
}

Serial Monitor ...

 currentMillis: 4294967291  lastMillis: 4294967290  remainder: 1
 currentMillis: 4294967292  lastMillis: 4294967291  remainder: 1
 currentMillis: 4294967293  lastMillis: 4294967292  remainder: 1
 currentMillis: 4294967294  lastMillis: 4294967293  remainder: 1
 currentMillis: 4294967295  lastMillis: 4294967294  remainder: 1
 currentMillis: 0  lastMillis: 4294967295  remainder: 1
 currentMillis: 1  lastMillis: 0  remainder: 1
 currentMillis: 2  lastMillis: 1  remainder: 1
 currentMillis: 3  lastMillis: 2  remainder: 1
 currentMillis: 4  lastMillis: 3  remainder: 1
 currentMillis: 5  lastMillis: 4  remainder: 1
 currentMillis: 6  lastMillis: 5  remainder: 1
 currentMillis: 7  lastMillis: 6  remainder: 1
 currentMillis: 8  lastMillis: 7  remainder: 1
 currentMillis: 9  lastMillis: 8  remainder: 1
 currentMillis: 10  lastMillis: 9  remainder: 1
 currentMillis: 11  lastMillis: 10  remainder: 1
 currentMillis: 12  lastMillis: 11  remainder: 1
 currentMillis: 13  lastMillis: 12  remainder: 1
 currentMillis: 14  lastMillis: 13  remainder: 1

I would like to keep the RTC module as if I get a power loss in the system I don't want the lights coming on at the wrong time if I loose power. The greenhouse is in a area where power loss occasionally occurs and the crop being grown is sensitive to the amount of light it gets. I'm guessing this method would reset the time on power loss.

How much memory does the compiler say you're using?

Sketch uses 21488 bytes (66%) of program storage space. Maximum is 32256 bytes.
Global variables use 766 bytes (37%) of dynamic memory, leaving 1282 bytes for local variables. Maximum is 2048 bytes

Those numbers look fine, but I still suspect that you have a memory issue. It occurs to me that the Adafruit library probably allocates memory for a display buffer at runtime.

Grab their freememory function and print what it tells you somewhere - printData perhaps. It'll tell you if you have a leak.

The DS3231 will work with the DS3232RTC library by Jack Christensen .

This should be in setup, not loop:

  setSyncProvider(RTC.get);

Do not update the display every time through loop, only update when the data being displayed changes, that will greatly decrease the amount of data being sent over I2C.

You can use the Time library without an RTC, at least for testing purposes, by setting the time and date in setup. The actual clock is run in software and is based off millis. When an RTC is used as the sync provider the software clock is synchronized with the RTC at five minute intervals (unless you specify a different sync interval).

Is this for marihuana?

Ok here is the return from the serial monitor when I have it print the available memory every 8 secs

SCD30 And SD1306 Read and Write
RTC has set the system time
0
0
0
0
0
co2(ppm):456 temp(C):26.4 humidity(%):19.8
freeMemory:206

Here is the modified code:


/**************************************************************************
This is code to run a greenhouse. It uses the SCD30 airsensor and a DS3231 rtc module to take sensor readings and keep the date. It 
has two functions, an automatic mode and a manual control mode. The automatic mode runs the SSR's controlling the various outputs 
depending on the sensor readings. Manual Control allows the SSR's to be controlled by 4 switches along with a 5 to toggle between 
modes. 
 
 This example is for a 128x64 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 
 **************************************************************************/
// Libraries to include
#include <SPI.h>
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "SparkFun_SCD30_Arduino_Library.h" 
#include <MemoryFree.h>
#include <pgmStrToRAM.h>


// Start the SCD30 air sensor                  
SCD30 airSensor;

#define SCREEN_WIDTH 128                                      // OLED display width, in pixels
#define SCREEN_HEIGHT 64                                      // OLED display height, in pixels

                                                              // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
                                                              // The pins for I2C are defined by the Wire-library. 
                                                              // On an arduino UNO:       A4(SDA), A5(SCL)
                                                              // On an arduino MEGA 2560: 20(SDA), 21(SCL)
                                                              // On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     4                                      // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C                                   //< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Input for the switch contol of SSR
const int lightswitchA = 3;
const int lightswitchB = 5;
const int waterswitchC = 6;
const int fanswitchD = 7;
const int controlswitchE = 12;

// outputs for SSR and control LED
const int outputA = 8;
const int outputB = 9;
const int outputC = 10;
const int outputD = 11;
const int outputE = 13;

// Var to store state of switch
int switchAstate = 0;
int switchBstate = 0;
int switchCstate = 0;
int switchDstate = 0;
int switchEstate = 0;

// interval for cumulating function and timing of screen display
const int interval = 8000;           
unsigned long currentMillis;
unsigned long lastMillis;
int remainder = 0;
 
void setup() {                                                // put your setup code here, to run once: 
  
Serial.begin(9600);
  Serial.println("SCD30 And SD1306 Read and Write"); 
  Wire.begin(); 

// Different method for syncing the Time library with the RTC
//tmElements_t tm;  
//RTC.read(tm);  
//setTime(tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year);  //(hours, minutes, seconds, days, months, years)

 // the function to get the time from the RTC
setSyncProvider(RTC.get);
  
// Warning of state of RTC
if(timeStatus()!= timeSet) 
     {Serial.println("Unable to sync with the RTC");}
  else
     {Serial.println("RTC has set the system time");}  

// Warning for SCD30 sensor       
 if (airSensor.begin() == false)
  {Serial.println("Air sensor not detected. Please check wiring. Freezing...");
    while (1);}


// Establishes the pinMode inputs
pinMode(lightswitchA, INPUT);   
pinMode(lightswitchB, INPUT);    
pinMode(waterswitchC, INPUT);    
pinMode(fanswitchD, INPUT);    
pinMode(controlswitchE, INPUT);  

// Establishes the pinMode OUTPUT
pinMode(outputA, OUTPUT);  
pinMode(outputB, OUTPUT);  
pinMode(outputC, OUTPUT);  
pinMode(outputD, OUTPUT);
pinMode(outputE, OUTPUT);  

// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
    {Serial.println(F("SSD1306 allocation failed"));
    for(;;);} // Don't proceed, loop forever
  

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(500); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();
}



void loop() {
switchAstate = digitalRead(lightswitchA);
switchBstate = digitalRead(lightswitchB);
switchCstate = digitalRead(waterswitchC);
switchDstate = digitalRead(fanswitchD);
switchEstate = digitalRead(controlswitchE);

int c = airSensor.getCO2();
int t = airSensor.getTemperature();
float h = airSensor.getHumidity();

if(switchEstate == HIGH)
{manualControl();
 printData();}
else
{automaticControl();
 printData();}
 

  printData();
  sensorReading();
}

// This function prints the sensor readings and the date and time to the OLED display
void printData(){
  
  int c = airSensor.getCO2();
  int t = airSensor.getTemperature();
  float h = airSensor.getHumidity();

  currentMillis = millis();
  remainder = currentMillis - lastMillis;
  if (remainder >= interval)
  {lastMillis += interval;}
  
  if (remainder >= 0 && remainder <2000)
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(45, 0);
  display.println(F("CO2"));
  display.setTextSize(3); // Draw 2X-scale text
  display.setCursor(35, 27);
  display.println(c);
  display.display();      // Show initial text
  }

else if (remainder >= 2000 && remainder <4000) 
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(32, 0);
  display.println(F("Temp(F)"));
  display.setTextSize(3); // Draw 2X-scale text
  display.setCursor(35, 27);
  display.println((int)(1.8*t+32));
  display.display();      // Show initial text
  }

else if (remainder >= 4000 && remainder <6000) 
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(22, 0);
  display.println(F("Humidity"));
  display.setTextSize(3); // Draw 2X-scale text
  display.setCursor(35, 27);
  display.println(h, 1);
  display.display();      // Show initial text
  }

else
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(27, 0);
  display.println(F("Time"));
  display.setTextSize(2); // Draw 2X-scale text
  display.setCursor(0, 27);
  display.print(hourFormat12());
  display.print(":");
  display.print(minute());
  display.print(":");
  display.print(second());
  if (isAM() == HIGH)
  {display.print("AM");}
  else
  {display.print("PM");}
  display.setCursor(5,47);
  display.print(month());
  display.print("-");
  display.print(day());
  display.print("-");
  display.print(year()); 
  display.display();      // Show initial text
  }
}

// This function is for the manual control of the SSR's
void manualControl(){

 digitalWrite(outputE, HIGH);

// send data to the OLED screen
 printData();
//   sensorReading(); 
  
  if(switchAstate == HIGH)
{digitalWrite(outputA, HIGH);}
  else
  {digitalWrite(outputA, LOW);}
if(switchBstate == HIGH)
{digitalWrite(outputB, HIGH);}
  else
  {digitalWrite(outputB, LOW);}
if(switchCstate == HIGH)
{digitalWrite(outputC, HIGH);}
  else
  {digitalWrite(outputC, LOW);}
if(switchDstate == HIGH)
{digitalWrite(outputD, HIGH);}
  else
  {digitalWrite(outputD, LOW);}  
}

// function to print sensor readings to the serial monitor 
void sensorReading(){
  if (remainder >= 7950)
  { 
    Serial.println(switchAstate);
    Serial.println(switchBstate);
    Serial.println(switchCstate);
    Serial.println(switchDstate);
    Serial.println(switchEstate);
    
    Serial.print("co2(ppm):");
    Serial.print(airSensor.getCO2());

    Serial.print(" temp(C):");
    Serial.print(airSensor.getTemperature(), 1);

    Serial.print(" humidity(%):");
    Serial.println(airSensor.getHumidity(), 1);

    Serial.print("freeMemory:");
    Serial.println(freeMemory());  // print how much RAM is available in bytes.
  } 
}

void automaticControl(){

 digitalWrite(outputE, LOW);
  
  int c = airSensor.getCO2();
  int t = airSensor.getTemperature();
  float h = airSensor.getHumidity();
  int lightsTime = hour();
  int waterTime = minute();
  int tempF = (1.8*t+32);

//Send data to the OLED screen
//  printData();  
//  sensorReading(); 

// Fan Control based of temp 85F degrees 
  if (tempF >= 85)
  {digitalWrite(outputD, HIGH);}
  else
   {digitalWrite(outputD, LOW);}

// Control of Lights 7am to 7pm on
  if (lightsTime >= 7 && lightsTime <=19)
   {digitalWrite(outputA, HIGH);
    digitalWrite(outputB, HIGH);}
  else
   {digitalWrite(outputA, LOW);
    digitalWrite(outputB, LOW);}

// Control of the watering, at 8am and 1pm for 10 minutes
   if (lightsTime == 9 || lightsTime == 13){
    if (waterTime >= 0 && waterTime <= 10)
    {digitalWrite(outputC, HIGH);}
    else if (waterTime > 10)
    {digitalWrite(outputC, LOW);}
   } 
}

Hemp but basically the same thing

How would you change the sync time?

There are a few print function calls where you are not using the F() macro, change those to free up a little more memory.

That's not good. @david_2018's advice will do you the most good, but you can recover a bit more by checking on whether all those globals could be byte rather than int. lightswitchA for example has no need to be int.

You are wasting quite a bit of RAM by not using the F macro in Serial.print() statements. F() puts character strings in program memory.

Replace lines like this:

Serial.println("Air sensor not detected. Please check wiring. Freezing...");

with this:

Serial.println(F("Air sensor not detected. Please check wiring. Freezing..."));

It shows that this library works with both the DS1307 and the DS3231.

In manualControl()

  if(switchAstate == HIGH)
  {
   digitalWrite(outputA, HIGH);
  }
  else
  {
   digitalWrite(outputA, LOW);
  }

  if(switchBstate == HIGH)
  {
   digitalWrite(outputB, HIGH);
  }
  else
  {
   digitalWrite(outputB, LOW);
  }

  if(switchCstate == HIGH)
  {
   digitalWrite(outputC, HIGH);
  }
  else
  {
   digitalWrite(outputC, LOW);
  }

  if(switchDstate == HIGH)
  {
   digitalWrite(outputD, HIGH);
  }
  else
  {
   digitalWrite(outputD, LOW);
  }  

CAN BE REPLACED WITH --

  digitalWrite(outputA,(digitalRead(lightswitchA)));
  digitalWrite(outputB,(digitalRead(lightswitchB)));  
  digitalWrite(outputC,(digitalRead(lightswitchC)));
  digitalWrite(outputD,(digitalRead(lightswitchD)));

OK so I went and optimized the code, I am now getting this from the serial monitor. I've almost doubled the available free memory so hopefully this helps.

SCD30 And SD1306 Read and Write
RTC has set the system time
0
0
0
0
0
co2(ppm):536 temp(C):26.3 humidity(%):21.2
freeMemory:409

and here is the optimized code, I'm going to let it run and see if this helps

/**************************************************************************
This is code to run a greenhouse. It uses the SCD30 airsensor and a DS3231 rtc module to take sensor readings and keep the date. It 
has two functions, an automatic mode and a manual control mode. The automatic mode runs the SSR's controlling the various outputs 
depending on the sensor readings. Manual Control allows the SSR's to be controlled by 4 switches along with a 5 to toggle between 
modes. 
 
 This example is for a 128x64 pixel display using I2C to communicate
 3 pins are required to interface (two I2C and one reset).

 
 **************************************************************************/
// Libraries to include
#include <SPI.h>
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "SparkFun_SCD30_Arduino_Library.h" 
#include <MemoryFree.h>
#include <pgmStrToRAM.h>

// Start the SCD30 air sensor                  
SCD30 airSensor;

#define SCREEN_WIDTH 128                                      // OLED display width, in pixels
#define SCREEN_HEIGHT 64                                      // OLED display height, in pixels

                                                              // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
                                                              // The pins for I2C are defined by the Wire-library. 
                                                              // On an arduino UNO:       A4(SDA), A5(SCL)
                                                              // On an arduino MEGA 2560: 20(SDA), 21(SCL)
                                                              // On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     4                                      // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C                                   //< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Input for the switch contol of SSR
const byte lightswitchA = 3;
const byte lightswitchB = 5;
const byte waterswitchC = 6;
const byte fanswitchD = 7;
const byte controlswitchE = 12;

// outputs for SSR and control LED
const byte outputA = 8;
const byte outputB = 9;
const byte outputC = 10;
const byte outputD = 11;
const byte outputE = 13;

// Var to store state of switch
byte switchAstate = 0;
byte switchBstate = 0;
byte switchCstate = 0;
byte switchDstate = 0;
byte switchEstate = 0;

// interval for cumulating function and timing of screen display
const int interval = 8000;           
unsigned long currentMillis;
unsigned long lastMillis;
int remainder = 0;
 
void setup() {                                                // put your setup code here, to run once: 
  
Serial.begin(9600);
  Serial.println(F("SCD30 And SD1306 Read and Write")); 
  Wire.begin(); 

// Different method for syncing the Time library with the RTC
//tmElements_t tm;  
//RTC.read(tm);  
//setTime(tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year);  //(hours, minutes, seconds, days, months, years)

 // the function to get the time from the RTC
setSyncProvider(RTC.get);
  
// Warning of state of RTC
if(timeStatus()!= timeSet) 
     {Serial.println(F("Unable to sync with the RTC"));}
  else
     {Serial.println(F("RTC has set the system time"));}  

// Warning for SCD30 sensor       
 if (airSensor.begin() == false)
  {Serial.println(F("Air sensor not detected. Please check wiring. Freezing..."));
    while (1);}


// Establishes the pinMode inputs
pinMode(lightswitchA, INPUT);   
pinMode(lightswitchB, INPUT);    
pinMode(waterswitchC, INPUT);    
pinMode(fanswitchD, INPUT);    
pinMode(controlswitchE, INPUT);  

// Establishes the pinMode OUTPUT
pinMode(outputA, OUTPUT);  
pinMode(outputB, OUTPUT);  
pinMode(outputC, OUTPUT);  
pinMode(outputD, OUTPUT);
pinMode(outputE, OUTPUT);  

// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
    {Serial.println(F("SSD1306 allocation failed"));
    for(;;);} // Don't proceed, loop forever
  

  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(500); // Pause for 0.5 seconds

  // Clear the buffer
  display.clearDisplay();
}



void loop() {
switchAstate = digitalRead(lightswitchA);
switchBstate = digitalRead(lightswitchB);
switchCstate = digitalRead(waterswitchC);
switchDstate = digitalRead(fanswitchD);
switchEstate = digitalRead(controlswitchE);

int c = airSensor.getCO2();
byte t = airSensor.getTemperature();
int h = airSensor.getHumidity();

currentMillis = millis();
remainder = currentMillis - lastMillis;
  if (remainder >= interval)
  {lastMillis += interval;}

if(switchEstate == HIGH)
{manualControl();
 printData();}
else
{automaticControl();
 printData();}
 

//  printData();
  sensorReading();
}

// This function prints the sensor readings and the date and time to the OLED display
void printData(){
  
  int c = airSensor.getCO2();
  byte t = airSensor.getTemperature();
  int h = airSensor.getHumidity();
  
  if (remainder >= 0 && remainder < 2000)
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(45, 0);
  display.println(F("CO2"));
  display.setTextSize(3); // Draw 2X-scale text
  display.setCursor(35, 27);
  display.println(c);
  display.display();      // Show initial text
  }

else if (remainder >= 2000 && remainder < 4000) 
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(32, 0);
  display.println(F("Temp(F)"));
  display.setTextSize(3); // Draw 2X-scale text
  display.setCursor(35, 27);
  display.println((int)(1.8*t+32));
  display.display();      // Show initial text
  }

else if (remainder >= 4000 && remainder < 6000) 
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(22, 0);
  display.println(F("Humidity"));
  display.setTextSize(3); // Draw 2X-scale text
  display.setCursor(35, 27);
  display.print(h, 1);
  display.print(" %");
  display.display();      // Show initial text
  }

else if (remainder >= 6000 && remainder < 8000)
 {display.clearDisplay();
  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(27, 0);
  display.println(F("Time"));
  display.setTextSize(2); // Draw 2X-scale text
  display.setCursor(3, 27);
  display.print(hourFormat12());
  display.print(F(":"));
  display.print(minute());
  display.print(F(":"));
  display.print(second());
  if (isAM() == HIGH)
  {display.print(F("AM"));}
  else
  {display.print(F("PM"));}
  display.setCursor(5,47);
  display.print(month());
  display.print(F("-"));
  display.print(day());
  display.print(F("-"));
  display.print(year()); 
  display.display();      // Show initial text
  }
}

// This function is for the manual control of the SSR's
void manualControl(){

 digitalWrite(outputE, HIGH);

// send data to the OLED screen
//   printData();
//   sensorReading(); 
  
  digitalWrite(outputA, switchAstate);
  digitalWrite(outputB, switchBstate);  
  digitalWrite(outputC, switchCstate);
  digitalWrite(outputD, switchDstate);
}

// function to print sensor readings to the serial monitor 
void sensorReading(){
  if (remainder >= 7950)
  { 
    Serial.println(switchAstate);
    Serial.println(switchBstate);
    Serial.println(switchCstate);
    Serial.println(switchDstate);
    Serial.println(switchEstate);
    
    Serial.print(F("co2(ppm):"));
    Serial.print(airSensor.getCO2());

    Serial.print(F(" temp(C):"));
    Serial.print(airSensor.getTemperature(), 1);

    Serial.print(F(" humidity(%):"));
    Serial.println(airSensor.getHumidity(), 1);

    Serial.print(F("freeMemory:"));
    Serial.println(freeMemory());  // print how much RAM is available in bytes.
  } 
}

void automaticControl(){

 digitalWrite(outputE, LOW);
  
  int c = airSensor.getCO2();
  int t = airSensor.getTemperature();
  float h = airSensor.getHumidity();
  int lightsTime = hour();
  int waterTime = minute();
  int tempF = (1.8*t+32);

//Send data to the OLED screen
//  printData();  
//sensorReading(); 

// Fan Control based of temp 85F degrees 
  if (tempF >= 85)
  {digitalWrite(outputD, HIGH);}
  else
   {digitalWrite(outputD, LOW);}

// Control of Lights 7am to 7pm on
  if (lightsTime >= 7 && lightsTime <=19)
   {digitalWrite(outputA, HIGH);
    digitalWrite(outputB, HIGH);}
  else
   {digitalWrite(outputA, LOW);
    digitalWrite(outputB, LOW);}

// Control of the watering, at 8am and 1pm for 10 minutes
   if (lightsTime == 9 || lightsTime == 13){
    if (waterTime >= 0 && waterTime <= 10)
    {digitalWrite(outputC, HIGH);}
    else if (waterTime > 10)
    {digitalWrite(outputC, LOW);}
   } 
}```

The critical point is to observe free memory over time and see that it remains the same.

You have a lot more headroom now, so hopefully it is resolved.