Strange characters on LCD... is my program haunted??

Program is to regulate temp, humidity and light cycle (water cycle to be added later) for..... GROWING POT. yeah, that's right, I'm Canadian, I can actually say it. lol.

Everything appears to be working fine but if the program runs ~24h+ I get very strange characters appearing on the LCD. I'm starting to think maybe there's a problem with the way I'm calling variables in the timer section?? Or is there something inherently bad about the way I've structured it?

Any advice would be appreciated. It's been running fine for roughly a week so long as I reset it once a day but the point was to be able to get away and leave the girls for a few days once in a while lol.

/*
Light, heat and humidity control for grow room
*/
//libraries
#include <LiquidCrystal.h>
#include "DHT.h"
//definitions
const int lightout = 6, heatout = 7, fanout = 9, timeset = 10;
int lightstate = 1, heatstate = 0, fanstate = 0, timestate = 0;
short seconds, minutes, hours;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#define DHTPIN 8
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
void setup() {
//open serial
    Serial.begin(9600);
//set pins as ins or outs
    pinMode(lightout, OUTPUT);
    pinMode(heatout, OUTPUT);
    pinMode(fanout, OUTPUT);
    pinMode(timeset, INPUT);
// start temp/hum sensor
  dht.begin();
// set up the LCD's number of columns and rows and print initial text
  lcd.begin(16, 2);
  lcd.print("Te:   Hu:   Ti:");      
//reset time to zero. set startup states for apppliances
    hours = 0;
    minutes = 0;
    seconds = 0;
    digitalWrite(lightout, LOW);
    digitalWrite(heatout, HIGH);
    digitalWrite(fanout, HIGH);
}
void loop() {
//data to lcd. temp, humidity and hours of light cycle
  lcd.setCursor(0, 1);
  float h = dht.readHumidity();
  float t = dht.readTemperature();
// 5% calibration for DHT  
  t = t - (t*0.20);
  if (isnan(h) || isnan(t))
  {
    lcd.print("ERROR");
    return;
  }
  lcd.print(t);
  lcd.setCursor(6,1);
  lcd.print(h);  
  lcd.setCursor(12,1);
  lcd.print(hours);
//temperature control
  //heater control
    if (((t < 22.00) && (lightstate == 1)) || ((t < 17.00) && (lightstate == 0)))
    {
     digitalWrite(heatout, LOW);
     heatstate = 1;
    }
    if (((t > 28.00) && (lightstate == 1)) || ((t > 23.00) && (lightstate == 0)))
    {
     digitalWrite(heatout, HIGH);
     heatstate = 0;
    }
  //fan control
    if (h > 72.00)
     {
      digitalWrite(fanout, LOW);
      fanstate = 1;
     }
    if (h <= 65.00)
     {
      digitalWrite(fanout, HIGH);
      fanstate = 0;
     }  
//twelve hour timer which toggles the light
    if (hours == 12)
  {
    hours = 0;
    minutes = 0;
    seconds = 0;
    digitalWrite(lightout, !digitalRead(lightout));
    lightstate = !lightstate;
    lcd.setCursor(12,1); //clear second digit from time display
    lcd.print("0 ");
  }
    else if (minutes == 60)
  {
    minutes = 0;
    seconds = 0;
    hours ++; 
  }
  else if (seconds == 60)
  {
    seconds = 0;
    minutes ++;
  } 
  delay(1000);
  seconds++;
// pressing a button advances the hours by 1
  timestate = digitalRead(timeset);
  if (timestate == LOW)
  {
    hours ++; 
  }
//print data to serial for debugging
    Serial.print("Temp = ");
    Serial.print(t);
    Serial.print(" Hum = ");
    Serial.print(h);
    Serial.println("%  "); 
    Serial.print("Time =  ");
    Serial.print(hours);
    Serial.print(" : ");
    Serial.print(minutes);
    Serial.print(" : ");
    Serial.println(seconds);
    Serial.print("Light: ");
    if (lightstate == 1)
    {
      Serial.print("ON ");
    }
    if (lightstate == 0)
    {
      Serial.print("OFF ");
    }
    Serial.print("Heat: ");
    if (heatstate == 1)
    {
      Serial.print("ON ");
    }
    if (heatstate == 0)
    {
      Serial.print("OFF ");
    }
    Serial.print("Fan: ");
    if (fanstate == 1)
    {
      Serial.println("ON ");
    }
    if (fanstate == 0)
    {
      Serial.println("OFF ");
    }    
}

I'm starting to think maybe there's a problem with the way I'm calling variables in the timer section?

You don't/can't call variables.
Maybe that's your problem.

okay, you got me... don't have the nomenclature down yet lol. But what I mean is the use of int vs float etc...

Get a RTC, don't use floats for times, don't use delay.

Can you provide more detailed info on the "very strange characters" you see and where on the display they show up?

Is the whole LCD randomly affected or specific fields?

Are you sure about the quality and consistency of the power supply to the Arduino?

Relays and lcd displays often do not play well together.

What relays are you using and how are they wired?

First thing is to place a large capacitor across the power terminals of the lcd.

Zornocology:
... I get very strange characters appearing on the LCD.

Same characters? Same place on the screen? Exactly 24-hours or approximately?

Thanks for all your replies.

The strange characters are in the same place on the lower half of the LCD. this morning it's an r and a tiny backwards 3. It also did this last night about an hour before the light was scheduled to go off. Happened when the fan turned off so thinking cattledog is onto something about the co-operation of LCDs and relays.

Relay board: https://www.elegoo.com/product/elegoo-4-channel-dc-5v-relay-module-with-optocoupler/

@cattledog - how big a cap we talking?
@blackfin - power supply is def. good. 12V 1.2A wall wart.
@whandall - I changed the code over to use millis instead of delay... issue is still there.

/*
Light, heat and humidity control for grow room
*/
//libraries
#include <LiquidCrystal.h>
#include "DHT.h"
//definitions
unsigned long startMillis, currentMillis;
const unsigned long period = (1000);
const int lightout = 6, heatout = 7, fanout = 9, timeset = 10;
int lightstate = 1, heatstate = 0, fanstate = 0, timestate = 0;
short hours, minutes, seconds;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#define DHTPIN 8
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
void setup() {
//open serial
    Serial.begin(9600);
//start clock
    startMillis = millis();
//set pins as ins or outs
    pinMode(lightout, OUTPUT);
    pinMode(heatout, OUTPUT);
    pinMode(fanout, OUTPUT);
    pinMode(timeset, INPUT);
// start temp/hum sensor
  dht.begin();
// set up the LCD's number of columns and rows and print initial text
  lcd.begin(16, 2);
  lcd.print("Te:   Hu:   Ti:");      
//reset time to zero. set startup states for apppliances
    hours = 0;
    digitalWrite(lightout, LOW);
    digitalWrite(heatout, HIGH);
    digitalWrite(fanout, HIGH);
}
void loop() {
//data to lcd. temp, humidity and hours of light cycle
  lcd.setCursor(0, 1);
  float h = dht.readHumidity();
  float t = dht.readTemperature();
// 5% calibration for DHT  
  t = t - (t*0.20);
  if (isnan(h) || isnan(t))
  {
    lcd.print("ERROR");
    return;
  }
  lcd.print(t);
  lcd.setCursor(6,1);
  lcd.print(h);  
  lcd.setCursor(12,1);
  lcd.print(hours);
//temperature control
  //heater control
    if (((t < 22.00) && (lightstate == 1)) || ((t < 17.00) && (lightstate == 0)))
    {
     digitalWrite(heatout, LOW);
     heatstate = 1;
    }
    if (((t > 28.00) && (lightstate == 1)) || ((t > 23.00) && (lightstate == 0)))
    {
     digitalWrite(heatout, HIGH);
     heatstate = 0;
    }
  //fan control
    if (h > 72.00)
     {
      digitalWrite(fanout, LOW);
      fanstate = 1;
     }
    if (h <= 65.00)
     {
      digitalWrite(fanout, HIGH);
      fanstate = 0;
     }  
//twelve hour timer which toggles the light

    currentMillis = millis(); 
    if (currentMillis - startMillis >= period)
    {
      seconds++;
      startMillis = currentMillis;
    }

    if (hours == 12)
  {
    hours = 0;
    minutes = 0;
    seconds = 0;
    digitalWrite(lightout, !digitalRead(lightout));
    lightstate = !lightstate;
    lcd.setCursor(12,1); //clear second digit from time display
    lcd.print("0 ");
  }
    else if (minutes == 60)
  {
    minutes = 0;
    seconds = 0;
    hours ++; 
  }
    else if (seconds == 60)
  {
    seconds = 0;
    minutes ++;
  } 
// pressing a button advances the hours by 1
  timestate = digitalRead(timeset);
  if (timestate == LOW)
  {
    hours ++; 
  }
//print data to serial for debugging    
    Serial.print("Temp = ");
    Serial.print(t);
    Serial.print(" Hum = ");
    Serial.print(h);
    Serial.println("%  "); 
    Serial.print("Time =  ");
    Serial.print(hours);
    Serial.print(" : ");
    Serial.print(minutes);
    Serial.print(" : ");
    Serial.println(seconds);
    Serial.print("Light: ");
    if (lightstate == 1)
    {
      Serial.print("ON ");
    }
    if (lightstate == 0)
    {
      Serial.print("OFF ");
    }
    Serial.print("Heat: ");
    if (heatstate == 1)
    {
      Serial.print("ON ");
    }
    if (heatstate == 0)
    {
      Serial.print("OFF ");
    }
    Serial.print("Fan: ");
    if (fanstate == 1)
    {
      Serial.println("ON ");
    }
    if (fanstate == 0)
    {
      Serial.println("OFF ");
    }
}

are in the same place on the lower half of the LCD. this morning it's an r and a tiny backwards 3.

Why not tell us where on the lower half of the screen? Telling us everything you can in detail may help speed up the debug process.

0123456789ABCDEF <-- column numbers
Te:   Hu:   Ti:
t.tt  h.hh  tt

Which of the fields -- t, h or hours -- is affected?

@cattledog - how big a cap we talking?

As big as you have. 100uf makes a good decoupling capacitor. 10uf would be worth trying if that is what you have. Pay attention to polarity when you hook it up.

How is the relay module wired and powered?

If you want complete optical isolation, connect "Vcc" to Arduino +5 volts but do NOT connect Arduino Ground. Remove the Vcc to JD-Vcc jumper. Connect a separate +5 supply to "JD-Vcc" and board Gnd. This will supply power to the transistor drivers and relay coils.

The system wiring may also be an issue, and how power lines are run in relationship to signal lines, etc. These lcd/system noise problems can be very difficult to unravel. If you can identify a particular relay among your active ones, it might help.