Thanks @madmark2150 for your quick response.
The wiring of this chamber is done using DIN rails and all components are wired to a central board that include relays. It has two power supplies for 12VDC and 24VDC for relays and other parts. The arduino is also connected to a circuit board with two transistor arrays (https://www.digikey.com/en/products/detail/texas-instruments/ULN2004AIN/659671) to send signals to relays.
I'm posting this code which is the final version after various modifications on the chamber and it wiring. I hope it is not confusing/nonsensical for you since I recently modified the code to reset the arduino after 8 hours and some of the previous functions are not being used anymore. It was to solve the problem I have but apparently it was not helpful.
For serial port, yes. As you can see in the code below I monitor the chamber checking each sensor. I'll try to prep the wiring diagram.
//The following libaries are required for the operation of the chamber
#include <Arduino.h>
#include <Wire.h>
#include "Adafruit_SHT31.h"
#include <LiquidCrystal.h>
//Variable required for testing the Multiplexer
#define TCAADDR 0x70 //serial address of the TCA9458A Multiplexer
#include <avr/wdt.h>
//Temperature Sensor setup
bool enableHeater = false;
//Initialize the SHT31 sensor(s) *NOTE THAT IT MIGHT BE THE CASE THAT ONLY ONE INSTANCE NEED BE INITIALIZED*
Adafruit_SHT31 sht31_1 = Adafruit_SHT31();
Adafruit_SHT31 sht31_2 = Adafruit_SHT31();
Adafruit_SHT31 sht31_3 = Adafruit_SHT31();
Adafruit_SHT31 sht31_4 = Adafruit_SHT31();
//define relay pins (these values are not changeable unless the requisite change has been made on the arduino board)
const int Safety_Lights_Relay = 22; // Relay : https://www.mcmaster.com/8262T14/ ; Safety Lights: Some 12VDC lights
const int Heat_Mist = 26; // Relay : https://www.omega.ca/en/control-monitoring/relays/solid-state-relays/ssrl240-660/p/SSRL240DC25 ; Components (a heating pad and a solenoid valve): https://www.omega.ca/en/industrial-heaters/surface-heaters/flexible-heaters/srfra-srfga/p/SRFGA-508-10-P & https://www.mcmaster.com/4738K147/
const int Chamber_Fan_Relay1 = 24; // Relay : https://www.mcmaster.com/8262T14/ ; Fan: https://www.digikey.ca/en/products/detail/qualtek/FMA1-08025WBHT12/10228356
const int Humidifier_Fan_Relay = 25; // Relay : https://www.mcmaster.com/8262T14/ ; Fan: a 24 VDC fan
const int Humidifier_Relay = 23; // Relay : https://www.celduc-relais.com/Technical_DataSheet/FA_SOM020200.pdf ;
const int UV_Light_Relay = 27; // Relay : https://www.mcmaster.com/8262T14/ ; UV Lamps: two 340nm UVA lamps
const int Chamber_Fan_Relay2 = 28; // Relay : https://www.mcmaster.com/8262T14/ ; Fan: https://www.digikey.ca/en/products/detail/qualtek/FAA1-08025NBMT31/2599974
const int Heater_Relay = 29; // Relay : https://www.digikey.com/en/products/detail/sensata-crydom/CKRD6010/1771443 ; Heater: https://www.omega.com/en-us/industrial-heaters/duct-and-enclosures-heaters/duct-heaters/ahf-heater/p/AHF-10120
//define UV sensor pins (these values are not changeable unless the requisite change has been made on the arduino board) and placeholder values
const int UV_Sensor_1 = A8;
const int UV_Sensor_2 = A9;
float uvValue1 = 0;
float uvValue2 = 0;
// define Interrupt function to avoid arduino freezing
const byte ledPin = 53;
const byte interruptPin = 18;
volatile byte state = LOW;
//Time Constant Values
const long seconds = 1000; //number of milliseconds in a second
const long minutes = 60000; //number of milliseconds in a minute
const long hours = 3600000; //number of milliseconds in an hour
//Temperature & Humidity Variables
const float sensorTempVariance = 0.5; //variable to modify target temperature set points ******************
const float sensorHumVariance = 0.5; //variable to modify target temperature set points ******************
const float RH_set_Range = 10; //variable to add moisture slowly after a certain level **************
const int targetTemp1 = 60.9; //target temperature for dry cycle
const int targetTemp2 = 50.9; //target temperature for wet cycle
float targetTemp = 60.9; //system initializing temperature (This is the value that gets modified in the code with specific cycle temps)
const float maxSafetyTemp = 85; //maximum temperature allowable for the safety sensor to reach ******************
const int targetHum = 65; //target humidity for wet cycle
const float sht3TempWeight3 = 8; //Weighting for the 1st SHT3 sensor temperature value ******************
const float sht3TempWeight1 = 0; //Weighting for the 2nd SHT3 sensor temperature value ******************
const float sht3TempWeight4 = 8; //Weighting for the 3rd SHT3 sensor temperature value ******************
const float sht3TempWeight2 = 8; //Weighting for the 4th SHT3 sensor temperature value ******************
const float sht3HumWeight3 = 6; //Weighting for the right SHT3 sensor humidity value ******************
const float sht3HumWeight1 = 0; //Weighting for the central SHT3 sensor humidity value ******************
const float sht3HumWeight4 = 9; //Weighting for the floating SHT3 sensor humidity value ******************
const float sht3HumWeight2 = 9; //Weighting for the left SHT3 sensor humidity value ******************
const int heaterPulse = 5000; //Duration for heater to run before turning off ******************
int chamberFanPulseDuration = 1000; //Duration to turn on the chamber fan during humidity control (must be in milliseconds) ******************
int humFanPulseDuration = 1000; //Duration to turn on the humidifier fan during humidity control (must be in milliseconds) ******************
int mistLoading = 2000; //Duration to run the humidifier to load the mist chamber (must be in milliseconds) *****************
//Global Variables
long startTime = 0; //dummy variable that will eventually tell the arduino at which millisecond the test began
float cycleDuration = 4 ; //number of hours that each cycle will run ******************
float singleBatchTime = cycleDuration * 2 * 3600000; // [ms]
int cycleCount = 0; //initial value for cycle counter
const int totalCycleCount = 42; //number of total cycles for test
long int timeVar = 0;
//select pins used for LCD and define LCD values
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int lcd_key = 5; //Default value equivalent to pressing no button
int adc_key_in = 2000; //Arbitrary value assigned
//Values are arbitarily assigned but should not be changed as they are prolific throughout the code
#define btnDOWN 0
#define btnUP 1
#define btnLEFT 2
#define btnRIGHT 3
#define btnSELECT 4
#define btnNONE 5
//Code to initialize system before test cycle begins (will only be ran one time immediately after powering on)
void setup() {
wdt_disable();
//establish digital pins for relay control and set them all to low
pinMode(Heater_Relay, OUTPUT);
digitalWrite(Heater_Relay, LOW);
pinMode(UV_Light_Relay, OUTPUT);
digitalWrite(UV_Light_Relay, LOW);
pinMode(Chamber_Fan_Relay1, OUTPUT);
digitalWrite(Chamber_Fan_Relay1, LOW);
pinMode(Humidifier_Fan_Relay, OUTPUT);
digitalWrite(Humidifier_Fan_Relay, LOW);
pinMode(Heat_Mist, OUTPUT);
digitalWrite(Heat_Mist, LOW);
pinMode(Safety_Lights_Relay, OUTPUT);
digitalWrite(Safety_Lights_Relay, LOW);
pinMode(Humidifier_Relay, OUTPUT);
digitalWrite(Humidifier_Relay, LOW);
pinMode(Chamber_Fan_Relay2, OUTPUT);
digitalWrite(Chamber_Fan_Relay2, LOW);
//this section is to initialize the LCD display and Serial Monitor
Serial.begin(115200); //baud rate is determined by the requirement of the UV sensors
lcd.begin(16, 2);
lcd.setCursor(0, 0);
//Initialize the temperature & Humidity sensors
sht31_1.begin();
sht31_2.begin();
sht31_3.begin();
sht31_4.begin();
//Troubleshooting stuff
//this section of code allows for testing of Multiplexer function and displays which of the 8 ports the sensors are connected to in the serial monitor (*please note that if physical changes were required that the corresponding values for "tcaselect" function will need to be adjusted)
while (!Serial);
delay(1000);
Wire.begin();
Serial.println("\nTCAScanner Ready!");
//Setting up the multiplexer for SHT3x sensors
for (uint8_t t = 0; t < 8; t++) {
tcaselect(t);
Serial.print("TCA Port #");
Serial.println(t);
for (uint8_t addr = 0; addr <= 127; addr++) {
if (addr == TCAADDR) continue;
Wire.beginTransmission(addr);
if (!Wire.endTransmission()) {
Serial.print("Found I2C 0x");
Serial.println(addr, HEX);
}
}
}
Serial.println("\ndone");
Serial.println("");
digitalWrite(Safety_Lights_Relay, HIGH);
} //End of Setup
//main loop that controls cycles
void loop() {
//This is to reset the arduino every 8 hours
timeVar = millis() - startTime;
if (timeVar > singleBatchTime && cycleCount != 0) {
wdt_enable(WDTO_250MS);
}
//This section reads the input from the LCD panel and performs specific actions
while (cycleCount == 0 && averageTemp() < targetTemp) { //Waits for user to press SELECT while maintaining target temperature
print2Serial(averageTemp(), averageHum());
delay(1000);
displayData(lcd_key);
tempMaintenance(targetTemp);
}
if (cycleCount == 0 && averageTemp() >= targetTemp) { //Cycle count will only be 0 upon first turning on the system, this prevents a user from restarting the test by accidentally pressing select
startupProcedure();
}
else {
displayData(lcd_key);
}
//This section maintains the specified temperature and humidity (depending on cycle)
if (cycleCounter() == true) { //Only enters on even number cycles ("wet cycles")
humMaintenance(targetTemp);
} else {
digitalWrite(Humidifier_Relay, LOW);
digitalWrite(Heat_Mist, LOW);
digitalWrite(Humidifier_Fan_Relay, LOW);
print2Serial(averageTemp(), averageHum());
delay(1000);
tempMaintenance(targetTemp);
}
//This section checks to see if the cycle has been running for the specified time and the number of completed cycles (ends the program if cycle count is reached)
checkTime();
//cyclesCompleted();
} //End of Loop
//This function allows the selection of different I2C addresses on the multiplexer
void tcaselect(uint8_t i) {
if (i > 7) return;
Wire.beginTransmission(TCAADDR);
Wire.write(1 << i);
Wire.endTransmission();
}
//This function averages the temperatures of all three sensors in a weighted manner
float averageTemp() {
tcaselect(2);
float sht3Temp1 = sht31_1.readTemperature();
tcaselect(3);
float sht3Temp2 = sht31_2.readTemperature();
tcaselect(4);
float sht3Temp3 = sht31_3.readTemperature();
tcaselect(5);
float sht3Temp4 = sht31_4.readTemperature();
float tempAverage = ((sht3TempWeight1 * sht3Temp1) + (sht3TempWeight2 * sht3Temp2) + (sht3TempWeight3 * sht3Temp3) + (sht3TempWeight4 * sht3Temp4)) / 24;
return tempAverage;
}
//This function averages the humidity of all three sensors in a weighted manner
float averageHum() {
tcaselect(2);
float sht3Hum1 = sht31_1.readHumidity();
tcaselect(3);
float sht3Hum2 = sht31_2.readHumidity();
tcaselect(4);
float sht3Hum3 = sht31_3.readHumidity();
tcaselect(5);
float sht3Hum4 = sht31_4.readHumidity();
float humAverage = ((sht3HumWeight1 * sht3Hum1) + (sht3HumWeight2 * sht3Hum2) + (sht3HumWeight3 * sht3Hum3) + (sht3HumWeight4 * sht3Hum4)) / 24;
return humAverage;
}
//This function reads the value of the button on the LCD Display
int read_LCD_buttons() {
adc_key_in = analogRead(0);
if (adc_key_in < 50) return btnRIGHT; //3
if (adc_key_in < 250) return btnUP; //1
if (adc_key_in < 450) return btnDOWN; //0
if (adc_key_in < 650) return btnLEFT; //2
if (adc_key_in < 850) return btnSELECT; //4
if (adc_key_in > 1000) return btnNONE; //5
return btnNONE;
}
//This function maintains the temperature of the system (for dry-cycles)
void tempMaintenance(float temperature) {
float maintenanceTargetTemp = temperature - sensorTempVariance;
if (averageTemp() <= maintenanceTargetTemp) {
digitalWrite(Chamber_Fan_Relay1, HIGH);
digitalWrite(Chamber_Fan_Relay2, HIGH);
digitalWrite(Heater_Relay, HIGH);
if (averageTemp() <= maintenanceTargetTemp) {
print2Serial(averageTemp(), averageHum());
delay(1500);
}
delay(1000);
digitalWrite(Heater_Relay, LOW);
}
else {
digitalWrite(Chamber_Fan_Relay1, LOW);
digitalWrite(Chamber_Fan_Relay2, LOW);
}
}
//This function maintains the temperature of the system (for wet-cycles)
void tempRH_Maintenance(float temperature) {
float maintenance_TargetTemp = temperature - sensorTempVariance;
digitalWrite(Chamber_Fan_Relay1, LOW);
if (averageTemp() < maintenance_TargetTemp) {
digitalWrite(Chamber_Fan_Relay2, HIGH);
digitalWrite(Heater_Relay, HIGH);
if (averageTemp() <= maintenance_TargetTemp) {
print2Serial(averageTemp(), averageHum());
delay(1000);
if (averageHum() < targetHum - 2.5 ) {
digitalWrite(Humidifier_Fan_Relay, HIGH);
}
else {
digitalWrite(Humidifier_Fan_Relay, LOW);
}
}
}
else {
digitalWrite(Heater_Relay, LOW);
delay(1000); //Delay is required so that residual heat in the inline heater will be blown into the chamber
digitalWrite(Chamber_Fan_Relay2, LOW);
//digitalWrite(Humidifier_Fan_Relay, LOW);
}
}
//This function prints the data to the serial monitor and lcd display
void displayData(int sensor2Display) { //Value of 1 = top sensor, value of 2 = left sensor, value of 3 = right sensor, and value of 0 or 5 = average
float temp2Print = 0;
float hum2Print = 0;
if (sensor2Display == btnUP) {
tcaselect(2); //value in parenthesis corresponds to the physical sensor at the highest location in the chamber
temp2Print = sht31_1.readTemperature();
hum2Print = sht31_1.readHumidity();
} else if (sensor2Display == btnLEFT) {
tcaselect(3); //value in parenthesis corresponds to the physical sensor at the left most location in the chamber
temp2Print = sht31_2.readTemperature();
hum2Print = sht31_2.readHumidity();
} else if (sensor2Display == btnRIGHT) {
tcaselect(4); //value in parenthesis corresponds to the physical sensor at the right most location in the chamber
temp2Print = sht31_3.readTemperature();
hum2Print = sht31_3.readHumidity();
} else {
temp2Print = averageTemp();
hum2Print = averageHum();
}
lcd.home();
lcd.print("T:");
lcd.print(round(temp2Print));
lcd.print("C");
lcd.print(" H:");
lcd.print(round(hum2Print));
lcd.print("%");
lcd.setCursor(0, 1);
long int timeMilliseconds = timeVar;
long int timeHours = timeMilliseconds / hours;
long int timeMins = (timeMilliseconds - (timeHours * hours)) / minutes;
lcd.print("t:");
lcd.print(timeHours);
lcd.print("h");
lcd.print(timeMins);
lcd.setCursor(6, 1);
lcd.print("m");
lcd.print(" Cycle:");
lcd.print(cycleCount);
}
//This function is for starting the initial test
void startupProcedure() {
lcd.clear();
lcd.print("Starting...");
delay(2000); //Delay is added just for user experience
digitalWrite(UV_Light_Relay, HIGH);
delay(500);
int dummyVar = uvCheck(); //Added to confirm via the serial monitor that the UV is operational
startTime = millis(); //Officially begins the start of the first cycle
cycleCount = 1; //Initialize the cycle counting and ensures that the requirements to trigger the startup procedure can no longer be met
}
//This function checks to see if UV is on
int uvCheck() {
uvValue1 = analogRead(UV_Sensor_1);
uvValue2 = analogRead(UV_Sensor_2);
float averageUV = (uvValue1 + uvValue2) / 2;
if (averageUV != 0) {
return 1;
}
else {
return 0;
}
}
//This function keeps count of which cycle the system is on
bool cycleCounter() {
if (cycleCount % 2 == 0) { //Even numbered cycles (wet cycles) will return true
return true;
}
else {
return false;
}
}
//This function removes the excess moisture if the target RH is passed. Also, by momentarily turning on the humidifier fan it tries to keep all sensors in the same RH range.
void excess_Humidity() {
float adjustedHumTarget = targetHum - RH_set_Range * sensorHumVariance;
if (averageHum() > adjustedHumTarget) { // If RH is gone too much high, play with chamber fans to remove the excessive mist
if (averageHum() > adjustedHumTarget) { //while
digitalWrite(Heat_Mist, LOW);
digitalWrite(Humidifier_Fan_Relay, LOW);
// if (swap == true) {
digitalWrite(Chamber_Fan_Relay1, HIGH);
delay(800);
digitalWrite(Chamber_Fan_Relay1, LOW);
delay(2200);
}
}
else {
tcaselect(3);
float T_Last = sht31_2.readTemperature();
float RH_Last = sht31_2.readHumidity();
tcaselect(4);
float T_First = sht31_3.readTemperature();
float RH_First = sht31_3.readHumidity();
if (T_First != T_Last || RH_First != RH_Last) {
//tempRH_Maintenance(targetTemp);
digitalWrite(Humidifier_Fan_Relay, HIGH);
delay(250);
digitalWrite(Humidifier_Fan_Relay, LOW);
print2Serial(averageTemp(), averageHum());
delay(2000);
}
}
}
//This function maintains the humidity of the system
void humMaintenance(float temperature) {
float adjustedHumTarget = targetHum - RH_set_Range * sensorHumVariance;
digitalWrite(Humidifier_Relay, HIGH);
//Heat_Mist_counter+;
if (digitalRead(Humidifier_Fan_Relay) == HIGH) { // If target RH is reached turn the fogger OFF
if (averageHum() >= adjustedHumTarget) { // First see if RH is in the desired range
digitalWrite(Humidifier_Fan_Relay, LOW);
delay(mistLoading);
if (averageHum() < targetHum - RH_set_Range / 2 * sensorHumVariance) { //Going a little bit higher than the RH setpoint
digitalWrite(Humidifier_Fan_Relay, HIGH);
print2Serial(averageTemp(), averageHum());
//lcd_key = read_LCD_buttons();
delay(mistLoading / 2);
digitalWrite(Humidifier_Fan_Relay, LOW);
}
tempRH_Maintenance(targetTemp);
}
excess_Humidity();
}
if (averageHum() < targetHum) {
digitalWrite(Humidifier_Fan_Relay, HIGH);
if (averageHum() < targetHum - RH_set_Range / 2 * sensorHumVariance) {
digitalWrite(Heat_Mist, HIGH);
}
print2Serial(averageTemp(), averageHum());
//lcd_key = read_LCD_buttons();
delay(700);
tempRH_Maintenance(targetTemp);
}
else {
excess_Humidity();
}
}
//This function checks to see if the current cycle has been running for more than the specified cycle time and increases the cycle count
void checkTime() {
//int convertedTime = (int) cycleDuration * hours; //convert cycle duration to milliseconds (casting to int drops the fractions of the millisecond if any and makes it possible to convert to long)
long cycleDurationMills = cycleDuration * hours;
//long timeCheckVar = timeVar;
if (timeVar >= cycleDurationMills) {
lcd.clear();
cycleCount = 2;
toggleUV();
toggleTargetTemp();
}
else {
cycleCount = 1;
}
}
//This functions turns the UV light on/off depending on the cycle
void toggleUV() {
if (cycleCounter() == true) {
digitalWrite(UV_Light_Relay, LOW);
delay(500); //Delay is required so that the sensors have adequate time to determine if the UV light is off
int dummyVar = uvCheck();
}
else {
digitalWrite(UV_Light_Relay, HIGH);
delay(500); //Delay is required so that the sensors have adequate time to determine if the UV light is on
int dummyVar = uvCheck();
//startTime = millis(); //Starts the timer for the current cycle as soon as the UV light is on
}
}
//This function sets the target temperature depending on the cycle
void toggleTargetTemp() {
if (cycleCounter() == true) {
targetTemp = targetTemp2;
//print2Serial(averageTemp(), averageHum());
humMaintenance(targetTemp); //Brings system to the specified operating conditions for wet cycle
//startTime = millis(); //Timer for the wet cycle begins when target humidity is reached
}
else {
targetTemp = targetTemp1;
//print2Serial(averageTemp(), averageHum());
}
}
//This function prints the necessary data to the serial monitor
void print2Serial(float temperatureValue, float humidityValue) {
float temp2Print = 0;
float hum2Print = 0;
Serial.println("THE ORDER OF SENSORS IS ACCORDING TO THE AIR FLOW DIRECTION");
Serial.println("");
Serial.print("Target Temperature: ");
Serial.println(targetTemp);
Serial.print("Current Avg. Temperature: ");
Serial.print(temperatureValue);
Serial.println(" °C");
Serial.println("");
Serial.print("Right Temperature (1st sensor): ");
tcaselect(4);
temp2Print = sht31_3.readTemperature();
Serial.print(temp2Print);
Serial.println(" °C");
//Serial.print("Floating 1 Temperature (2nd sensor): ");
//tcaselect(2);
//temp2Print = sht31_1.readTemperature();
//Serial.print(temp2Print);
//Serial.println(" °C");
Serial.print("Floating 2 Temperature (3rd sensor): ");
tcaselect(5);
temp2Print = sht31_4.readTemperature();
Serial.print(temp2Print);
Serial.println(" °C");
Serial.print("Left Temperature (4th sensor): ");
tcaselect(3);
temp2Print = sht31_2.readTemperature();
Serial.print(temp2Print);
Serial.println(" °C");
Serial.println("");
Serial.print("Current Avg. RH: ");
Serial.print(humidityValue);
Serial.println(" %");
Serial.println("");
Serial.print("Right Humidity: ");
tcaselect(4);
hum2Print = sht31_3.readHumidity();
Serial.print(hum2Print);
Serial.println(" %");
//Serial.print("Floating 1 Humidity: ");
//tcaselect(2);
//hum2Print = sht31_1.readHumidity();
//Serial.print(hum2Print);
//Serial.println(" %");
Serial.print("Floating 2 Humidity: ");
tcaselect(5);
hum2Print = sht31_4.readHumidity();
Serial.print(hum2Print);
Serial.println(" %");
Serial.print("Left Humidity: ");
tcaselect(3);
hum2Print = sht31_2.readHumidity();
Serial.print(hum2Print);
Serial.println(" %");
Serial.println("");
Serial.print("Cycle Count: ");
Serial.println(cycleCount);
Serial.print("Current Time Elapsed Since Start of the Batch: ");
long int timeVarHrs = timeVar / hours;
long int timeVar2 = (timeVar - (timeVarHrs * hours));
long int timeVarMins = timeVar2 / minutes;
long int timeVarSecs = (timeVar2 - (timeVarMins * minutes)) / seconds;
Serial.print(timeVarHrs);
Serial.print(" Hrs ");
Serial.print(timeVarMins);
Serial.print(" Mins ");
Serial.print(timeVarSecs);
Serial.println(" Secs");
Serial.println("*****************************************************************");
Serial.println("");