#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>
// Initialize LCD
LiquidCrystal_I2C lcd(0x27, 20, 4); // Change 0x27 to your LCD's I2C address
// Initialize Encoder
Encoder encoder(2, 3); // Pins for the rotary encoder
long oldPosition = -999;
// Define push buttons
const int buttonUp = 4;
const int buttonDown = 5;
// Menu Structure
enum MenuLevel { MAIN, SYSTEM, TIME, NETWORK };
MenuLevel currentLevel = MAIN;
int menuIndex[] = {0, 0, 0, 0}; // To track selection in each level
// Menu Items
const char* mainMenu[] = { "SYSTEM", "TIME", "NETWORK" };
const char* systemMenu[] = { "MODE", "LOGGING" };
const char* timeMenu[] = { "SET DATE", "SET TIME" };
const char* networkMenu[] = { "UPDATE RATE", "NETWORK TYPE" };
// Number of items in each menu
const int mainMenuCount = 3;
const int systemMenuCount = 2;
const int timeMenuCount = 2;
const int networkMenuCount = 2;
void setup() {
// Setup LCD
lcd.begin(20, 4);
lcd.backlight();
// Setup buttons
pinMode(buttonUp, INPUT_PULLUP);
pinMode(buttonDown, INPUT_PULLUP);
// Initial display
updateMenu();
}
void loop() {
// Handle rotary encoder
long newPosition = encoder.read() / 4;
if (newPosition != oldPosition) {
oldPosition = newPosition;
int maxIndex = getMaxMenuIndex(currentLevel);
menuIndex[currentLevel] = constrain(newPosition, 0, maxIndex - 1);
updateMenu();
}
// Handle button presses
if (digitalRead(buttonUp) == LOW) {
moveUpMenuLevel();
delay(200); // Debounce delay
updateMenu();
}
if (digitalRead(buttonDown) == LOW) {
moveDownMenuLevel();
delay(200); // Debounce delay
updateMenu();
}
}
void moveUpMenuLevel() {
if (currentLevel == MAIN) return; // Already at the top
currentLevel = static_cast<MenuLevel>(currentLevel - 1);
oldPosition = menuIndex[currentLevel] * 4; // Sync the encoder position with menu index
}
void moveDownMenuLevel() {
if (currentLevel == MAIN) {
currentLevel = static_cast<MenuLevel>(menuIndex[MAIN] + 1);
} else {
// Add actions for the selected menu item here
// Example: if SYSTEM is selected, toggle mode or logging etc.
}
oldPosition = menuIndex[currentLevel] * 4; // Sync the encoder position with menu index
}
void updateMenu() {
lcd.clear();
switch (currentLevel) {
case MAIN:
displayMenu(mainMenu, mainMenuCount, menuIndex[MAIN]);
break;
case SYSTEM:
displayMenu(systemMenu, systemMenuCount, menuIndex[SYSTEM]);
break;
case TIME:
displayMenu(timeMenu, timeMenuCount, menuIndex[TIME]);
break;
case NETWORK:
displayMenu(networkMenu, networkMenuCount, menuIndex[NETWORK]);
break;
}
}
void displayMenu(const char* menuItems[], int itemCount, int selectedIndex) {
for (int i = 0; i < itemCount; i++) {
lcd.setCursor(0, i);
if (i == selectedIndex) {
lcd.print(">"); // Indicate selection
} else {
lcd.print(" ");
}
lcd.print(menuItems[i]);
}
}
int getMaxMenuIndex(MenuLevel level) {
switch (level) {
case MAIN: return mainMenuCount;
case SYSTEM: return systemMenuCount;
case TIME: return timeMenuCount;
case NETWORK: return networkMenuCount;
default: return 0;
}
}