#include <OneButton.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2);
#define outputA 25
#define outputB 26
int counter = 0;
int aState;
int aLastState;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // Further increased debounce delay
int buttonPin = 27;
OneButton button0(buttonPin, true);
const int numMainMenus = 5;
const char* mainMenuOptions[numMainMenus] = {"Auto Mode", "Manual Mode", "Mode 3", "Mode 4", "Mode 5"};
const int numSchedules = 4;
const int numStates = 2;
const char* stateOptions[numStates] = {"On", "Off"};
int currentMainMenu = 0;
int currentSubMenu = 0;
int currentState = 0;
bool isSubMenuSelected = false;
bool isStateMenuSelected = false;
void setup() {
Serial.begin(9600);
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
button0.attachClick(singleClick);
button0.attachDoubleClick(doubleClick);
lcd.init();
lcd.setBacklight(HIGH);
lcd.home();
pinMode(outputA, INPUT);
pinMode(outputB, INPUT);
aLastState = digitalRead(outputA);
displayMenu();
}
void loop() {
button0.tick();
readEncoder();
delay(20); // Small delay to stabilize Wokwi response
}
void readEncoder() {
int currentStateA = digitalRead(outputA);
int currentStateB = digitalRead(outputB);
unsigned long currentTime = millis();
// Debounce: Only consider changes that are stable over the debounce period
if ((currentTime - lastDebounceTime) > debounceDelay) {
// Detect rotation only when outputA state changes
if (currentStateA != aLastState) {
lastDebounceTime = currentTime;
if (currentStateB != currentStateA) {
counter++;
handleRotation(true);
} else {
counter--;
handleRotation(false);
}
displayMenu();
Serial.print("Main Menu: ");
Serial.print(mainMenuOptions[currentMainMenu]);
Serial.print(" | Schedule: ");
Serial.print(currentSubMenu + 1);
Serial.print(" | State: ");
Serial.println(stateOptions[currentState]);
}
aLastState = currentStateA; // Update previous state
}
}
void handleRotation(bool clockwise) {
if (currentMainMenu == 1 && isSubMenuSelected) { // Manual Mode
if (isStateMenuSelected) {
currentState = (currentState + (clockwise ? 1 : numStates - 1)) % numStates;
} else {
currentSubMenu = (currentSubMenu + (clockwise ? 1 : numSchedules - 1)) % numSchedules;
}
} else if (!isSubMenuSelected) {
currentMainMenu = (currentMainMenu + (clockwise ? 1 : numMainMenus - 1)) % numMainMenus;
}
}
void singleClick() {
if (currentMainMenu == 1) { // Manual Mode
if (isSubMenuSelected && !isStateMenuSelected) {
isStateMenuSelected = true; // Enter state submenu (On/Off)
} else if (isStateMenuSelected) {
isStateMenuSelected = false; // Exit state submenu
} else {
isSubMenuSelected = true; // Enter schedule submenu
}
} else {
isSubMenuSelected = !isSubMenuSelected;
}
displayMenu();
}
void doubleClick() {
isSubMenuSelected = false;
isStateMenuSelected = false;
currentSubMenu = 0;
currentState = 0;
displayMenu();
}
void displayMenu() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(mainMenuOptions[currentMainMenu]);
if (currentMainMenu == 1 && isSubMenuSelected) { // Manual Mode with submenus
lcd.setCursor(0, 1);
lcd.print("Sch ");
lcd.print(currentSubMenu + 1); // Schedule number
if (isStateMenuSelected) {
lcd.print(" State: ");
lcd.print(stateOptions[currentState]);
} else {
lcd.print(" Time Set"); // Placeholder for time setting
}
} else if (currentMainMenu != 1 && isSubMenuSelected) {
lcd.setCursor(0, 1);
lcd.print("No submenu");
}
}