first commit

This commit is contained in:
Anwaraja12 2025-08-15 14:15:50 +07:00
commit fe6a860886
5 changed files with 453 additions and 0 deletions

58
ReadMe.adoc Normal file
View File

@ -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]

3
arduino_secrets.h Normal file
View File

@ -0,0 +1,3 @@
#define SECRET_DEVICE_KEY "4!eycuJaVr97eMewzUKs6byPj"
#define SECRET_OPTIONAL_PASS "kosputrac3c4"
#define SECRET_SSID "ZTE-C3-C4"

338
main.ino Normal file
View File

@ -0,0 +1,338 @@
#include "thingProperties.h"
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
// --- 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);
}
}
}

22
sketch.json Normal file
View File

@ -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": []
}

32
thingProperties.h Normal file
View File

@ -0,0 +1,32 @@
// Code generated by Arduino IoT Cloud, DO NOT EDIT.
#include <ArduinoIoTCloud.h>
#include <Arduino_ConnectionHandler.h>
#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);