#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Stepper.h>
#include <DHT.h>
#define rlight 4 // LED pin
#define dht_pin 5 // DHT22 pin
#define mq2_do 1 // MQ2 Digital Output pin
#define mq2_ao A1 // MQ2 Analog Output pin
// Konstanta untuk konfigurasi
#define stepsPerRevolution 2048 // Jumlah langkah per revolusi motor
// Inisialisasi objek
Stepper myStepper(stepsPerRevolution, 8, 9, 10, 11);
LiquidCrystal_I2C lcd(0x27, 16, 2);
DHT dht22(dht_pin, DHT22); // Menggunakan DHT22
// Kecerahan LED
const int LED_LOW = 50; // PWM value untuk kecerahan rendah
const int LED_MEDIUM = 150; // PWM value untuk kecerahan sedang
const int LED_HIGH = 255; // PWM value untuk kecerahan tinggi
// Kecepatan motor
const int MOTOR_RPM_MIN = 10; // Misal batas minimal RPM motor
const int MOTOR_RPM_MAX = 60; // Batas maksimal RPM motor
// Variabel untuk fuzzy logic
// Range untuk suhu (Celsius)
const float TEMP_LOW_MAX = 30.0;
const float TEMP_MEDIUM_MIN = 28.0;
const float TEMP_MEDIUM_MAX = 42.0;
const float TEMP_HIGH_MIN = 40.0;
// Range untuk gas (nilai analog)
const int GAS_LOW_MAX = 120;
const int GAS_MEDIUM_MIN = 118;
const int GAS_MEDIUM_MAX = 402;
const int GAS_HIGH_MIN = 400;
// Struktur untuk menyimpan hasil fuzzy inference
// PENTING: Mendefinisikan struktur SEBELUM fungsi yang menggunakannya
struct FuzzyOutput {
float dangerLevel; // Tingkat bahaya (0.0-1.0)
int ledBrightness; // Kecerahan LED (0-255)
int motorSpeed; // Kecepatan motor (RPM)
String statusMessage; // Pesan status
};
void setup() {
// Inisialisasi serial monitor
Serial.begin(9600);
// Inisialisasi LCD
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Fuzzy System");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
// Set pin mode
pinMode(rlight, OUTPUT); // LED sebagai output (menggunakan PWM)
pinMode(mq2_do, INPUT); // MQ2 digital sebagai input
pinMode(mq2_ao, INPUT); // MQ2 analog sebagai input
// Inisialisasi sensor DHT22
dht22.begin();
// Tunda sebentar untuk stabilisasi sensor
delay(2000);
lcd.clear();
}
// Fungsi keanggotaan fuzzy untuk suhu
float tempLowMembership(float temp) {
if (temp <= TEMP_LOW_MAX - 2) return 1.0;
if (temp >= TEMP_LOW_MAX) return 0.0;
return (TEMP_LOW_MAX - temp) / 2.0;
}
float tempMediumMembership(float temp) {
if (temp <= TEMP_MEDIUM_MIN) return 0.0;
if (temp >= TEMP_MEDIUM_MIN && temp <= (TEMP_MEDIUM_MIN + TEMP_MEDIUM_MAX)/2)
return (temp - TEMP_MEDIUM_MIN) / ((TEMP_MEDIUM_MIN + TEMP_MEDIUM_MAX)/2 - TEMP_MEDIUM_MIN);
if (temp > (TEMP_MEDIUM_MIN + TEMP_MEDIUM_MAX)/2 && temp < TEMP_MEDIUM_MAX)
return (TEMP_MEDIUM_MAX - temp) / (TEMP_MEDIUM_MAX - (TEMP_MEDIUM_MIN + TEMP_MEDIUM_MAX)/2);
return 0.0;
}
float tempHighMembership(float temp) {
if (temp <= TEMP_HIGH_MIN) return 0.0;
if (temp >= TEMP_HIGH_MIN + 2) return 1.0;
return (temp - TEMP_HIGH_MIN) / 2.0;
}
// Fungsi keanggotaan fuzzy untuk gas
float gasLowMembership(int gas) {
if (gas <= GAS_LOW_MAX - 100) return 1.0;
if (gas >= GAS_LOW_MAX) return 0.0;
return (float)(GAS_LOW_MAX - gas) / 100.0;
}
float gasMediumMembership(int gas) {
if (gas <= GAS_MEDIUM_MIN) return 0.0;
if (gas >= GAS_MEDIUM_MIN && gas <= (GAS_MEDIUM_MIN + GAS_MEDIUM_MAX)/2)
return (float)(gas - GAS_MEDIUM_MIN) / ((GAS_MEDIUM_MIN + GAS_MEDIUM_MAX)/2 - GAS_MEDIUM_MIN);
if (gas > (GAS_MEDIUM_MIN + GAS_MEDIUM_MAX)/2 && gas < GAS_MEDIUM_MAX)
return (float)(GAS_MEDIUM_MAX - gas) / (GAS_MEDIUM_MAX - (GAS_MEDIUM_MIN + GAS_MEDIUM_MAX)/2);
return 0.0;
}
float gasHighMembership(int gas) {
if (gas <= GAS_HIGH_MIN) return 0.0;
if (gas >= GAS_HIGH_MIN + 50) return 1.0;
return (float)(gas - GAS_HIGH_MIN) / 50.0;
}
// Fungsi untuk melakukan fuzzy inference
FuzzyOutput fuzzyInference(float temperature, int gasLevel) {
FuzzyOutput output;
// Fuzzifikasi - menghitung nilai keanggotaan
float tempLow = tempLowMembership(temperature);
float tempMedium = tempMediumMembership(temperature);
float tempHigh = tempHighMembership(temperature);
float gasLow = gasLowMembership(gasLevel);
float gasMedium = gasMediumMembership(gasLevel);
float gasHigh = gasHighMembership(gasLevel);
// Debug: tampilkan nilai keanggotaan di serial monitor
Serial.println("Membership Values:");
Serial.print("Temp Low: "); Serial.print(tempLow);
Serial.print(", Medium: "); Serial.print(tempMedium);
Serial.print(", High: "); Serial.println(tempHigh);
Serial.print("Gas Low: "); Serial.print(gasLow);
Serial.print(", Medium: "); Serial.print(gasMedium);
Serial.print(", High: "); Serial.println(gasHigh);
// Rule evaluation dan defuzzification
// Aturan fuzzy:
// 1. Jika suhu LOW dan gas LOW, maka bahaya LOW
// 2. Jika suhu LOW dan gas MEDIUM, maka bahaya LOW-MEDIUM
// 3. Jika suhu LOW dan gas HIGH, maka bahaya MEDIUM-HIGH
// 4. Jika suhu MEDIUM dan gas LOW, maka bahaya LOW-MEDIUM
// 5. Jika suhu MEDIUM dan gas MEDIUM, maka bahaya MEDIUM
// 6. Jika suhu MEDIUM dan gas HIGH, maka bahaya HIGH
// 7. Jika suhu HIGH dan gas LOW, maka bahaya MEDIUM-HIGH
// 8. Jika suhu HIGH dan gas MEDIUM, maka bahaya HIGH
// 9. Jika suhu HIGH dan gas HIGH, maka bahaya VERY HIGH
// Menggunakan metode max-min untuk inferensi dan centroid untuk defuzzifikasi
// Rule 1: Jika suhu LOW dan gas LOW, maka bahaya LOW
float rule1 = min(tempLow, gasLow);
// Rule 2: Jika suhu LOW dan gas MEDIUM, maka bahaya LOW-MEDIUM
float rule2 = min(tempLow, gasMedium) * 0.3;
// Rule 3: Jika suhu LOW dan gas HIGH, maka bahaya MEDIUM-HIGH
float rule3 = min(tempLow, gasHigh) * 0.6;
// Rule 4: Jika suhu MEDIUM dan gas LOW, maka bahaya LOW-MEDIUM
float rule4 = min(tempMedium, gasLow) * 0.3;
// Rule 5: Jika suhu MEDIUM dan gas MEDIUM, maka bahaya MEDIUM
float rule5 = min(tempMedium, gasMedium) * 0.5;
// Rule 6: Jika suhu MEDIUM dan gas HIGH, maka bahaya HIGH
float rule6 = min(tempMedium, gasHigh) * 0.8;
// Rule 7: Jika suhu HIGH dan gas LOW, maka bahaya MEDIUM-HIGH
float rule7 = min(tempHigh, gasLow) * 0.6;
// Rule 8: Jika suhu HIGH dan gas MEDIUM, maka bahaya HIGH
float rule8 = min(tempHigh, gasMedium) * 0.8;
// Rule 9: Jika suhu HIGH dan gas HIGH, maka bahaya VERY HIGH
float rule9 = min(tempHigh, gasHigh) * 1.0;
// Defuzzifikasi menggunakan metode centroid sederhana
float totalWeight = rule1 + rule2 + rule3 + rule4 + rule5 + rule6 + rule7 + rule8 + rule9;
float weightedSum = rule1 * 0.0 + rule2 * 0.3 + rule3 * 0.6 + rule4 * 0.3 +
rule5 * 0.5 + rule6 * 0.8 + rule7 * 0.6 + rule8 * 0.8 + rule9 * 1.0;
if (totalWeight > 0) {
output.dangerLevel = weightedSum / totalWeight;
} else {
output.dangerLevel = 0;
}
// Menentukan kecerahan LED berdasarkan tingkat bahaya
if (output.dangerLevel < 0.4) {
output.ledBrightness = LED_LOW;
output.statusMessage = "STATUS: NORMAL";
} else if (output.dangerLevel < 0.7) {
output.ledBrightness = LED_MEDIUM;
output.statusMessage = "STATUS: WARNING";
} else {
output.ledBrightness = LED_HIGH;
output.statusMessage = "STATUS: DANGER!";
}
// MOTOR: Variabel, tidak tetap, linear dari dangerLevel:
output.motorSpeed = MOTOR_RPM_MIN + (output.dangerLevel * (MOTOR_RPM_MAX - MOTOR_RPM_MIN));
return output;
}
void loop() {
// Baca nilai sensor
float humidity = dht22.readHumidity();
float temperature = dht22.readTemperature();
int gasAnalog = analogRead(mq2_ao);
// Periksa apakah pembacaan sensor berhasil
if (isnan(humidity) || isnan(temperature)) {
Serial.println("Gagal membaca dari sensor DHT22!");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sensor DHT Error");
delay(2000);
return;
}
// Tampilkan nilai sensor pada serial monitor
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.print(" *C, Humidity: ");
Serial.print(humidity);
Serial.print("%, Gas Value: ");
Serial.println(gasAnalog);
// Lakukan inferensi fuzzy
FuzzyOutput result = fuzzyInference(temperature, gasAnalog);
// Tampilkan hasil fuzzy pada serial monitor
Serial.print("Danger Level: ");
Serial.print(result.dangerLevel);
Serial.print(", LED Brightness: ");
Serial.print(result.ledBrightness);
Serial.print(", Motor Speed: ");
Serial.println(result.motorSpeed);
// Tampilkan nilai sensor dan status pada LCD
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("T:");
lcd.print(temperature, 1);
lcd.print("C G:");
lcd.print(gasAnalog);
lcd.setCursor(0, 1);
// Print status pendek
if(result.dangerLevel < 0.4) {
lcd.print("NORMAL ");
} else if (result.dangerLevel < 0.7) {
lcd.print("WARNING");
} else {
lcd.print("DANGER!");
}
// Print RPM motor
lcd.print(" ");
lcd.print(result.motorSpeed);
lcd.print("rpm");
// Atur kecerahan LED menggunakan PWM
analogWrite(rlight, result.ledBrightness);
// Atur kecepatan motor
myStepper.setSpeed(result.motorSpeed);
// Jika bahaya tinggi, putar motor untuk ventilasi
if (result.dangerLevel > 0.5) {
// Jumlah langkah berdasarkan tingkat bahaya
int steps = map(result.ledBrightness, LED_LOW, LED_HIGH,
stepsPerRevolution/8, stepsPerRevolution/2);
myStepper.step(steps);
delay(200);
myStepper.step(-steps); // Putar balik
}
// Delay sebelum pembacaan berikutnya
delay(2000);
}