commit fa79a7f73ee6ddb44049cb955a57a4cb034e9116 Author: Atilah Date: Thu Jun 18 13:53:51 2026 +0700 Upload files to "/" diff --git a/Rangkaian_IOT_2.ino b/Rangkaian_IOT_2.ino new file mode 100644 index 0000000..70cc279 --- /dev/null +++ b/Rangkaian_IOT_2.ino @@ -0,0 +1,613 @@ +/***************** KONFIGURASI BLYNK *****************/ +#define BLYNK_TEMPLATE_ID "TMPL6IJqYTM5v" +#define BLYNK_TEMPLATE_NAME "IOT Hamster" +#define BLYNK_AUTH_TOKEN "cyVp3mgGDuB3pu0sGBoJlIGYKao-HKS1" + +/***************** LIBRARY *****************/ +#include +#include +#include +#include +#include "esp_system.h" +#include "DHT.h" +#include +#include +#include + +/***************** PIN SENSOR *****************/ +#define DHTPIN 4 +#define DHTTYPE DHT22 +#define DHT_POWER 16 +#define RELAY_KIPAS 27 +#define RELAY_PUMP 26 +#define SERVO_PIN 14 +#define WATER_SENSOR_PIN 35 + +DHT dht(DHTPIN, DHTTYPE); +RTC_DS3231 rtc; +Servo servo; + +/***************** VARIABEL *****************/ +int lastDay = -1; + +bool manualKipas = false; +bool manualPump = false; +float lastSuhu = NAN; + +bool feedingDoneToday = false; +bool servoAutoAktif = false; +unsigned long servoAutoMulai = 0; + +const int JAM_FEEDING = 18; +const int MENIT_FEEDING = 0; +const unsigned long DURASI_BUKA_SERVO = 300; + +unsigned long lastFeedingTime = 0; +const unsigned long INTERVAL_FEEDING = 60000; // 1 menit (60000 ms) + +/* ===== PERBAIKAN DHT ===== */ +bool pumpAktif = false; +unsigned long waktuPompaBerubah = 0; +bool dhtValid = false; +bool dhtEnabled = true; +unsigned long lastPrint = 0; +/* ======================== */ + +/* ===== AUTO RESET DHT NON-BLOCKING (MAX 3 DETIK) ===== */ +unsigned long dhtErrorSejak = 0; +unsigned long dhtResetMulai = 0; +bool dhtSedangReset = false; + +const unsigned long DHT_TIMEOUT = 3000; // +const unsigned long DHT_RESET_OFF = 500; // +/* ==================================================== */ + +/* ===== TAMBAHAN AUTO KIPAS ===== */ +unsigned long waktuKipasDimatikanManual = 0; +bool tungguAutoKipas = false; +/* ============================== */ + +/* ===== TAMBAHAN AUTO OFF POMPA ===== */ +const unsigned long POMPA_AUTO_OFF = 10000; +/* ================================= */ + +/* ===== VARIABEL WATER SENSOR ===== */ +int waterValue = 0; +int waterPercent = 0; // <-- TAMBAHAN +float waterFiltered = 0; +bool adaAir = false; +#define WATER_EMPTY 100 //air habis +#define WATER_FULL 950 //air penuh + +bool pompaOtomatisAktif = false; + +/* NAMA DAN PASSWORD WIFI */ + +char ssid[] = "OPPO Reno5 F"; +char pass[] = "87654321"; + +WiFiClientSecure client; +BlynkTimer timer; + +/***************** STATUS SERVO KE BLYNK (V5) *****************/ +void updateServoStatus(int posisi) { + if (posisi == 0) { + Blynk.virtualWrite(V5, "Tertutup"); + } else if (posisi == 90) { + Blynk.virtualWrite(V5, "Terbuka"); + } else { + Blynk.virtualWrite(V5, "Bergerak"); + } +} + +/************** CONTROL MANUAL DARI BLYNK (Kipas) **************/ +BLYNK_WRITE(V1) { + int state = param.asInt(); + manualKipas = true; + digitalWrite(RELAY_KIPAS, state ? LOW : HIGH); + + if (state == 0) { + waktuKipasDimatikanManual = millis(); + tungguAutoKipas = true; + } else { + tungguAutoKipas = false; + } +} + +/************** CONTROL MANUAL DARI BLYNK (Pompa) **************/ +BLYNK_WRITE(V2) { + int state = param.asInt(); + + if (waterValue >= WATER_FULL && state == 1) { + Blynk.virtualWrite(V2, 0); + return; + } + + manualPump = true; + pompaOtomatisAktif = false; + dhtEnabled = false; + + digitalWrite(RELAY_PUMP, state ? LOW : HIGH); + pumpAktif = state; + dhtEnabled = false; + waktuPompaBerubah = millis(); + + if (state == 1) { + dhtEnabled = false; + + String pesan = "šŸ’§ POMPA MANUAL MENYALA\n"; + pesan += "Status: Minum Dibuka\n"; + pesan += "Level Air: " + String(waterValue); + + kirimTelegram(pesan); + + } else { + dhtEnabled = true; + + String pesan = "šŸ’§ POMPA MANUAL DIMATIKAN\n"; + pesan += "Status: Minum Ditutup\n"; + pesan += "Level Air: " + String(waterValue); + + kirimTelegram(pesan); + } +} + +/************** CONTROL MANUAL DARI BLYNK (SERVO) **************/ +BLYNK_WRITE(V8) { + + if (param.asInt() == 1) { + + servo.write(90); + updateServoStatus(90); + + kirimLogFeeding("Manual Feeding Dimulai"); + + delay(1000); + + servo.write(0); + updateServoStatus(0); + + kirimLogFeeding("Manual Feeding Selesai"); + + Blynk.virtualWrite(V8, 0); + } +} + +/***************** KIRIM DATA KE BLYNK *****************/ +void sendToBlynk() { + + DateTime now = rtc.now(); + int jam = now.hour(); + int menit = now.minute(); + + Blynk.virtualWrite(V3, jam); + Blynk.virtualWrite(V4, menit); + + char waktu[10]; + sprintf(waktu, "%02d:%02d", jam, menit); + Blynk.virtualWrite(V7, waktu); + + if (dhtValid) { + Blynk.virtualWrite(V10, 1); + Blynk.virtualWrite(V11, "DHT BERFUNGSI"); + } else { + Blynk.virtualWrite(V10, 0); + Blynk.virtualWrite(V11, "DHT ERROR"); + } +} + +/***************** SETUP *****************/ +void setup() { + Serial.begin(115200); + + pinMode(DHT_POWER, OUTPUT); + digitalWrite(DHT_POWER, HIGH); + delay(2000); + + pinMode(RELAY_KIPAS, OUTPUT); + pinMode(RELAY_PUMP, OUTPUT); + digitalWrite(RELAY_KIPAS, HIGH); + digitalWrite(RELAY_PUMP, HIGH); + + pinMode(WATER_SENSOR_PIN, INPUT); // === TAMBAHAN === + + dht.begin(); + rtc.begin(); + // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); + + servo.attach(SERVO_PIN, 500, 2400); + servo.write(0); + updateServoStatus(0); + + Serial.println("\nšŸ“¶ WiFi Terhubung"); + Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass); + + timer.setInterval(2000L, sendToBlynk); + timer.setInterval(3000L, bacaDHT); + timer.setInterval(500L, bacaWaterSensor); // === TAMBAHAN === + // timer.setInterval(300000L, printSuhuSerial); + + Serial.println("Sistem IoT + Blynk berjalan..."); + //Pompa set adc + analogSetPinAttenuation(WATER_SENSOR_PIN, ADC_11db); + analogReadResolution(12); + timer.setInterval(30000L, kirimSuhuTelegram); // tiap 10 detik + +/***************** WAKTU *****************/ + configTime(7 * 3600, 0, "pool.ntp.org"); // WIB = GMT+7 + +struct tm timeinfo; +if (getLocalTime(&timeinfo)) { + rtc.adjust(DateTime( + timeinfo.tm_year + 1900, + timeinfo.tm_mon + 1, + timeinfo.tm_mday, + timeinfo.tm_hour, + timeinfo.tm_min, + timeinfo.tm_sec + )); +} +} + +/************** TELEGRAM CONFIG **************/ +const char* botToken = "8710332774:AAHdWZwsIB2KbkkuvWBnFwWQgeQ15Io_L_c"; +const char* chatID = "5535845901"; + +void kirimTelegram(String pesan) { + + client.setInsecure(); // bypass SSL + + if (!client.connect("api.telegram.org", 443)) { + Serial.println("Gagal koneksi ke Telegram"); + return; + } + + String url = "/bot" + String(botToken) + "/sendMessage?chat_id=" + String(chatID) + "&text=" + pesan; + + client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: api.telegram.org\r\n" + "Connection: close\r\n\r\n"); + + delay(500); + while (client.available()) { + client.read(); + } + + client.stop(); +} + +/***************** LOOP *****************/ +void loop() { + Blynk.run(); + timer.run(); + + autoFeedingRTC(); + + + if (dhtValid) { + if (!manualKipas) { + if (lastSuhu > 30) { + digitalWrite(RELAY_KIPAS, LOW); + Blynk.virtualWrite(V1, 1); + } else { + digitalWrite(RELAY_KIPAS, HIGH); + Blynk.virtualWrite(V1, 0); + } + } + } else { + digitalWrite(RELAY_KIPAS, HIGH); + } + + if (tungguAutoKipas && manualKipas && dhtValid) { + if (lastSuhu > 30 && millis() - waktuKipasDimatikanManual >= 5000) { + digitalWrite(RELAY_KIPAS, LOW); + Blynk.virtualWrite(V1, 1); + tungguAutoKipas = false; + Serial.println("šŸ”„ Kipas otomatis menyala kembali"); + } + } + + if (manualKipas && millis() - waktuKipasDimatikanManual > 10000) { + manualKipas = false; + } + + if (millis() - lastPrint >= 300000) { + + Serial.print("🌔 Suhu: "); + Serial.print(lastSuhu); + Serial.println(" °C"); + + lastPrint = millis(); +} + + // ===== AUTO RESET DHT NON-BLOCKING ===== + if (!dhtValid) { + + // mulai hitung error + if (dhtErrorSejak == 0 && !dhtSedangReset) { + dhtErrorSejak = millis(); + } + + // jika error >= 3 detik → mulai reset + if (!dhtSedangReset && millis() - dhtErrorSejak >= DHT_TIMEOUT) { + // Serial.println("āš ļø DHT error > 3 detik → reset dimulai"); + digitalWrite(DHT_POWER, LOW); // matikan DHT + dhtResetMulai = millis(); + dhtSedangReset = true; + } + + if (dhtSedangReset && millis() - dhtResetMulai >= DHT_RESET_OFF) { + digitalWrite(DHT_POWER, HIGH); + + // ===== RELEASE SYSTEM LOCK ===== + dhtSedangReset = false; + dhtErrorSejak = 0; + dhtEnabled = true; // šŸ”“ izinkan baca DHT lagi + pumpAktif = false; // šŸ”“ reset status pompa + Blynk.virtualWrite(V2, 0); + + Serial.println("šŸ”„ DHT reset selesai, sistem dilepas"); + } + + } else { + // jika DHT normal → reset semua flag + dhtErrorSejak = 0; + dhtSedangReset = false; + } + // ====================================== + + // ===== AUTO WATER LEVEL ===== + + // Jika air habis → aktifkan mode otomatis + if (!adaAir && !manualPump) { + if (!pumpAktif) { + pompaOtomatisAktif = true; + } + } + + // Jika tangki penuh → pompa harus mati + if (waterPercent >= 80) { + digitalWrite(RELAY_PUMP, HIGH); + pumpAktif = false; + pompaOtomatisAktif = false; + manualPump = false; + Blynk.virtualWrite(V2, 0); + String pesan = "āœ… POMPA OTOMATIS BERHENTI\n"; + pesan += "Alasan: Tangki Penuh\n"; + pesan += "Level Air: " + String(waterValue); + kirimTelegram(pesan); + } + + + // Jika mode otomatis aktif dan belum penuh → pompa nyala + if (!manualPump && pompaOtomatisAktif && waterValue < WATER_FULL) { + if (!pumpAktif) { + digitalWrite(RELAY_PUMP, LOW); + pumpAktif = true; + waktuPompaBerubah = millis(); + Blynk.virtualWrite(V2, 1); + String pesan = "🚰 POMPA OTOMATIS MENYALA\n"; + pesan += "Alasan: Air Habis\n"; + pesan += "Level Air: " + String(waterValue); + kirimTelegram(pesan); + } + } + + // ===== AUTO OFF POMPA + RESET MANUAL ===== + if (pumpAktif && !pompaOtomatisAktif && millis() - waktuPompaBerubah >= POMPA_AUTO_OFF) { + + digitalWrite(RELAY_PUMP, HIGH); + pumpAktif = false; + Blynk.virtualWrite(V2, 0); + + String pesan = "ā± POMPA DIMATIKAN OTOMATIS\n"; + pesan += "Alasan: Timeout 10 detik\n"; + pesan += "Level Air: " + String(waterValue); + kirimTelegram(pesan); + } + + // reset manual setelah 10 detik + if (manualPump && millis() - waktuPompaBerubah > 10000) { + manualPump = false; + } + + // =========================== +} + +/***************** FUNGSI BACA DHT *****************/ +void bacaDHT() { + + float suhuTotal = 0; + int berhasil = 0; + + for (int i = 0; i < 5; i++) { + + float t = dht.readTemperature(); + + if (!isnan(t)) { + suhuTotal += t; + berhasil++; + } + + vTaskDelay(250 / portTICK_PERIOD_MS); + } + + if (berhasil > 0) { + + lastSuhu = suhuTotal / berhasil; + dhtValid = true; + + Blynk.virtualWrite(V0, lastSuhu); + + } else { + + dhtValid = false; + + Serial.println("āŒ Gagal membaca DHT22"); + } +} +/***************** FUNGSI WATER SENSOR (TAMBAHAN) *****************/ +void bacaWaterSensor() { + + // ===== AMBIL BEBERAPA SAMPLE ===== + long totalADC = 0; + + for (int i = 0; i < 10; i++) { + totalADC += analogRead(WATER_SENSOR_PIN); + vTaskDelay(2 / portTICK_PERIOD_MS); + } + + // ===== RATA-RATA ADC ===== + int rawValue = totalADC / 10; + + // ===== FILTER SMOOTHING ===== + // lebih stabil dan tidak loncat-loncat + waterFiltered = (0.8 * waterFiltered) + (0.2 * rawValue); + + waterValue = (int)waterFiltered; + + // ===== KALIBRASI SENSOR ===== + // sesuaikan hasil Serial Monitor + int adcKosong = 250; + int adcPenuh = 950; + + waterPercent = map(waterValue, adcKosong, adcPenuh, 0, 100); + waterPercent = constrain(waterPercent, 0, 100); + + // ===== HYSTERESIS ===== + // supaya status tidak berubah-ubah + + static bool statusAir = false; + + // baru dianggap ADA AIR jika >5% + if (waterPercent >= 5) { + statusAir = true; + } + + // baru dianggap KOSONG jika <5% + if (waterPercent <= 5) { + statusAir = false; + } + + adaAir = statusAir; + + // ===== STATUS FULL ===== + static bool statusFull = false; + + if (waterPercent >= 100) { + statusFull = true; + } + + if (waterPercent <= 90) { + statusFull = false; + } + + // ===== KIRIM KE BLYNK ===== + static unsigned long lastBlynk = 0; + + if (millis() - lastBlynk >= 500) { + + Blynk.virtualWrite(V12, waterPercent); + + if (statusFull) { + Blynk.virtualWrite(V13, "FULL"); + } + else if (adaAir) { + Blynk.virtualWrite(V13, "AIR TERSEDIA"); + } + else { + Blynk.virtualWrite(V13, "KOSONG"); + } + + lastBlynk = millis(); + } + + // ===== SERIAL MONITOR ===== + static unsigned long lastSerial = 0; + + if (millis() - lastSerial >= 500) { + + // Serial.print("RAW ADC : "); + // Serial.print(rawValue); + + // Serial.print(" | FILTER : "); + // Serial.print(waterValue); + + // Serial.print(" | LEVEL : "); + // Serial.print(waterPercent); + + // Serial.print("% | STATUS : "); + + if (adaAir) { + // Serial.println("ADA AIR"); + } else { + // Serial.println("KOSONG"); + } + + lastSerial = millis(); + } +} + +/***************** LOG Feeding *****************/ +void kirimLogFeeding(String status) { + + DateTime now = rtc.now(); + + char waktu[20]; + sprintf(waktu, "%02d:%02d:%02d", + now.hour(), + now.minute(), + now.second()); + + String logFeeding = String(waktu) + " - " + status; + + Blynk.virtualWrite(V14, logFeeding); +} + + +/***************** AUTO FEEDING *****************/ +void autoFeedingRTC() { + + // === MULAI FEEDING SETIAP 1 MENIT === + if (!servoAutoAktif && millis() - lastFeedingTime >= INTERVAL_FEEDING) { + + servo.write(80); + updateServoStatus(80); + servoAutoMulai = millis(); + servoAutoAktif = true; + lastFeedingTime = millis(); + + String pesan = "🐹 AUTO FEEDING DIMULAI\n"; + pesan += "Interval: 1 Menit\n"; + pesan += "Status: Pakan Dibuka\n"; + + kirimTelegram(pesan); + // Blynk.virtualWrite(V9, "Auto Feeding Dimulai"); + kirimLogFeeding("Auto Feeding Dimulai"); + } + + // === TUTUP SETELAH DURASI === + if (servoAutoAktif && millis() - servoAutoMulai >= DURASI_BUKA_SERVO) { + + servo.write(0); + updateServoStatus(0); + servoAutoAktif = false; + + String pesan = "🐹 AUTO FEEDING SELESAI\n"; + pesan += "Status: Pakan Ditutup\n"; + + kirimTelegram(pesan); + // Blynk.virtualWrite(V9, "Auto Feeding Selesai"); + kirimLogFeeding("Auto Feeding Selesai"); + } +} +void kirimSuhuTelegram() { + if (dhtValid) { + String pesan = "🌔 Update Suhu:\n"; + pesan += "Suhu saat ini: "; + pesan += String(lastSuhu); + pesan += " °C"; + + kirimTelegram(pesan); + } +} + +