// --- Library yang dibutuhkan ---
#include <Wire.h>
#include "DHT.h"
#include <PID_v1.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// --- Konfigurasi Pin ---
#define DHT_PIN 4
#define RELAY_PIN 5
#define RESET_BUTTON_PIN 15 // Tombol untuk reset ke mode standar
#define SET_MODE_BUTTON_PIN 23 // <-- Tombol BARU untuk masuk ke mode potensiometer
#define BUZZER_PIN 13
#define STOP_BUZZER_PIN 14
#define POT_PIN 34
#define DHT_TYPE DHT22
// --- Konfigurasi OLED ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// --- Konfigurasi Sistem & Mode ---
enum ControlMode { MODE_DEFAULT, MODE_POTENTIOMETER }; // <-- Logika mode baru
ControlMode currentMode = MODE_DEFAULT; // <-- Sistem mulai di mode default
#define DEFAULT_SETPOINT 22.0
const float ALARM_THRESHOLD = 2.0;
bool isAlarmActive = false;
bool alarmAcknowledged = false; // Variabel baru untuk mengatasi bug alarm
// --- Variabel untuk Debouncing Non-Blocking ---
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 200; // Jeda waktu untuk debounce
// Inisialisasi Perangkat & Variabel Lain
DHT dht(DHT_PIN, DHT_TYPE);
const int numReadings = 10;
float tempReadings[numReadings]; // Mengganti nama variabel agar lebih jelas
int tempReadIndex = 0;
float tempTotal = 0;
float tempAverage = 0;
float currentHumidity = 0;
double Setpoint, Input, Output;
double Kp = 50, Ki = 20, Kd = 5;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, REVERSE);
unsigned long windowSize = 5000;
unsigned long windowStartTime;
void setup() {
Serial.begin(115200);
// Inisialisasi Pin
pinMode(RESET_BUTTON_PIN, INPUT_PULLUP);
pinMode(SET_MODE_BUTTON_PIN, INPUT_PULLUP); // <-- Inisialisasi pin baru
pinMode(STOP_BUZZER_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
// Inisialisasi OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Coba 0x3D jika gagal
Serial.println(F("Alokasi SSD1306 gagal"));
for(;;);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.println("Sistem Siap!");
display.display();
dht.begin();
// Inisialisasi variabel filter
for (int i = 0; i < numReadings; i++) tempReadings[i] = 0;
Setpoint = DEFAULT_SETPOINT;
windowStartTime = millis();
myPID.SetOutputLimits(0, windowSize);
myPID.SetMode(AUTOMATIC);
delay(2000);
}
void checkButtons() {
// Hanya proses tombol jika jeda waktu debounce sudah lewat
if ((millis() - lastDebounceTime) > debounceDelay) {
if (digitalRead(RESET_BUTTON_PIN) == LOW) {
currentMode = MODE_DEFAULT;
Setpoint = DEFAULT_SETPOINT;
Serial.println(">>> Tombol Reset Ditekan: Mode Standar Aktif <<<");
lastDebounceTime = millis(); // Reset timer debounce
}
else if (digitalRead(SET_MODE_BUTTON_PIN) == LOW) {
currentMode = MODE_POTENTIOMETER;
Serial.println(">>> Tombol Set Ditekan: Mode Potensiometer Aktif <<<");
lastDebounceTime = millis(); // Reset timer debounce
}
else if (digitalRead(STOP_BUZZER_PIN) == LOW) {
isAlarmActive = false;
alarmAcknowledged = true;
Serial.println(">>> Tombol Stop Buzzer Ditekan <<<");
lastDebounceTime = millis(); // Reset timer debounce
}
}
}
void loop() {
// --- Proses Input ---
checkButtons(); // <-- Panggil fungsi untuk cek tombol secara non-blocking
// Hanya baca potensiometer jika dalam mode yang sesuai
if (currentMode == MODE_POTENTIOMETER) {
int potValue = analogRead(POT_PIN);
Setpoint = 18.0 + ((float)potValue / 4095.0) * (30.0 - 18.0);
}
// --- Baca & Filter Sensor ---
float rawTemp = dht.readTemperature();
currentHumidity = dht.readHumidity(); // Membaca kelembaban
if (!isnan(rawTemp) && !isnan(currentHumidity)) {
tempTotal -= tempReadings[tempReadIndex];
tempReadings[tempReadIndex] = rawTemp;
tempTotal += tempReadings[tempReadIndex];
tempReadIndex = (tempReadIndex + 1) % numReadings;
tempAverage = tempTotal / numReadings;
}
Input = tempAverage;
myPID.Compute();
// --- Logika Alarm (Sudah diperbaiki) ---
if (abs(Input - Setpoint) > ALARM_THRESHOLD) {
if (!alarmAcknowledged) {
isAlarmActive = true;
}
} else {
alarmAcknowledged = false;
isAlarmActive = false;
}
// --- Kontrol Buzzer & Relay ---
if (isAlarmActive) { tone(BUZZER_PIN, 1000); } else { noTone(BUZZER_PIN); }
unsigned long now = millis();
if (now - windowStartTime > windowSize) windowStartTime += windowSize;
digitalWrite(RELAY_PIN, (Output > now - windowStartTime));
// --- Tampilkan Info ke OLED ---
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0, 0);
display.print(String(Input, 1));
display.print((char)247);
display.print("C");
display.setTextSize(1);
display.setCursor(90, 8);
display.print(String(currentHumidity, 0) + "% RH"); // Menampilkan kelembaban
display.setCursor(0, 24);
display.print("Target: " + String(Setpoint, 1) + "C");
if(currentMode == MODE_POTENTIOMETER) display.print(" (P)"); // Tanda mode Potensiometer
display.setCursor(0, 40);
display.print("Power Kipas: " + String(Output/windowSize*100, 0) + "%");
display.setCursor(0, 54);
if(isAlarmActive){
display.print("Status: ! ALARM !");
} else {
display.print("Status: Normal");
}
display.display();
}