#include "thingProperties.h" #include #include // --- DEFINISI PIN --- #define PIN_DHT 4 #define TIPE_DHT DHT11 #define PIN_MQ3 34 #define PIN_MQ135 35 #define PIN_BUZZER 15 #define PIN_TOMBOL1 13 // Tombol List Buah #define PIN_TOMBOL2 12 // Tombol Pindah Bawah #define PIN_TOMBOL3 14 // Tombol Pilih/Enter // --- KONFIGURASI LCD --- #define ALAMAT_LCD 0x27 #define KOLOM_LCD 20 #define BARIS_LCD 4 // Inisialisasi komponen DHT dht(PIN_DHT, TIPE_DHT); LiquidCrystal_I2C lcd(ALAMAT_LCD, KOLOM_LCD, BARIS_LCD); struct DataSensor { float suhu; float kelembaban; int nilaiAnalogMQ3; int nilaiAnalogMQ135; float nilaiPpmMQ3; float nilaiPpmMQ135; String tingkatKematangan; String namaBuahTerpilih; }; // --- VARIABEL GLOBAL --- String daftarBuah[] = {"Apel", "Pepaya", "Pisang", "Alpukat"}; int buahTerpilihLokal = 0; bool statusBuahDipilih = false; bool statusTampilDaftarBuah = false; unsigned long waktuTerakhirSensorDibaca = 0; unsigned long waktuTerakhirTombolDibaca = 0; unsigned long waktuTerakhirCloudUpdate = 0; const unsigned long INTERVAL_SENSOR = 2000; // 2 detik const unsigned long INTERVAL_CLOUD = 5000; // 5 detik untuk update cloud const unsigned long JEDA_ANTIBOUNCE = 200; // 200 milidetik // Status tombol bool kondisiTombol1 = HIGH, kondisiTombol2 = HIGH, kondisiTombol3 = HIGH; bool kondisiTerakhirTombol1 = HIGH, kondisiTerakhirTombol2 = HIGH, kondisiTerakhirTombol3 = HIGH; // Variabel untuk menyimpan tingkat kematangan sebelumnya String tingkatKematanganSebelumnya = ""; // ============================================================================== // KONSTANTA KALIBRASI SENSOR MQ // ============================================================================== const float TEGANGAN_VCC = 5.0; // Tegangan suplai modul sensor MQ (V) const float RESISTANSI_BEBAN = 10.0; // Resistansi beban (kOhm) const int RESOLUSI_ADC = 4095; // Resolusi ADC ESP32 // MQ3 (Alkohol/Etanol) - Ganti dengan nilai kalibrasi Anda! const float MQ3_R0 = 60.0; const float MQ3_SLOPE = -0.685; const float MQ3_INTERCEPT = 1.258; // MQ135 (Kualitas Udara) - Ganti dengan nilai kalibrasi Anda! const float MQ135_R0 = 35.7; const float MQ135_SLOPE = -0.395; const float MQ135_INTERCEPT = 1.488; // ============================================================================== // Struktur untuk ambang batas kematangan buah (dalam PPM) struct AmbangBatasBuah { float mentahMQ3Maks; float matangMQ3Min; float matangMQ3Maks; float busukMQ3Min; float mentahMQ135Maks; float matangMQ135Min; float matangMQ135Maks; float busukMQ135Min; }; // Definisi ambang batas untuk setiap buah (sesuaikan berdasarkan kalibrasi) AmbangBatasBuah daftarAmbangBatas[] = { {29.1, 29.5, 77.0, 78.0, 11.2, 11.7, 17.5, 18.0}, // Apel {30.0, 30.5, 90.0, 91.0, 13.0, 13.5, 24.0, 24.5}, // Pepaya {32.0, 32.5, 102.0, 103.0, 12.0, 12.5, 19.0, 19.5}, // Pisang {29.2, 29.7, 59.0, 60.5, 13.2, 13.7, 21.0, 21.5} // Alpukat }; void setup() { Serial.begin(9600); delay(1500); // Initialize Arduino IoT Cloud initProperties(); ArduinoCloud.begin(ArduinoIoTPreferredConnection); setDebugMessageLevel(2); ArduinoCloud.printDebugInfo(); dht.begin(); lcd.init(); lcd.backlight(); pinMode(PIN_BUZZER, OUTPUT); pinMode(PIN_TOMBOL1, INPUT_PULLUP); pinMode(PIN_TOMBOL2, INPUT_PULLUP); pinMode(PIN_TOMBOL3, INPUT_PULLUP); tampilkanPesanSelamatDatang(); Serial.println("=== SISTEM DETEKSI KEMATANGAN BUAH ==="); Serial.println("Sistem siap digunakan!"); delay(5000); tampilkanMenuUtama(); } // Fungsi untuk menghitung resistansi sensor Rs float hitungResistansiSensor(int nilaiAnalog, float teganganVcc, float resistansiBeban, int resolusiAdc) { float teganganKeluar = nilaiAnalog * (teganganVcc / resolusiAdc); if (teganganKeluar == 0) return resistansiBeban * 1000; float resistansiSensor = (teganganVcc / teganganKeluar - 1) * resistansiBeban; return resistansiSensor; // dalam kOhm } // Fungsi untuk mengkonversi Rs/R0 ke PPM float konversiKePPM(float rasioRsR0, float slope, float intercept) { if (rasioRsR0 <= 0) return 0.0; float logRsR0 = log10(rasioRsR0); return pow(10, (slope * logRsR0) + intercept); } void loop() { ArduinoCloud.update(); unsigned long waktuSekarang = millis(); if (waktuSekarang - waktuTerakhirTombolDibaca >= JEDA_ANTIBOUNCE) { kelolaTombol(); waktuTerakhirTombolDibaca = waktuSekarang; } if (statusBuahDipilih && (waktuSekarang - waktuTerakhirSensorDibaca >= INTERVAL_SENSOR)) { perbaruiSensor(); waktuTerakhirSensorDibaca = waktuSekarang; } // Update Arduino Cloud setiap 5 detik if (statusBuahDipilih && (waktuSekarang - waktuTerakhirCloudUpdate >= INTERVAL_CLOUD)) { updateCloudVariables(); waktuTerakhirCloudUpdate = waktuSekarang; } } void updateCloudVariables() { DataSensor data = bacaSemuaSensor(); // Update cloud variables sesuai dengan yang ada di Arduino Cloud mq135_ppm = data.nilaiPpmMQ135; mq3_ppm = data.nilaiPpmMQ3; namaBuah = daftarBuah[buahTerpilihLokal]; // Mengirim nama buah dari array suhu = data.suhu; tingkatKematangan = data.tingkatKematangan; Serial.println("=== DATA DIKIRIM KE ARDUINO CLOUD ==="); Serial.println("MQ135 PPM: " + String(mq135_ppm, 4)); Serial.println("MQ3 PPM: " + String(mq3_ppm, 4)); Serial.println("Nama Buah Index: " + String(namaBuah)); Serial.println("Suhu: " + String(suhu, 1) + " °C"); Serial.println("Tingkat Kematangan: " + tingkatKematangan); Serial.println("====================================="); } void tampilkanPesanSelamatDatang() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(" SISTEM PENDETEKSI "); lcd.setCursor(0, 1); lcd.print(" KEMATANGAN BUAH "); lcd.setCursor(0, 2); lcd.print(" BERBASIS SENSOR "); lcd.setCursor(0, 3); lcd.print(" E-NOSE CLOUD "); } void tampilkanMenuUtama() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(" MENU UTAMA "); lcd.setCursor(0, 1); lcd.print(" Pilih buah untuk "); lcd.setCursor(0, 2); lcd.print(" mulai deteksi "); lcd.setCursor(0, 3); lcd.print("=> tekan tombol list"); } void tampilkanDaftarBuah() { lcd.clear(); for (int i = 0; i < 4; i++) { lcd.setCursor(0, i); if (i == buahTerpilihLokal) { lcd.print("> " + daftarBuah[i]); } else { lcd.print(" " + daftarBuah[i]); } } } void tampilkanHasilBacaSensor() { DataSensor data = bacaSemuaSensor(); lcd.clear(); lcd.setCursor(0, 0); lcd.print(daftarBuah[buahTerpilihLokal] + ": " + data.tingkatKematangan); lcd.setCursor(0, 1); lcd.print("MQ3:" + String(data.nilaiPpmMQ3, 2) + "ppm"); lcd.setCursor(0, 2); lcd.print("MQ135:" + String(data.nilaiPpmMQ135, 2) + "ppm"); lcd.setCursor(0, 3); lcd.print("T:" + String(data.suhu, 1) + "C H:" + String(data.kelembaban, 1) + "%"); // Cek jika tingkat kematangan berubah if (data.tingkatKematangan != tingkatKematanganSebelumnya) { // Jika status kematangan berubah, bunyikan buzzer if (data.tingkatKematangan == "Busuk") { bunyikanBuzzer(1, 100); // Bunyikan buzzer untuk status "Busuk" } else if (data.tingkatKematangan == "Matang") { bunyikanBuzzer(1, 200); // Bunyikan buzzer untuk status "Matang" } // Update tingkat kematangan sebelumnya tingkatKematanganSebelumnya = data.tingkatKematangan; } // Tampilkan data di Serial Monitor Serial.println("=== DATA SENSOR LOKAL ==="); Serial.println("Buah: " + data.namaBuahTerpilih); Serial.println("Tingkat Kematangan: " + data.tingkatKematangan); Serial.println("Suhu: " + String(data.suhu, 1) + " °C"); Serial.println("Kelembaban: " + String(data.kelembaban, 1) + " %"); Serial.println("MQ3: " + String(data.nilaiPpmMQ3, 4) + " ppm"); Serial.println("MQ135: " + String(data.nilaiPpmMQ135, 4) + " ppm"); Serial.println("========================"); } DataSensor bacaSemuaSensor() { DataSensor data; data.suhu = dht.readTemperature(); data.kelembaban = dht.readHumidity(); if (isnan(data.suhu) || isnan(data.kelembaban)) { Serial.println("Error: Gagal membaca sensor DHT11!"); data.suhu = 0.0; data.kelembaban = 0.0; } data.nilaiAnalogMQ3 = analogRead(PIN_MQ3); data.nilaiAnalogMQ135 = analogRead(PIN_MQ135); float Rs_MQ3 = hitungResistansiSensor(data.nilaiAnalogMQ3, TEGANGAN_VCC, RESISTANSI_BEBAN, RESOLUSI_ADC); float rasioRsR0_MQ3 = Rs_MQ3 / MQ3_R0; data.nilaiPpmMQ3 = konversiKePPM(rasioRsR0_MQ3, MQ3_SLOPE, MQ3_INTERCEPT); float Rs_MQ135 = hitungResistansiSensor(data.nilaiAnalogMQ135, TEGANGAN_VCC, RESISTANSI_BEBAN, RESOLUSI_ADC); float rasioRsR0_MQ135 = Rs_MQ135 / MQ135_R0; data.nilaiPpmMQ135 = konversiKePPM(rasioRsR0_MQ135, MQ135_SLOPE, MQ135_INTERCEPT); data.tingkatKematangan = tentukanKematangan(data.nilaiPpmMQ3, data.nilaiPpmMQ135, buahTerpilihLokal); data.namaBuahTerpilih = daftarBuah[buahTerpilihLokal]; return data; } String tentukanKematangan(float nilaiPpmMQ3, float nilaiPpmMQ135, int indeksBuah) { AmbangBatasBuah ambangBatas = daftarAmbangBatas[indeksBuah]; if (nilaiPpmMQ3 >= ambangBatas.busukMQ3Min || nilaiPpmMQ135 >= ambangBatas.busukMQ135Min) { return "Busuk"; } else if ((nilaiPpmMQ3 >= ambangBatas.matangMQ3Min && nilaiPpmMQ3 <= ambangBatas.matangMQ3Maks) || (nilaiPpmMQ135 >= ambangBatas.matangMQ135Min && nilaiPpmMQ135 <= ambangBatas.matangMQ135Maks)) { return "Matang"; } else { return "Mentah"; } } void kelolaTombol() { bool tekanTombol1 = digitalRead(PIN_TOMBOL1); bool tekanTombol2 = digitalRead(PIN_TOMBOL2); bool tekanTombol3 = digitalRead(PIN_TOMBOL3); // Tombol 1 - Tampilkan daftar buah atau kembali ke menu if (tekanTombol1 == LOW && kondisiTerakhirTombol1 == HIGH) { if (statusBuahDipilih) { statusBuahDipilih = false; statusTampilDaftarBuah = false; buahTerpilihLokal = 0; tampilkanMenuUtama(); } else { statusTampilDaftarBuah = true; buahTerpilihLokal = 0; tampilkanDaftarBuah(); } } // Tombol 2 - Navigasi ke bawah if (tekanTombol2 == LOW && kondisiTerakhirTombol2 == HIGH && statusTampilDaftarBuah && !statusBuahDipilih) { buahTerpilihLokal = (buahTerpilihLokal + 1) % 4; tampilkanDaftarBuah(); } // Tombol 3 - Pilih buah if (tekanTombol3 == LOW && kondisiTerakhirTombol3 == HIGH && statusTampilDaftarBuah && !statusBuahDipilih) { statusBuahDipilih = true; statusTampilDaftarBuah = false; lcd.clear(); lcd.setCursor(0, 0); lcd.print("Buah dipilih:"); lcd.setCursor(0, 1); lcd.print(daftarBuah[buahTerpilihLokal]); lcd.setCursor(0, 2); lcd.print("Letakkan buah..."); lcd.setCursor(0, 3); lcd.print("Membaca sensor..."); delay(2000); waktuTerakhirSensorDibaca = millis() - INTERVAL_SENSOR; } kondisiTerakhirTombol1 = tekanTombol1; kondisiTerakhirTombol2 = tekanTombol2; kondisiTerakhirTombol3 = tekanTombol3; } void perbaruiSensor() { if (statusBuahDipilih) { tampilkanHasilBacaSensor(); } } void bunyikanBuzzer(int jumlahBip, int durasi) { for (int i = 0; i < jumlahBip; i++) { digitalWrite(PIN_BUZZER, HIGH); delay(durasi); digitalWrite(PIN_BUZZER, LOW); if (i < jumlahBip - 1) { delay(200); } } }