#include "DHTesp.h"
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR    0x27
#define LCD_COLUMNS 20
#define LCD_LINES   4

DHTesp dhtSensor;
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);

//////////////// PINS VARIABLE ////////////////
const int DHT_PIN = 15;
const int buzz_pin = 13;
const int lamp_pin = 12;
const int button1 = 2;
const int button2 = 4;
const int button3 = 5;
const int button4 = 18;

//////////////// BUTTON PARAMETERS ////////////////
bool button1_flag = LOW;
bool button2_flag = LOW;
bool button3_flag = LOW;
bool button4_flag = LOW;

//////////////// SETTINGS PARAMETERS ////////////////
int min_spec = 0;                    // Humidity minimum specification
int max_spec = 0;                    // Humidity maximum specification

unsigned long time_delay;            // Settable time (from 0 - 59 sec/min) for timer delay
unsigned long timer_interval;        // Converts time_delay to milliseconds
unsigned long Total_time_ms;         // Summing up time (time_sec + time_min + time_hr) in milliseconds
unsigned long sec_prev;              // Last millis when the time_sec was updated

short int index1 = 0;                // Displays index (0 = home, 1 = humidity specs, 2 = timer delay)
short int index2 = 0;                // Humidity settings (0 = no selection, 1 = forwards selection, 2 = reverse selection)
short int index3 = 0;                // Time settings (0 = no selection, 1 = forwards selection, 2 = reverse selection)
short int index4 = 0;                // Time unit settings (0 = forwards selection, 1 = reverse selection)

const int sec_interval = 1000;       // Interval of updating the time_sec

unsigned int time_sec = 0;           // Gets the seconds the humidity is out of specs
unsigned int time_min = 0;           // Gets the minutes the humidty is out of specs
unsigned int time_hr = 0;            // Gets the hours the humidity is out of specs
String unit_dic[2] = {"sec", "min"}; // Array to choose between strings of sec and min
String time_unit;                    // Gets the selected unit (unit_dic[0] = sec, unit_dic[1] = min)

TempAndHumidity  data;

void setup() {
  Serial.begin(115200);
  dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
  lcd.init();
  lcd.backlight();

  pinMode(buzz_pin, OUTPUT);
  pinMode(lamp_pin, OUTPUT);
  pinMode(button1, INPUT);
  pinMode(button2, INPUT);
  pinMode(button3, INPUT);
  pinMode(button4, INPUT);

  min_spec = 5;
  max_spec = 15;
  time_delay = 30;
  time_unit = unit_dic[0];
}

void loop() {
  read_sensor();
  lcd_display();
}

void read_sensor() {
  data = dhtSensor.getTempAndHumidity();
  if (time_unit == unit_dic[0]) {timer_interval = time_delay * 1000;}
  if (time_unit == unit_dic[1]) {timer_interval = time_delay * 60000;}
  if (data.humidity > max_spec) {
    digitalWrite(lamp_pin, HIGH);
    if (millis() - sec_prev >= sec_interval) {time_sec++;
      if (time_sec > 59) {time_min++; time_sec = 0;
        if (time_min > 59) {time_hr++; time_min = 0;
          if (time_hr > 59) {time_hr = 0;}
        }
      }
      sec_prev = millis();
    }
    Total_time_ms = (time_sec * 1000) + (time_min * 60 * 1000) + (time_hr * 60 * 60 * 1000);
    Serial.println(Total_time_ms);
    Serial.println(timer_interval);
    if (Total_time_ms == timer_interval) {digitalWrite(buzz_pin, HIGH);}
  }
  if (data.humidity <= max_spec) {digitalWrite(buzz_pin, LOW);time_sec = 0; time_min = 0; time_hr = 0;}
  if (data.humidity <= min_spec) {digitalWrite(lamp_pin, LOW);}
}

void lcd_display() {
  displayControl();
  buttonControl();
}

void buttonControl() {
  if (digitalRead(button1) != button1_flag) {
    if (digitalRead(button1) == HIGH) {index1++;Serial.print(index1);}
    button1_flag = digitalRead(button1);
    if (index1 > 2) {index1 = 0;}
  }
  if (digitalRead(button2) != button2_flag) {
    if (digitalRead(button2) == HIGH) {index1--;Serial.print(index1);}
    button2_flag = digitalRead(button2);
    if (index1 < 0) {index1 = 2;}
  }

  if (index1 == 0) {displayHome();}
  if (index1 == 1) {
    displayHumidity();
    if (digitalRead(button4) != button4_flag) {
      if (digitalRead(button4) == HIGH) {index2++;Serial.print(index2);}
      button4_flag = digitalRead(button4);
      if (index2 > 2) {index2 = 0;}
    }
    if (digitalRead(button3) != button3_flag) {
      if (digitalRead(button3) == HIGH) {index2--;Serial.print(index2);}
      button3_flag = digitalRead(button3);
      if (index2 < 0) {index2 = 2;}
    } 
    while (index2 == 1 || index2 == 2) {displayHumidity();
      if (digitalRead(button4) != button4_flag) {
        if (digitalRead(button4) == HIGH) {index2++;Serial.print(index2);}
        button4_flag = digitalRead(button4);
        if (index2 > 2) {index2 = 0;}
      }
      if (digitalRead(button3) != button3_flag) {
        if (digitalRead(button3) == HIGH) {index2--;Serial.print(index2);}
        button3_flag = digitalRead(button3);
        if (index2 < 0) {index2 = 2;}
      }
      if (index2 == 1) {
        // Blinking effect when selected
        lcd.setCursor(9, 1);
        read_padding(false, true, false, true, true, true, true, min_spec, " ", " ", 0, 1, 2);
        if (digitalRead(button1) == HIGH && digitalRead(button2) == LOW) {
          min_spec++;
          if (min_spec >= 100) {min_spec = 0;}
          lcd.setCursor(9, 1);
          lcd.print(min_spec);
        }
        if (digitalRead(button2) == HIGH && digitalRead(button1) == LOW) {
          min_spec--;
          if (min_spec <= 0) {min_spec = 100;}
          lcd.setCursor(9, 1);
          lcd.print(min_spec);
        }
      }
      if (index2 == 2) {
        // Blinking effect when selected
        lcd.setCursor(9, 2);
        read_padding(false, true, false, true, true, true, true, max_spec, " ", " ", 0, 1, 2);
        if (digitalRead(button1) == HIGH) {
          max_spec++;
          if (max_spec >= 100) {max_spec = 0;}
          lcd.setCursor(9, 2);
          lcd.print(max_spec);
        }
        if (digitalRead(button2) == HIGH) {
          max_spec--;
          if (max_spec <= 0) {max_spec = 100;}
          lcd.setCursor(9, 2);
          lcd.print(max_spec);
        }
      }
    }
  }
  if (index1 == 2) {displayTimer();
    if (digitalRead(button4) != button4_flag) {
      if (digitalRead(button4) == HIGH) {index3++;Serial.print(index3);}
      button4_flag = digitalRead(button4);
      if (index3 > 2) {index3 = 0;}
    }
    if (digitalRead(button3) != button3_flag) {
      if (digitalRead(button3) == HIGH) {index3--;Serial.print(index3);}
      button3_flag = digitalRead(button3);
      if (index3 < 0) {index3 = 2;}
    }
    while (index3 == 1 || index3 == 2) {displayTimer();
      if (digitalRead(button4) != button4_flag) {
        if (digitalRead(button4) == HIGH) {index3++;Serial.print(index3);}
        button4_flag = digitalRead(button4);
        if (index3 > 2) {index3 = 0;}
      }
      if (digitalRead(button3) != button3_flag) {
        if (digitalRead(button3) == HIGH) {index3--;Serial.print(index3);}
        button3_flag = digitalRead(button3);
        if (index3 < 0) {index3 = 2;}
      }
      if (index3 == 1) {
        // Blinking effect when selected
        lcd.setCursor(6, 1);
        read_padding(false, true, false, true, true, false, true, time_delay, " ", " ", 0, 1, 0);
        if (digitalRead(button1) == HIGH) {
          time_delay++;
          if (time_delay > 59) {time_delay = 0;}
          lcd.setCursor(6, 1);
          lcd.print(time_delay);
        }
        if (digitalRead(button2) == HIGH) {
          time_delay--;
          if (time_delay <= 0) {time_delay = 59;}
          lcd.setCursor(6, 1);
          lcd.print(time_delay);
        }
      }
      if (index3 == 2) {
        // Blinking effect when selected
        lcd.setCursor(6, 2);
        lcd.print("   ");
        if (digitalRead(button1) != button1_flag) {
          if (digitalRead(button1) == HIGH) {
            index4++;
            if (index4 == 2) {index4 = 0;}
            time_unit = unit_dic[index4];
            lcd.setCursor(6, 2);
            lcd.print(time_unit);
          }
          button1_flag = digitalRead(button1);
        }
        if (digitalRead(button2) != button2_flag) {
          if (digitalRead(button2) == HIGH) {
            index4--;
            if (index4 == -1) {index4 = 1;}
            time_unit = unit_dic[index4];
            lcd.setCursor(6, 2);
            lcd.print(time_unit);
          }
          button2_flag = digitalRead(button2);
        }
      }
    }
  }
}

void displayHome() {

  lcd.setCursor(0, 0);
  lcd.print("Humidity: ");
  read_padding(true, true, false, true, true, true, false, data.humidity, "%", " ", 5, 4, 3);

  lcd.setCursor(0, 1);
  lcd.print("Temperature: ");
  read_padding(true, true, true, true, true, false, false, data.temperature, String((char)223), "C", 1, 0, 0);

  lcd.setCursor(0, 2);
  lcd.print("Out Specs: ");  //hour:min:sec
  time_Template(time_hr);
  lcd.print(":");
  time_Template(time_min);
  lcd.print(":");
  time_Template(time_sec);
}

void displayHumidity() {
  lcd.setCursor(0, 0);
  lcd.print("---Humidity Specs---");

  lcd.setCursor(0, 1);
  lcd.print("Minimum: ");
  lcd.print(min_spec);
  read_padding(false, true, false, true, true, true, true, min_spec, "%", " ", 9, 8, 7);

  lcd.setCursor(0, 2);
  lcd.print("Maximum: ");
  lcd.print(max_spec);
  read_padding(false, true, false, true, true, true, true, max_spec, "%", " ", 9, 8, 7);
}

void displayTimer() {
  lcd.setCursor(0, 0);
  lcd.print("----Timer Delay-----");

  lcd.setCursor(0, 1);
  lcd.print("Time: ");
  lcd.print(time_delay);
  read_padding(false, true, false, true, true, false, true, time_delay, " ", " ", 13, 12, 0);

  lcd.setCursor(0, 2);
  lcd.print("Unit: ");
  lcd.print(time_unit);
  lcd.print("           ");
}

void displayControl() {
  lcd.setCursor(1, 3);
  lcd.print("[^]");
  lcd.setCursor(6, 3);
  lcd.print("[v]");
  lcd.setCursor(11, 3);
  lcd.print("[<]");
  lcd.setCursor(16, 3);
  lcd.print("[>]");
}

void read_padding(bool rflag, bool strflag1, bool strflag2, bool pad10, bool pad100, bool pad1000, bool convert, float reading, String string1, String string2, int pno10, int pno100, int pno1000){
  if (convert == true) {reading = int(reading);}
  if (rflag == true) {lcd.print(reading);}
  if (strflag1 == true) {lcd.print(string1);}
  if (strflag2 == true) {lcd.print(string2);}
  if (reading >= 0 && reading < 10) {
    if (pad10 == true){for (int sc1=0; sc1<pno10; sc1++){lcd.print(" ");}}
  }
  if (reading >= 10 && reading < 100) {
    if (pad100 == true){for (int sc2=0; sc2<pno100; sc2++){lcd.print(" ");}}
  }
  if (reading >= 100 && reading < 1000) {
    if (pad1000 == true){for (int sc3=0; sc3<pno1000; sc3++){lcd.print(" ");}}
  }
}

void time_Template(int x) {
  if (x >= 0 && x < 10) {lcd.print("0"); lcd.print(x);}
  if (x >= 10 && x < 100) {lcd.print(x);}
}
$abcdeabcde151015202530fghijfghij
NOCOMNCVCCGNDINLED1PWRRelay Module
NOCOMNCVCCGNDINLED1PWRRelay Module