TKK_E32230939/esp32.ino

1039 lines
18 KiB
C++

#include "HX711.h"
#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include <Wire.h>
#include <RTClib.h>
#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);
}
}