// 1602 MultiMenu v2.0
// by Myth!
// Intuitive multi-level menu navigation with customizable interface and button controls.
// Seamlessly integrate user-defined functions for efficient task execution.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Define pins for button controls
const int pinUp = 2;
const int pinDown = 3;
const int pinOK = 4;
const int pinCancel = 5;
// Define menu options for level 1 and level 2
const char* menuLevel1[] = {"Option 1", "Option 2", "Option 3", "Exit"};
const char* menuLevel2[][4] = {
{"Option 1.1", "Option 1.2", "Option 1.3", "Back"},
{"Option 2.1", "Option 2.2", "Option 2.3", "Back"},
{"Option 3.1", "Option 3.2", "Option 3.3", "Back"}
};
// Define custom characters for menu arrows
byte arrowUp[8] = {
B00000,
B00100,
B01110,
B11111,
B00000,
B00000,
B00000,
B00000
};
byte arrowDown[8] = {
B00000,
B00000,
B00000,
B11111,
B01110,
B00100,
B00000,
B00000
};
// Variables to track current selection and menu level
int selectionLevel1 = 0;
int selectionLevel2 = 0;
int currentLevel = 1;
// Setup function to initialize pins and LCD
void setup() {
pinMode(pinUp, INPUT_PULLUP);
pinMode(pinDown, INPUT_PULLUP);
pinMode(pinOK, INPUT_PULLUP);
pinMode(pinCancel, INPUT_PULLUP);
lcd.init();
lcd.backlight();
lcd.createChar(0, arrowUp);
lcd.createChar(1, arrowDown);
lcd.setCursor(0, 0);
lcd.print(" 1602 MultiMenu ");
lcd.setCursor(0, 1);
lcd.print(" by Myth! ");
delay(3000);
lcd.clear();
showMenuLevel1();
}
// Main loop for menu navigation and button handling
void loop() {
// Check if "Up" button is pressed
if (digitalRead(pinUp) == LOW) {
if (currentLevel == 1) {
// Move selection up in level 1 menu
selectionLevel1 = (selectionLevel1 - 1 + sizeof(menuLevel1) / sizeof(menuLevel1[0])) % (sizeof(menuLevel1) / sizeof(menuLevel1[0]));
showMenuLevel1();
} else {
// Move selection up in level 2 menu
selectionLevel2 = (selectionLevel2 - 1 + sizeof(menuLevel2[selectionLevel1]) / sizeof(menuLevel2[selectionLevel1][0])) % (sizeof(menuLevel2[selectionLevel1]) / sizeof(menuLevel2[selectionLevel1][0]));
showMenuLevel2();
}
delay(200);
}
// Check if "Down" button is pressed
if (digitalRead(pinDown) == LOW) {
if (currentLevel == 1) {
// Move selection down in level 1 menu
selectionLevel1 = (selectionLevel1 + 1) % (sizeof(menuLevel1) / sizeof(menuLevel1[0]));
showMenuLevel1();
} else {
// Move selection down in level 2 menu
selectionLevel2 = (selectionLevel2 + 1) % (sizeof(menuLevel2[selectionLevel1]) / sizeof(menuLevel2[selectionLevel1][0]));
showMenuLevel2();
}
delay(200);
}
// Check if "OK" button is pressed
if (digitalRead(pinOK) == LOW) {
if (currentLevel == 1) {
if (selectionLevel1 == sizeof(menuLevel1) / sizeof(menuLevel1[0]) - 1) {
// Exit program if "Exit" option is selected in level 1
lcd.clear();
lcd.print("Prog. terminated");
while (true);
} else {
// Enter level 2 menu
currentLevel = 2;
showMenuLevel2();
}
} else {
if (selectionLevel2 == sizeof(menuLevel2[selectionLevel1]) / sizeof(menuLevel2[selectionLevel1][0]) - 1) {
// Return to level 1 menu if "Back" option is selected in level 2
currentLevel = 1;
showMenuLevel1();
} else {
// Execute selected function
executeFunction(selectionLevel1, selectionLevel2);
}
}
delay(200);
}
// Check if "Cancel" button is pressed
if (digitalRead(pinCancel) == LOW) {
if (currentLevel == 2) {
// Return to level 1 menu if in level 2 menu
currentLevel = 1;
showMenuLevel1();
}
delay(200);
}
}
// Function to display level 1 menu on LCD
void showMenuLevel1() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(menuLevel1[selectionLevel1]);
lcd.setCursor(15, 0);
lcd.write(byte(0)); // Display up arrow
lcd.setCursor(15, 1);
lcd.write(byte(1)); // Display down arrow
}
// Function to display level 2 menu on LCD
void showMenuLevel2() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(menuLevel1[selectionLevel1]);
lcd.setCursor(15, 0);
lcd.write(byte(0)); // Display up arrow
lcd.setCursor(0, 1);
lcd.print(menuLevel2[selectionLevel1][selectionLevel2]);
lcd.setCursor(15, 1);
lcd.write(byte(1)); // Display down arrow
}
// Function to execute selected function
void executeFunction(int indexLevel1, int indexLevel2) {
lcd.clear();
lcd.print("Executing: ");
lcd.setCursor(0, 1);
lcd.print(menuLevel2[indexLevel1][indexLevel2]);
delay(3000);
lcd.clear();
lcd.print("Function done");
delay(2000);
showMenuLevel2();
}