From 1f4a9497e742535ca608ad274ccf0501c68b8b26 Mon Sep 17 00:00:00 2001 From: Anakayutserian Date: Thu, 3 Jul 2025 16:19:29 +0700 Subject: [PATCH] Upload files to "/" --- Kode_Arduino_Sistem_Ventilasi_Cerdas.ino | 431 +++++++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 Kode_Arduino_Sistem_Ventilasi_Cerdas.ino diff --git a/Kode_Arduino_Sistem_Ventilasi_Cerdas.ino b/Kode_Arduino_Sistem_Ventilasi_Cerdas.ino new file mode 100644 index 0000000..8af53c3 --- /dev/null +++ b/Kode_Arduino_Sistem_Ventilasi_Cerdas.ino @@ -0,0 +1,431 @@ +// ======= IMPORT LIBRARY & DEFINISI PIN ======= +#include +#include +#include +#include + +#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; +}