From 517c134e2248e76f3d1ab10b8ec45b8fa5fa3e60 Mon Sep 17 00:00:00 2001 From: riofirmansyah39 Date: Thu, 2 Jul 2026 14:39:00 +0700 Subject: [PATCH] Upload files to "/" --- esp32.ino | 1039 +++++++++++++++++++++++++++++++++++++++++++++++++++ esp8266.ino | 174 +++++++++ 2 files changed, 1213 insertions(+) create mode 100644 esp32.ino create mode 100644 esp8266.ino diff --git a/esp32.ino b/esp32.ino new file mode 100644 index 0000000..d5e43cc --- /dev/null +++ b/esp32.ino @@ -0,0 +1,1039 @@ +#include "HX711.h" +#include +#include +#include +#include +#include "time.h" + +// ====================================================== +// WIFI +// ====================================================== +const char* ssid = "Rio"; +const char* password = "Rio1234567"; + +// ====================================================== +// FIREBASE +// ====================================================== +#define API_KEY "AIzaSyC-YYniuYaVJAZl0LywCbC-HKvMED0_uUY" +#define DATABASE_URL "https://poultryrail-1205e-default-rtdb.firebaseio.com/" +#define USER_EMAIL "esp32@gmail.com" +#define USER_PASSWORD "12345678" +#define FIREBASE_PROJECT_ID "poultryrail-1205e" + +FirebaseData fbdo; +FirebaseAuth auth; +FirebaseConfig config; + +// ====================================================== +// RTC +// ====================================================== +RTC_DS3231 rtc; + +// ====================================================== +// STEPPER A4988 +// ====================================================== +#define STEP_PIN 4 +#define DIR_PIN 2 +#define EN_PIN 5 + +long totalSteps = 3900; +long posisiStepper = 0; + +// ====================================================== +// HX711 +// ====================================================== +#define DT1 25 +#define SCK1 26 + +#define DT2 32 +#define SCK2 33 + +HX711 scale1; +HX711 scale2; + +float calibration_factor1 = 422; +float calibration_factor2 = 399; + +// ====================================================== +// TIMER +// ====================================================== +unsigned long lastRTC = 0; +unsigned long lastRead = 0; +unsigned long lastJadwal = 0; +unsigned long lastHeartbeat = 0; +unsigned long lastManual = 0; + +void updateHeartbeat() { + + if (millis() - lastHeartbeat >= 5000) { + + lastHeartbeat = millis(); + + Firebase.RTDB.setInt( + &fbdo, + "/status/heartbeat", + millis() + ); + } +} + +String lastTrigger = ""; + +bool wifiTerhubungSebelumnya = true; + +void kirimNotifikasi(String pesan) { + + Firebase.RTDB.setString( + &fbdo, + "/notifikasi/pesan", + pesan + ); + + Firebase.RTDB.setString( + &fbdo, + "/notifikasi/status", + "baru" + ); +} + +void simpanRiwayat( + String waktu, + String jam, + String tanggal, + String status +); + +void simpanRiwayatFirestore( + String waktu, + String jam, + String tanggal, + String status, + float beratKanan, + float beratKiri +); + +// ====================================================== +// STEPPER +// ====================================================== +void stepMotor(long jumlahStep, +int speedDelay, +bool maju) { + +for (long i = 0; i < jumlahStep; i++) { + + if (i % 500 == 0) { + updateHeartbeat(); + } + + +digitalWrite(STEP_PIN, HIGH); +delayMicroseconds(speedDelay); + +digitalWrite(STEP_PIN, LOW); +delayMicroseconds(speedDelay); +} + +// simpan posisi stepper +if (maju) { + +posisiStepper += jumlahStep; +} +else { + +posisiStepper -= jumlahStep; + +if (posisiStepper < 0) { +posisiStepper = 0; +} +} +} + +// ====================================================== +// BACA BERAT +// ====================================================== +float bacaBerat1() { + +float b1 = scale1.get_units(3); + +if (abs(b1) < 10) b1 = 0; + +return round(b1 * 10) / 10.0; +} + +float bacaBerat2() { + +float b2 = scale2.get_units(3); + +if (abs(b2) < 10) b2 = 0; + +return round(b2 * 10) / 10.0; +} + + +// BACA JARAK DARI FIREBASE + +float bacaJarakFirebase() { + +if (Firebase.RTDB.getFloat( +&fbdo, +"/sensor/jarak")) { + +return fbdo.floatData(); +} + +return 999; +} + +// ====================================================== +// HENTAKAN PAKAN +// ====================================================== +void hentakanPakan() { + +// maju sedikit +digitalWrite(DIR_PIN, HIGH); + +stepMotor(50, 800, true); + +delay(50); + +// mundur sedikit +digitalWrite(DIR_PIN, LOW); + +stepMotor(50, 800, false); + +delay(50); + +// balik maju lagi +digitalWrite(DIR_PIN, HIGH); +} + +void prosesPemberianPakan( + String key, + String waktu, + String jam, + String tanggal +) +{ +float jarakAwal = bacaJarakFirebase(); + +Serial.print("Jarak Awal: "); +Serial.println(jarakAwal); + +// stok kurang dari 15% +if (jarakAwal >= 13) { + +Serial.println("STOK PAKAN KURANG"); +Serial.println("JADWAL DIBATALKAN"); + +kirimNotifikasi( + "Pemberian pakan gagal karena stok habis" +); + +Firebase.RTDB.setString( +&fbdo, +"/jadwal/" + key + "/status", +"Pemberian Pakan gagal dijalankan karena stok habis" +); + +simpanRiwayatFirestore( +waktu, +jam, +tanggal, +"Pemberian Pakan gagal dijalankan karena stok habis", +bacaBerat1(), // kanan +bacaBerat2() // kiri +); + +Firebase.RTDB.deleteNode( + &fbdo, + "/jadwal/" + key +); + +return; +} + +float sisaKanan = bacaBerat1(); +float sisaKiri = bacaBerat2(); + +bool isiKanan = (sisaKanan <= 30); +bool isiKiri = (sisaKiri <= 30); + +// jika keduanya masih banyak +if (!isiKanan && !isiKiri) { + + String pesan = + "Pemberian pakan gagal karena kedua wadah masih diatas 30 gram"; + + Serial.println(pesan); + + if (key != "manual") { + + Firebase.RTDB.setString( + &fbdo, + "/jadwal/" + key + "/status", + pesan + ); + + Firebase.RTDB.deleteNode( + &fbdo, + "/jadwal/" + key + ); + } + + simpanRiwayatFirestore( + waktu, + jam, + tanggal, + pesan, + sisaKanan, + sisaKiri + ); + + return; +} + +Firebase.RTDB.setString( +&fbdo, +"/jadwal/" + key + "/status", +"running" +); + +// ====================================================== +// PAGI / SORE +// ====================================================== +int jumlahTitik; +long langkahPerTitik; + +if (waktu == "Pagi") { + +jumlahTitik = 3; +langkahPerTitik = 1250; +} +else { + +jumlahTitik = 3; +langkahPerTitik = 1250; +} + +// ====================================================== +// TARGET +// ====================================================== +float maxKeluar1 = 5.0; +float maxKeluar2 = 5.0; + +// ====================================================== +// DRIVER ON +// ====================================================== +digitalWrite(EN_PIN, LOW); + +// ====================================================== +// ARAH MAJU +// ====================================================== +digitalWrite(DIR_PIN, HIGH); + +// ====================================================== +// LOOP TITIK +// ====================================================== +for (int titik = 1; titik <= jumlahTitik; titik++) { + +updateHeartbeat(); + +// CEK STOK SAAT BERJALAN +float jarakSekarang = +bacaJarakFirebase(); + +Serial.print("Jarak Sekarang: "); +Serial.println(jarakSekarang); + +// stok habis +// ========================================== +// CEK ULANG STOK +// ========================================== +int deteksiHabis = 0; + +for (int i = 0; i < 3; i++) { + +float cekUlang = bacaJarakFirebase(); + +Serial.print("Cek Ulang "); +Serial.print(i); +Serial.print(" : "); +Serial.println(cekUlang); + +if (cekUlang >= 13) { + +deteksiHabis++; +} + +delay(50); +} + +// ========================================== +// JIKA 3X TERDETEK HABIS +// ========================================== +if (deteksiHabis >= 3) { + +Serial.println("PAKAN HABIS"); +Serial.println("KEMBALI KE AWAL"); + +kirimNotifikasi( + "Pakan habis saat proses pemberian berjalan" +); + +Firebase.RTDB.setString( +&fbdo, +"/servo1/action", +"close" +); + +Firebase.RTDB.setString( +&fbdo, +"/servo2/action", +"close" +); + +delay(300); + +digitalWrite(DIR_PIN, LOW); + +stepMotor(posisiStepper, 800, false); + +digitalWrite(EN_PIN, HIGH); + +Firebase.RTDB.setString( +&fbdo, +"/jadwal/" + key + "/status", +"Pemberian pakan belum tercapai stok habis dijalan" +); + +simpanRiwayatFirestore( +waktu, +jam, +tanggal, +"Pemberian pakan belum tercapai stok habis dijalan", +bacaBerat1(), // kanan +bacaBerat2() // kiri +); + +Firebase.RTDB.deleteNode( + &fbdo, + "/jadwal/" + key +); + +return; +} + +Serial.print("TITIK KE : "); +Serial.println(titik); + +// ========================================== +// STEPPER MAJU +// ========================================== +stepMotor(langkahPerTitik, 700, true); + +delay(300); + +// ========================================== +// BERAT AWAL +// ========================================== +float awal1 = bacaBerat1(); +float awal2 = bacaBerat2(); + +// ========================================== +// BUKA SERVO +// ========================================== +bool close1 = !isiKanan; +bool close2 = !isiKiri; + +if (isiKanan) { + + Firebase.RTDB.setString( + &fbdo, + "/servo2/action", + "open" + ); + + Serial.println("SERVO1 OPEN"); +} + +if (isiKiri) { + + Firebase.RTDB.setString( + &fbdo, + "/servo1/action", + "open" + ); + + Serial.println("SERVO2 OPEN"); +} + +// ========================================== +// LOOP PAKAN +// ========================================== +while (!close1 || !close2) { + +updateHeartbeat(); + +hentakanPakan(); + +float sekarang1 = bacaBerat1(); +float sekarang2 = bacaBerat2(); + +float keluar1 = +sekarang1 - awal1; + +float keluar2 = +sekarang2 - awal2; + +if (keluar1 < 0) keluar1 = 0; +if (keluar2 < 0) keluar2 = 0; + +Firebase.RTDB.setFloat( +&fbdo, +"/sensor/keluar1", +keluar1 +); + +Firebase.RTDB.setFloat( +&fbdo, +"/sensor/keluar2", +keluar2 +); + +Serial.print("Keluar1: "); +Serial.print(keluar1); + +Serial.print(" | Keluar2: "); +Serial.println(keluar2); + +// ========================================== +// SERVO1 CLOSE +// ========================================== +if (isiKanan && + !close1 && + keluar1 >= maxKeluar1) +{ + Firebase.RTDB.setString( + &fbdo, + "/servo2/action", + "close" + ); + + close1 = true; + + Serial.println("SERVO2 CLOSE"); +} + +// ========================================== +// SERVO2 CLOSE +// ========================================== +if (isiKiri && + !close2 && + keluar2 >= maxKeluar2) +{ + Firebase.RTDB.setString( + &fbdo, + "/servo1/action", + "close" + ); + + close2 = true; + + Serial.println("SERVO1 CLOSE"); +} + +delay(10); +} + +Serial.println("SEMUA SERVO TUTUP"); + +delay(300); +} + +// ====================================================== +// PAKSA TUTUP +// ====================================================== +Firebase.RTDB.setString( +&fbdo, +"/servo1/action", +"close" +); + +Firebase.RTDB.setString( +&fbdo, +"/servo2/action", +"close" +); + +delay(500); + +// ====================================================== +// BALIK AWAL +// ====================================================== +digitalWrite(DIR_PIN, LOW); + +stepMotor(posisiStepper, 800, false); + +// ====================================================== +// DRIVER OFF +// ====================================================== +digitalWrite(EN_PIN, HIGH); + +// ====================================================== +// DONE +// ====================================================== +if (key != "manual") { + + Firebase.RTDB.setString( + &fbdo, + "/jadwal/" + key + "/status", + "done" + ); + + Firebase.RTDB.deleteNode( + &fbdo, + "/jadwal/" + key + ); +} + +simpanRiwayatFirestore( +waktu, +jam, +tanggal, +"done", +bacaBerat1(), // kanan +bacaBerat2() // kiri +); + +Firebase.RTDB.deleteNode( + &fbdo, + "/jadwal/" + key +); + +kirimNotifikasi( + "Pemberian pakan selesai" +); + +Serial.println("SELESAI"); +} + +// ====================================================== +// CEK JADWAL +// ====================================================== +void cekJadwal() { + +if (!Firebase.ready()) return; + +if (!Firebase.RTDB.getJSON(&fbdo, "/jadwal")) { +return; +} + +FirebaseJson &json = fbdo.jsonObject(); + +size_t len = json.iteratorBegin(); + +if (len == 0) { + +json.iteratorEnd(); +return; +} + +DateTime now = rtc.now(); + +char jamNow[6]; + +sprintf(jamNow, +"%02d:%02d", +now.hour(), +now.minute()); + +String tglFix = +String(now.day()) + "-" + +String(now.month()) + "-" + +String(now.year()); + + +// ====================================================== +// LOOP JADWAL +// ====================================================== +for (size_t i = 0; i < len; i++) { + +String key, value; +int type; + +json.iteratorGet(i, type, key, value); + +FirebaseJsonData data; +json.get(data, key); + +FirebaseJson obj; +obj.setJsonData(data.stringValue); + +FirebaseJsonData jam, tanggal, status, waktu; + +obj.get(jam, "jam"); +obj.get(tanggal, "tanggal"); +obj.get(status, "status"); +obj.get(waktu, "waktu"); + +bool matchJam = +(jam.stringValue.substring(0,5) +== String(jamNow)); + +bool matchTanggal = +(tanggal.stringValue == tglFix); + +bool statusIdle = +(status.stringValue == "idle" || +status.stringValue == ""); + +String id = key + String(jamNow); + +// ====================================================== +// JALANKAN +// ====================================================== +if (matchJam && + matchTanggal && + statusIdle && + id != lastTrigger) +{ + lastTrigger = id; + + Serial.println("JADWAL JALAN"); + + Firebase.RTDB.setString( + &fbdo, + "/jadwal/" + key + "/status", + "running" + ); + + prosesPemberianPakan( + key, + waktu.stringValue, + jam.stringValue, + tanggal.stringValue + ); +} + +} +json.iteratorEnd(); +} + +void simpanRiwayatFirestore( + String waktu, + String jam, + String tanggal, + String status, + float beratKanan, + float beratKiri +) { + +FirebaseJson content; + +content.set("fields/waktu/stringValue", waktu); +content.set("fields/jam/stringValue", jam); +content.set("fields/tanggal/stringValue", tanggal); +content.set("fields/status/stringValue", status); + +content.set("fields/beratKanan/doubleValue", beratKanan); +content.set("fields/beratKiri/doubleValue", beratKiri); + +content.set( + "fields/timestamp/integerValue", + String(time(nullptr)) +); + + String documentPath = + "riwayat/" + String(millis()); + + Firebase.Firestore.createDocument( + &fbdo, + FIREBASE_PROJECT_ID, + "", + documentPath.c_str(), + content.raw()); +} + +void cekManual() { + + if (Firebase.RTDB.getString(&fbdo, "/manual/action")) { + + String cmd = fbdo.stringData(); + + if (cmd == "start") { + + Firebase.RTDB.setString( + &fbdo, + "/manual/action", + "running" + ); + + DateTime now = rtc.now(); + + char jamManual[6]; + sprintf( + jamManual, + "%02d:%02d", + now.hour(), + now.minute() + ); + + String tanggalManual = + String(now.day()) + "-" + + String(now.month()) + "-" + + String(now.year()); + + prosesPemberianPakan( + "manual", + "Manual", + String(jamManual), + tanggalManual + ); + + Firebase.RTDB.setString( + &fbdo, + "/manual/action", + "done" + ); + } + } +} + +// ====================================================== +// SETUP +// ====================================================== +void setup() { + +Serial.begin(115200); + +// ====================================================== +// STEPPER +// ====================================================== +pinMode(STEP_PIN, OUTPUT); +pinMode(DIR_PIN, OUTPUT); +pinMode(EN_PIN, OUTPUT); + +digitalWrite(EN_PIN, HIGH); + +// ====================================================== +// HX711 +// ====================================================== +scale1.begin(DT1, SCK1); +scale2.begin(DT2, SCK2); + +delay(1000); + +scale1.set_scale(calibration_factor1); +scale2.set_scale(calibration_factor2); + +scale1.tare(); +scale2.tare(); + +// ====================================================== +// RTC +// ====================================================== +Wire.begin(21, 22); + +if (!rtc.begin()) { + + Serial.println("RTC ERROR"); + + kirimNotifikasi( + "RTC DS3231 tidak terdeteksi" + ); + + while (1); +} + +// ====================================================== +// WIFI +// ====================================================== +WiFi.begin(ssid, password); + +Serial.print("CONNECT WIFI"); + +while (WiFi.status() != WL_CONNECTED) { + +delay(500); +Serial.print("."); +} + +Serial.println(""); +Serial.println("WIFI CONNECT"); + +// ====================================================== +// WAKTU INTERNET +// ====================================================== +configTime(7 * 3600, 0, +"pool.ntp.org", +"time.nist.gov"); + +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 +)); +} + +// ====================================================== +// FIREBASE +// ====================================================== +config.api_key = API_KEY; +config.database_url = DATABASE_URL; + +auth.user.email = USER_EMAIL; +auth.user.password = USER_PASSWORD; + +Firebase.begin(&config, &auth); +Firebase.reconnectWiFi(true); + +Serial.println("READY"); +} + +// ====================================================== +// LOOP +// ====================================================== +void loop() { + +// ======================================= +// CEK WIFI +// ======================================= +if (WiFi.status() != WL_CONNECTED) { + + if (wifiTerhubungSebelumnya) { + + Serial.println("WIFI PUTUS"); + + wifiTerhubungSebelumnya = false; + } + + WiFi.reconnect(); + + delay(1000); + + return; +} +else { + + // WiFi baru tersambung lagi + if (!wifiTerhubungSebelumnya) { + + wifiTerhubungSebelumnya = true; + + kirimNotifikasi( + "Koneksi WiFi kembali normal" + ); + } +} + +// ======================================= +// CEK FIREBASE +// ======================================= +if (!Firebase.ready()) { + + kirimNotifikasi( + "Koneksi ke Firebase terputus" + ); + + return; +} + +// ======================================= +// HEARTBEAT +// ======================================= +if (millis() - lastHeartbeat > 5000) { + + lastHeartbeat = millis(); + + Firebase.RTDB.setInt( + &fbdo, + "/status/heartbeat", + millis() + ); + +} + +// ====================================================== +// RTC +// ====================================================== +if (millis() - lastRTC > 5000) { + + Firebase.RTDB.setInt( + &fbdo, + "/status/heartbeat", + millis() +); + +lastRTC = millis(); + +DateTime now = rtc.now(); + +char waktu[9]; + +sprintf(waktu, +"%02d:%02d:%02d", +now.hour(), +now.minute(), +now.second()); + +Firebase.RTDB.setString( +&fbdo, +"/rtc/time", +waktu +); +} + + +// ====================================================== +// CEK JADWAL +// ====================================================== +if (millis() - lastJadwal > 5000) { + + lastJadwal = millis(); + + cekJadwal(); +} + +if (millis() - lastManual > 1000) { + + lastManual = millis(); + + cekManual(); +} + +// ====================================================== +// BERAT REALTIME +// ====================================================== +if (millis() - lastRead > 1000) { + +lastRead = millis(); + +float b1 = bacaBerat1(); +float b2 = bacaBerat2(); + +Firebase.RTDB.setFloat( +&fbdo, +"/sensor/berat1", +b1 +); + +Firebase.RTDB.setFloat( +&fbdo, +"/sensor/berat2", +b2 +); + +Serial.print("B1: "); +Serial.print(b1); + +Serial.print(" | B2: "); +Serial.println(b2); +} +} \ No newline at end of file diff --git a/esp8266.ino b/esp8266.ino new file mode 100644 index 0000000..6e23a7b --- /dev/null +++ b/esp8266.ino @@ -0,0 +1,174 @@ +#include +#include +#include + +// ================= WIFI ================= +const char* ssid = "Rio"; +const char* password = "Rio1234567"; + +// ================= FIREBASE ================= +#define API_KEY "AIzaSyC-YYniuYaVJAZl0LywCbC-HKvMED0_uUY" +#define DATABASE_URL "https://poultryrail-1205e-default-rtdb.firebaseio.com/" +#define USER_EMAIL "esp32@gmail.com" +#define USER_PASSWORD "12345678" + +FirebaseData fbdo; +FirebaseAuth auth; +FirebaseConfig config; + +// ================= SERVO ================= +Servo servo1; +Servo servo2; + +#define SERVO1_PIN 14 +#define SERVO2_PIN 12 + +// ================= ULTRASONIK ================= +#define TRIG_PIN 2 +#define ECHO_PIN 5 + +unsigned long lastServo = 0; +unsigned long lastUltra = 0; + +// ================= STATUS ================= +String lastCmd1 = ""; +String lastCmd2 = ""; + +void setup() { + +Serial.begin(115200); + +// ================= SERVO ================= +servo1.attach(SERVO1_PIN); +servo2.attach(SERVO2_PIN); + +servo1.writeMicroseconds(1500); +servo2.writeMicroseconds(1500); + +// ================= ULTRASONIK ================= +pinMode(TRIG_PIN, OUTPUT); +pinMode(ECHO_PIN, INPUT); + +// ================= WIFI ================= +WiFi.begin(ssid, password); + +while (WiFi.status() != WL_CONNECTED) { + +delay(300); +Serial.print("."); +} + +Serial.println("\nWiFi Connected"); + +// ================= FIREBASE ================= +config.api_key = API_KEY; +config.database_url = DATABASE_URL; + +auth.user.email = USER_EMAIL; +auth.user.password = USER_PASSWORD; + +Firebase.begin(&config, &auth); +Firebase.reconnectWiFi(true); +} + +// ================= LOOP ================= +void loop() { + +// ================= WIFI ================= +if (WiFi.status() != WL_CONNECTED) { + +WiFi.reconnect(); +} + +// ================= SERVO ================= +if (millis() - lastServo > 150) { + +lastServo = millis(); + +// ================= SERVO1 ================= +if (Firebase.RTDB.getString( +&fbdo, +"/servo1/action")) { + +String cmd1 = fbdo.stringData(); + +if (cmd1 != lastCmd1) { + +lastCmd1 = cmd1; + +if (cmd1 == "open") { + +servo1.writeMicroseconds(1900); + +Serial.println("Servo1 OPEN"); + +} else { + +servo1.writeMicroseconds(1500); + +Serial.println("Servo1 CLOSE"); +} +} +} + +// ================= SERVO2 ================= +if (Firebase.RTDB.getString( +&fbdo, +"/servo2/action")) { + +String cmd2 = fbdo.stringData(); + +if (cmd2 != lastCmd2) { + +lastCmd2 = cmd2; + +if (cmd2 == "open") { + +servo2.writeMicroseconds(1100); + +Serial.println("Servo2 OPEN"); + +} else { + +servo2.writeMicroseconds(1500); + +Serial.println("Servo2 CLOSE"); +} +} +} +} + +// ================= ULTRASONIK ================= +if (millis() - lastUltra > 1000) { + +lastUltra = millis(); + +digitalWrite(TRIG_PIN, LOW); +delayMicroseconds(2); + +digitalWrite(TRIG_PIN, HIGH); +delayMicroseconds(10); + +digitalWrite(TRIG_PIN, LOW); + +long duration = +pulseIn(ECHO_PIN, HIGH); + +float distance = +duration * 0.034 / 2; + +if (distance > 2 && +distance < 400) { + +Firebase.RTDB.setFloat( +&fbdo, +"/sensor/jarak", +distance +); + +Serial.print("Jarak: "); +Serial.println(distance); +} +} +} +