// Схема подключения и симуляция https://wokwi.com/arduino/projects/https://wokwi.com/arduino/projects/322863857963893330

#include <EEPROM.h>

#include <LiquidCrystal_I2C.h>        // библиотека для дисплея
LiquidCrystal_I2C LCD(0x27, 16, 2);   // присваиваем имя дисплею

#include <PID_v2.h>
#define RELAY_PIN 12

#include "DHT.h"
#define DHTPIN 5
//#define DHTTYPE DHT11
//#define DHTTYPE DHT21
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

#define LED 13 
int LEDStatus = LOW;

#define ENCODER_CLK 2                 // пин 2 подключаем к CLK энкодера
#define ENCODER_DT  3                 // пин 3 подключаем к DT энкодера
#define ENCODER_SW  4                 // пин 11 подключаем к SW энкодера

#define T_MAX 40.0                    // Установка: максимальная температура
#define T_MIN 37.0                    // Установка: минимальная температура
#define H_MAX 70.0                    // Установка: максимальная влажность
#define H_MIN 40.0                    // Установка: минимальная влажность
#define WindowSize 500

unsigned long windowStartTime;
int lastClk = HIGH;

double Input, Output, T_SETUP = T_MIN, H_SETUP = H_MIN;
double Kp=2, Ki=5, Kd=1.5;
PID T_PID(&Input, &Output, &T_SETUP, Kp, Ki, Kd, DIRECT);

void setup() {
  dht.begin();

  pinMode(LED, OUTPUT);
  pinMode(ENCODER_CLK, INPUT);
  pinMode(ENCODER_DT, INPUT);
  pinMode(ENCODER_SW, INPUT);
  pinMode(RELAY_PIN, OUTPUT);

  LCD.init();                         // инициализация LCD дисплея и подсветки
  LCD.backlight();

  T_PID.SetOutputLimits(0, WindowSize);
  T_PID.SetMode(AUTOMATIC);

  if (EEPROM.read(0) == 255 && EEPROM.read(3) == 255) {
    EEPROM.update(0, 0);
    EEPROM.update(1, 0);
    EEPROM.update(2, 20);
    EEPROM.update(3, 66);
  }
  EEPROM.get(0, T_SETUP);
  if (EEPROM.read(4) == 255 && EEPROM.read(7) == 255) {
    EEPROM.update(4, 0);
    EEPROM.update(5, 0);
    EEPROM.update(6, 32);
    EEPROM.update(7, 66);
  }
  EEPROM.get(4, H_SETUP);
}

void loop() {
  float T_CURRENT = dht.readTemperature();
  float H_CURRENT = dht.readHumidity();
  Input = T_CURRENT;
  
  T_PID.Compute();

  if (digitalRead(ENCODER_SW) == LOW) {
    int newClk = digitalRead(ENCODER_CLK);
    if (newClk != lastClk) {
      lastClk = newClk;
      int dtValue = digitalRead(ENCODER_DT);
      if (newClk == LOW && dtValue == HIGH) {
        T_SETUP += .1;
        if (T_SETUP > T_MAX) T_SETUP = T_MAX;
      }
      if (newClk == LOW && dtValue == LOW) {
        T_SETUP -= .1;
        if (T_SETUP < T_MIN) T_SETUP = T_MIN;
      }
      EEPROM.put(0, T_SETUP);
    }
    LCD.clear();
    LCD.setCursor(0, 0);
    LCD.print(T_SETUP, 1);
    LCD.print("\xDF  ");
    LCD.setCursor(6, 0);
    LCD.print(H_SETUP, 0);
    LCD.print("%  ");
  } else {
    LCD.setCursor(0, 0);
    LCD.print(T_CURRENT, 1);
    LCD.print("\xDF  ");
    LCD.setCursor(6, 0);
    LCD.print(H_CURRENT, 0);
    LCD.print("%  ");

    LCD.setCursor(0, 1);
    LCD.print(Output, 0);
    LCD.print("mS  ");
    LCD.setCursor(6, 1);
    LCD.print(map(Output, 0, WindowSize, 0, 100));
    LCD.print("%  ");
  }

  if (millis() - windowStartTime >= WindowSize) {
    windowStartTime += WindowSize;
    if (windowStartTime > millis()) windowStartTime = 0;
  }
  if (Output < millis() - windowStartTime) {
    digitalWrite(RELAY_PIN, LOW);
    LEDStatus = LOW;
  } else {
    digitalWrite(RELAY_PIN, HIGH);
    LEDStatus = !LEDStatus;
  }
  digitalWrite(LED, LEDStatus);
}