Upload files to "/"
This commit is contained in:
commit
1f4a9497e7
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue