#define BLYNK_TEMPLATE_ID "TMPL3ZXjLWzis"
#define BLYNK_TEMPLATE_NAME "Basic EV Monitoring and Operation Testing System"
#define BLYNK_AUTH_TOKEN "X5NJPhLp7hT9g6tl3hVQvh1VfU54S6ni"
#define BLYNK_PRINT Serial

#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <DHT_U.h>
#include <Adafruit_MPU6050.h>
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <RTClib.h>
#include <Adafruit_Sensor.h>
#include <Arduino.h>

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

// Define sensor pins
const int voltagePin = 34; // Analog input pin for voltage sensor
const int currentPin = 35; // Analog input pin for current sensor
const int tempPin = 27; // DHT22 data pin
const int pirPin = 33; // PIR motion sensor pin
const int buttonPin = 14; // Push button pin
const int buzzerPin = 26; // Buzzer pin
const int ldrPin = 36; // LDR pin
const int trigPin = 5; // Ultrasonic sensor trig pin
const int echoPin = 18; // Ultrasonic sensor echo pin

// Variables to hold the button state and buzzer state
int buttonState = 0;
bool buzzerState = false;

DHT dht(tempPin, DHT22);
RTC_DS1307 rtc;
Adafruit_MPU6050 mpu;
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
BlynkTimer timer;

// Function to check the button state
void checkButton() {
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) { // If button is pressed
    if (!buzzerState) {
      buzzerState = true;
      digitalWrite(buzzerPin, HIGH); // Activate buzzer
      tone(buzzerPin, 500);
      delay(1000);
      tone(buzzerPin, 800);
      delay(1000);
      Serial.println("Emergency! Buzzer Activated");
      Blynk.virtualWrite(V15, HIGH); // Update Blynk switch and LED state
      Blynk.virtualWrite(V15, "Emergency! Buzzer Activated!"); // Update Label Widget
    }
  } else {
    if (buzzerState) {
      buzzerState = false;
      digitalWrite(buzzerPin, LOW); // Deactivate buzzer
      Serial.println("Buzzer Deactivated");
      noTone(buzzerPin);
      Blynk.virtualWrite(V15, LOW); // Update Blynk switch and LED state
      Blynk.virtualWrite(V15, "Buzzer Deactivated"); // Update Label Widget
    }
  }
}

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);
  Blynk.begin(auth, ssid, pass);
  dht.begin();
  Wire.begin();
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (true);
  }
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  
   // For MPU6050
  if (!mpu.begin()) {
    Serial.println("MPU6050 connection failed");
    while (true);
  }
  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    while (true);
  }
  display.display();
  delay(2000);
  display.clearDisplay();

  pinMode(voltagePin, INPUT);
  pinMode(currentPin, INPUT);
  pinMode(pirPin, INPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(ldrPin, INPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(buzzerPin, OUTPUT);
// Set the initial state of the buzzer
  digitalWrite(buzzerPin, LOW);

  // Setup a timer to check the button state
  timer.setInterval(1000L, checkButton); // Check button status every second
}

void loop() {
  Blynk.run();
  timer.run();
  
  DateTime now = rtc.now();
  Serial.print("Current date: ");
  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();
  // Read voltage sensor
  int voltageValue = analogRead(voltagePin);
  float voltage = (voltageValue / 4095.0) * 3.3 * 5;
  Serial.print("Voltage: ");
  Serial.println(voltage);
  Blynk.virtualWrite(V1, voltageValue);


  // Read current sensor
  int currentValue = analogRead(currentPin);
  float current = (currentValue / 4095.0) * 3.3;
  Serial.print("Current: ");
  Serial.println(currentValue);
  Blynk.virtualWrite(V2, currentValue);

  // Read temperature and humidity
  float temp = dht.readTemperature();
  Serial.print("Temperature: ");
  Serial.println(temp);
  float humidity = dht.readHumidity();
  Blynk.virtualWrite(V3, temp);
  //Blynk.virtualWrite(V4, humidity);
  Serial.print("Humidity: ");
  Serial.println(humidity);

  // Read PIR motion sensor for smoke detection
  int pirValue = digitalRead(pirPin);
  if (pirValue == HIGH) {
    Blynk.virtualWrite(V5, "Fire alert! Smoke detected!");
   // Blynk.notify("Fire alert! Smoke detected!");
  }
  Serial.print("Smoke value: ");
  Serial.println(pirValue);
  Blynk.virtualWrite(V5, pirValue);

  // Read light level
  int lightLevel = analogRead(ldrPin);
  Serial.print("Light level: ");
  Serial.println(lightLevel);
  // MPU6050 sensor readings
  sensors_event_t a, g, tempSensor;
  mpu.getEvent(&a, &g, &tempSensor);
  Serial.print("Rotation X: ");
  Serial.print(g.gyro.x);
  Serial.print(", Y: ");
  Serial.print(g.gyro.y);
  Serial.print(", Z: ");
  Serial.print(g.gyro.z);
  Serial.println(" rad/s");
  Serial.print("Acceleration: ");
  Serial.print("ax: ");
  Serial.print(a.acceleration.x);
  Serial.print(", ay: ");
  Serial.print(a.acceleration.y);
  Serial.print(", az: ");
  Serial.print(a.acceleration.z);
  Serial.println(" m/s^2");

 // Blynk.virtualWrite(V6, lightLevel);
  /*Blynk.virtualWrite(V7, ax);
  Blynk.virtualWrite(V8, ay);
  Blynk.virtualWrite(V9, az);
  Blynk.virtualWrite(V10, gx);
  Blynk.virtualWrite(V11, gy);
  Blynk.virtualWrite(V12, gz);*/

  // Read ultrasonic sensor
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);
  int distance = duration * 0.034 / 2;
  Serial.print("Distance from objects: ");
  Serial.println(distance);
  //Blynk.virtualWrite(V14, distance);

  // Display data on OLED
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print(F("Voltage: ")); display.print(voltage); display.println(" V");
  display.print("Current: "); display.print(current);  display.println(" A");
  display.print("Temp: "); display.print(temp); display.println(" *C");
  display.print("Humidity: "); display.print(humidity); display.println(" %");
  display.print("Smoke: "); display.println(pirValue ? "Detected" : "Not Detected");
  display.print("Light: "); display.println(lightLevel);
  display.print("Accel: ");
  display.print(a.acceleration.x);
  display.print(", ");
  display.print(a.acceleration.y);
  display.print(", ");
  display.print(a.acceleration.z);
  display.println(" m/s^2");
  display.display();
  delay(10000);

  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("Date: ");
  display.print(now.year());
  display.print('/');
  display.print(now.month());
  display.print('/');
  display.println(now.day());
  display.print("Time: ");
  display.print(now.hour());
  display.print(":");
  display.print(now.minute());
  display.print(":");
  display.print(now.second());
  display.println();
  display.print("Distance: "); display.println(distance);
  display.display();
  delay(10000);
  display.clearDisplay();

  delay(1000);
}

voltage-sensorBreakout
current-sensorBreakout
lithium-ion-batteryBreakout
GND5VSDASCLSQWRTCDS1307+