//===========================================================================================
// Sven Posner, Hendrik Steffen / (2020)
//-------------------------------------------------------------------------------------------
// Title: DisplayOutput
// Comment/Function: 
// Author: Sven Posner, Hendrik Steffen / sven.posner@stud.hshl.de, hendrik.steffen@stud.hshl.de
// Target System: Ardunino UNO R3
// Engineering: Arduino IDE (V 1.8.13)
// Restrictions: -
// Requirements: 
// Inputs: 
//      
//-------------------------------------------------------------------------------------------
// Change log table:
// Version     | Date       | Expert in charge          | Changes applied
//-------------|------------|---------------------------|------------------------------------
// 000.001.000 | 16-10-2020 | S. Posner                 | Erster Versuch
// 000.002.000 | 23-10-2020 | S. Posner                 | Aufräumen und Strukturieren
// 001.000.000 | 25-10-2020 | S. Posner H. Steffen      | Erste lauffähige Version
// 002.000.000 | 17-12-2020 | S. Posner H. Steffen      | PID-Regler hinzugefügt
// 002.000.001 | 08-01-2021 | S. Posner                 | Ergänzung der Kommentare
//===========================================================================================

// Benötigte Bibliotheken
#include <TouchScreen.h> //touch library
#include <LCDWIKI_GUI.h> //Core graphics library
#include <LCDWIKI_KBV.h> //Hardware-specific library
#include <TimerOne.h>             // Bibliothek für Timer Interrupts
#include <Wire.h>                 // Bibliothek für I2C Verbindung
#include <DS3231.h>               // Bibliothek für Real Time Clock
#include <MQ135.h>                // Bibliothek für C02 Sensor
#include <DHT.h>                  // Bibliothek für DHT11 Sensor

// color definition 
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
#define LIGHTGREY 0xD69A

// pins for tochscreen
#define YP A3  // must be an analog pin, use "An" notation!
#define XM A2  // must be an analog pin, use "An" notation!
#define YM 9   // can be a digital pin
#define XP 8   // can be a digital pin

// In- and Outputs
#define S1_Lamp 13
#define Q2_Bright 10
#define Fotodiode_Pin A1
#define Gassensor_DPin 11
#define DHTPIN 12
#define DHTTYPE DHT11

//calibration values for touchscreen
#define TS_MINX 906
#define TS_MAXX 116
#define TS_MINY 92
#define TS_MAXY 952
//define min and max Pressure
#define MINPRESSURE 10
#define MAXPRESSURE 1000

// define LCD display
LCDWIKI_KBV my_lcd(ILI9486,A3,A2,1,A0,0); //A4, A1 zu D0, D1 geändert
// define Tochscreen
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

// Counting Timerinterrupts once every millisecund
volatile int interruptCounter;
void onTimer(){
  interruptCounter++;
}

//Offse Werte für Temperatur und Feuchte

#define OffsetTemp 4
#define OffsetHumidity 15

//example variable for temperature
float T = 6;


// positions of dispaly objects
struct pos{
  int x;
  int y;
  int w;
  int h;
};

//Brightness settings
boolean flagAutoBrightness;
byte brightSetting;
int autoBrightSetting;
int e;

// variables for tile positioning 
byte edge = 5; //width of edges
int widthTile = my_lcd.Get_Display_Width()-edge*2; 
int widthHalfTile =(widthTile-edge)/2;
byte heightDateTime = 75; //height of the date and time tile
int height2Row = 2*edge+heightDateTime; //starting position of the 2nd row tiles
int height3Row = height2Row+widthHalfTile+edge; //starting position of the 3rd row tiles
int height4Row = height3Row+widthHalfTile+edge; //starting position of the 4th row tiles

pos DateTimeTile = {edge,edge,widthTile,heightDateTime};
pos TempTile = {edge,height2Row,widthHalfTile,widthHalfTile};
pos HumTile= {edge*2+widthHalfTile,height2Row,widthHalfTile,widthHalfTile};
pos CO2Tile = {edge,height3Row,widthHalfTile,widthHalfTile};
pos BrightTile = {edge*2+widthHalfTile,height3Row,widthHalfTile,widthHalfTile};
pos SettingsTile = {edge, height4Row,widthTile, heightDateTime};

pos ButtonDec = {40, height4Row+25, 40, 40};
pos ButtonInc = {240, height4Row+25, 40,40};
pos ButtonAutoB = {widthTile-edge*3, height4Row+edge, 14,14};


// text formats
byte cSizeSmall = 2;
byte cSizeBig= 3;


// Variables for Sensors
bool SwitchLampOn;
float Gasdichte;
bool co2;
DS3231 clock;                   // Clock initialisieren
RTCDateTime dt;                 // Instanz der Klasse RTCDateTime erstellen:  dt
DHT dht(DHTPIN, DHTTYPE);       // Instanz der Klasse DHT erstellen

float temperature;
float humidity;
int year;
byte month;
byte day;
byte hour;
byte minute;
byte second;
int helligkeit;
int helligkeitprozent;

// Parameter für Regelung
byte Kp = 5;
byte Ki= 0;
byte Kd= 0;

int hSoll = 80;

int Ta = 5; // Update Zeit in Sekunden gilt für Display und Regler


//==================================================================================
// Funktion show_text
//----------------------------------------------------------------------------------
// Schreibt einen schwarzen Text mit angegbener Groesse and bestimmte Position
//
// Requirements: LCDWIKI_GUI.h, definition of my_lcd
// Inputs: 
//      uint8_t     *txt   - Text
//      int16_t     x      - X position von oben links gesehen
//      int16_t     x      - Y position von oben links gesehen
//      uint8_t     cSize  - groesse der Buchstaben von 1-10
//==================================================================================      
void show_text(uint8_t *txt,int16_t x,int16_t y, uint8_t cSize){    
    my_lcd.Set_Text_Mode(true);
    my_lcd.Set_Text_Size(cSize);
    my_lcd.Set_Text_colour(BLACK);
    my_lcd.Set_Text_Back_colour(BLACK);
    my_lcd.Print_String(txt,x,y);
}

//Display Values for temperature, hummidity and brightness on Screen
void show_value(uint8_t *nam, float value, uint8_t *unit,int16_t x,int16_t y){
    my_lcd.Fill_Rect(x,y,widthHalfTile,widthHalfTile,LIGHTGREY);
    show_text(nam,x+edge,y+edge,cSizeSmall);
    my_lcd.Set_Text_Size(cSizeBig);
    show_text(unit,x+widthHalfTile-25,y+widthHalfTile/2-cSizeBig*5, cSizeBig);
    my_lcd.Print_Number_Float(value,2,x+20,y+widthHalfTile/2-cSizeBig*5,',',3,' ');
}

// Showing CO2 Status
void show_state(uint8_t *nam, bool state,int16_t x,int16_t y){
    my_lcd.Fill_Rect(x,y,widthHalfTile,widthHalfTile,LIGHTGREY);
    show_text(nam,x+edge,y+edge,cSizeSmall);
    my_lcd.Set_Text_Size(cSizeBig);
    if (state == 1){
    show_text("OK",x+50,y+widthHalfTile/2-cSizeBig*5, cSizeBig);
    }
    else
    {
    my_lcd.Set_Text_colour(RED);
    my_lcd.Print_String("nicht OK",x+10,y+widthHalfTile/2-cSizeBig*5);
    } 
}

// Tiefpass 1. Ordnung
// dt_f: Schrittweite der Abtastung in s
// T0_f: Filterfrequenz in s/rad! T = 1/omega = 1/(2*pi*f), f dann in Hz
// T0_f groß --> Frequenz klein --> starke Filterung/Glättung
// T0_f klein --> Frequenz groß --> geringe Filterung/Glättung
int lowpass( byte unfillt , byte dt_f, byte T0_f){
  
  static byte alt_f =0;
  float k_f= dt_f/(T0_f+dt_f);
  byte f =(1-k_f)*alt_f+k_f*unfillt;
  alt_f =f;
  return  f;
}

//==================================================================================
// Funktion pid
//----------------------------------------------------------------------------------
// PID-Regelung , hier Regelung der Helligkeit
//
// Requirements: Kp, Ki, Kd  (hier  Kp=5, Ki=0, Kd=0)
// Inputs: 
//      int16_t     e   - Regeldifferenbz der Helligkeit
//==================================================================================      
uint8_t pid (int16_t e){
  static int16_t esum = 0;      //Aufsummierte Abweichung für I-Anteil
  static int16_t ediff;         //Diffenz der Abweichung für D-Anteil
  static int16_t ealt =0;
  int16_t y;

  //Aufintegrieren der Abweichung
  esum = esum + e;
  if (esum > 255) esum = 255;

  // Berecnhung des Differentials mit Filterung
  ediff =e-ealt;
  ediff = lowpass (ediff, 1,1);
  
  y = Kp * e + Ki * Ta * esum + Kd * ediff/Ta;
  if (y > 255) y = 255;
  else if (y<0) y =0;
  ealt = e;
  return y;
}
void update_time(){

  byte startPosTime = widthHalfTile-45;
  byte startPosDate = widthHalfTile-45;
  my_lcd.Set_Text_Size(cSizeBig);
  my_lcd.Print_Number_Int(hour,startPosTime,edge*4,2,' ',10);
  my_lcd.Print_String(":",startPosTime+40,edge*4);
  my_lcd.Print_Number_Int(minute,startPosTime+60,edge*4,2,' ',10);

  my_lcd.Set_Text_Size(cSizeSmall);
  my_lcd.Print_Number_Int(day,startPosDate,edge*2+45,2,' ',10);
  my_lcd.Print_String(".",startPosDate+20,edge*2+45);
  my_lcd.Print_Number_Int(month,startPosDate+30,edge*2+45,2,' ',10);
  my_lcd.Print_String(".",startPosDate+50,edge*2+45);
  my_lcd.Print_Number_Int(year,startPosDate+60,edge*2+45,2,' ',10);
}

//Check whether Button is pressed or not
boolean is_pressed(int16_t x1,int16_t y1,int16_t x2,int16_t y2,int16_t px,int16_t py)
{
    if((px > x1 && px < x2) && (py > y1 && py < y2))
    {
        return true;  
    } 
    else
    {
        return false;  
    }
 }
 
 
void setup() {
  // configure Timer 1
  Timer1.initialize(1000); //initialize Timer 1, runs given time in us
  Timer1.attachInterrupt(onTimer);

   clock.begin();
   dht.begin();
   pinMode(Gassensor_DPin, INPUT);
   pinMode(DHTPIN, INPUT);
  
  // initialise display
  my_lcd.Init_LCD();
  
  //rotate screen (0-0 degree,1-90 degree,2-180 degree,3-270 degree)
  my_lcd.Set_Rotation(2);
  // clear screen
  my_lcd.Fill_Screen(WHITE);
  
  // draw rectangles 
  // Date Time Tile
  my_lcd.Fill_Rect(DateTimeTile.x,DateTimeTile.y,DateTimeTile.w,DateTimeTile.h, LIGHTGREY);
  // Temperature Tile
  show_value("T", 0, "C",TempTile.x,TempTile.y);
  // Humidity Tile
  show_value("H", 0, "%",HumTile.x,HumTile.y);
  // CO2 Tile
  show_state("C", 0, CO2Tile.x,CO2Tile.y);
  // brightness Tile
  show_value("B", 0, "%",BrightTile.x,BrightTile.y);
  
  // Settings Tile
  update_Buttons();
}

void show_Button(uint8_t *sign, int x, int y, int w, int h){
  my_lcd.Set_Draw_color(BLACK);
  my_lcd.Fill_Rect(x,y,w,h,WHITE);
  my_lcd.Draw_Rectangle(x,y,x+w,y+h);
  show_text(sign,x+w*0.3,y+h*0.3,cSizeBig);
}
//==================================================================================
// Funktion update_displaView
//----------------------------------------------------------------------------------
// Anzeige auf dem Diplay aktualisieren
//
// Requirements: update_time(),
//               show_value(uint8_t *nam, float value, uint8_t *unit,int16_t x,int16_t y)
// Inputs: 
//      
//==================================================================================
void update_DisplayView(){
  my_lcd.Fill_Rect(DateTimeTile.x,DateTimeTile.y,DateTimeTile.w,DateTimeTile.h, LIGHTGREY);
  update_time();
  show_value("Temperatur: ", temperature, "C",TempTile.x,TempTile.y);
  // Humidity Tile
  show_value("Feuchte: ",humidity, "%",HumTile.x,HumTile.y);
  // CO2 Tile
  show_state("C02-Gehalt:", co2,CO2Tile.x,CO2Tile.y);
  // brightness Tile
  show_value("Helligkeit:", helligkeitprozent, "%",BrightTile.x,BrightTile.y); 
}

void update_Buttons(){
  my_lcd.Fill_Rect(edge, height4Row,widthTile, heightDateTime, LIGHTGREY);
  show_text("Auto Helligkeit", 2*edge, height4Row+edge,cSizeSmall);

  if(flagAutoBrightness){
  my_lcd.Set_Draw_color(BLACK);
  my_lcd.Fill_Rect(ButtonAutoB.x,ButtonAutoB.y,ButtonAutoB.w,ButtonAutoB.h,WHITE);
  my_lcd.Draw_Rectangle(ButtonAutoB.x,ButtonAutoB.y,ButtonAutoB.x+ButtonAutoB.w,ButtonAutoB.y+ButtonAutoB.h);
  show_text(".",ButtonAutoB.x-2,ButtonAutoB.y-10,cSizeBig);
  }
  else{
  show_Button("",ButtonAutoB.x,ButtonAutoB.y,ButtonAutoB.w,ButtonAutoB.h);
  show_Button("-",ButtonDec.x,ButtonDec.y,ButtonDec.w,ButtonDec.h);
  my_lcd.Print_Number_Int(brightSetting,150,ButtonDec.y+11,2,' ',10);
  show_Button("+",ButtonInc.x,ButtonInc.y,ButtonInc.w,ButtonInc.h);
  }
}




void loop() {
  if (T>120) T=0;
  
  // update interrupts
  if (interruptCounter >Ta*1000){
    helligkeit  = analogRead(Fotodiode_Pin);
    helligkeitprozent= helligkeit*0.133;
    update_DisplayView();
    e = hSoll-helligkeitprozent;
    autoBrightSetting = pid(e);
    interruptCounter = 0;
    
  }
  // update Sensor values
  SwitchLampOn = digitalRead(S1_Lamp);
  dt = clock.getDateTime();       // Variablen mit Messwerten füllen
  year=dt.year;
  month=dt.month;
  day= dt.day;
  hour= dt.hour;
  minute= dt.minute;
  second= dt.second;
  
  co2=digitalRead(Gassensor_DPin);          // false= schlecht,true=gut

  helligkeit  = analogRead(Fotodiode_Pin);
  helligkeitprozent= helligkeit*0.133;

  humidity= dht.readHumidity()+OffsetHumidity;
  temperature= dht.readTemperature()-OffsetTemp;
  
  //--------------------------------------------------------------------
  //update touch switches

  TSPoint p = ts.getPoint();


  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
  if (p.z > MINPRESSURE && p.z < MAXPRESSURE)
  {
    p.x = my_lcd.Get_Display_Width()-map(p.x, TS_MINX, TS_MAXX, my_lcd.Get_Display_Width(), 0);
    p.y = my_lcd.Get_Display_Height()-map(p.y, TS_MINY, TS_MAXY, my_lcd.Get_Display_Height(), 0);
     if(is_pressed(ButtonAutoB.x,ButtonAutoB.y,ButtonAutoB.x+ButtonAutoB.w,ButtonAutoB.y+ButtonAutoB.h,p.x,p.y))
       {
         if(flagAutoBrightness)
         {           
           flagAutoBrightness = false;
           update_Buttons(); 
         }
         else
         {
           flagAutoBrightness = true;
           update_Buttons();
         }
        
       }
       if(SwitchLampOn&!flagAutoBrightness){
         if(is_pressed(ButtonDec.x,ButtonDec.y,ButtonDec.x+ButtonDec.w,ButtonDec.y+ButtonDec.w,p.x,p.y)&brightSetting >0){
            brightSetting--;
            update_Buttons();
         }
          if(is_pressed(ButtonInc.x,ButtonInc.y,ButtonInc.x+ButtonInc.w,ButtonInc.y+ButtonInc.w,p.x,p.y)&brightSetting <10){
            brightSetting++;
            update_Buttons();
         }
       }
  }
  //--------------------------------------------------------------------
  
  if (SwitchLampOn &!flagAutoBrightness){ 
  analogWrite(Q2_Bright, sqrt(255*brightSetting/10.0)*16);
  }
  else if(SwitchLampOn &flagAutoBrightness){
    unsigned int wert=sqrt(autoBrightSetting)*16;
    analogWrite(Q2_Bright, wert);
  }
  else analogWrite(Q2_Bright, 0);

 
  
}
