Upload files to "/"

This commit is contained in:
Atilah 2026-06-18 13:53:51 +07:00
commit fa79a7f73e
1 changed files with 613 additions and 0 deletions

613
Rangkaian_IOT_2.ino Normal file
View File

@ -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 <WiFi.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <BlynkSimpleEsp32.h>
#include "esp_system.h"
#include "DHT.h"
#include <Wire.h>
#include <RTClib.h>
#include <ESP32Servo.h>
/***************** 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);
}
}