// Including libraries
#include <SD.h>
#include <OneWire.h>
#include <Wire.h>
#include <DallasTemperature.h> // Waterproof temperature sensor library
#include <SPI.h>
#include "RTClib.h"
RTC_DS3231 rtc;
// ======= Variables for DHT sensor =============
#include "DHTStable.h"
DHTStable DHT;
#define DHT22_PIN 2
// ======= Adjustable Values ===========
// double setPointTemp = 24; // Set temperature value PID needs to achieve
double dewPoint = 0; // Variable to hold calculated dew point value
const int dewConstant = 1; // Dew Point constant
int humidityThreshold = 60; // Humidity value to start PID
int voltageThreshold = 12; // Voltage Threshold
bool lowWaterLogic = LOW; // logic for when water is LOW
bool relayLogic = LOW; // Relay working logic for led and PUMP
// PID Constants
int kP = 100; // Proportional Constant
int kI = 5; // Integral constant
int kD = 1; // Derivative constant
int minOutputLimit = 0; // PID Minimum output value (PWM range is from 0-255)
int maxOutputLimit = 255; // PID Maximum output value (PWM range is from 0-255)
// ======================================
// ========= DEBUGGING ============
// ======= Variable for Dallas Temeperature =========
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS_in 3
#define ONE_WIRE_BUS_out 4
#define ONE_WIRE_BUS_tec 5
const byte waterSensorPin = 6; // Water sensor pin
const byte lowVoltageLed = 7; // Low voltage led pin
const byte waterPump = 8; // water pump pin
const byte tecPin = 9; // PWM pin for TEC
const int chipSelect = 10; // SD Card Chip Select Pin
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWireIn(ONE_WIRE_BUS_in);
OneWire oneWireOut(ONE_WIRE_BUS_out);
OneWire oneWireTec(ONE_WIRE_BUS_tec);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensorIn(&oneWireIn);
DallasTemperature sensorOut(&oneWireOut);
DallasTemperature sensorTec(&oneWireTec);
// ------- Voltage Sensor --------------
const byte currentSensorPin = A1;
const int voltageSensorPin = A0; // Analog pin where the voltage divider is connected
const float R1 = 30000.0; // Resistance connected to the input voltage (30K Ohms)
const float R2 = 7500.0; // Resistance connected to the ground (7.5K Ohms)
const float VDRSensorVoltage = 4.82; // Voltage provided to Voltage sensor
float originalVoltage = 0.0; // Variable to store voltage reading
// ------- Current Sensor -------------
float zeroLoad = 508.0; // Zero Load ADC value use find zero Load code for this
float AcsValue = 0.0, Samples = 0.0, AvgAcs = 0.0, AcsValueF = 0.0; // variables to hold readings for current sensor
// ------- Variables Declaration -------
float outSideTemperature = 0.0; // variable to hold outside temperature
float outSideHumidity = 0.0; // variable to hold outside humidity
float waterTempIn = 0.0; // variable to hold input water temperature
double waterTempOut = 0.0; // variable to hold output water temperature
float waterTempTec = 0.0; // variable to hold TEC temperature
int pidOutput = 0; // PID Calculated output PWM value
// ====== Error Flags ========
bool humidityReadError = false; // flag for reading humidity error
bool outletTempReadError = false; // flag for reading outlet temperautre error
bool lowWaterFlag = false; // flag for no water warning
// ------- PID Control -----------------
#include <PIDController.h>
// Objects
PIDController pid; // Create an instance of the PID controller class, called "pid"
bool pidStart = false; // flag to be set when pid needs to start after the humidity cross threshold
unsigned long pidCheckTime = 50; // PID loop check time in milliseconds (After this time pid will recalculate the output value)
unsigned long logTime = 15000; // Log time after how many seconds it needs to save log in milliseconds
unsigned long pidLastCheck; // variable to hold last pid check time
unsigned long recheckDewValueTime = 60000; // Time to recheck Dewpoint condition for PID calculation
unsigned long prevRecheckDewValueTime = millis() - recheckDewValueTime; // Time to recheck Dewpoint condition for PID calculation
unsigned long lastLogTime = millis(); // Variable to hold last log time
unsigned long lastSensorCheckTime = millis(); // variable to hold last sensor check time
void setup() {
Serial.begin(115200);
Serial.println("Starting");
pinMode(waterSensorPin, INPUT_PULLUP); // Set water sensor pin as input
pinMode(lowVoltageLed, OUTPUT); // Set pin mode as output
pinMode(waterPump, OUTPUT); // Set pin mode as output
pinMode(tecPin, OUTPUT); // Set TEC PWM pin as output
delay(100);
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed");
// don't do anything more:
for (byte j = 0; j < 10; j++) { // Blink low voltage led in case of sd card not working
digitalWrite(lowVoltageLed, !digitalRead(lowVoltageLed));
delay(500); // wait for 1 second
}
// while (1)
// ;
}
// Serial.println("card initialized.");
digitalWrite(tecPin, LOW); // Initially turn off TEC
digitalWrite(lowVoltageLed, !relayLogic); // Initially turn off low water led
digitalWrite(waterPump, !relayLogic); // Initially turn off pump
Wire.begin();
delay(100);
if (!rtc.begin()) {
Serial.println("RTC Error");
while (1) delay(10);
}
delay(10);
sensorIn.begin(); // Start waterproof temperature sensor on Inlet water
sensorOut.begin(); // Start waterproof temperature sensor on outlet water
sensorTec.begin(); // Start waterproof temperature sensor on TEC
pid.begin(); // initialize the PID instance
pid.tune(kP, kI, kD); // Tune the PID, arguments: kP, kI, kD
pid.limit(minOutputLimit, maxOutputLimit); // Limit the PID output between 0 and 255, this is important to get rid of integral windup!
}
void loop() {
// ====== Check Status of Sensors =============
if ((millis() - lastLogTime) > logTime) {
// Serial.println("Check Sensors");
readDht(); // Read DHT sensor for outside temperature and humidity
calculateDewPoint(); // Calculate DEW Point
readWaterTemp(); // Read TEC and water Temperatures
readVoltage(); // Read voltage value by calling it's function
readCurrent(); // Read current value by calling it's function
saveLogData(); // Call save log function to save log
lastLogTime = millis(); // note current log time
}
if ((digitalRead(waterSensorPin) == lowWaterLogic) && (lowWaterFlag == false)) {
// In case of low water turn off TEC, turn off water pump, turn on led
// Serial.println("LOW Water");
pidStart = false; // Set pid start flag to false
digitalWrite(tecPin, LOW); // turn off TEC
digitalWrite(waterPump, !relayLogic); // turn off pump
digitalWrite(lowVoltageLed, relayLogic); // Turn on low water led
lowWaterFlag = true; // Set the low water flag
delay(200); // wait for 200 milliseconds
} else if ((digitalRead(waterSensorPin) != lowWaterLogic) && (lowWaterFlag == true)) {
// Serial.println("Water get Normal");
lowWaterFlag = false; // If previously we have no water and now water is present then reset flag
digitalWrite(lowVoltageLed, !relayLogic); // Turn off low water led
delay(200); // wait for 200 milliseconds
}
if ((originalVoltage > voltageThreshold) && (lowWaterFlag == false) && (waterTempTec > dewPoint) && ((dewPoint - dewConstant) > 0) && (outSideHumidity > humidityThreshold) && (pidStart == false)) {
// If outside humidity is greater then specified range start pid loop
pidStart = true; // Set the pid flag to true
digitalWrite(waterPump, relayLogic); // turn on pump
pid.setpoint(dewPoint - dewConstant); // The "goal" the PID controller tries to "reach"
// } else if (((originalVoltage < voltageThreshold) || (lowWaterFlag == true) || (waterTempTec < dewPoint) || ((dewPoint - dewConstant) < 0) || (outSideHumidity < humidityThreshold)) && (pidStart == true)) {
} else if (pidStart == true) {
// If any condition get's wrong and PID is working stop TEC and turn off PUMP
pidStart = false; // Set the pid flag to false
digitalWrite(tecPin, LOW); // turn off TEC
digitalWrite(waterPump, !relayLogic); // turn off pump
}
if ((pidStart) && ((millis() - prevRecheckDewValueTime) > recheckDewValueTime)) {
prevRecheckDewValueTime = millis();
pid.setpoint(dewPoint - dewConstant); // The "goal" the PID controller tries to "reach"
}
if (pidStart && ((millis() - pidLastCheck) > pidCheckTime)) {
readWaterTemp();
pidOutput = pid.compute(waterTempOut); // Let the PID compute the value, returns the optimal output
Serial.print("PID Output:");
Serial.println(pidOutput);
analogWrite(tecPin, pidOutput); // Write the output to the output pin
pidLastCheck = millis(); // Note curren time
}
}
void calculateDewPoint() {
// Td = T - ((100 - RH)/5.);
dewPoint = outSideTemperature - ((100 - outSideHumidity) / 5.);
}
void saveLogData() {
// Serial.println("Save log to SD card");
DateTime now = rtc.now();
char buf2[] = "YYMMDD-hh:mm:ss"; // Time format to save log on sd card
// ------- Create the string with data to be stored on SD card ------------
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("datalog.txt", FILE_WRITE);
// The log is saved in this order
//"Date_Time,Out_Temp,Out_Humidity,Water_In_TEMP,Water_OUT_TEMP,TEC_TEMP,DELTA_TEMP,DEW_POINT,SET_POINT,Voltage,Current,Power,Water_Switch,PID_OUTPUT"); // Store data and time in format "YYMMDD-hh:mm:ss"
// if the file is available, write to it:
if (dataFile) {
dataFile.print(now.toString(buf2)); // Store data and time in format "YYMMDD-hh:mm:ss"
dataFile.print(",");
dataFile.print(outSideTemperature); // Store outside temperature
dataFile.print(" *C,");
dataFile.print(outSideHumidity); // Store outside humidity
dataFile.print(" %,");
dataFile.print(waterTempIn); // Store water In temperature
dataFile.print(" *C,");
dataFile.print(waterTempOut); // Store water Out temperature
dataFile.print(" *C,");
dataFile.print(waterTempTec); // Store TEC temperature
dataFile.print(" *C,");
dataFile.print(waterTempIn - waterTempOut); // Store delta temperature
dataFile.print(" *C,");
dataFile.print(dewPoint); // Store dew point
dataFile.print(" *C,");
dataFile.print(dewPoint - dewConstant); // Store set point
dataFile.print(" *C,");
dataFile.print(originalVoltage); // Store Voltage
dataFile.print(" V,");
dataFile.print(AcsValueF); // Store current
dataFile.print(" A,");
dataFile.print(originalVoltage * AcsValueF); // Store power
if (pidStart) { // if PID is working than save it's output as well
dataFile.print(" W,");
dataFile.print(digitalRead(waterSensorPin)); // Store float switch status
dataFile.print(",PID ON,"); // Save PID output as well
dataFile.println(pidOutput); // Store float switch status
} else {
dataFile.print(" W,");
dataFile.println(digitalRead(waterSensorPin)); // Store float switch status
}
dataFile.close();
// print to the serial port too:
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.txt");
}
}
void readVoltage() {
int sensorValue = analogRead(voltageSensorPin);
// Convert the sensor value to voltage
float voltageDivider = sensorValue * (VDRSensorVoltage / 1023.0); // 5.0V is the Arduino reference voltage
// Calculate the original voltage using the voltage divider formula
originalVoltage = voltageDivider * (R1 + R2) / R2;
// Print the original voltage to the serial monitor
Serial.print("Original Voltage: ");
Serial.print(originalVoltage);
Serial.println(" V");
}
void readCurrent() {
Samples = 0;
for (int x = 0; x < 150; x++) { //Get 150 samples
AcsValue = analogRead(currentSensorPin); //Read current sensor values
Samples = Samples + AcsValue; //Add samples together
delay(5); // let ADC settle before next sample 3ms
}
AvgAcs = Samples / 150.0; //Taking Average of Samples
//((AvgAcs * (5.0 / 1024.0)) is converitng the read voltage in 0-5 volts
//2.5 is offset(I assumed that arduino is working on 5v so the viout at no current comes
//out to be 2.5 which is out offset. If your arduino is working on different voltage than
//you must change the offset according to the input voltage)
//0.066(66mV) is rise in output voltage when 1A current flows at input for 30A sensor
//0.1(100mV) is rise in output voltage when 1A current flows at input for 20A sensor
//0.185(185mV) is rise in output voltage when 1A current flows at input for 10A sensor
// Serial.print("Current ADC:");
// Serial.print(AvgAcs);
// Serial.print(" ");
AcsValueF = (abs(zeroLoad - AvgAcs) * (5.0 / 1024.0)) / 0.066;
Serial.print("Current in Ampere:");
Serial.println(AcsValueF); //Print the read current on Serial monitor
}
void readDht() { // this function will read temp and humidity from dht sensor
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
// Serial.println("Read DHT Sensor");
int chk = DHT.read22(DHT22_PIN);
switch (chk) {
case DHTLIB_OK:
Serial.print("OK,\t");
break;
default:
Serial.print("Unknown error,\t");
break;
}
// Serial.print(" DONE Reading DHT");
// DISPLAY DATA
outSideHumidity = DHT.getHumidity();
outSideTemperature = DHT.getTemperature();
Serial.print(" | Out Temp:"); // Print outside temperature and humidity on serial monitor if debug was enabeled
Serial.print(outSideTemperature);
Serial.print(" | Out Humidity:");
Serial.println(outSideHumidity);
}
void readWaterTemp() { // This function will read water temperautres
sensorIn.requestTemperatures(); // Request temperature
delay(5);
sensorOut.requestTemperatures(); // Request temperature
delay(5);
sensorTec.requestTemperatures(); // Request temperature
waterTempIn = sensorIn.getTempCByIndex(0); // Get inlet water temperature
waterTempOut = sensorOut.getTempCByIndex(0); // Get outlet water temperature
waterTempTec = sensorTec.getTempCByIndex(0); // Get TEC temperature
if (waterTempIn != DEVICE_DISCONNECTED_C) {
Serial.print("Water inlet Temp:");
Serial.print(waterTempIn);
} else {
Serial.print("Inlet Temp Error!");
}
if (waterTempOut != DEVICE_DISCONNECTED_C) {
Serial.print(" | Water outlet Temp:");
Serial.print(waterTempOut);
} else {
Serial.print(" | outlet Temp Error!");
outletTempReadError = true; // set error flag in case of output read temperature error
}
if (waterTempTec != DEVICE_DISCONNECTED_C) {
Serial.print("| TEC Temp:");
Serial.println(waterTempTec);
} else {
Serial.println(" | TEC Temp Error!");
}
}