From fe6a860886b6ddc50d3202b4b8bba60ae5cb17fc Mon Sep 17 00:00:00 2001 From: Anwaraja12 Date: Fri, 15 Aug 2025 14:15:50 +0700 Subject: [PATCH] first commit --- ReadMe.adoc | 58 ++++++++ arduino_secrets.h | 3 + main.ino | 338 ++++++++++++++++++++++++++++++++++++++++++++++ sketch.json | 22 +++ thingProperties.h | 32 +++++ 5 files changed, 453 insertions(+) create mode 100644 ReadMe.adoc create mode 100644 arduino_secrets.h create mode 100644 main.ino create mode 100644 sketch.json create mode 100644 thingProperties.h diff --git a/ReadMe.adoc b/ReadMe.adoc new file mode 100644 index 0000000..d57fff4 --- /dev/null +++ b/ReadMe.adoc @@ -0,0 +1,58 @@ +:Author: anwaraja12 +:Email: +:Date: 10/08/2025 +:Revision: version# +:License: Public Domain + += Project: {Project} + +Describe your project + +== Step 1: Installation +Please describe the steps to install this project. + +For example: + +1. Open this file +2. Edit as you like +3. Release to the World! + +== Step 2: Assemble the circuit + +Assemble the circuit following the diagram layout.png attached to the sketch + +== Step 3: Load the code + +Upload the code contained in this sketch on to your board + +=== Folder structure + +.... + sketch123 => Arduino sketch folder + ├── sketch123.ino => main Arduino file + ├── schematics.png => (optional) an image of the required schematics + ├── layout.png => (optional) an image of the layout + └── ReadMe.adoc => this file +.... + +=== License +This project is released under a {License} License. + +=== Contributing +To contribute to this project please contact: + +=== BOM +Add the bill of the materials you need for this project. + +|=== +| ID | Part name | Part number | Quantity +| R1 | 10k Resistor | 1234-abcd | 10 +| L1 | Red LED | 2345-asdf | 5 +| A1 | Arduino Zero | ABX00066 | 1 +|=== + + +=== Help +This document is written in the _AsciiDoc_ format, a markup language to describe documents. +If you need help you can search the http://www.methods.co.nz/asciidoc[AsciiDoc homepage] +or consult the http://powerman.name/doc/asciidoc[AsciiDoc cheatsheet] diff --git a/arduino_secrets.h b/arduino_secrets.h new file mode 100644 index 0000000..d4d1d23 --- /dev/null +++ b/arduino_secrets.h @@ -0,0 +1,3 @@ +#define SECRET_DEVICE_KEY "4!eycuJaVr97eMewzUKs6byPj" +#define SECRET_OPTIONAL_PASS "kosputrac3c4" +#define SECRET_SSID "ZTE-C3-C4" diff --git a/main.ino b/main.ino new file mode 100644 index 0000000..d48fb72 --- /dev/null +++ b/main.ino @@ -0,0 +1,338 @@ +#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); + } + } +} \ No newline at end of file diff --git a/sketch.json b/sketch.json new file mode 100644 index 0000000..ef8766c --- /dev/null +++ b/sketch.json @@ -0,0 +1,22 @@ +{ + "cpu": { + "fqbn": "esp32:esp32:esp32", + "name": "", + "type": "serial" + }, + "secrets": [ + { + "name": "SECRET_DEVICE_KEY", + "value": "" + }, + { + "name": "SECRET_OPTIONAL_PASS", + "value": "" + }, + { + "name": "SECRET_SSID", + "value": "" + } + ], + "included_libs": [] +} \ No newline at end of file diff --git a/thingProperties.h b/thingProperties.h new file mode 100644 index 0000000..6f5accf --- /dev/null +++ b/thingProperties.h @@ -0,0 +1,32 @@ +// Code generated by Arduino IoT Cloud, DO NOT EDIT. + +#include +#include +#include "arduino_secrets.h" + +const char DEVICE_LOGIN_NAME[] = "a7087090-eb37-4732-8876-eb657530570c"; + +const char SSID[] = SECRET_SSID; // Network SSID (name) +const char PASS[] = SECRET_OPTIONAL_PASS; // Network password (use for WPA, or use as key for WEP) +const char DEVICE_KEY[] = SECRET_DEVICE_KEY; // Secret device password + + +String tingkatKematangan; +float mq135_ppm; +float mq3_ppm; +float suhu; +String namaBuah; + +void initProperties(){ + + ArduinoCloud.setBoardId(DEVICE_LOGIN_NAME); + ArduinoCloud.setSecretDeviceKey(DEVICE_KEY); + ArduinoCloud.addProperty(tingkatKematangan, READ, ON_CHANGE, NULL); + ArduinoCloud.addProperty(mq135_ppm, READ, ON_CHANGE, NULL); + ArduinoCloud.addProperty(mq3_ppm, READ, ON_CHANGE, NULL); + ArduinoCloud.addProperty(suhu, READ, ON_CHANGE, NULL); + ArduinoCloud.addProperty(namaBuah, READ, ON_CHANGE, NULL); + +} + +WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);