#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <ESP32Servo.h>
#include "icons.h"
#include <Wire.h>
#include "WiFi.h"
#include <esp_wifi.h>

#include "ThingsBoard.h"
#include "DHTesp.h"

#ifdef CORE_DEBUG_LEVEL
#undef CORE_DEBUG_LEVEL
#endif

#define CURRENT_FIRMWARE_TITLE    "TEST"
#define CURRENT_FIRMWARE_VERSION  "1.0.0"

#define CORE_DEBUG_LEVEL 3
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG

// ini re-map pin SDA dan SCL
#define I2C_SDA 1
#define I2C_SCL 2

TwoWire I2CBME = TwoWire(0);

/* Locking mechanism definitions */
#define SERVO_PIN 41
#define SERVO_LOCK_POS   20
#define SERVO_UNLOCK_POS 90

Servo ServoMotor; // menamakan objek servo

int buzzpin  = 4;
int pinPIR   = 42;
int PIRState = LOW;
//int pinRelay1 = 7; //kipas angin

int kipasAngin = 7; // hidup kalau suhu tinggi
int pengkabut = 8; // hidup kalau kelembaban tinggi
int pemanas = 9; // hidup kalau suhu rendah
int exhauseFan = 10; // hidup kalau kipas angin hidup
int fanAmonia = 11; // hidup kalau amonia >= 20 ppm, exhause fan juga hidup
int fanCO2 = 12; // hidup kalau CO2 > 0,21% exhause juga hidup
int lampudalam = 13; // hidup kalau mulai agak gelap
int lampuluar = 14; // hidup kalau sudah gelap

int lampuDalamLDR = 16; // ini lampu dalam kandang
int lampuLuarLDR = 17; // ini lampu luar kandangss

int gelapkandang; // batas kandang dikatakan gelap
// sekalian memicu lampu dalam aktif
int gelapluar; // batas luar kandang dikatakan gelap.
// sekalian memicu PIR aktif dan lampu luar aktif

int RedpinLock = 18; // indikator terkunci
int GreenpinUnlock = 15; //indikator terbuka
int position = 0;
char* password = "789"; 

boolean bolehMasuk = false;
//deklarasi milis checkpoint dan set ke 0
unsigned long millis_check = 0;
unsigned long millis_skrg = 0;
//unsigned long millis_sebelumnya = 0;
//delay penyususup
unsigned long millis_cekPenyusup_sblm = 0;

LiquidCrystal_I2C lcd(0x27,20,4);

const uint8_t ROWS = 4;
const uint8_t COLS = 4;
char keys[ROWS][COLS] = {
  { '1', '2', '3', 'A' },
  { '4', '5', '6', 'B' },
  { '7', '8', '9', 'C' },
  { '*', '0', '#', 'D' }
};

uint8_t colPins[COLS] = { 26, 21, 20, 19 }; // Pins connected to C1, C2, C3, C4
uint8_t rowPins[ROWS] = { 36, 35, 34, 33 }; // Pins connected to R1, R2, R3, R4

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

#define WIFI_SSID           "Wokwi-GUEST" //deklarasi ssid wifi yang digunakan, disesuiakan dengan wifi yang digunakan
#define WIFI_PASSWORD       "" //deklarasi password wifi yang digunakan

// See https://thingsboard.io/docs/getting-started-guides/helloworld/
// to understand how to obtain an access token
//define TOKEN               "p8ycSsCzIpcJMi6ZCeS5" //SESUAIKAN token masing-masing
#define TOKEN               "QVpRARW4LR1GWJC9Rb8O" //SESUAIKAN token masing-masing
#define THINGSBOARD_SERVER  "thingsboard.cloud" //alamat server dari thingsboard, jika menggunakan server probadi alamatnya bisa disesuaikan, apabila menggunakan server yang disediakan thingsboard alamatnya adalah thingsboar.cloud

const int DHTSuhuHuma_PIN = 5; //deklarasi nomor pin dht
const int DHTAmoniaCO2_PIN = 3; //deklarasi nomor pin dht

DHTesp dhtSuhu; // deklarasi objek suhu dan kelembaban
DHTesp dhtGas; // deklarasi objek gas amonia dan metana

// Deklarasi Thingsboard client dan mengubah perintah WiFiClient menjadi espClient
WiFiClient espClient; 
// Deklarasi untuk mengubah perintah Thingsboard menjadi tb dan memasukkan perintah WiFiClient yang sudah diubah menjadi espClient ke dalam library Thingsboard
ThingsBoard tb(espClient);

int status = WL_IDLE_STATUS; //deklarasi status wifi yang dimasukkan dalam variabel status

uint8_t newMACAddress[] = {0x32, 0xAE, 0xA4, 0x07, 0x0D, 0x66};
// uint8_t newMACAddress[] = {0x32, 0xAE, 0xA4, 0x07, 0x0D, 0x44};
// uint8_t newMACAddress[] = {0x32, 0xAE, 0xA4, 0x07, 0x0D, 0x55};

void connect() //void mengoneksikan esp dengan wifi pertama kali
{
  Serial.println("Connecting to AP ...");
  // attempt to connect to WiFi network

  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {

    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected to AP");



  while (!tb.connected()) {
    Serial.print("Connecting to ThingsBoard node ...");
    // Attempt to connect (clientId, username, password)
    if ( tb.connect(THINGSBOARD_SERVER, TOKEN) ) {
      Serial.println( "[DONE]" );
    } else {
      Serial.print( "[FAILED]" );
      Serial.println( " : retrying in 5 seconds" );
      // Wait 5 seconds before retrying
      delay( 500 );
    }
  }

}

void reconnect() { //perintah recconect wifi apabila esp terputus dengan jaringan wifinya
  // Loop until we're reconnected
  status = WiFi.status();
  if ( status != WL_CONNECTED) {
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    Serial.println("Connected to AP");
  }
}

int delay_milis(  unsigned long millis_sebelumnya, unsigned long threshold ){
  millis_skrg = millis();
  if (millis_skrg - millis_sebelumnya >= threshold ){ 
//    millis_sebelumnya = millis_skrg;//
    return 1;
  }else{
    return 0;
  }
}

void showStartupMessage() {
  lcd.setCursor(4, 0);
  lcd.print("Welcome!");
  delay(500);
  lcd.setCursor(0,1);
  lcd.println("IoT4 Kelompok 4");
  lcd.setCursor(0, 2);
  String message = "Farm Controller";
  for (byte i = 0; i < message.length(); i++) {
    lcd.print(message[i]);
    delay(50);
  }
  delay(500);
}

void setup() {
  
  Wire.setPins(I2C_SDA, I2C_SCL); // re-map
  lcd.init();
  lcd.backlight();
  lcd.begin(16,2);

  Serial.begin(9600);

  pinMode(SERVO_PIN, OUTPUT);
  pinMode(buzzpin, OUTPUT);
  pinMode(kipasAngin, OUTPUT);
  pinMode(pengkabut, OUTPUT);
  pinMode(pemanas, OUTPUT);
  pinMode(exhauseFan, OUTPUT);
  pinMode(fanAmonia, OUTPUT);
  pinMode(fanCO2, OUTPUT);
  pinMode(lampudalam, OUTPUT);
  pinMode(lampuluar, OUTPUT);

  pinMode(RedpinLock, OUTPUT); // indikator terkunci
  pinMode(GreenpinUnlock, OUTPUT); //indikator terbuka

  pinMode(lampuDalamLDR, INPUT);
  pinMode(lampuLuarLDR, INPUT);
  pinMode(pinPIR, INPUT);

  gelapkandang = analogRead(lampuDalamLDR);
  gelapluar    = analogRead(lampuLuarLDR);

  digitalWrite(RedpinLock, HIGH); // indikator terkunci
  digitalWrite(GreenpinUnlock, LOW); //indikator terbuka  

  dhtSuhu.setup(DHTSuhuHuma_PIN, DHTesp::DHT22); // suhu dan kelembaban
  dhtGas.setup(DHTAmoniaCO2_PIN, DHTesp::DHT22); // amonia dan CO2
//    Serial.print("[OLD] ESP32 Board MAC Address:  ");
//   Serial.println(WiFi.macAddress());
//  esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress[0]);
//    Serial.print("[NEW] ESP32 Board MAC Address:  ");
//   Serial.println(WiFi.macAddress());

 


  init_icons(lcd);
/*
	ESP32PWM::allocateTimer(0);
	ESP32PWM::allocateTimer(1);
	ESP32PWM::allocateTimer(2);
	ESP32PWM::allocateTimer(3);
	ServoMotor.setPeriodHertz(50);    // standard 50 hz servo
*/  
  showStartupMessage();
  delay(500);
  position = 0; // posisi servo
  ServoMotor.attach(SERVO_PIN, 500, 2400);
  LockedPosition(true);
  connect() ;


  Serial.print("[OLD] ESP32 Board MAC Address:  ");
  Serial.println(WiFi.macAddress());
  
  // ESP32 Board add-on before version < 1.0.5
  //esp_wifi_set_mac(ESP_IF_WIFI_STA, &newMACAddress[0]);
  
  // ESP32 Board add-on after version > 1.0.5
 //esp_wifi_set_mac(WIFI_IF_STA, &newMACAddress[0]);
  
  Serial.print("[NEW] ESP32 Board MAC Address:  ");
  Serial.println(WiFi.macAddress());  

}

void loop() 
{
  char key = keypad.getKey();
  if (key == '*' || key == '#')
    {
      position = 0;
      LockedPosition(true);
    }
  if (key == password[position])
    {
      position ++;
    }
  if (position == 3)
    {
      LockedPosition(false);
    }
  delay(100);
  if (bolehMasuk)
  {
    lcd.clear();
    cekPenyusup();
    cekLampuLuar();
    cekLampuKandang();
    cekSuhuKelembaban();
    cekAmoniaCO2();     
  }
}

void LockedPosition(int locked)
{
  if (locked)
    {
      lcd.clear();
      lcd.setCursor(4,0);
      lcd.print("Masukkan kode"); // lode 789

      digitalWrite(RedpinLock, HIGH);
      digitalWrite(GreenpinUnlock, LOW);
      ServoMotor.write(180);
    }
  else
    {
      digitalWrite(RedpinLock, LOW);
      digitalWrite(GreenpinUnlock, HIGH);
      ServoMotor.write(90);
      delay(15);
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Kunci terbuka");        
      delay(3000);  
      bolehMasuk = true;
    }
     
}

void cekLampuKandang()
{
  int gelapkandang = analogRead(lampuDalamLDR); 
 
  if(gelapkandang <  800)
  {
    digitalWrite (lampudalam, HIGH);
    delay(1000);      
  }
  else
  {
    digitalWrite (lampudalam, LOW);
    delay(1000);
  }  
}

void cekLampuLuar()
{
  int gelapluar = digitalRead(lampuLuarLDR); 
 
  if(gelapluar <  200)
  {
    analogWrite (lampuluar, HIGH);
    delay(1000);
  }
  else
  {
    digitalWrite (lampuluar, LOW);
    delay(1000);    
  }  
}

void cekSuhuKelembaban() 
{
  float t = dhtSuhu.getTemperature();
  float h = dhtSuhu.getHumidity();
  tb.sendTelemetryFloat("Temperature", t);
  tb.sendTelemetryFloat("Humidity", h);    

  if(t > 24 )
  {
    tone(buzzpin,1500, 250);
    digitalWrite(kipasAngin, HIGH);
    digitalWrite(exhauseFan, HIGH);
    digitalWrite(pengkabut, HIGH);
    digitalWrite(pemanas, LOW);
//    delay(1000);    
  } else 
  {
    tone(buzzpin,1500, 250);    
    digitalWrite(pemanas, HIGH);
    digitalWrite(exhauseFan, HIGH);
    digitalWrite(pengkabut, LOW);    
//    delay(1000);    
  }

  if(h > 70 )
  {
    tone(buzzpin,1000,250);
    digitalWrite(kipasAngin, HIGH);
    digitalWrite(exhauseFan, HIGH);
    digitalWrite(pengkabut, HIGH);
    digitalWrite(pemanas, HIGH);
//    delay(1000);    
  } else 
  {
    tone(buzzpin,1000, 250);    
    digitalWrite(pemanas, LOW);
    digitalWrite(pengkabut, LOW);
    digitalWrite(exhauseFan, HIGH);
//    delay(1000);    
  }

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Suhu & Kelembaban");
  lcd.setCursor(0,1);
  lcd.print("Temp: " + String(t, 2) + "°C");
  lcd.setCursor(0,2);
  lcd.print("Humidity: " + String(h, 1) + "%");
  delay(1000);
}

void cekAmoniaCO2() 
{
  float a = dhtGas.getTemperature(); // amonia
  float c = dhtGas.getHumidity(); // co2
  tb.sendTelemetryFloat("CO2", c);
  tb.sendTelemetryFloat("amonia", a);

  if (a > 25)
  {
    tone(buzzpin,1750, 250);      
    digitalWrite(kipasAngin, HIGH);
    digitalWrite(exhauseFan, HIGH);
    digitalWrite(fanAmonia, HIGH);
//    delay(1000);    
  }
  else
  {
    tone(buzzpin,1750, 250);         
    digitalWrite(pemanas, LOW);
    digitalWrite(pengkabut, LOW);
    digitalWrite(exhauseFan, HIGH);
//    delay(1000);    
  }

  if (c > 50)
  {
    tone(buzzpin,1950, 250);         
    digitalWrite(kipasAngin, HIGH);
    digitalWrite(exhauseFan, HIGH);
    digitalWrite(fanCO2, HIGH);
//    delay(1000);
  }
  else
  {
    tone(buzzpin,1950, 250);         
    digitalWrite(pemanas, LOW);
    digitalWrite(pengkabut, LOW);
    digitalWrite(exhauseFan, LOW);
//    delay(1000);    
  }

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Amonia & CO2");
  lcd.setCursor(0,1);
  lcd.print("Amonia: " + String(a, 2) + "ppm");
  lcd.setCursor(0,2);
  lcd.print("CO2   : " + String(c, 1) + "%");
  delay(1000);  
}

void cekPenyusup() 
{
      // millis_check = millis();
        // jika delta 5 menit, lanjut loop, jika tidak
        // Serial.println("millis_cekPenyusup_sblm:" + String(millis_cekPenyusup_sblm) );
      if(delay_milis(millis_cekPenyusup_sblm,5000) || millis_cekPenyusup_sblm ==0.0 ){
        unsigned long delta =   millis_cekPenyusup_sblm - millis() ;
      millis_cekPenyusup_sblm = millis(); 
      
        // Serial.println("millis_cekPenyusup_sblm:" + String(millis_cekPenyusup_sblm) );
        // Serial.println("delta reach 5 minutes");
      

  int val = digitalRead(pinPIR); 
  if (val == HIGH) {           
    if (PIRState == LOW) {
      
      tone(buzzpin, 800, 150);
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Motion detected!");
      Serial.println("Motion Detected:" + String(millis_cekPenyusup_sblm) + "|" + String( millis() ) + "|" + String( delta ) );
      PIRState = HIGH;
      digitalWrite(pinPIR, HIGH);
      
//      delay(1000);      

    }
  } else {
    if (PIRState == HIGH) {
      tone(buzzpin,800,150);
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Motion ended!");
      Serial.println("Motion ended:" + String(millis_cekPenyusup_sblm) + "|" + String( millis() ) + "|" + String( delta ) );
      Serial.println("---------------" );
      PIRState = LOW;
      // delay(1000);      
    }
  }  

}

}