#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
byte arrowSelect[] = { B10000, B11000, B11100, B11110, B11100, B11000, B10000, B00000 };
byte titikTitik[] = { B00000, B00000, B00000, B00000, B00000, B00000, B00000, B10101 };
// BUTTON
const int buttonPinA = 2;
const int buttonPinB = 4;
const int buttonPinSW = 3;
int buttonState = HIGH;
int buttonStatePrev = HIGH;
String prevMenu;
volatile bool clsRequested = false;
const int MAX_SUBMENUS = 8;
const int MAX_ITEMS = 8;
const int itemNameLength = 20;
int cursorPosition = 0;
int tempCursorPositionMain = 0;
int tempCursorPositionSub = 0;
int itemsPerPage = 3;
int index_mainMenu = 0;
int index_subMenu = 0;
int index_subSubMenu = 0;
char mainMenuItems[][itemNameLength] = { { "Kucing" }, { "Litter Box" }, { "Stepper" }, { "Servo" }, { "Makan & Minum" }, { "Cek Sensor" }, { "Kalibrasi" }, { "Setting" } };
char subMenuItems[MAX_SUBMENUS][MAX_ITEMS][itemNameLength] = {
{ "Informasi", "Kembali" },
{ "Ketinggian Pasir", "Posisi Tabung", "Kembali" },
{ "Homing", "Jarak Deteksi", "Kecepatan", "Kembali" },
{ "Sudut Desinfektan", "Kembali" },
{ "Jadwal Makan", "Berat Makan", "Jumlah Air", "Kembali" },
{ "Ultrasonic", "LIDAR", "PIR", "Load Cell", "Kembali" },
{ "Stepper", "Servo", "LIDAR", "Ultrasonic", "Load Cell", "Pengisian Pasir", "Pengisian Makan", "Kembali" },
{ "Cahaya Latar", "Durasi Latar", "Bahasa", "Kembali" }
};
char subSubMenuItems[8][7][4][12] = {
{ { "Nama" } },
{ { "Periksa", "Rata-rata", "Kembali" }, { "Periksa", "Sudut", "Kembali" } },
{ { "Posisi", "Set", "Kembali" }, { "Jarak", "Kembali" }, { "Kecepatan", "Kembali" } },
{ { "Sudut", "Set", "Kembali" } },
{ { "Set Jam", "Set Menit", "Kembali" }, { "Default", "Custom", "Kembali" }, { "Berat Air", "Kembali" } },
{ { "Cek Jarak", "Kembali" }, { "Cek Jarak", "Kembali" }, { "Cek Deteksi", "Kembali" }, { "Cek Berat", "Kembali" } },
{ { "Putaran", "Kalibrasi", "Kembali" }, { "Sudut", "Kalibrasi", "Kembali" }, { "Jarak", "Kalibrasi", "Kembali" }, { "Jarak", "Kalibrasi", "Kembali" }, { "Berat", "Kalibrasi", "Kembali" }, { "Durasi", "Kalibrasi", "Kembali" }, { "Durasi", "Kalibrasi", "Kembali" } },
{ { "Cahaya Ltr", "Kembali" }, { "Durasi", "Kembali" }, { "Bahasa", "Kembali" } },
};
const int numItemsMainMenu = sizeof(mainMenuItems) / sizeof(mainMenuItems[0]);
enum menuState { MAIN_MENU,
SUB_MENU,
SUB_SUB_MENU };
menuState currentMenuState = MAIN_MENU; // State awal
enum Language { ID,
EN };
Language currentLanguage = ID; // Default bahasa
//Setting Nilai
bool settingMode = false;
bool periksaKetinggianPasir = false;
bool periksaPosisiTabung = false;
bool setBacklight = true;
bool tensioToMotor = false;
int PWM_Value = 0;
int ketinggianPasir = 0;
int posisiTabung = 0;
void setup() {
//Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.createChar(0, arrowSelect);
lcd.createChar(1, titikTitik);
pinMode(buttonPinSW, INPUT_PULLUP);
pinMode(buttonPinA, INPUT);
pinMode(buttonPinB, INPUT);
attachInterrupt(digitalPinToInterrupt(buttonPinA), isr, LOW);
attachInterrupt(digitalPinToInterrupt(buttonPinSW), button, CHANGE);
}
void loop() {
clearScreen();
updateDisplay();
blinkCursor();
handleCursorPosition();
//Serial.println(getNumItems());
}
void button() {
buttonState = digitalRead(buttonPinSW);
if (buttonState == LOW && buttonStatePrev == HIGH) {
handleButtonPress();
clsRequested = true;
}
buttonStatePrev = buttonState;
}
void isr() {
static unsigned long lastInterruptTime = 0;
unsigned long currentTime = millis();
if (currentTime - lastInterruptTime > 5) {
handleEncoderRotation();
clsRequested = true;
lastInterruptTime = currentTime;
}
}
void updateDisplay() {
int offset = (20 - getTitle().length()) / 2;
lcd.setCursor(offset, 0);
lcd.print(getTitle());
int startItem = (cursorPosition / itemsPerPage) * itemsPerPage + 1;
for (int i = 0; i < itemsPerPage; i++) {
int menuItemIndex = startItem + i;
if ((currentMenuState == MAIN_MENU || currentMenuState == SUB_MENU) && currentMenuState != SUB_SUB_MENU) {
lcd.setCursor(1, i + 1);
lcd.print(menuItemIndex <= getNumItems() ? String(menuItemIndex) : " ");
}
if (currentMenuState == MAIN_MENU) {
lcd.setCursor(3, i + 1);
lcd.print(menuItemIndex <= getNumItems() ? getMenuText(mainMenuItems[menuItemIndex - 1]) : " ");
} else if (currentMenuState == SUB_MENU) {
lcd.setCursor(3, i + 1);
lcd.print(menuItemIndex <= getNumItems() ? getMenuText(subMenuItems[index_mainMenu][menuItemIndex - 1]) : " ");
} else {
lcd.setCursor(2, i + 1);
lcd.print(menuItemIndex <= getNumItems() ? getMenuText(subSubMenuItems[index_mainMenu][index_subMenu][menuItemIndex - 1]) : " ");
displayValueSubSubMenu();
}
}
}
void displayValueSubSubMenu() {
// Menampilkan submenu yang lebih dalam sesuai kebutuhan
switch (index_mainMenu) {
case 0: // Kucing
if (index_subMenu == 0) {
lcd.setCursor(11, 1);
lcd.print("Kucing1");
}
break;
case 1: // Litter Box
switch (index_subMenu) {
case 0: // Ketinggian Pasir
printSubSubMenu(periksaKetinggianPasir ? "Check." : "OFF", String(ketinggianPasir).c_str(), "", "cm");
break;
case 1: // Posisi Tabung
printSubSubMenu(periksaPosisiTabung ? "Set..." : "OFF", String(ketinggianPasir).c_str(), "", String((char)223).c_str());
// Fungsi deteksi
deteksiPosisi();
break;
}
break;
case 2: // Stepper
switch (index_subMenu) {
case 0: // Homing
printSubSubMenu(tensioToMotor ? "Motor" : "Tensio", "Idle", "", "");
break;
case 1: // Jarak Deteksi
printSubSubMenu("0", "", "cm", "");
// Fungsi deteksi
deteksiPosisi();
break;
case 2: // Kecepatan
printSubSubMenu("0", "", "/s", "");
// Fungsi deteksi
deteksiPosisi();
break;
}
break;
case 3: // Servo
switch (index_subMenu) {
case 0: // Sudut Servo
// Pilih Posisi
printSubSubMenu("0", "Idle", String((char)223).c_str(), "");
break;
}
break;
case 4: // Makan & Minum
switch (index_subMenu) {
case 0: // Jadwal Makan
printSubSubMenu("17", "30", "", "");
break;
case 1: // Berat Makan
printSubSubMenu("17", "30", "mg", "mg");
break;
case 2: // Berat Air
printSubSubMenu("15", "", "mg", "");
break;
}
break;
case 5: // Cek Sensor
switch (index_subMenu) {
case 0: // Ultrasonic
printSubSubMenu("167", "", "cm", "");
break;
case 1: // Lidar
printSubSubMenu("527", "", "cm", "");
break;
case 2: // PIR
printSubSubMenu("True", "", "", "");
break;
case 3: // Load Cell
printSubSubMenu("74", "", "gr", "");
break;
}
break;
case 7: // Setting
switch (index_subMenu) {
case 0: // Backlight
printSubSubMenu(setBacklight ? "ON" : "ON/OFF", "", "", "");
break;
case 1: // Durasi
printSubSubMenu("4", "", "s", "");
break;
case 2: // Durasi
printSubSubMenu(currentLanguage == ID ? "ID" : "EN", "", "", "");
break;
}
break;
default:
// Tambahkan logika untuk menu lainnya jika diperlukan
break;
}
}
void printSubSubMenu(const char *value1, const char *value2, const char *unit1, const char *unit2) {
lcd.setCursor(14, 1);
lcd.print(value1);
lcd.setCursor(18, 1);
lcd.print(unit1);
lcd.setCursor(14, 2);
lcd.print(value2);
lcd.setCursor(18, 2);
lcd.print(unit2);
}
void handleButtonPress() {
if (currentMenuState == MAIN_MENU) {
if (cursorPosition >= 0 && cursorPosition < numItemsMainMenu) {
index_mainMenu = cursorPosition;
tempCursorPositionMain = cursorPosition;
cursorPosition = 0; //Reset 0 saat masuk ke submenu
currentMenuState = SUB_MENU;
}
} else if (currentMenuState == SUB_MENU) {
if (cursorPosition >= 0 && cursorPosition < getNumItems() - 1) {
index_subMenu = cursorPosition;
tempCursorPositionSub = cursorPosition;
cursorPosition = 0; //Reset 0 saat masuk ke subsubmenu
currentMenuState = SUB_SUB_MENU;
} else if (cursorPosition == getNumItems() - 1) {
tempCursorPositionSub = 0; //Reset 0 kembali saat kembali ke mainmenu
cursorPosition = tempCursorPositionMain;
currentMenuState = MAIN_MENU;
}
} else {
if (cursorPosition >= 0 && cursorPosition < getNumItems() - 1) {
index_subSubMenu = cursorPosition;
if (index_mainMenu == 1 && index_subMenu == 0 && index_subSubMenu == 0) {
periksaKetinggianPasir = !periksaKetinggianPasir;
} else if (index_mainMenu == 1 && index_subMenu == 1 && index_subSubMenu == 0) {
periksaPosisiTabung = !periksaPosisiTabung;
} else if (index_mainMenu == 2 && index_subMenu == 0 && index_subSubMenu == 0) {
tensioToMotor = !tensioToMotor;
} else if (index_mainMenu == 7 && index_subMenu == 0 && index_subSubMenu == 0) {
setBacklight = !setBacklight;
} else if (index_mainMenu == 7 && index_subMenu == 2 && index_subSubMenu == 0) {
if (currentLanguage == ID) {
currentLanguage = EN;
} else {
currentLanguage = ID;
}
} else {
settingMode = !settingMode;
}
} else if (cursorPosition == getNumItems() - 1) {
cursorPosition = tempCursorPositionSub;
currentMenuState = SUB_MENU;
}
}
}
void handleEncoderRotation() {
const int direction = (digitalRead(buttonPinB) == LOW) ? 1 : -1;
if (settingMode) {
if (index_mainMenu == 1) {
if (index_subMenu == 0) {
if (index_subSubMenu == 0) {
//periksaKetinggianPasir = (direction == 1);
}
}
}
} else {
cursorPosition += direction;
}
}
void handleCursorPosition() {
//if ((currentMenuState == MAIN_MENU || currentMenuState == SUB_MENU) && currentMenuState != SUB_SUB_MENU) {
if (cursorPosition < 0) {
cursorPosition = 0;
} else if (cursorPosition >= getNumItems()) {
cursorPosition = getNumItems() - 1;
}
}
String getTitle() {
switch (currentMenuState) {
case MAIN_MENU:
return currentLanguage == ID ? "Menu Utama" : "Main Menu";
case SUB_MENU:
return currentLanguage == ID ? String(mainMenuItems[index_mainMenu]) : getMenuText(mainMenuItems[index_mainMenu]);
default:
String splitTitle1 = currentLanguage == ID ? String(mainMenuItems[index_mainMenu]) : getMenuText(mainMenuItems[index_mainMenu]);
String splitTitle2 = currentLanguage == ID ? String(subMenuItems[index_mainMenu][index_subMenu]) : getMenuText(subMenuItems[index_mainMenu][index_subMenu]);
splitTitle1.replace(" ", "");
splitTitle2.replace(" ", "");
splitTitle1 = splitTitle1.substring(0, min(6, splitTitle1.length()));
splitTitle2 = splitTitle2.substring(0, min(6, splitTitle2.length()));
return splitTitle1 + (char)1 + " > " + splitTitle2 + (char)1;
}
}
int getNumItems() {
if (currentMenuState == MAIN_MENU) {
return numItemsMainMenu;
} else if (currentMenuState == SUB_MENU) {
int numItemsSubMenu = 0;
for (int i = 0; i < MAX_ITEMS && subMenuItems[index_mainMenu][i][0] != '\0'; i++) {
numItemsSubMenu++;
}
return numItemsSubMenu;
} else {
int numItemsSubSubMenu = 0;
for (int i = 0; i < MAX_ITEMS && subSubMenuItems[index_mainMenu][index_subMenu][i][0] != '\0'; i++) {
numItemsSubSubMenu++;
}
return numItemsSubSubMenu;
}
}
void clearScreen() {
if (clsRequested) {
lcd.clear();
clsRequested = false;
}
}
void blinkCursor() {
static unsigned long previousMillis = 0;
int interval = 300;
static int blinkState = LOW;
if (millis() - previousMillis > interval) {
previousMillis = millis();
blinkState = !blinkState;
if (settingMode) {
lcd.setCursor(13, (cursorPosition % itemsPerPage) + 1);
} else {
lcd.setCursor(0, (cursorPosition % itemsPerPage) + 1);
}
if (blinkState) {
lcd.write(0);
} else {
lcd.print(" ");
}
}
}
// Fungsi untuk mendapatkan teks menu sesuai bahasa
String getMenuText(const char *text) {
if (currentLanguage == ID) {
return String(text);
} else {
// Daftar terjemahan menu dari Bahasa ke Inggris
if (String(text) == "Menu Utama") return "Main Menu";
if (String(text) == "Kucing") return "Cat";
if (String(text) == "Nama") return "Name";
if (String(text) == "Kotak Pasir") return "Litter Box";
if (String(text) == "Makan & Minum") return "Food & Drink";
if (String(text) == "Cek Sensor") return "Sensor Check";
if (String(text) == "Kalibrasi") return "Calibration";
if (String(text) == "Pengaturan") return "Settings";
if (String(text) == "Informasi") return "Information";
if (String(text) == "Ketinggian Pasir") return "Sand Height";
if (String(text) == "Posisi Tabung") return "Tube Position";
if (String(text) == "Jarak Deteksi") return "Detection Dist.";
if (String(text) == "Kecepatan") return "Speed";
if (String(text) == "Sudut Desinfektan") return "Disinfectant Ang";
if (String(text) == "Jadwal Makan") return "Feeding Schedule";
if (String(text) == "Berat Makan") return "Food Weight";
if (String(text) == "Jumlah Air") return "Water Amount";
if (String(text) == "Ultrasonik") return "Ultrasonic";
if (String(text) == "Pengisian Pasir") return "Sand Filling";
if (String(text) == "Pengisian Makan") return "Food Filling";
if (String(text) == "Cahaya Latar") return "Backlight";
if (String(text) == "Cahaya Ltr") return "Backlight";
if (String(text) == "Durasi Latar") return "Screen Duration";
if (String(text) == "Durasi") return "Duration";
if (String(text) == "Bahasa") return "Language";
if (String(text) == "Kembali") return "Back";
if (String(text) == "Periksa") return "Check";
if (String(text) == "Rata-rata") return "Average";
if (String(text) == "Sudut") return "Angle";
if (String(text) == "Set Jam") return "Set Hour";
if (String(text) == "Set Menit") return "Set Minute";
if (String(text) == "Berat Air") return "Water Weight";
if (String(text) == "Cek Jarak") return "Check Distance";
if (String(text) == "Cek Deteksi") return "Check Detect.";
if (String(text) == "Cek Berat") return "Check Weight";
if (String(text) == "Putaran") return "Rotation";
// Tambahkan terjemahan lain jika diperlukan
}
return String(text);
}
//Fungsi Alat
void deteksiPosisi() {
}