Upload files to "/"

This commit is contained in:
Anakayutserian 2025-07-03 16:19:29 +07:00
commit 1f4a9497e7
1 changed files with 431 additions and 0 deletions

View File

@ -0,0 +1,431 @@
// ======= IMPORT LIBRARY & DEFINISI PIN =======
#include <DHT.h>
#include <ESP32Servo.h>
#include <Wire.h>
#include <RTClib.h>
#define DHTPIN 4
#define DHTTYPE DHT22
#define FAN_PIN_2 26
#define SERVO_PIN 14
#define LIMIT_SWITCH_PIN 27
#define I2C_SDA_PIN_0 21
#define I2C_SCL_PIN_0 22
// ======= DEKLARASI OBJEK =======
DHT dht(DHTPIN, DHTTYPE);
Servo myServo;
RTC_DS3231 rtc;
// ======= VARIABEL GLOBAL =======
float suhu = NAN;
float kelembapan = NAN;
bool isAutoMode = false;
bool isIncubationStarted = false;
int currentDay = 1;
int currentServoPos = 40;
int servoAngleByDay = 40;
unsigned long lastDHTRead = 0;
const unsigned long DHT_INTERVAL = 10000;
DateTime tanggalMulai;
int overrideDay = 0;
unsigned long lastStatusPrint = 0;
const unsigned long STATUS_PRINT_INTERVAL = 2000;
// ======= ARRAY UNTUK FILTER SENSOR DHT =======
const int NUM_DHT_READINGS = 10;
float dhtTempReadings[NUM_DHT_READINGS];
float dhtHumReadings[NUM_DHT_READINGS];
int dhtReadingIndex = 0;
bool dhtReadingsInitialized = false;
// ======= ARRAY UNTUK HISTORI HARIAN =======
float dailyTemperatures[21];
float dailyHumidities[21];
int dailyVentAngles[21];
// ======= VARIABEL TAMBAHAN SISTEM =======
int angleReadingIndex = 0;
bool angleReadingsInitialized = false;
uint32_t lastTickUpdate = 0;
bool detachServoFlag = false;
uint32_t detachServoTick = 0;
uint32_t skipTick = 0;
// ======= FUNGSI PERHITUNGAN HARI INKUBASI =======
void hitungHariInkubasi() {
  if (overrideDay > 0) {
  } else if (!isIncubationStarted) {
  } else {
    DateTime now = rtc.now();
    TimeSpan durasi = now - tanggalMulai;
    currentDay = durasi.days() + 1;
    if (currentDay < 1) currentDay = 1;
    if (currentDay > 21) currentDay = 21;
  }
}
// ======= FUNGSI SET TANGGAL MULAI INKUBASI MANUAL =======
void setTanggalMulaiManualHariKe(int hariKe) {
  DateTime now = rtc.now();
  if (hariKe < 1) hariKe = 1;
  if (hariKe > 21) hariKe = 21;
  DateTime newStart = now - TimeSpan(hariKe - 1, 0, 0, 0);
  tanggalMulai = newStart;
  overrideDay = 0;
  currentDay = hariKe;
  isAutoMode = true;
  isIncubationStarted = true;
  tampilkanStatus();
  Serial.printf("Tgl mulai diset ke %02d-%02d-%04d (Hari %d), mode AUTO aktif.\n",
                tanggalMulai.day(), tanggalMulai.month(), tanggalMulai.year(), hariKe);
}
// ======= FUNGSI BACA SUHU TERFILTER =======
float readFilteredTemperature() {
  float rawTemp = dht.readTemperature();
  if (isnan(rawTemp)) {
    if (dhtReadingsInitialized || dhtReadingIndex > 0) {
      float sum = 0;
      int count = 0;
      if (dhtReadingsInitialized) {
        for (int i = 0; i < NUM_DHT_READINGS; i++) sum += dhtTempReadings[i];
        count = NUM_DHT_READINGS;
      } else {
        for (int i = 0; i < dhtReadingIndex; i++) sum += dhtTempReadings[i];
        count = dhtReadingIndex;
      }
      if (count > 0) return sum / count;
    }
    return suhu;
  }
  dhtTempReadings[dhtReadingIndex] = rawTemp;
  dhtReadingIndex = (dhtReadingIndex + 1) % NUM_DHT_READINGS;
  if (dhtReadingIndex == 0) dhtReadingsInitialized = true;
  float sum = 0;
  int count = 0;
  if (dhtReadingsInitialized) {
    for (int i = 0; i < NUM_DHT_READINGS; i++) sum += dhtTempReadings[i];
    count = NUM_DHT_READINGS;
  } else {
    for (int i = 0; i < dhtReadingIndex; i++) sum += dhtTempReadings[i];
    count = dhtReadingIndex;
  }
  if (count == 0) return rawTemp;
  return sum / count;
}
// ======= FUNGSI BACA KELEMBAPAN TERFILTER =======
float readFilteredHumidity() {
  float rawHum = dht.readHumidity();
  if (isnan(rawHum)) {
    if (dhtReadingsInitialized || dhtReadingIndex > 0) {
      float sum = 0;
      int count = 0;
      if (dhtReadingsInitialized) {
        for (int i = 0; i < NUM_DHT_READINGS; i++) sum += dhtHumReadings[i];
        count = NUM_DHT_READINGS;
      } else {
        for (int i = 0; i < dhtReadingIndex; i++) sum += dhtHumReadings[i];
        count = dhtReadingIndex;
      }
      if (count > 0) return sum / count;
    }
    return kelembapan;
  }
  dhtHumReadings[dhtReadingIndex] = rawHum;
  float sum = 0;
  int count = 0;
  if (dhtReadingsInitialized) {
    for (int i = 0; i < NUM_DHT_READINGS; i++) sum += dhtHumReadings[i];
    count = NUM_DHT_READINGS;
  } else {
    for (int i = 0; i < dhtReadingIndex; i++) sum += dhtHumReadings[i];
    count = dhtReadingIndex;
  }
  if (count == 0) return rawHum;
  return sum / count;
}
// ======= FUNGSI BACA SENSOR DHT SECARA PERIODIK =======
void bacaSensor() {
  suhu = readFilteredTemperature();
  kelembapan = readFilteredHumidity();
  Serial.printf("DHT: Suhu=%.2fC, Kelembapan=%.2f%%\n", suhu, kelembapan);
}
// ======= FUNGSI SIMPAN HISTORI HARIAN KE ARRAY =======
void saveDailyHistory(int dayIndex, float temp, float hum, int ventAngle) {
  if (dayIndex >= 0 && dayIndex < 21) {
    dailyTemperatures[dayIndex] = temp;
    dailyHumidities[dayIndex] = hum;
    dailyVentAngles[dayIndex] = ventAngle;
    Serial.printf("Riwayat Hari %d tersimpan: S=%.2f, K=%.2f, Vent=%d\n",
                  dayIndex + 1, temp, hum, ventAngle);
  } else {
    Serial.println("Indeks hari tidak valid.");
  }
}
// ======= FUNGSI RESET HISTORI HARIAN =======
void loadDailyHistory() {
  Serial.println("Inisialisasi riwayat harian...");
  for (int i = 0; i < 21; i++) {
    dailyTemperatures[i] = 0.0;
    dailyHumidities[i] = 0.0;
    dailyVentAngles[i] = 0;
  }
  Serial.println("Riwayat harian terinisialisasi.");
}
void clearDailyHistory() {
  Serial.println("Mengosongkan riwayat harian (RAM)...");
  for (int i = 0; i < 21; i++) {
    dailyTemperatures[i] = 0.0;
    dailyHumidities[i] = 0.0;
    dailyVentAngles[i] = 0;
  }
  Serial.println("Riwayat harian terkuras (RAM).");
}
// ======= FUNGSI SETUP AWAL SISTEM =======
void setup() {
  delay(2000);
  Serial.begin(115200);
  loadDailyHistory();
  Wire.begin(I2C_SDA_PIN_0, I2C_SCL_PIN_0);
  Serial.printf("I2C Bus 0 aktif: SDA=%d, SCL=%d\n", I2C_SDA_PIN_0, I2C_SCL_PIN_0);
  Serial.println("Inkubator ON");
  Serial.println("Memuat...");
  delay(2000);
  angleReadingsInitialized = true;
  angleReadingIndex = 0;
  dht.begin();
  Serial.println("Sensor DHT22 aktif.");
  pinMode(FAN_PIN_2, OUTPUT);
  pinMode(LIMIT_SWITCH_PIN, INPUT_PULLUP);
  digitalWrite(FAN_PIN_2, LOW);
  myServo.attach(SERVO_PIN);
  myServo.write(currentServoPos);
  if (!rtc.begin(&Wire)) {
    Serial.println("RTC tidak terdeteksi/gagal!");
    tanggalMulai = DateTime(F(__DATE__), F(__TIME__));
    isIncubationStarted = false;
    currentDay = 1;
  } else {
    Serial.println("RTC berhasil aktif.");
    if (rtc.lostPower()) {
      rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }
    isIncubationStarted = false;
    currentDay = 1;
    tanggalMulai = rtc.now();
  }
  if (rtc.now() < tanggalMulai) {
    tanggalMulai = rtc.now();
  }
  isAutoMode = false;
  currentDay = 1;
  Serial.println("Sistem ON. Mode MANUAL aktif.");
}
// ======= FUNGSI LOOP UTAMA =======
void loop() {
  unsigned long nowMillis = millis();
  // Filter agar loop tidak terlalu sering jalan
  if (nowMillis - skipTick >= 50) {
    skipTick = nowMillis;
  } else {
    return;
  }
  // ======= BAGIAN PEMROSESAN PERINTAH DARI SERIAL =======
  if (Serial.available()) {
    String input = Serial.readStringUntil('\n');
    input.trim();
    if (input.startsWith("start ")) {
      int d, m, y;
      if (sscanf(input.c_str(), "start %d-%d-%d", &d, &m, &y) == 3) {
        tanggalMulai = DateTime(y, m, d);
        currentDay = 1;
        isAutoMode = true;
        overrideDay = 0;
        isIncubationStarted = true;
        clearDailyHistory();
        Serial.printf("Inkubasi mulai: %02d-%02d-%04d (Hari %d).\n", d, m, y, currentDay);
      } else {
        Serial.println("Format salah! Gunakan: start DD-MM-YYYY");
      }
    } else if (input.startsWith("setday ")) {
      int h = input.substring(7).toInt();
      setTanggalMulaiManualHariKe(h);
    } else if (input.equalsIgnoreCase("auto")) {
      if (isIncubationStarted) {
        isAutoMode = true;
        Serial.println("Mode: AUTO aktif.");
      } else {
        Serial.println("Inkubasi belum dimulai. Gunakan 'start DD-MM-YYYY'.");
      }
    } else if (input.equalsIgnoreCase("manual")) {
      if (!isIncubationStarted) {
        isAutoMode = false;
        Serial.println("Mode: MANUAL aktif.");
      } else {
        Serial.println("Inkubasi sedang berjalan. Mode MANUAL tidak diizinkan.");
        isAutoMode = true;
      }
    } else if (input.equalsIgnoreCase("stop")) {
      isIncubationStarted = false;
      isAutoMode = false;
      currentDay = 1;
      tanggalMulai = rtc.now();
      clearDailyHistory();
      matikanSemua();
      Serial.println("Inkubasi berhenti. Aktuator OFF. Mode MANUAL aktif.");
    }
  }
  // ======= BAGIAN PERHITUNGAN PERGANTIAN HARI INKUBASI OTOMATIS =======
  if (isIncubationStarted && overrideDay == 0) {
    DateTime now = rtc.now();
    TimeSpan durasi = now - tanggalMulai;
    int calculatedDay = durasi.days() + 1;
    if (calculatedDay != currentDay && calculatedDay >= 1 && calculatedDay <= 21) {
      if (currentDay >= 1 && currentDay <= 21) {
        saveDailyHistory(currentDay - 1, suhu, kelembapan, currentServoPos);
      }
      currentDay = calculatedDay;
      Serial.printf("Hari berubah ke Hari %d. (%s)\n", currentDay, now.timestamp().c_str());
    }
  }
  // ======= BAGIAN PEMBACAAN SENSOR DHT PERIODIK =======
  if (nowMillis - lastDHTRead >= DHT_INTERVAL) {
    bacaSensor();
    lastDHTRead = nowMillis;
  }
  // ======= BAGIAN MODE OTOMATIS KONTROL KIPAS DAN SERVO =======
  if (isAutoMode) {
    bool fan2State = false;
    if (suhu > 38.0 || kelembapan > 70) {
      digitalWrite(FAN_PIN_2, HIGH);
      fan2State = true;
    } else if (suhu < 37.6 && kelembapan < 68) {
      digitalWrite(FAN_PIN_2, LOW);
      fan2State = false;
    }
    digitalWrite(FAN_PIN_2, fan2State ? HIGH : LOW);
    // ======= BAGIAN PENYESUAIAN SUDUT SERVO BERDASARKAN HARI INKUBASI =======
    switch (currentDay) {
      case 1: case 2: case 3:
        servoAngleByDay = 40;
        break;
      case 4:
        servoAngleByDay = 60;
        break;
      case 5:
        servoAngleByDay = 85;
        break;
      case 6:
        servoAngleByDay = 110;
        break;
      case 7 ... 17:
        servoAngleByDay = 135;
        break;
      case 18: case 19: case 20: case 21:
        servoAngleByDay = 40;
        break;
      default:
        servoAngleByDay = 40;
        break;
    }
    // ======= BAGIAN GERAKKAN SERVO VENTILASI JIKA PERLU =======
    if (currentServoPos != servoAngleByDay) {
      if (bolehGerakKe(servoAngleByDay)) {
        myServo.write(servoAngleByDay);
        Serial.printf("✅ Servo ke %d°\n", servoAngleByDay);
        delay(200);
        currentServoPos = servoAngleByDay;
        detachServoFlag = true;
        detachServoTick = millis();
      } else {
        Serial.println("🚫 Servo tertahan (limit switch/sudut tidak valid).");
      }
    } else {
      if (detachServoFlag) {
        if (millis() - detachServoTick >= 500) {
          detachServoFlag = false;
          myServo.detach();
          Serial.println("Servo terlepas");
        }
      }
    }
  } else {
    // ======= MODE MANUAL: KIPAS & SERVO KE KONDISI DEFAULT =======
    digitalWrite(FAN_PIN_2, LOW);
    myServo.write(40);
    currentServoPos = 40;
  }
  // ======= TAMPILKAN STATUS KE SERIAL SETIAP INTERVAL =======
  if (nowMillis - lastStatusPrint >= STATUS_PRINT_INTERVAL) {
    tampilkanStatus();
    lastStatusPrint = nowMillis;
  }
  // ======= PERHITUNGAN ULANG HARI INKUBASI TIAP 1 DETIK (REDUNDAN) =======
  if (nowMillis - lastTickUpdate >= 1000) {
    if (isIncubationStarted && overrideDay == 0) {
      DateTime now = rtc.now();
      TimeSpan durasi = now - tanggalMulai;
      int calculatedDay = durasi.days() + 1;
      if (calculatedDay != currentDay && calculatedDay >= 1 && calculatedDay <= 21) {
        saveDailyHistory(currentDay - 1, suhu, kelembapan, currentServoPos);
        currentDay = calculatedDay;
        Serial.printf("Hari berubah ke Hari %d.\n", currentDay);
      }
    }
    lastTickUpdate = nowMillis;
  }
}
// ======= FUNGSI MATIKAN SEMUA AKTUATOR =======
void matikanSemua() {
  digitalWrite(FAN_PIN_2, LOW);
  Serial.println("Kipas OFF.");
  myServo.write(40);
  currentServoPos = 40;
  Serial.println("Servo ventilasi ke 40°.");
  Serial.println("Semua aktuator OFF.");
}
// ======= FUNGSI TAMPILKAN STATUS SISTEM =======
void tampilkanStatus() {
  Serial.printf("Status: S=%.2fC, K=%.2f%%, Hari=%d, Vent=%d°\n",
                suhu, kelembapan, currentDay, currentServoPos);
}
// ======= FUNGSI CEK APAKAH SERVO BOLEH BERGERAK KE SUDUT TERTENTU =======
bool bolehGerakKe(int targetAngle) {
  if (digitalRead(LIMIT_SWITCH_PIN) == LOW) {
    Serial.println("Limit switch aktif, servo diblokir.");
    return false;
  }
  if (targetAngle < 40 || targetAngle > 135) {
    Serial.printf("Sudut target %d° di luar jangkauan (40-135°).\n", targetAngle);
    return false;
  }
  return true;
}