#define BLYNK_TEMPLATE_ID "TMPL6KnkemqtC"
#define BLYNK_TEMPLATE_NAME "Penyedot Debu"
#define BLYNK_AUTH_TOKEN "jXs_KKhygO6CQwgeZO3fGq-B1zocsVIf"
#define BLYNK_PRINT Serial
#include <WiFi.h>
#include <WiFiClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <BlynkSimpleEsp32.h>
// Blynk Authentication Token
char auth[] = BLYNK_AUTH_TOKEN;
char ssid[] = "Wokwi-GUEST";
char pass[] = "";
// Pin Definisi
//#define GP2Y_LED_PIN 35 // LED simulasi sensor debu
#define GP2Y_SENSOR_PIN 32 // Potentiometer untuk debu
#define GP2Y_SENSOR_PIN2 35 // Potentiometer untuk debu tambahan
#define MQ135_PIN 33 // Potentiometer untuk gas
#define RELAY1_PIN 27 // LED untuk kipas 1
#define RELAY2_PIN 14 // LED untuk kipas 2
#define BUZZER_PIN 26 // Buzzer
#define RESET_BUTTON_PIN 25 // Tombol reset runtime
// Blynk Virtual Pins
#define V_PIN_RESET V0
#define V_PIN_RUNTIME V1
#define V_PIN_PPM V2
#define V_PIN_PM25 V3
#define V_PIN_STATUS V4
#define V_PIN_FAN_CONTROL V5
#define V_PIN_PM251 V6
BlynkTimer timer;
// Konfigurasi LCD I2C
LiquidCrystal_I2C LCD = LiquidCrystal_I2C(0x27, 16, 2);
// Variabel Global
float R0 = 10; // Resistansi baseline MQ-135
long totalRunTime = 0; // Total waktu operasional (ms)
long lastRunTimeUpdate = 0; // Timer pembaruan waktu operasional
bool filterReplaceNotified = false;
bool fanActive = false; // Status kipas aktif
bool systemActive = true; // Status aktif mesin
bool canReset = false; // Apakah tombol reset diizinkan
bool manualFanControl = false; // Apakah kipas dikontrol secara manual
//bool manualFanStatus = false; // Status kipas dalam mode manual
// Fungsi untuk menghitung konsentrasi gas dalam PPM (MQ-135)
float calculatePPM(float sensorValue) {
float voltage = sensorValue * (3.3 / 1024.0); // Tegangan analog (V)
float ratio = (5.0 - voltage) / voltage; // Rasio sensor
float ppm = pow(10, ((log10(ratio) - 1.9) / -0.38)); // Persamaan PPM MQ-135
return ppm;
}
// Fungsi untuk mengevaluasi kualitas udara berdasarkan PPM
String evaluateAirQualityPPM(float ppm) {
if (ppm <= 350) return "Baik (Hijau)";
else if (ppm <= 1000) return "Sedang (Kuning)";
else if (ppm <= 2000) return "Tidak Sehat (Oranye)";
else if (ppm <= 5000) return "Berbahaya (Merah)";
else return "Sangat Berbahaya (Cokelat)";
}
// Fungsi untuk membaca konsentrasi PM2.5 (GP2Y1010AU0F)
float readPM25(int sensorPin) {
int sensorValue = analogRead(sensorPin); // Membaca nilai dari potentiometer
float voltage = sensorValue * (3.3 / 1024.0); // Tegangan output (V)
float dustDensity = voltage * 170; // Rumus konversi GP2Y1010AU0F (ug/m3)
return dustDensity; // Mengembalikan nilai PM2.5
}
//Fungsi untuk mengevaluasi kualitas udara berdasarkan PM2.5
String evaluateAirQualityPM25(float pm25) {
if (pm25 <= 12) return "Baik (Hijau)";
else if (pm25 <= 35) return "Sedang (Kuning)";
else if (pm25 <= 55) return "Tidak Sehat (Oranye)";
else if (pm25 <= 150) return "Berbahaya (Merah)";
else return "Sangat Berbahaya (Cokelat)";
}
// Reset fungsi waktu operasi
void resetRuntime() {
if (canReset) { // Hanya reset jika diizinkan
totalRunTime = 0;
filterReplaceNotified = false;
systemActive = true;
canReset = false; // Reset selesai, disable tombol reset
manualFanControl = false; // Nonaktifkan mode manual setelah reset
LCD.clear();
LCD.setCursor(0, 0);
LCD.print("System Reset!");
delay(2000);
LCD.clear();
} else {
LCD.clear();
LCD.setCursor(0, 0);
LCD.print("Reset Locked!");
delay(1000);
LCD.clear();
}
}
// Fungsi untuk menangani tombol reset dari Blynk
BLYNK_WRITE(V_PIN_RESET){
int value = param.asInt();
if (value == 1) {
resetRuntime();
}
}
// Fungsi untuk mengatur runtime dari Blynk
BLYNK_WRITE(V_PIN_RUNTIME) {
long newRuntime = param.asLong() * 1000; // Konversi detik ke milidetik
totalRunTime = newRuntime;
}
BLYNK_WRITE(V_PIN_FAN_CONTROL) {
int value = param.asInt(); // Membaca nilai dari Blynk (0: mati, 1: hidup)
if (value == 1) {
// Aktifkan kontrol manual
manualFanControl = true;
fanActive = true; // Status kipas aktif
// Atur relay berdasarkan status manual
digitalWrite(RELAY1_PIN, HIGH);
digitalWrite(RELAY2_PIN, HIGH);
// Tampilkan status ke LCD
LCD.setCursor(0, 0);
LCD.print("Manual Mode ");
LCD.setCursor(0, 1);
LCD.print("Fan ON ");
} else {
// Nonaktifkan kontrol manual
manualFanControl = false;
fanActive = false; // Status kipas aktif
// Matikan relay
digitalWrite(RELAY1_PIN, LOW);
digitalWrite(RELAY2_PIN, LOW);
// Kembali ke mode otomatis dan tampilkan data sensor
LCD.clear();
LCD.setCursor(0, 0);
LCD.print("PPM: ");
LCD.print(calculatePPM(analogRead(MQ135_PIN)));
LCD.setCursor(0, 1);
LCD.print("PM2.5: ");
LCD.print(readPM25(GP2Y_SENSOR_PIN));
}
}
// Fungsi untuk mengirim data ke Blynk secara periodik
void sendToBlynk() {
int mq135Value = analogRead(MQ135_PIN);
float ppm = calculatePPM(mq135Value);
float pm25_1 = readPM25(GP2Y_SENSOR_PIN);
float pm25_2 = readPM25(GP2Y_SENSOR_PIN2);
// Kirim data ke Blynk
Blynk.virtualWrite(V_PIN_PPM, ppm);
Blynk.virtualWrite(V_PIN_PM25, pm25_1);
Blynk.virtualWrite(V_PIN_PM251, pm25_2);
Blynk.virtualWrite(V_PIN_RUNTIME, totalRunTime / 1000); // Kirim runtime dalam detik
String status = systemActive ? (fanActive ? "Fan On" : "Fan Off") : "System Off";
Blynk.virtualWrite(V_PIN_STATUS, status);
// Kirim status mesin dan kipas
//if (!systemActive) {
// Blynk.virtualWrite(V_PIN_STATUS, "Inactive");
// } else if (fanActive) {
// Blynk.virtualWrite(V_PIN_STATUS, "Active (Fan On)");
//} else {
// Blynk.virtualWrite(V_PIN_STATUS, "Active (No Fan)");
//}
}
void setup() {
Serial.begin(115200);
Blynk.begin(auth, ssid, pass, "blynk.cloud", 80);
// Inisialisasi Pin
//pinMode(GP2Y_LED_PIN, OUTPUT);
pinMode(GP2Y_SENSOR_PIN, INPUT);
pinMode(GP2Y_SENSOR_PIN2, INPUT);
pinMode(MQ135_PIN, INPUT);
pinMode(RELAY1_PIN, OUTPUT);
pinMode(RELAY2_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(RESET_BUTTON_PIN, INPUT_PULLUP); // Tombol reset
// Matikan relay dan buzzer saat awal
digitalWrite(RELAY1_PIN, LOW);
digitalWrite(RELAY2_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
// Inisialisasi LCD
LCD.init();
LCD.backlight();
LCD.setCursor(0, 0);
LCD.print("Penyedot Debu");
delay(2000);
LCD.clear();
// Timer untuk kirim data ke Blynk setiap 2 detik
timer.setInterval(2000L, sendToBlynk);
}
void checkAndNotify(float ppm, float pm25_1) {
if (ppm > 2000 || pm25_1 > 150) {
Blynk.logEvent("bad_air_quality", "Kualitas udara buruk! Alat dinyalakan.");
}
if (filterReplaceNotified) {
Blynk.logEvent("filter_replacement", "Runtime 100 jam tercapai. Ganti HEPA & Carbon filter segera.");
}
}
void loop() {
Blynk.run();
timer.run();
if (manualFanControl) {
if (fanActive && millis() - lastRunTimeUpdate > 1000) {
totalRunTime += 1000; // Tambah 1 detik ke runtime
lastRunTimeUpdate = millis();
if (totalRunTime >= 5000) { // Setelah 100 jam
filterReplaceNotified = true;
canReset = true; // Izinkan reset
systemActive = false; // Matikan sistem
manualFanControl = false;
fanActive = false;
digitalWrite(RELAY1_PIN, LOW);
digitalWrite(RELAY2_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
}
}
sendToBlynk();
return; // Kembali tanpa menjalankan logika otomatis
}
if (!systemActive) {
// Jika sistem tidak aktif, tampilkan pesan dan tunggu reset
LCD.setCursor(0, 0);
LCD.print("System OFF ");
LCD.setCursor(0, 1);
LCD.print("Press Reset ");
digitalWrite(BUZZER_PIN, HIGH); // Buzzer menyala
delay(1000);
if (digitalRead(RESET_BUTTON_PIN) == LOW) {
resetRuntime();
}
return;
}
// Membaca Sensor MQ-135 (PPM)
int mq135Value = analogRead(MQ135_PIN);
float ppm = calculatePPM(mq135Value);
String airQualityPPM = evaluateAirQualityPPM(ppm);
// Membaca Sensor GP2Y1010AU0F (PM2.5)
float pm25_1 = readPM25(GP2Y_SENSOR_PIN);
String airQualityPM25_1 = evaluateAirQualityPM25(pm25_1);
float pm25_2 = readPM25(GP2Y_SENSOR_PIN2);
String airQualityPM25_2 = evaluateAirQualityPM25(pm25_2);
// Kontrol Relay dan Buzzer Berdasarkan Kualitas Udara
if (ppm > 2000 || pm25_1 > 150) { // Kondisi udara buruk
digitalWrite(RELAY1_PIN, HIGH); // Aktifkan kipas 1
digitalWrite(RELAY2_PIN, HIGH); // Aktifkan kipas 2
digitalWrite(BUZZER_PIN, HIGH); // Buzzer menyala
fanActive = true; // Tandai kipas aktif
} else {
digitalWrite(RELAY1_PIN, LOW); // Matikan kipas 1
digitalWrite(RELAY2_PIN, LOW); // Matikan kipas 2
digitalWrite(BUZZER_PIN, LOW); // Matikan buzzer
fanActive = false; // Tandai kipas tidak aktif
}
// Menampilkan Data di LCD
LCD.setCursor(0, 0);
LCD.print("PPM: ");
LCD.print(ppm);
LCD.setCursor(0, 1);
LCD.print("PM2.5: ");
LCD.print(pm25_1);
sendToBlynk();
// Membaca data sensor
//int mq135Value = analogRead(MQ135_PIN);
//float ppm = calculatePPM(mq135Value);
//float pm25 = readPM25();
// Kirim data ke Blynk
// Periksa kondisi dan kirim notifikasi
checkAndNotify(ppm, pm25_1);
// Perhitungan Runtime hanya jika kipas aktif
if (fanActive && millis() - lastRunTimeUpdate > 1000) {
totalRunTime += 1000; // Tambah 1 detik
lastRunTimeUpdate = millis();
if (totalRunTime >= 5000) { // Setelah 100 jam
filterReplaceNotified = true;
canReset = true; // Izinkan reset
systemActive = false; // Matikan sistem
manualFanControl = false;
digitalWrite(RELAY1_PIN, LOW);
digitalWrite(RELAY2_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
}
}
// Tombol Reset Runtime
if (digitalRead(RESET_BUTTON_PIN) == LOW) {
resetRuntime();
delay(500); // Debounce delay
}
}