#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// OLED display settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Define button pins
#define BUTTON_UP 2
#define BUTTON_DOWN 3
#define BUTTON_NEXT 4
#define BUTTON_MENU 5
// Debounce variables
unsigned long lastDebounceTimeUp = 0;
unsigned long lastDebounceTimeDown = 0;
unsigned long lastDebounceTimeNext = 0;
unsigned long lastDebounceTimeMenu = 0;
unsigned long debounceDelay = 0;
int buttonStateUp = LOW;
int lastButtonStateUp = LOW;
int buttonStateDown = LOW;
int lastButtonStateDown = LOW;
int buttonStateNext = LOW;
int lastButtonStateNext = LOW;
int buttonStateMenu = LOW;
int lastButtonStateMenu = LOW;
enum MenuState {CLOCK, MAIN_MENU, SET_TIME_MINUTES, SET_TIME_HOURS, SET_FORMAT, SET_DISPLAY};
MenuState menuState = CLOCK;
bool is12HourFormat = true;
bool isInverseDisplay = false;
int hours = 12;
int minutes = 0;
int seconds = 0;
unsigned long lastMillis = 0;
// Menu navigation variables
int menuSelection = 0; // Tracks the current menu selection
const int mainMenuOptions = 3;
void setup() {
Serial.begin(9600);
// Initialize the OLED display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.display();
// Initialize buttons
pinMode(BUTTON_UP, INPUT_PULLUP);
pinMode(BUTTON_DOWN, INPUT_PULLUP);
pinMode(BUTTON_NEXT, INPUT_PULLUP);
pinMode(BUTTON_MENU, INPUT_PULLUP);
// Display initial time
updateDisplay();
}
void loop() {
// Read the state of the buttons
int readingUp = digitalRead(BUTTON_UP);
int readingDown = digitalRead(BUTTON_DOWN);
int readingNext = digitalRead(BUTTON_NEXT);
int readingMenu = digitalRead(BUTTON_MENU);
// Debounce buttons
if (readingUp != lastButtonStateUp) {
lastDebounceTimeUp = millis();
}
if (readingDown != lastButtonStateDown) {
lastDebounceTimeDown = millis();
}
if (readingNext != lastButtonStateNext) {
lastDebounceTimeNext = millis();
}
if (readingMenu != lastButtonStateMenu) {
lastDebounceTimeMenu = millis();
}
if ((millis() - lastDebounceTimeUp) > debounceDelay) {
if (readingUp != buttonStateUp) {
buttonStateUp = readingUp;
if (buttonStateUp == LOW) {
handleButtonUp();
}
}
}
if ((millis() - lastDebounceTimeDown) > debounceDelay) {
if (readingDown != buttonStateDown) {
buttonStateDown = readingDown;
if (buttonStateDown == LOW) {
handleButtonDown();
}
}
}
if ((millis() - lastDebounceTimeNext) > debounceDelay) {
if (readingNext != buttonStateNext) {
buttonStateNext = readingNext;
if (buttonStateNext == LOW) {
handleButtonNext();
}
}
}
if ((millis() - lastDebounceTimeMenu) > debounceDelay) {
if (readingMenu != buttonStateMenu) {
buttonStateMenu = readingMenu;
if (buttonStateMenu == LOW) {
handleButtonMenu();
}
}
}
lastButtonStateUp = readingUp;
lastButtonStateDown = readingDown;
lastButtonStateNext = readingNext;
lastButtonStateMenu = readingMenu;
// Update the internal clock
updateClock();
// Display the current time or menu
if (menuState == CLOCK) {
displayTime();
} else {
displayMenu();
}
delay(10);
}
void updateClock() {
unsigned long currentMillis = millis();
if (currentMillis - lastMillis >= 1000) {
lastMillis = currentMillis;
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
if (is12HourFormat && hours > 12) hours = 1;
if (!is12HourFormat && hours >= 24) hours = 0;
}
}
if (menuState == CLOCK) {
updateDisplay();
}
}
}
void handleButtonUp() {
if (menuState == MAIN_MENU) {
menuSelection = (menuSelection - 1 + mainMenuOptions) % mainMenuOptions;
} else if (menuState == SET_TIME_MINUTES) {
minutes++;
if (minutes >= 60) {
minutes = 0;
}
} else if (menuState == SET_TIME_HOURS) {
hours++;
if (is12HourFormat && hours > 12) hours = 1;
if (!is12HourFormat && hours >= 24) hours = 0;
} else if (menuState == SET_FORMAT) {
is12HourFormat = !is12HourFormat;
} else if (menuState == SET_DISPLAY) {
isInverseDisplay = !isInverseDisplay;
display.invertDisplay(isInverseDisplay);
}
updateDisplay();
}
void handleButtonDown() {
if (menuState == MAIN_MENU) {
menuSelection = (menuSelection + 1) % mainMenuOptions;
} else if (menuState == SET_TIME_MINUTES) {
minutes--;
if (minutes < 0) {
minutes = 59;
}
} else if (menuState == SET_TIME_HOURS) {
hours--;
if (is12HourFormat && hours < 1) hours = 12;
if (!is12HourFormat && hours < 0) hours = 23;
}
updateDisplay();
}
void handleButtonNext() {
if (menuState == MAIN_MENU) {
switch (menuSelection) {
case 0:
menuState = SET_TIME_MINUTES;
break;
case 1:
menuState = SET_FORMAT;
break;
case 2:
menuState = SET_DISPLAY;
break;
}
} else if (menuState == SET_TIME_MINUTES) {
menuState = SET_TIME_HOURS;
} else if (menuState == SET_TIME_HOURS) {
menuState = MAIN_MENU;
} else if (menuState == SET_FORMAT || menuState == SET_DISPLAY) {
menuState = MAIN_MENU;
}
updateDisplay();
}
void handleButtonMenu() {
if (menuState == CLOCK) {
menuState = MAIN_MENU;
} else {
menuState = CLOCK;
}
updateDisplay();
}
void displayTime() {
display.clearDisplay();
display.setTextSize(2.5);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
if (is12HourFormat) {
int displayHour = hours % 12;
if (displayHour == 0) displayHour = 12;
display.print(displayHour);
display.print(':');
if (minutes < 10) display.print('0');
display.print(minutes);
display.print(hours < 12 ? " AM" : " PM");
} else {
display.print(hours);
display.print(':');
if (minutes < 10) display.print('0');
display.print(minutes);
}
display.display();
}
void displayMenu() {
display.clearDisplay();
display.setTextSize(1.8);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
if (menuState == MAIN_MENU) {
displayMenuOption("Set Time", 0);
displayMenuOption("Set Format", 1);
displayMenuOption("Set Display", 2);
} else if (menuState == SET_TIME_MINUTES) {
display.println("Set Minutes");
display.setTextSize(2);
display.setCursor(0, 20);
display.print(minutes);
} else if (menuState == SET_TIME_HOURS) {
display.println("Set Hours");
display.setTextSize(2);
display.setCursor(0, 20);
display.print(hours);
} else if (menuState == SET_FORMAT) {
display.println("Set Format");
display.setTextSize(2);
display.setCursor(0, 20);
display.print(is12HourFormat ? "12H" : "24H");
} else if (menuState == SET_DISPLAY) {
display.println("Set Display");
display.setTextSize(2);
display.setCursor(0, 30);
display.print(isInverseDisplay ? "Inverse" : "Normal");
}
display.display();
}
void displayMenuOption(const char* option, int index) {
if (index == menuSelection) {
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Invert text color
} else {
display.setTextColor(SSD1306_WHITE);
}
display.setTextSize(1);
display.setCursor(0, 20 + (index * 10));
display.println(option);
display.setTextColor(SSD1306_WHITE); // Reset to default
}
void updateDisplay() {
if (menuState == CLOCK) {
displayTime();
} else {
displayMenu();
}
}