/*    
Based on work by  
 RedTar4
 Bobs_DewPoint_LCD_meter_DHT22_rev7
 10/18/18 - 3/10/19
 and
 Jobit Joseph  Semicon Media Pvt Ltd (https://circuitdigest.com)

 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;
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(1000);   
  }

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);
    }
  }