#define BLYNK_PRINT Serial
#define BLYNK_TEMPLATE_ID "TMPL6Dv30VOBm"
#define BLYNK_TEMPLATE_NAME "praktikum8"
#define BLYNK_AUTH_TOKEN "2fCNU026MPxFq4UxACrq1QDpOVoJTYJ-"

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SimpleDHT.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include "DHTesp.h"

// OLED display definitions
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// DHT sensor definitions
#define DHTPIN 26
DHTesp dhtSensor;
#define GAMMA 0.7
#define RL10 50

// Pin definitions
#define HEATER_PIN 18
#define COOLER_PIN 19
#define PIR_PIN 34
#define BUZZER_PIN 5
#define TRIG_PIN 27
#define ECHO_PIN 12
#define LED_WATER_PIN 4

char auth[] = "2fCNU026MPxFq4UxACrq1QDpOVoJTYJ-";
char ssid[] = "Wokwi-GUEST";
char pass[] = "";

BlynkTimer timer;

// Function to measure water level using HCSR04
float measureWaterLevel() {
  long duration;
  float distance;

  // Clear the trigPin
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);

  // Set the trigPin on HIGH state for 10 microseconds
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Read the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(ECHO_PIN, HIGH);

  // Calculate the distance
  distance = (duration * 0.034) / 2;

  return distance; // Distance in cm
}

void sendSensorAndControl() {
  // Read PIR sensor
  int pirStatus = digitalRead(PIR_PIN);

  // Read light sensor data
  int analogValue = analogRead(33);
  float voltage = analogValue * 5 / 4095.0;
  float resistance = 2000 * voltage / (1 - voltage / 5);
  float lux = pow(RL10 * 1e3 * pow(10, GAMMA) / resistance, (1 / GAMMA));
  float waterLevel = measureWaterLevel();
  Serial.print("Lux       : ");
  Serial.println(lux);
  Blynk.virtualWrite(V0, lux); 

  TempAndHumidity data = dhtSensor.getTempAndHumidity();
  Serial.print("Temp      : ");
  Serial.print(data.temperature, 2);
  Serial.println("°C");
  Serial.print("Humidity  : ");
  Serial.print(data.humidity, 1);
  Serial.println("%");
  // Measure water level
  Serial.print("Water Level: ");
  Serial.print(waterLevel);
  Serial.println(" cm");

  Serial.print("PIR Status: ");
  Serial.println(pirStatus == HIGH ? "Object Detected" : "Object Not Found");
  Serial.println("--------------");
  Blynk.virtualWrite(V13, data.temperature); 
  Blynk.virtualWrite(V14, data.humidity);
  Blynk.virtualWrite(V15, waterLevel);

  // Update OLED display
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Lux          : ");
  display.println(lux);
  display.print("Temperature  : ");
  display.println(data.temperature, 2);
  display.print("Humidity     : ");
  display.println(data.humidity, 1);
  display.print("Water Level  : ");
  display.println(waterLevel);
  display.setCursor(35, 40);
  display.print("PIR Status");
  display.setCursor(18, 50);
  display.println(pirStatus == HIGH ? "Object Detected" : "Object Not Found");
  
  // Control buzzer
  if (pirStatus == HIGH) {
    tone(BUZZER_PIN, 1000); // Turn on buzzer with frequency 1000Hz
    delay(500);
  } else {
    noTone(BUZZER_PIN); // Turn off buzzer
  }
  
  // Control logic
  if (data.temperature > 25) {
    // Turn on cooler
    digitalWrite(COOLER_PIN, HIGH);
    digitalWrite(HEATER_PIN, LOW);
  } else if (data.temperature < 20) {
    // Turn on heater
    digitalWrite(COOLER_PIN, LOW);
    digitalWrite(HEATER_PIN, HIGH);
  } else {
    // Turn off both cooler and heater
    digitalWrite(COOLER_PIN, LOW);
    digitalWrite(HEATER_PIN, LOW);
  }

  // Control LED for water level
  if (waterLevel <= 20) { // Assuming 10 cm is the maximum water level
    digitalWrite(LED_WATER_PIN, HIGH);
  } else {
    digitalWrite(LED_WATER_PIN, LOW);
  }

  display.display();
}

void setup() {
  Serial.begin(115200);
  dhtSensor.setup(DHTPIN, DHTesp::DHT22);
  pinMode(HEATER_PIN, OUTPUT);
  pinMode(COOLER_PIN, OUTPUT);
  pinMode(PIR_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(LED_WATER_PIN, OUTPUT);

  Blynk.begin(auth, ssid, pass);
  timer.setInterval(1000L, sendSensorAndControl); // Adjust interval as needed
  
  // Initialize OLED display
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000);
  display.clearDisplay();
}

void loop() {
  Blynk.run();
  timer.run();
  sendSensorAndControl();
}