Arduino based controller with a random relay switching issue

Hi, this is my first time posting here so I apologize in advance if the topic isn't in the right section or if I forgot to add something which might help someone troubleshoot this.

I was working on building a temperature controller for a walk-in refrigeration unit using Arduino UNO started kit, 3 x MAX31855K chipsets and an 8 channel relay switch (Its a JBtek 8 channel relay, got it off cheap from Amazon). The whole point of it is to turn on/off an exhaust fan system and evaporator fans after checking a few simple temperature conditions (evaporator coils, box temperature and the outside temperature).

There are two issues I am facing right now

(1) I get a few errors (null values for temperature read) here and there in the middle randomly. I try to correct them in the code by averaging the values with the previous ones. I have also connected small capacitors (0.01uF) in parallel to the connections of the K type thermocouples to keep the readings stable but I keep getting random errors in the first few hours of booting up the arduino. Overtime the errors do decrease.

(2) This is actually my most frustrating issue right now. The system works great for first day or two but then does completely weird (well weird because I am probably missing out on something and I can't figure out what’s going on). As the code below shows, the evaporators turn on (the relay turns on by Arduino) once the temperature of the evaporators goes below 32F and the LCD screen shows the status as "E: On". The screen shows the correct status and the temperatures of the evaporators are also below 32 but it never turns on! It never turns the Relay on.

This happened twice already.. last time when this happened, I had to turn the board off (everything including the relay was powered by arduino) to run evaporators to prevent lines from freezing. The evaporator fans are plugged into NC on relay so as soon as the power goes off, the fans turn on (it’s more of a fail-safe). I didn’t have a multimeter at the time and I thought it was probably because Arduino was not providing enough power to the whole thing. So I ordered a few 5V power supplies and powered the circuit separately from the Arduino. Basically using Arduino only to drive the relays and sending/receiving signals.

Today, surprisingly, it did the same thing after running fine for last 2 days… So I kept the arduino on and turned off the relays. I checked the voltages on each relay driving lines on the Arduino. Pin 12 i.e. R1 was around 1.7mV (which is LOW instead of HIGH) and Pin13 i.e. R2 was at 4.98V (which is HIGH, as its supposed to be). This is what's driving me crazy! the LCD shows the correct status as well, the temperatures look fine to me too (no red screens or errors) but it should be HIGH not LOW.

(let me post the code in the second post, I have reached the character limit)

Here is my Code (I am not good at programming so any suggestions to improve this would be greatly appreciated).

// Import all the libraries , includes temp sensor library, LCD/Adafruit LCD library, Math library for conversion 

#include <SPI.h>
#include "Adafruit_MAX31855.h"
#include <math.h>
#include <Wire.h>
#include <Adafruit_RGBLCDShield.h>
#include <utility/Adafruit_MCP23017.h>


// Example creating a thermocouple instance with software SPI on any three
// digital IO pins.
#define MAXDO   3
#define MAXCS   4
#define MAXCLK  5
#define MAXDO2  6
#define MAXDO3  7 
#define R1 12
#define R2 13

// Initialize the LCD screen (create an object)

Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

// These #defines make it easy to set the backlight color
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7

// initialize both Thermocouples using the same pins with different DO to save pins

Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO);
Adafruit_MAX31855 thermocouple2(MAXCLK, MAXCS, MAXDO2);
Adafruit_MAX31855 thermocouple3(MAXCLK, MAXCS, MAXDO3);

// Start timer values for multitasking

unsigned long previousMillis = 0;        // will store last time actuators were turned on or off
unsigned long interval = 2L*60*1000;     // milliseconds of interval time to keep actuators open/close
unsigned long temp_av_time = 10.1L*1000;   // Time Interval needed to average box and outside temperature readings.
unsigned long temp_av_evap = 1100;       // Time interval needed to average evaporator temperature readings.
unsigned long previousMillis2 = 0;       // Previous time for To and Tb averages
unsigned long previousMillis3 = 0;       // Previous time for Te 
unsigned long i = 1;                     // Number of average cycles for To and Tb
unsigned long j = 1;                     // Number of average cycles for Te
double To, Tb, Te = -300;                // Variables for temperature measurements
double offset = 3.00;                    // Thermometer offset
//int error = 0;  


unsigned long sample_time = 200;          // Number of times arduino runs the loop


// Setup, this runs only once when the board is first initialized

void setup() {
 
  // Set up serial communication link 
  while (!Serial);                         // wait for Serial on Arduino
  Serial.begin(9600);                      // Serial rate is 9600bps
  
  // Setup LCD and a cool Bootup message 
  lcd.begin(16, 2);                         // Initialize LCD with rows x colums (16 x 2)
  lcd.print("<<Jags Tech>>");
  lcd.setCursor(0,1);
  lcd.print("Version 1.5.5");
  delay(1000);
  lcd.setBacklight(WHITE);
  
  // Setup the pins that are outputs for relays, Relay 1 is R1 and Relay 2 is R2 - Apply default conditions
  pinMode (R1, OUTPUT);                     // Relay for evaporator fans 
  pinMode (R2, OUTPUT);                     // Relay for exhaust system/ actuators 
  digitalWrite(R1, HIGH);                   // Evaporators on by default (On = HIGH)
  digitalWrite(R2, HIGH);                   // Exhaust system not working by default (Off = HIGH) and (On = LOW)

  
  delay(500);                               // wait for MAX chip to stabilize

  
  // Take initial readings for the temperatures
  To = (double)(thermocouple.readFarenheit() - offset) ;               // Thermocouple 1 temp in F - Outside temp
  Tb = (double)(thermocouple2.readFarenheit() - offset);               // Thermocouple 2 temp in F - Inside Box temp
  Te = (double)(thermocouple3.readFarenheit() - offset);              // Thermocouple 2 temp in F - Evaporator temp

  
}

void loop() {

   // New values of temperature probes 
   double To_new = (double)(thermocouple.readFarenheit() - offset) ;               // Thermocouple 1 temp in F - Outside temp
   double Tb_new = (double)(thermocouple2.readFarenheit() - offset);               // Thermocouple 2 temp in F - Inside Box temp
   double Te_new = (double)(thermocouple3.readFarenheit() - offset);;              // Thermocouple 2 temp in F - Evaporator temp

   
    // Get the current time 
    unsigned long currentMillis = millis();               // Used for actuator timing
    unsigned long currentMillis2 = millis();              // Used for averaging box and outside temperature
    unsigned long currentMillis3 = millis();             // Used for averaging evaporator temperature 
  

   // Check new values for any errors, if yes then apply default conditions and change LCD color to Red
   if (isnan(To_new) || isnan(Tb_new) || isnan(Te_new)) {

     // Set Error value to let loop conditions know what happened.
     
     // Serial print out and failure conditions.
     Serial.println("Something wrong with thermocouple 1, 2 or 3");
     
     if (R1 == LOW){
       digitalWrite(R1, HIGH); // Evaporators On since we want them running. (HIGH = On)
     }  
     if (R2 == LOW){
       digitalWrite(R2, HIGH); // Exhaust system not working in case of error (HIGH = Off)
     }

     // Set LCD messages to show what messed up.
     lcd.clear();
     lcd.setBacklight(RED);
     lcd.setCursor(0,0);
     lcd.print("O:"); lcd.print(To); lcd.print(char(223));
     lcd.print("B:"); lcd.print(Tb); lcd.print(char(223));
     lcd.setCursor(0,1);
     lcd.print("E:"); lcd.print(Te); lcd.write(char(223));lcd.print("Error");

     // Individually check all three thermocouples and fix readings. 
     if (isnan(To_new)){   // Check To
      To_new = To;
     }
     
     if (isnan(Tb_new)) {   // Check Tb
      Tb_new = Tb;
     }

     if (isnan(Te_new)){    // Check Te
      Te_new = Te;
     }

     delay(1000); // Wait 1 second and try again 

     } 
     
   else {

    
    // New Looping method for To and Tb, allows for multitasking with no impact on evaporator temperature monitored in real-time:
    if ((unsigned long)(currentMillis2 - previousMillis2) < temp_av_time)
    {
        To += (double)(To_new);
        Tb += (double)(Tb_new);
        i = (double)2.0;

    }
    else
    {
      previousMillis2 = currentMillis2;
      i = (double)1.0;
    }

    // Averaging Loop for evaporator temperature
    if ((unsigned long)(currentMillis3 - previousMillis3) < temp_av_evap)
    {
        Te += (double)(Te_new);
        j = (double)2.0;
    }
    else
    {
      previousMillis3 = currentMillis3;
      j = (double)1.0;
    }

     // Average out the values according to the total number of cycles 
     To /= (double)i;
     Tb /= (double)i;
     Te /= (double)j;

     // LCD display 
     lcd.setCursor(0,0);
     lcd.print("O:"); lcd.print(To); lcd.print(char(223));
     lcd.print("B:"); lcd.print(Tb); lcd.print(char(223));
     lcd.setCursor(0,1);
     lcd.print("E:"); lcd.print(Te); lcd.write(char(223));


     // Beta Code for gradient calculation (might add later)
 
     

     // Loop conditions

          
       if (Te <= 32.00){
        digitalWrite(R1, HIGH); // Evaporators = On to start cooling otherwise the evaporator coils would freeze!
        lcd.setCursor(8,1);
        lcd.print("E:On ");
        
       }
       else if (Te >= 33.00){ 
       digitalWrite(R1, LOW); // Evaporators = off, since compressor is off to save power
       lcd.setCursor(8,1);
       lcd.print("E:Off");
        
       }
       if ((round (To) <= 29) && (round(Tb) >= 32) && ((unsigned long)(currentMillis - previousMillis) >= interval)){
        digitalWrite(R2, LOW); // Exhaust system on to start cooling
        lcd.setBacklight(GREEN);
        previousMillis = currentMillis;  
       
       }
       if ((round (To) >= 30) && ((unsigned long)(currentMillis - previousMillis) >= interval)){
        digitalWrite(R2, HIGH); // Exhaust system off since its too warm outside. (Newer Condition)
        lcd.setBacklight(VIOLET);
        previousMillis = currentMillis; 
       }
  
       if ((round(To) <= 30) && (round(Tb) <= 31) && ((unsigned long)(currentMillis - previousMillis) >= interval)){
        digitalWrite(R2, HIGH); // Exhaust system turned off because its cool enough to work
        lcd.setBacklight(VIOLET);
        previousMillis = currentMillis;
       }
     
   }

  delay(sample_time);
 
}

Lastly, I greatly appreciate the help and time anyone spends to help me. Any suggestions or constructive criticism is welcomed.

Here is an image of the circuit (I know my wire management is terrible)

// Import all the libraries , includes temp sensor library, LCD/Adafruit LCD library, Math library for conversion

Java imports libraries. C/C++ does not.

Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO);
Adafruit_MAX31855 thermocouple2(MAXCLK, MAXCS, MAXDO2);
Adafruit_MAX31855 thermocouple3(MAXCLK, MAXCS, MAXDO3);

Do you count “uh-huh, 2, 3…”? Number ALL variables in a set. Or learn about arrays.

  To = (double)(thermocouple.readFarenheit() - offset) ;               // Thermocouple 1 temp in F - Outside temp
  Tb = (double)(thermocouple2.readFarenheit() - offset);               // Thermocouple 2 temp in F - Inside Box temp
  Te = (double)(thermocouple3.readFarenheit() - offset);              // Thermocouple 2 temp in F - Evaporator temp

Even better, of course, would be to give the instances MEANINGFUL names, so the comments are unnecessary.

    // Get the current time
    unsigned long currentMillis = millis();               // Used for actuator timing
    unsigned long currentMillis2 = millis();              // Used for averaging box and outside temperature
    unsigned long currentMillis3 = millis();             // Used for averaging evaporator temperature

You do NOT need three copies of now. If you have three task to perform at 3:30, do you need to look at your watch three times to see if it is 3:30?

     if (R1 == LOW){
       digitalWrite(R1, HIGH); // Evaporators On since we want them running. (HIGH = On)
     }  
     if (R2 == LOW){
       digitalWrite(R2, HIGH); // Exhaust system not working in case of error (HIGH = Off)
     }

12 is NOT LOW. 13 is NOT LOW. Neither pin will be set HIGH with this useless code.

Why is HIGH on in one case and off in the other?

        i = (double)2.0;

One letter global variable names are a recipe for disaster. Why do you think you need to cast 2.0 to a double?

Why are you storing 2.0 in a long?