#include #include #include #include #include #include #include #include "addons/TokenHelper.h" #include "addons/RTDBHelper.h" #include #define LCD_ADDR 0x27 #define LCD_COLS 16 #define LCD_ROWS 2 #define API_KEY "AIzaSyBvMyCNKKCBrSjDaArHc3VF7TpjqJOYNS4" #define DATABASE_URL "https://jam-makan-default-rtdb.asia-southeast1.firebasedatabase.app" // Pin konfigurasi #define SERVO_PIN 13 // Inisialisasi komponen Servo servo; RTC_DS3231 rtc; LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS); FirebaseData fbdo; FirebaseAuth auth; FirebaseConfig config; #define JSON_DOCUMENT_SIZE 1024 StaticJsonDocument doc; // Deklarasi Fungsi String hitungWaktuPakanTerdekat(int jam, int menit, int pagi_hour, int pagi_minute, int siang_hour, int siang_minute, int malam_hour, int malam_minute); DateTime kalibrasiWaktu(const DateTime& dt); // Tambahkan deklarasi fungsi kalibrasiWaktu unsigned long sendDataPrevMillis = 0; bool signupOK = false; unsigned long servoFinishTime = 0; const unsigned long fetchInterval = 15000; // 15 detik String waktuPakanTerdekat; bool servoFinished = false; // Variabel untuk menyimpan jumlah putaran servo untuk setiap waktu makan int pagiPutaran = 0; int siangPutaran = 0; int malamPutaran = 0; // Variabel status untuk melacak aktivasi servo bool pagiPakanDiberikan = false; bool siangPakanDiberikan = false; bool malamPakanDiberikan = false; const char* mqtt_server = "broker.hivemq.com"; const char* topic = "piCo/manualFeed"; WiFiClient espClient; PubSubClient client(espClient); // Fungsi untuk kalibrasi waktu RTC DateTime kalibrasiWaktu(const DateTime& dt) { int seconds = dt.second() + 15; int minutes = dt.minute(); int hours = dt.hour(); int days = dt.day(); int months = dt.month(); int years = dt.year(); // Atur detik if (seconds >= 60) { seconds -= 60; minutes++; if (minutes >= 60) { minutes -= 60; hours++; if (hours >= 24) { hours -= 24; days++; // Anda bisa menambahkan kode untuk menangani bulan dan tahun jika diperlukan } } } return DateTime(years, months, days, hours, minutes, seconds); } void setup() { Serial.begin(57600); Wire.begin(); servo.attach(SERVO_PIN); if (!rtc.begin()) { Serial.println("RTC tidak ditemukan!"); while (1); } if (rtc.lostPower()) { Serial.println("RTC kehilangan daya, set waktu ke waktu kompilasi!"); rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); } lcd.init(); lcd.backlight(); // Tampilkan pesan selamat datang lcd.setCursor(0, 0); lcd.print(" PiCo"); lcd.setCursor(0, 1); lcd.print(" Initializing"); // Tunggu beberapa detik delay(2000); // Inisialisasi WiFi Serial.println(); Serial.println("Menghubungkan ke WiFi..."); WiFi.begin("POCO F4", "123456789"); // Ganti SSID dan password dengan yang sesuai while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi terhubung"); Serial.println("Alamat IP: "); Serial.println(WiFi.localIP()); Serial.println("Menghubungkan ke Firebase..."); setupFirebase(); client.setServer(mqtt_server, 1883); client.setCallback(callback); while (!client.connected()) { String client_id = "esp32-client-"; client_id += String(WiFi.macAddress()); Serial.printf("The client %s connects to the public mqtt broker\n", client_id.c_str()); if (client.connect(client_id.c_str())) { Serial.println("Public emqx mqtt broker connected"); } else { Serial.print("failed with state "); Serial.print(client.state()); delay(2000); } } // publish and subscribe client.publish(topic, "Hi EMQX I'm ESP32 ^^"); client.subscribe(topic); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect("ESP32Client")) { Serial.println("connected"); // Subscribe client.subscribe("piCo/manualFeed"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 5 seconds before retrying delay(5000); } } } void callback(char* topic, byte* message, unsigned int length) { Serial.print("Message arrived on topic: "); Serial.print(topic); Serial.print(". Message: "); String messageTemp; for (int i = 0; i < length; i++) { Serial.print((char)message[i]); messageTemp += (char)message[i]; } Serial.println(); if (String(topic) == "piCo/manualFeed") { if (messageTemp == "Makan") { Serial.println("Pemberian pakan manual"); activateServo(1); // Ganti angka dengan jumlah putaran servo yang diinginkan } } } void loop() { DateTime now = rtc.now(); // Kalibrasi waktu DateTime kalibrasiNow = kalibrasiWaktu(now); if (!client.connected()) { reconnect(); } client.loop(); getDataFromFirebase(kalibrasiNow); tampilkanWaktuDanJadwal(kalibrasiNow); delay(1000); } // Fungsi untuk mengaktifkan servo void activateServo(int putaran) { Serial.print("Makan Done"); servo.write(90); // Putar servo ke posisi tengah (contoh) delay(1000); // Tunggu satu detik for (int i = 0; i < putaran; i++) { servo.write(90); // Putar servo ke posisi maksimal delay(1000); // Tunggu satu detik servo.write(0); // Kembalikan servo ke posisi awal (contoh) delay(1000); // Tunggu satu detik } servo.write(90); // Kembalikan servo ke posisi tengah (contoh) Serial.println("Makan selesai"); servoFinished = true; // Set flag untuk menampilkan notifikasi "selesai" servoFinishTime = millis(); // Catat waktu selesai servo } // Fungsi untuk mengonfigurasi Firebase void setupFirebase() { config.api_key = API_KEY; config.database_url = DATABASE_URL; if (Firebase.signUp(&config, &auth, "", "")) { Serial.println("Signup berhasil"); signupOK = true; } else { Serial.printf("Signup gagal, %s\n", config.signer.signupError.message.c_str()); } config.token_status_callback = tokenStatusCallback; // Mengatur callback status token Firebase.begin(&config, &auth); Firebase.reconnectWiFi(true); } void getDataFromFirebase(const DateTime& dt) { Serial.println("Masuk ke fungsi getDataFromFirebase"); if (Firebase.ready() && signupOK) { Serial.println("Firebase siap dan signup berhasil"); if (Firebase.RTDB.getJSON(&fbdo, "/jam_makan")) { Serial.println("Data diambil dari Firebase"); // Deserialisasi JSON DeserializationError error = deserializeJson(doc, fbdo.to()); if (error) { Serial.print("deserializeJson() gagal: "); Serial.println(error.c_str()); } else { Serial.println("deserializeJson() berhasil"); const char* malam = doc["malam"]; const char* pagi = doc["pagi"]; const char* siang = doc["siang"]; pagiPutaran = doc["pagiPutaran"]; siangPutaran = doc["siangPutaran"]; malamPutaran = doc["malamPutaran"]; Serial.print("Malam: "); Serial.println(malam); Serial.print("Pagi: "); Serial.println(pagi); Serial.print("Siang: "); Serial.println(siang); Serial.print("Putaran Pagi: "); Serial.println(pagiPutaran); Serial.print("Putaran Siang: "); Serial.println(siangPutaran); Serial.print("Putaran Malam: "); Serial.println(malamPutaran); int jam = dt.hour(); int menit = dt.minute(); String malamStr = String(malam); String pagiStr = String(pagi); String siangStr = String(siang); int malam_hour = malamStr.substring(0, malamStr.indexOf(":")).toInt(); int malam_minute = malamStr.substring(malamStr.indexOf(":") + 1).toInt(); int pagi_hour = pagiStr.substring(0, pagiStr.indexOf(":")).toInt(); int pagi_minute = pagiStr.substring(pagiStr.indexOf(":") + 1).toInt(); int siang_hour = siangStr.substring(0, siangStr.indexOf(":")).toInt(); int siang_minute = siangStr.substring(siangStr.indexOf(":") + 1).toInt(); // Menghitung waktu pakan terdekat waktuPakanTerdekat = hitungWaktuPakanTerdekat(jam, menit, pagi_hour, pagi_minute, siang_hour, siang_minute, malam_hour, malam_minute); // Memastikan servo hanya berputar sekali pada waktu yang ditentukan if (jam == malam_hour && menit == malam_minute && !malamPakanDiberikan) { activateServo(malamPutaran); malamPakanDiberikan = true; } else if (jam != malam_hour || menit != malam_minute) { malamPakanDiberikan = false; } if (jam == pagi_hour && menit == pagi_minute && !pagiPakanDiberikan) { activateServo(pagiPutaran); pagiPakanDiberikan = true; } else if (jam != pagi_hour || menit != pagi_minute) { pagiPakanDiberikan = false; } if (jam == siang_hour && menit == siang_minute && !siangPakanDiberikan) { activateServo(siangPutaran); siangPakanDiberikan = true; } else if (jam != siang_hour || menit != siang_minute) { siangPakanDiberikan = false; } Serial.println("Waktu pakan terdekat: " + waktuPakanTerdekat); Serial.print("Jam RTC: "); Serial.print(jam); Serial.print(":"); Serial.println(menit); } } else { Serial.println("Gagal mengambil data dari Firebase: " + fbdo.errorReason()); } } else { Serial.println("Firebase tidak siap atau signup gagal"); } } String hitungWaktuPakanTerdekat(int jam, int menit, int pagi_hour, int pagi_minute, int siang_hour, int siang_minute, int malam_hour, int malam_minute) { int currentMinutes = jam * 60 + menit; int pagiMinutes = pagi_hour * 60 + pagi_minute; int siangMinutes = siang_hour * 60 + siang_minute; int malamMinutes = malam_hour * 60 + malam_minute; int minDiff = 1440; // Jumlah menit dalam satu hari String nearestTime; if (pagiMinutes > currentMinutes && pagiMinutes - currentMinutes < minDiff) { minDiff = pagiMinutes - currentMinutes; nearestTime = String(pagi_hour) + ":" + (pagi_minute < 10 ? "0" : "") + String(pagi_minute); } if (siangMinutes > currentMinutes && siangMinutes - currentMinutes < minDiff) { minDiff = siangMinutes - currentMinutes; nearestTime = String(siang_hour) + ":" + (siang_minute < 10 ? "0" : "") + String(siang_minute); } if (malamMinutes > currentMinutes && malamMinutes - currentMinutes < minDiff) { minDiff = malamMinutes - currentMinutes; nearestTime = String(malam_hour) + ":" + (malam_minute < 10 ? "0" : "") + String(malam_minute); } // Jika tidak ada waktu pakan tersisa untuk hari ini, set ke jadwal pakan pertama besok if (nearestTime == "") { nearestTime = String(pagi_hour) + ":" + (pagi_minute < 10 ? "0" : "") + String(pagi_minute); } return nearestTime; } void tampilkanWaktuDanJadwal(const DateTime& dt) { char currentTime[16]; snprintf(currentTime, sizeof(currentTime), "%02d:%02d:%02d", dt.hour(), dt.minute(), dt.second()); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Waktu: "); lcd.print(currentTime); lcd.setCursor(0, 1); if (servoFinished) { if (millis() - servoFinishTime < 5000) { lcd.print("Pakan selesai"); } else { servoFinished = false; waktuPakanTerdekat = hitungWaktuPakanTerdekat(dt.hour(), dt.minute(), doc["pagi"].as(), doc["pagi"].as(), doc["siang"].as(), doc["siang"].as(), doc["malam"].as(), doc["malam"].as()); // Perbarui jadwal berikutnya } } else { // Tambahkan 0 di depan jam jika jam kurang dari 10 if (waktuPakanTerdekat[1] == ':') { lcd.print("Pakan: 0"); lcd.print(waktuPakanTerdekat); } else { lcd.print("Pakan: "); lcd.print(waktuPakanTerdekat); } } }