Overclock.net banner
1 - 2 of 2 Posts

· Registered
Joined
·
966 Posts
Discussion Starter · #1 · (Edited)
For those interested in running a chiller, but concerned with condensation, I'm posting this for your use. It can be built for less than $30.

The logic works by sampling the relative humidity of the air, water temperature, and the air temperature. It converts Air temp and Relative Humidity into dew point and compares the water temperature to the dew point. If the water temperature is dew point +1 it turns off the chiller. If the water temperature is dew point +5 it turns on the chiller, giving it a 4f hysteresis range (can be changed easily in code by adjusting minW (turn off temp) and maxW (turn on temp) in lines 141 and 142 respectively.

If you leave the chiller's own thermostat set to a low temp, and allow it to maintain it's duty cycle protection it may exceed the hysteresis range, but it will protect the chiller itself. If you hotwire the chiller to always be on, be careful of the cycle time of the chiller, on/off too often can and will kill them.

The rotary encoder allows you to manually enable the chiller, disable it by ignoring the logic, or enable the logic depending on where it's set. It acts as a simple 3 position state switch, turn it as many times as you like and it's always 1 notch away from a change of state, easy use.

This is only revision 1 2, I am still working on adding some functionality and improving the dewpoint calculation accuracy, as it is, dew point calculation is slightly off it should be close enough. Humidity calculation is dead on accurate now.

Parts list:
Arduino Nano
DHT22 - Temp/Relative Humidity Sensor
Rotary Encoder - Turn Counterclockwise to disable the chiller. Turn Clockwise to manually enable the chiller. Press the encoder button to enable dew point target logic.
Sparkfun Relay power strip part# COM-14236
10k NTC Thermistor for sampling water temp


NOTE: The wokwi.com page rotary encoder has a bug so use the code HERE not from that site as it has a concession to work despite the bug and the push button on the encoder is disabled in it (or use that code if you don't want the push button functionality and only want the rotary function).

OBVIOUSLY: Use at your own risk lol.


/*
Based on work by
RedTar4
Bobs_DewPoint_LCD_meter_DHT22_rev7
10/18/18 - 3/10/19
and
Jobit Joseph Semicon Media Pvt Ltd (Circuit Digest - Electronics Engineering News, Latest Products, Articles and Projects)
Currently written for Arduino Nano to be adapted to ESP32 in the future
Modified for use as an output trigger(alarmLed) to control a relay controller my water chiller to cool my PC with a 6f hysteresis around the calculated dew point (dewpoint -1 to dewpoint +5)
NTC Thermistor used to sample the water temperature should be setup using pin4 > 10k R1 > analog read > NTC 10k > GND
Rotary encoder is used to control manual enable of chiller, manual disable of chiller, or function of dew point target logic as a 3 state machine

NEED TO ADD:
PIN INPUT TO TRIGGER RELAY CONTROL FUNCTION ONLY WHILE HIGH AND PAUSE RELAY CONTROL WHILE LOW ALLOWS PC ON CONDITION TO CONTROL CHILLER OPERATION USING ANY CIRCUIT FROM PC ACTIVE WHILE RUNNING (FAN_HEADER)
BEEP OUTPUT ON FAILURE WHEN WATER IS HIGHER THAN DEW POINT + 5 AND PAUSE NOT ENABLED BUT RELAY NOT HIGH AS PRECAUTION AGAINST OVERHEATING WHEN CHILLER FAILS TO START
BEEP OTUPUT ON FAILURE WHEN WATER IS BELOW DEW POINT -2 BUT RELAY NOT LOW AS A PRECAUTION AGAINST RUNAWAY CHILLER
*/
// Library section
#include <LiquidCrystal.h> // LCD library
LiquidCrystal lcd(7, 8, 9, 10, 11, 12); // (rs,e,d4,d5,d6,d7),initialize the LCD with the numbers of the interface pins
#include <DHT.h> // DHT22 library, delete any "DHT.U" files from library,or it wont run

// Definition section
#define CLK 2
#define DT 3
#define SW 1
#define alarmLed 5 // define the output pin
#define DHTPIN 6 // what arduino pin we're connected to, arduino pin D2
DHT dht(DHTPIN, DHT22); // initialize DHT sensor and create sensor objects
#define DHTTYPE DHT22 // create DHT 22 (AM2302), AM2321 object
#define ntc_pin A1 // defines the ntc thermistor input pin
#define vd_power_pin 4 // defines the output pin for the thermistor
#define nominal_resistance 10000 //10k thermistor nominal resistance
#define nominal_temperature 25 //10k resistance temerpature point
#define samplingrate 5 //sample rate for the thermistor
#define beta 3950 //beta coefficient of the thermistor
#define Rref 10000 //resistance of voltage divider resitance value

//variable declaration section
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir = "";
unsigned long lastButtonPress = 0;
int samples = 0;
float minW = 0;
float maxW = 0;
float H = 0.0;
float C = 0.0;
float F = 0.0;
float W = 0.0;
float HiF = 0.0;
float DewPoint = 0.0;
float DP = 0.0;
int interval = 1000;
unsigned long previousMillis = 0;
uint8_t i;
float average;
float waterTemperature;
void setup()
{

// output pin setup section
pinMode(alarmLed, OUTPUT); // assigns alarmLed (arduino pin 3) as an output
pinMode(vd_power_pin, OUTPUT); // powers the temperature sampling circuit

//rotary encoder
pinMode(CLK, INPUT);
pinMode(DT, INPUT);
pinMode(SW, INPUT_PULLUP);
lastStateCLK = digitalRead(CLK);
attachInterrupt(0, encoderMonitor, CHANGE);
attachInterrupt(1, encoderMonitor, CHANGE);
// initilize and start systems
Serial.begin(115200); // sets up serial comm's to 9600 baud
dht.begin(); // initialize the DHT22
lcd.begin(16, 2); // initialize the LCD's number of columns and rows

// display and serial print initilization
Serial.println("DHTxx test!");
lcd.setCursor(0, 0); lcd.print("DHTxx test!");
delay(1500);
lcd.setCursor(0, 0); lcd.print(" ");
}
void loop()
{
samples = 0; // resets samples to 0 on each pass to allow for ADC averaging
// take voltage readings from the voltage divider and turn off circuit
digitalWrite(vd_power_pin,HIGH);
for (i=0; i< samplingrate; i++) {
samples += analogRead(ntc_pin);
delay(10);
}
digitalWrite(vd_power_pin, LOW);

//calculate average thermistor output voltage on analog pin
average = 0; //resets average values on each pass
average = samples / samplingrate; //calculates samples as average of samplingrate number of sampled values
// Calculate NTC resistance
average = 1023 / average - 1; //adjust the ADC average to scale to 1024 using 0 start
average = Rref / average; //divide nominal resistance of 10000 by the new ADC average
waterTemperature = average / nominal_resistance; // (R/Ro)
waterTemperature = log(waterTemperature); // ln(R/Ro)
waterTemperature /= beta; // 1/B * ln(R/Ro)
waterTemperature += 1.0 / (nominal_temperature + 273.15); // + (1/To)
waterTemperature = 1.0 / waterTemperature; // Invert
waterTemperature -= 273.15; // convert absolute temp to C
unsigned long currentMillis = millis();

// Reading temperature and humidity takes about 250 milliseconds
if ((unsigned long)(currentMillis-previousMillis)>=interval)
{
H = dht.readHumidity(); // read humitity as %f
C = dht.readTemperature(); // read temperature as Celsius (default)
F = dht.readTemperature(true); // read temperature as Fahrenheit (if true)
previousMillis=currentMillis; // saves currentMillis count as previousMillis for next cycle check
}
// DHT22 data error check section
if (isnan(H) || isnan(C) || isnan(F))
{
Serial.println("Failed to read from DHT sensor!");
lcd.setCursor(0, 0); lcd.print(" ");
lcd.setCursor(0, 0); lcd.print(" Failed to read ");
lcd.setCursor(0, 1); lcd.print(" ");
lcd.setCursor(0, 1); lcd.print("DHT sensor Fail!");
return; // returns to start of loop
}
// calculations and conversions section
DewPoint = (C - (14.55 + 0.114 * C) * (1 - (0.01 * H)) - pow(((2.5 + 0.007 * C) * (1 - (0.01 * H))),3) - (15.9 + 0.117 * C) * pow((1 - (0.01 * H)), 14)); // more advanced humidity calcuation, dead right
//DewPoint = (C - ((100 - H) / 5)); // dewpoint calculation using Celsius value basic fough calculation
DP = (DewPoint * 1.8) + 32; // converts dewPoint calculation to fahrenheit
W = (waterTemperature * 1.8) + 32; //converts water temp to farenheit
minW = DP+1; //sets low water target minimum value
maxW = DP+5; //sets high water target max value
//rotary encoder control of the chiller relay alarmLed trigger output
if (counter == -1)
{
digitalWrite(alarmLed, HIGH);
}
else if (counter == 1)
{
digitalWrite(alarmLed, LOW);
}
else if (counter == 0)
{
// alarm output section can be used to trigger relay for power to device instead of led output. Creates a 5 degree hysteresis
if(W < minW)
{digitalWrite(alarmLed, LOW);}
else if (W > maxW)
{digitalWrite(alarmLed, HIGH);}
}

// serial monitor and debug section
Serial.print("Sample OK: "); // prints sample ok to serial monitor
Serial.print("Temp: ");Serial.print((int)C);Serial.print("*C, "); // prints celsius value to serial monitor and returns cursor
Serial.print("Temp: ");Serial.print((int)F);Serial.print("*F, "); // prints fahrenheit value to serial monitor and returns cursor
Serial.print("Hum: ");Serial.print((int)H);Serial.print("%, "); // prints humidity value to serial monitor and returns cursor
Serial.print("Dew: ");Serial.print((int)DP);Serial.print("*F, "); // prints dewpoint value to serial monitor and returns cursor to next line
Serial.print("Dew: ");Serial.print((int)DewPoint);Serial.println("*C, "); // prints dewpoint value to serial monitor and returns cursor to next line
Serial.print("H20: ");Serial.print((int)W);Serial.println("*F, "); //prints temperature of water
// LCD print section
lcd.setCursor(0, 0); lcd.print("Tmp");
lcd.setCursor(4, 0); lcd.print(F);
lcd.setCursor(6, 0); lcd.print((char)223); lcd.print("f"); // prints the degree's symbol
lcd.setCursor(8, 0); lcd.print("Dew");
lcd.setCursor(12, 0); lcd.print(DP);
lcd.setCursor(14, 0); lcd.print((char)223); lcd.print("f");
lcd.setCursor(0, 1); lcd.print("Hum ");
lcd.setCursor(4, 1); lcd.print(H);
lcd.setCursor(6, 1); lcd.print(" % ");
lcd.setCursor(8, 1); lcd.print("H20");
lcd.setCursor(12, 1); lcd.print(W);
lcd.setCursor(14, 1); lcd.print((char)223); lcd.print("f");

// The DHT22 sampling rate is .5HZ (.5 Sec), this delay sets program sample rate to match so no errors happen
delay(2000);
}
void encoderMonitor()
{
//rotary encoder processing
currentStateCLK = digitalRead(CLK);
//checks for CLK state differences and reacts to only 1 state change to avoid erroneous double counts
if (currentStateCLK != lastStateCLK && currentStateCLK == 1)
{
if (digitalRead(DT) != currentStateCLK)
{
counter --;
if(counter <-1)
{
counter = -1;
}
}
else
{
counter ++;
if (counter > 1)
{
counter = 1;
}
}
}
lastStateCLK = currentStateCLK;
// samples and handles the SW button if the rotary encoder is pressed
int btnState = digitalRead(SW);
if (btnState == LOW)
{
if (millis() - lastButtonPress > 50)
{
counter = 0;
}
delay(1);
}
}

PS: I'm a programming student critique is always welcome.
 

· Registered
Joined
·
966 Posts
Discussion Starter · #2 · (Edited)
For those wondering how the loop should go, this loop order is what I've found works best over the last 4 years of dailying my chiller on my main PCs:

Large Reservoir - The bigger it is the longer the cycle times will be. Longer on times and longer off times. I'm using a 10-gallon cooler.
\/
Pumps - Currently, I'm using MCP35X2 at the reservoir output to push the water through the chiller and onwards
\/
Chiller - the bigger the better. Cycle time spent On gets shorter the larger/more efficient.
\/
PC1 - GPU > CPU > RAM > Reservoir > D5. This is done to trap air in the reservoir and make venting it out of the loop easy. The D5 just helps the flow to PC2.
\/
PC2 - GPU > CPU > RAM.
\/
Large Reservoir

Flow like this is about 1.5GPM.

More ideal would be an in-reservoir pump that flows to the chiller only (a future project, I have the pump on hand). Then a second circuit from the Large reservoir to the PCs. This would allow for a higher flow rate through the chiller, increasing its efficiency.
 
1 - 2 of 2 Posts
Top