add Arduino code folder

This commit is contained in:
konten-quotes 2025-07-18 15:54:28 +07:00
parent 0bcd9c4d21
commit 17079b84d0
1 changed files with 497 additions and 0 deletions

497
Arduino/medibox.ino Normal file
View File

@ -0,0 +1,497 @@
#include <LiquidCrystal_I2C.h>
#include <time.h>
#include <FirebaseESP32.h>
#include <Wire.h>
#include "RTClib.h"
#include <Stepper.h>
#include <ArduinoJson.h>
#include <WiFiManager.h>
// Firebase config
#define FIREBASE_HOST "https://coba-smartmedibox-default-rtdb.firebaseio.com"
#define FIREBASE_AUTH "AIzaSyAUVen3yiSUrNJw7HnRif0_IBmUdeStY0c"
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
bool signupOK = false;
// RTC
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Stepper motor
const int stepsPerRevolution = 2038;
#define IN1 32
#define IN2 33
#define IN3 25
#define IN4 26
Stepper kotak1(stepsPerRevolution, IN1, IN3, IN2, IN4);
#define IN5 17
#define IN6 5
#define IN7 18
#define IN8 19
Stepper kotak2(stepsPerRevolution, IN5, IN7, IN6, IN8);
// Jadwal dari Firebase
int kotak1Schedule[4][2];
int kotak2Schedule[4][2];
bool kotak1Activated[4] = {false, false, false, false};
bool kotak2Activated[4] = {false, false, false, false};
bool kotak1AlarmActive[4] = {false, false, false, false};
bool kotak2AlarmActive[4] = {false, false, false, false};
// Pin tambahan
const int buzzerPin = 27;
const int ledPin = 14;
const int buttonPin1 = 34;
const int buttonPin2 = 23;
int lastButtonState1 = LOW;
bool buttonPressed1 = false;
bool lastButtonStatus1 = false;
bool buttonStatus1 = false;
int lastButtonState2 = LOW;
bool buttonPressed2 = false;
bool lastButtonStatus2 = false;
bool buttonStatus2 = false;
unsigned long buttonTriggerTime1 = 0;
bool shouldResetFirebaseButton1 = false;
unsigned long buttonTriggerTime2 = 0;
bool shouldResetFirebaseButton2 = false;
unsigned long lastUpdateTime = 0;
const unsigned long updateInterval = 60000; // 60 detik
unsigned long lastUpdateTime2 = 0;
const unsigned long updateInterval2 = 30000; // 60 detik
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
lcd.init();
lcd.backlight();
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Hubungkan Wifi");
WiFi.mode(WIFI_STA);
WiFiManager wm;
wm.resetSettings();
bool res;
res = wm.autoConnect("AutoConnectAP", "password");
Serial.println("Hubungkan Wifi.....");
if (!res) {
Serial.println("Gagal konek");
} else {
Serial.println("Berhasil konek");
}
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Wifi Terhubung");
delay(1000);
config.database_url = FIREBASE_HOST;
config.api_key = FIREBASE_AUTH;
if (Firebase.signUp(&config, &auth, "", "")) {
Serial.println("Firebase signup ok");
signupOK = true;
} else {
Serial.printf("%s\n", config.signer.signupError.message.c_str());
}
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
if (!rtc.begin()) {
Serial.println("RTC tidak terdeteksi!");
while (1);
} else {
setRTCfromNTP(); // Sinkronkan waktu RTC dari internet saat awal nyala
}
kotak1.setSpeed(5);
kotak2.setSpeed(5);
pinMode(buzzerPin, OUTPUT);
pinMode(ledPin, OUTPUT);
pinMode(buttonPin1, INPUT);
pinMode(buttonPin2, INPUT);
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("Smart Medicine");
lcd.setCursor(6, 1);
lcd.print("Box");
delay(2000);
updateFromFirebase();
}
void setRTCfromNTP() {
configTime(25200, 0, "pool.ntp.org"); // WIB = UTC+7 = 25200 detik offset
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
));
Serial.println("RTC disinkronkan dengan waktu NTP (WIB)");
} else {
Serial.println("Gagal mendapatkan waktu dari NTP");
}
}
void updateFromFirebase() {
// Kotak 1
for (int i = 0; i < 4; i++) {
String pathJam = "/jadwal/kotak1/" + String(i) + "/jam";
String pathMenit = "/jadwal/kotak1/" + String(i) + "/menit";
if (Firebase.getInt(fbdo, pathJam)) {
kotak1Schedule[i][0] = fbdo.intData();
} else {
kotak1Schedule[i][0] = -1; // Tandai tidak valid
}
if (Firebase.getInt(fbdo, pathMenit)) {
kotak1Schedule[i][1] = fbdo.intData();
} else {
kotak1Schedule[i][1] = -1;
}
}
// Kotak 2
for (int i = 0; i < 4; i++) {
String pathJam = "/jadwal/kotak2/" + String(i) + "/jam";
String pathMenit = "/jadwal/kotak2/" + String(i) + "/menit";
if (Firebase.getInt(fbdo, pathJam)) {
kotak2Schedule[i][0] = fbdo.intData();
} else {
kotak2Schedule[i][0] = -1;
}
if (Firebase.getInt(fbdo, pathMenit)) {
kotak2Schedule[i][1] = fbdo.intData();
} else {
kotak2Schedule[i][1] = -1;
}
}
fbdo.clear(); // Bersihkan setelah digunakan
}
void lcdI2c() {
DateTime now = rtc.now();
int waktuSekarang = now.hour() * 60 + now.minute();
int waktuTerdekat1 = 24 * 60 + 1;
int indexTerdekat1 = -1;
int waktuTerdekatBesok1 = 24 * 60 + 1;
int indexTerdekatBesok1 = -1;
int waktuTerdekat2 = 24 * 60 + 1;
int indexTerdekat2 = -1;
int waktuTerdekatBesok2 = 24 * 60 + 1;
int indexTerdekatBesok2 = -1;
// Cari jadwal terdekat kotak 1
for (int i = 0; i < 4; i++) {
int jam = kotak1Schedule[i][0];
int menit = kotak1Schedule[i][1];
if (jam < 0 || menit < 0) continue;
int waktuJadwal = jam * 60 + menit;
if (waktuJadwal >= waktuSekarang && waktuJadwal - waktuSekarang < waktuTerdekat1) {
waktuTerdekat1 = waktuJadwal - waktuSekarang;
indexTerdekat1 = i;
}
if (waktuJadwal < waktuTerdekatBesok1) {
waktuTerdekatBesok1 = waktuJadwal;
indexTerdekatBesok1 = i;
}
}
// Cari jadwal terdekat kotak 2
for (int i = 0; i < 4; i++) {
int jam = kotak2Schedule[i][0];
int menit = kotak2Schedule[i][1];
if (jam < 0 || menit < 0) continue;
int waktuJadwal = jam * 60 + menit;
if (waktuJadwal >= waktuSekarang && waktuJadwal - waktuSekarang < waktuTerdekat2) {
waktuTerdekat2 = waktuJadwal - waktuSekarang;
indexTerdekat2 = i;
}
if (waktuJadwal < waktuTerdekatBesok2) {
waktuTerdekatBesok2 = waktuJadwal;
indexTerdekatBesok2 = i;
}
}
lcd.clear();
// Tampilkan Kotak 1
lcd.setCursor(0, 0);
lcd.print("Kotak 1 = ");
int jam1 = -1, menit1 = -1;
if (indexTerdekat1 != -1) {
jam1 = kotak1Schedule[indexTerdekat1][0];
menit1 = kotak1Schedule[indexTerdekat1][1];
} else if (indexTerdekatBesok1 != -1) {
jam1 = kotak1Schedule[indexTerdekatBesok1][0];
menit1 = kotak1Schedule[indexTerdekatBesok1][1];
}
if (jam1 != -1 && menit1 != -1) {
if (jam1 < 10) lcd.print("0");
lcd.print(jam1);
lcd.print(":");
if (menit1 < 10) lcd.print("0");
lcd.print(menit1);
} else {
lcd.print("--:--");
}
// Tampilkan Kotak 2
lcd.setCursor(0, 1);
lcd.print("Kotak 2 = ");
int jam2 = -1, menit2 = -1;
if (indexTerdekat2 != -1) {
jam2 = kotak2Schedule[indexTerdekat2][0];
menit2 = kotak2Schedule[indexTerdekat2][1];
} else if (indexTerdekatBesok2 != -1) {
jam2 = kotak2Schedule[indexTerdekatBesok2][0];
menit2 = kotak2Schedule[indexTerdekatBesok2][1];
}
if (jam2 != -1 && menit2 != -1) {
if (jam2 < 10) lcd.print("0");
lcd.print(jam2);
lcd.print(":");
if (menit2 < 10) lcd.print("0");
lcd.print(menit2);
} else {
lcd.print("--:--");
}
}
void loop() {
if (!Firebase.ready()) {
//Serial.println("Firebase tidak ready, mencoba reconnect...");
Firebase.begin(&config, &auth);
}
unsigned long currentMillis = millis();
// Perbarui jadwal dari Firebase setiap 60 detik
if (currentMillis - lastUpdateTime > updateInterval) {
updateFromFirebase();
lastUpdateTime = currentMillis;
}
unsigned long currentMillis2 = millis();
// Perbarui jadwal dari Firebase setiap 60 detik
if (currentMillis2 - lastUpdateTime2 > updateInterval2) {
lcdI2c();
lastUpdateTime2 = currentMillis2;
}
DateTime now = rtc.now();
int h = now.hour();
int m = now.minute();
// Deteksi tombol 1 fisik
int currentButtonState1 = digitalRead(buttonPin1);
if (currentButtonState1 == HIGH && lastButtonState1 == LOW) {
buttonPressed1 = true;
Serial.println("Tombol 1 fisik ditekan");
}
lastButtonState1 = currentButtonState1;
// Deteksi tombol 2 fisik
int currentButtonState2 = digitalRead(buttonPin2);
if (currentButtonState2 == HIGH && lastButtonState2 == LOW) {
buttonPressed2 = true;
Serial.println("Tombol 2 fisik ditekan");
}
lastButtonState2 = currentButtonState2;
// Baca status dari Firebase (dari APK)
Firebase.getBool(fbdo, "/status/button1");
bool firebaseButton1 = fbdo.boolData();
// Baca status dari Firebase (dari APK)
Firebase.getBool(fbdo, "/status/button2");
bool firebaseButton2 = fbdo.boolData();
// Jika tombol fisik ditekan
if (buttonPressed1) {
Firebase.setBool(fbdo, "/status/button1", true);
buttonTriggerTime1 = millis();
shouldResetFirebaseButton1 = true;
}
// Jika tombol fisik ditekan
if (buttonPressed2) {
Firebase.setBool(fbdo, "/status/button2", true);
buttonTriggerTime2 = millis();
shouldResetFirebaseButton2 = true;
}
// Jika tombol ditekan dari APK
if (firebaseButton1 && !lastButtonStatus1) {
buttonStatus1 = true;
buttonTriggerTime1 = millis();
shouldResetFirebaseButton1 = true;
}
lastButtonStatus1 = firebaseButton1;
// Jika tombol ditekan dari APK
if (firebaseButton2 && !lastButtonStatus2) {
buttonStatus2 = true;
buttonTriggerTime2 = millis();
shouldResetFirebaseButton2 = true;
}
lastButtonStatus2 = firebaseButton2;
// Cek dan jalankan alarm Stepper 1
for (int i = 0; i < 4; i++) {
int th = kotak1Schedule[i][0];
int tm = kotak1Schedule[i][1];
// Jika waktunya tiba dan belum dipicu
if (h == th && m == tm && !kotak1Activated[i]) {
kotak1Activated[i] = true;
kotak1AlarmActive[i] = true; // aktifkan alarm
kotak1.step(128);
String pathJumlahObat1 = "/jadwal/kotak1/jumlahObat";
if (Firebase.getInt(fbdo, pathJumlahObat1)) {
int jumlahObat1 = fbdo.intData();
if (jumlahObat1 > 0) {
jumlahObat1--;
Firebase.setInt(fbdo, pathJumlahObat1, jumlahObat1);
Serial.printf("Jumlah obat kotak1 berkurang menjadi %d\n", jumlahObat1);
} else {
Serial.println("Jumlah obat habis atau gagal dibaca");
}
}
}
// 🔔 Jika alarm aktif, tapi tombol belum ditekan
if (kotak1AlarmActive[i]) {
if (buttonPressed1 || buttonStatus1) {
kotak1AlarmActive[i] = false; // matikan alarm
digitalWrite(buzzerPin, LOW);
digitalWrite(ledPin, LOW);
Firebase.setBool(fbdo, "/status/kotak1", true);
Firebase.setBool(fbdo, "/status/buzzer", false);
Firebase.setBool(fbdo, "/status/led", false);
} else {
digitalWrite(ledPin, HIGH);
tone(buzzerPin, 1000);
delay(400);
noTone(buzzerPin);
delay(400);
Firebase.setBool(fbdo, "/status/alarm1", true);
Firebase.setBool(fbdo, "/status/buzzer", true);
Firebase.setBool(fbdo, "/status/led", true);
}
}
// Reset aktivasi stepper agar bisa dipicu besok pada jam yang sama
if (h != th || m != tm) {
kotak1Activated[i] = false;
}
}
// Cek dan jalankan alarm Stepper 2
for (int i = 0; i < 4; i++) {
int th = kotak2Schedule[i][0];
int tm = kotak2Schedule[i][1];
// Jika waktunya tiba dan belum dipicu
if (h == th && m == tm && !kotak2Activated[i]) {
kotak2Activated[i] = true;
kotak2AlarmActive[i] = true; // aktifkan alarm
kotak2.step(128);
String pathJumlahObat2 = "/jadwal/kotak2/jumlahObat";
if (Firebase.getInt(fbdo, pathJumlahObat2)) {
int jumlahObat2 = fbdo.intData();
if (jumlahObat2 > 0) {
jumlahObat2--;
Firebase.setInt(fbdo, pathJumlahObat2, jumlahObat2);
Serial.printf("Jumlah obat kotak2 berkurang menjadi %d\n", jumlahObat2);
} else {
Serial.println("Jumlah obat habis atau gagal dibaca");
}
}
}
// 🔔 Jika alarm aktif, tapi tombol belum ditekan
if (kotak2AlarmActive[i]) {
if (buttonPressed2 || buttonStatus2) {
kotak2AlarmActive[i] = false; // matikan alarm
digitalWrite(buzzerPin, LOW);
digitalWrite(ledPin, LOW);
Firebase.setBool(fbdo, "/status/kotak2", true);
Firebase.setBool(fbdo, "/status/buzzer", false);
Firebase.setBool(fbdo, "/status/led", false);
} else {
digitalWrite(ledPin, HIGH);
tone(buzzerPin, 1000);
delay(400);
noTone(buzzerPin);
delay(400);
Firebase.setBool(fbdo, "/status/alarm2", true);
Firebase.setBool(fbdo, "/status/buzzer", true);
Firebase.setBool(fbdo, "/status/led", true);
}
}
// Reset aktivasi stepper agar bisa dipicu besok pada jam yang sama
if (h != th || m != tm) {
kotak2Activated[i] = false;
}
}
// Reset tombol Firebase ke false setelah 1 detik
if (shouldResetFirebaseButton1 && millis() - buttonTriggerTime1 >= 2500) {
Firebase.setBool(fbdo, "/status/button1", false);
Firebase.setBool(fbdo, "/status/kotak1", false);
Firebase.setBool(fbdo, "/status/alarm1", false);
shouldResetFirebaseButton1 = false;
}
// Reset tombol Firebase ke false setelah 1 detik
if (shouldResetFirebaseButton2 && millis() - buttonTriggerTime2 >= 2500) {
Firebase.setBool(fbdo, "/status/button2", false);
Firebase.setBool(fbdo, "/status/kotak2", false);
Firebase.setBool(fbdo, "/status/alarm2", false);
shouldResetFirebaseButton2 = false;
}
buttonPressed1 = false;
buttonStatus1 = false;
buttonPressed2 = false;
buttonStatus2 = false;
}