#include <Arduino.h>
#include <Wire.h>
#include "DHTesp.h"
#include <Wifi.h>
#include "Firebase.h"
#include <FirebaseESP32.h>
#include <Firebase_ESP_Client.h>

#define WIFI_SSID "yog2_plus"// define ssid dari wifi
#define WIFI_PASSWORD "keretaapi" // define dari password wifi
#define FIREBASE_HOST "https://tugas-rancang-iot-made-default-rtdb.firebaseio.com/" // host dari firebase
#define FIREBASE_AUTH "YL9i9MfIYPOWrcPtDrK2P44Lp7kkfTVVZcAPI86K" // database secret firebase

#define DHT 15 // define PIN DHT
#define SOIL 34 // define PIN soil moisture sensor
#define RELAY 26 // define PIN relay


FirebaseData fbdo;        // 
FirebaseConfig fbConfig;  // Setup Firebase
FirebaseData fbdoStream;  //

DHTesp dht; // define DHTesp menjadi dht
int moist; // membuat int moist
int pumpsoil; // membuat integer pumpsoil
bool nValue; // membuat boolean nValue

unsigned long timers = 0; // timer untuk 

void Firebase_Init(const String& streamPath) // void fungsi untuk inisialisasi firebase
{
  FirebaseAuth fbAuth; // setup firebase authentication
  fbConfig.host = FIREBASE_HOST; // konfigurasi firebase host
  fbConfig.signer.tokens.legacy_token = FIREBASE_AUTH; //konfigurasi firebase menggunakan database secret
  Firebase.begin(&fbConfig, &fbAuth); //memulai koneksi ke firebase menggunakan informasi diatas
  Firebase.reconnectWiFi(true); // Akan mencoba reconnect jika terputus dengan firebase

fbdo.setResponseSize(1024); // Mensetting respon size dari firebase
Firebase.RTDB.setwriteSizeLimit(&fbdo, "small"); //
while (!Firebase.ready()) // While loop selama firebase ready
{
  Serial.println("Connecting to firebase...");
  delay(1000);
}
String path = "/data/pump"; // path untuk streaming
if (Firebase.RTDB.beginStream(&fbdoStream, path.c_str())) // jika dapat stream dari database
{
  Serial.println("Firebase stream on "+ path);
  Firebase.RTDB.setStreamCallback(&fbdoStream, onFirebaseStream, onFirebaseStreamTimeout); // fungsi callback setelah stream berhasil
}
  else
    Serial.println("Firebase stream failed: "+fbdoStream.errorReason()); // jika tidak dapat stream dari database
}



void onFirebaseStreamTimeout(bool timeout) // fungsi jika koneksi firebase ter timeout 
{
  Serial.printf("Firebase stream timeout: %d\n", timeout); // print message timeout
}


void onFirebaseStream(FirebaseStream data) // fungsi untuk stream firebase
{
  Serial.printf("Streamed data from %s type%s %s %s\n", 
  data.streamPath().c_str(), // path dari data yang akan di stream
  data.dataPath().c_str(), // 
  data.dataType().c_str(), //
  data.stringData().c_str()); // isi dari data
  if (data.dataType()=="bool")
  {
  String strDev = data.dataPath().substring(1);
  nValue = data.stringData().charAt(0)-'0';
  Serial.printf("Pump: %s -> %d\n", strDev.c_str(), nValue);
  }
}


void WifiConnect()
{
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // memulai setup wifi menggunakan ssid dan password yang sudah ditulis diatas
  while (WiFi.waitForConnectResult() != WL_CONNECTED) { //while loop jika koneksi gagal, akan reboot
    Serial.println("Connection Failed! Rebooting..."); // print jika ingin rebooting
    delay(3000); 
    ESP.restart(); // akan mereboot 
  }  
  Serial.print("System connected with IP address: "); // print jika sudah terconnect dengan wifi
  Serial.println(WiFi.localIP()); // print ip dari esp32
  Serial.printf("RSSI: %d\n", WiFi.RSSI()); // print RSSI dari wifi
}



void OnTimerReadDht()
{
  float humidity = dht.getHumidity(); // declare humidity
  float temperature = dht.getTemperature(); // declare temperature
  Firebase.RTDB.setFloat(&fbdo, "/data/humidity", humidity); // push data humidity ke firebase
  Firebase.RTDB.setFloat(&fbdo, "/data/temperature", temperature); // push data temperature ke firebase
  Serial.printf("Temperature: %.2f C, Humidity: %.2f %%\n", temperature, humidity); // print humidity dan temperature
}



void PumpRelay()
{
  moist = analogRead(SOIL); // mengambil data dari pin soil moisture
  moist = map(moist,0,550,0,100); // data yang diambil akan dimasukkan kedalam map
  if (moist > 400){ // jika moisture .  dari .  akan menyalakan pump
    digitalWrite(RELAY, HIGH); // untuk menyalakan relay
    Firebase.RTDB.setBool(&fbdo, "/data/pump", true); // push data pump (nyala) ke firebase 
    delay(1000); // lama pump akan menyala
    digitalWrite(RELAY, LOW); // untuk mematikan relay
    Firebase.RTDB.setBool(&fbdo, "/data/pump", false); // push data pump (mati) ke firebase
  }
}

void setup()
{
  Serial.begin(115200);
  pinMode(RELAY, OUTPUT); // define pin relay sebagai output
  dht.setup(DHT, DHTesp::DHT11); // define tipe DHT yang digunakan adalah DHT11

  WifiConnect(); // menjalankan fungsi WifiConnect

  Firebase_Init("cmd"); // menjalankan fungsi Firebase_Init
}

void loop()
{
  unsigned long currentMillis = millis(); // define currentMillis sebagai millis
  if (currentMillis - timers >= 5000) { // clock jika millis sudah sampai 5000 akan menjalankan fungsi dibawahnya
    timers = currentMillis; // reset millis 
    OnTimerReadDht(); // menjalankan fungsi OnTimerReadDht
    moist = analogRead(SOIL); // mengambil data dari pin soil moisture
    moist = map(moist,0,550,0,100); // data yang diambil akan dimasukkan kedalam map
    Firebase.RTDB.setInt(&fbdo, "/data/moist", moist); // stream data moisture ke firebase
    Serial.print("Moisture : "); 
    Serial.print(moist); // print moisture yang didapat
    Serial.println("%");
    PumpRelay(); // menjalankan fungsi PumpRelay
    }
}