#include <LiquidCrystal_I2C.h>
#include <DHT.h>

#define DHTPIN 6
#define DHTTYPE DHT22
#define TUP 4
#define TDN 3
#define OUT 5

#define I2C_ADDR    0x27
#define LCD_COLUMNS 16
#define LCD_LINES   2

LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
DHT dht(DHTPIN, DHTTYPE);

#define PERIOD 5000
#define OFFSET 15
#define REFRESHRATE 3000

void tempPrint(char *desc, float temp) {
    lcd.setCursor(0, 0);
    lcd.print(desc);
    lcd.print(temp);
    lcd.print(" \xDF");
    lcd.print("C");
}

void setup() {
  lcd.init();
  lcd.backlight();
  
  dht.begin();
  pinMode(TUP,INPUT_PULLUP);
  pinMode(TDN,INPUT_PULLUP);
  pinMode(OUT, OUTPUT);
}

void loop() {
  static unsigned long timeLapse = 0;
  static float tempSet = 38;
  static byte outputState = LOW;
  static unsigned long lastSwitchTime = 0;
  float temperature;
  float humidity;
  
  temperature = dht.readTemperature();
  humidity = dht.readHumidity();
  if (millis() > (timeLapse + REFRESHRATE)) {
    timeLapse = millis();
    lcd.clear();
    tempPrint("Temp: ", temperature);
    lcd.setCursor(0, 1);
    lcd.print("Hum: ");
    lcd.print(humidity);
    lcd.print(" %");
  }

  if (digitalRead(TUP) == LOW) {
    timeLapse = millis();
    tempSet += .1;
    lcd.clear();
    tempPrint("T. Set: ", tempSet);
    delay(100);
  }

  if (digitalRead(TDN) == LOW) {
    timeLapse = millis();
    tempSet -= .1;
    lcd.clear();
    tempPrint("T. Set: ", tempSet);
    delay(100);
  }

  byte dutyCycle = map(constrain(tempSet - temperature, -OFFSET , OFFSET), -OFFSET, OFFSET, 0, 100);
  unsigned long onTime = (dutyCycle * PERIOD) / 100;
  unsigned long offTime = PERIOD - onTime;

  unsigned long currentTime = millis();

  if (outputState == HIGH && (currentTime - lastSwitchTime >= onTime))
  {
    lastSwitchTime = currentTime;
    outputState = LOW;
  }

  if (outputState == LOW && (currentTime - lastSwitchTime >= offTime))
  {
    lastSwitchTime = currentTime;
    outputState = HIGH;
  }
  digitalWrite(OUT, outputState);
}
NOCOMNCVCCGNDINLED1PWRRelay Module