Hi all.
I'm new to Arduino programming and i am trying to make a simple menu for a retro watch using an oled , 3 push buttons and an esp32 c6 board. For now, I was able to make a digital and analog clock faces and a stop watch as a menu item. I have included a feature to edit time and date in place. For some reason the buttons meant for the menu are interfering with the time and date edit feature and iam not able to edit the time, I'm not sure how to fix this. All i was looking for is a simple watch logic like in Casio f91w. Attaching the code here.
#include <ESP32Time.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Initialize ESP32Time instance
ESP32Time rtc;
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
int second, minute, hour;
int xcenter = 30, ycenter = 32; // Move analog clock 2 pixels left
float pi = 3.14159265359;
float angle = 0;
int x, y;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
const char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
const char* daysOfWeek[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
// Time and date variables
//int hours = 12;
//int minutes = 0;
//int day = 1;
//int month = 1; // Month range: 1-12
//int year = 2023; // Starting year
// Menu navigation variables
int selectedField = 0; // 0: Hours, 1: Minutes, 2: Day, 3: Month, 4: Year
bool editing = false;
// Function prototypes
void drawMenu();
// Button pins
#define MODE_BUTTON_PIN D2 // Mode button
#define SELECT_BUTTON_PIN D0 // Select button
#define SET_BUTTON_PIN D1 // Set button
// Menu states
enum MenuState {
MENU_TIME_DATE,
MENU_EDIT_TIME_DATE,
MENU_STOPWATCH,
MENU_12_24_HOUR,
MENU_BRIGHTNESS,
MENU_TEMPERATURE_HUMIDITY,
MENU_COUNT
};
MenuState currentMenu = MENU_TIME_DATE;
// Brightness levels
int brightnessLevels[] = {10, 50, 100, 200};
int currentBrightnessIndex = 2;
// Internal RTC variables
unsigned long previousMillis = 0;
int seconds = 0, minutes = 0, hours = 15, day = 26, month = 12, year = 2024;
bool is24HourFormat = true;
bool settingTime = false;
int settingField = 0; // 0: Hours, 1: Minutes, 2: Day, 3: Month, 4: Year
// Stopwatch variables
bool stopwatchRunning = false;
unsigned long stopwatchStartTime = 0;
unsigned long stopwatchElapsedTime = 0;
void setup() {
Serial.begin(115200);
// Initialize display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.display();
delay(2000);
display.clearDisplay();
// Initialize buttons
pinMode(MODE_BUTTON_PIN, INPUT_PULLUP);
pinMode(SELECT_BUTTON_PIN, INPUT_PULLUP);
pinMode(SET_BUTTON_PIN, INPUT_PULLUP);
// Initialize ESP32Time with a specific date and time (if needed)
rtc.setTime(0, 16, 9, 29, 12, 2024); // Set to 12:00:00 on 1st Jan 2023
}
void loop() {
// Check mode button to cycle through menu
if (digitalRead(MODE_BUTTON_PIN) == LOW && (millis() - lastDebounceTime > debounceDelay)) {
lastDebounceTime = millis();
cycleMenu();
delay(300); // Debounce delay
}
// Check select button to select the current menu option
if (digitalRead(SELECT_BUTTON_PIN) == LOW && (millis() - lastDebounceTime > debounceDelay)) {
lastDebounceTime = millis();
selectMenuOption();
delay(300); // Debounce delay
}
// Check set button to adjust settings in the current menu option
if (digitalRead(SET_BUTTON_PIN) == LOW && (millis() - lastDebounceTime > debounceDelay)) {
lastDebounceTime = millis();
setMenuOption();
delay(300); // Debounce delay
}
// Default display for clock
if (currentMenu == MENU_TIME_DATE) {
// Get current time from ESP32Time
GetDateTime();
// Display current time on OLED
displayTime();
// Draw analog clock
drawAnalogClock();
//delay(1000);
}
// Display stopwatch if in stopwatch menu
if (currentMenu == MENU_STOPWATCH) {
displayStopwatch();
}
if (currentMenu == MENU_EDIT_TIME_DATE) {
// Update internal RTC time
drawMenu();
settingTime = !settingTime;
if (!settingTime) {
updateTime();
rtc.setTime(seconds, minutes, hours, day, month, year); // Set to 12:00:00 on 1st Jan 2023
display.println("Time/Date Set!");
delay(1000);
}
}
}
void updateTime() {
// Button pins
//MODE_BUTTON_PIN D2 // Mode button
//SELECT_BUTTON_PIN D0 // Select button
//SET_BUTTON_PIN D1 // Set button
// Handle button presses
if (digitalRead(SELECT_BUTTON_PIN) == LOW && (millis() - lastDebounceTime > debounceDelay)) {
lastDebounceTime = millis();
if (editing) {
selectedField = (selectedField + 1) % 5; // Cycle through fields
} else {
editing = true; // Enter editing mode
}
//drawMenu();
delay(200); // Debounce
}
if (digitalRead(SET_BUTTON_PIN) == LOW && (millis() - lastDebounceTime > debounceDelay)) {
lastDebounceTime = millis();
if (editing) {
// Increment the selected field
switch (selectedField) {
case 0: hours = (hours + 1) % 24; break;
case 1: minutes = (minutes + 1) % 60; break;
case 2: day = (day % 31) + 1; break;
case 3: month = (month % 12) + 1; break;
case 4: year++; break;
}
//drawMenu();
delay(200); // Debounce
}
}
if (digitalRead(MODE_BUTTON_PIN) == LOW && (millis() - lastDebounceTime > debounceDelay)) {
lastDebounceTime = millis();
if (editing) {
editing = false; // Exit editing mode
displayTime();
// Draw analog clock
drawAnalogClock();
}
delay(200); // Debounce
}
}
void cycleMenu() {
if (settingTime) {
settingField = (settingField + 1) % 5; // Cycle through fields
} else {
currentMenu = static_cast<MenuState>((currentMenu + 1) % MENU_COUNT);
displayMenu();
}
}
void selectMenuOption() {
display.clearDisplay();
display.setCursor(0, 0);
switch (currentMenu) {
case MENU_TEMPERATURE_HUMIDITY:
display.println("Show Temp/Humidity");
displayTemperatureAndHumidity();
return;
case MENU_STOPWATCH:
stopwatchRunning = !stopwatchRunning;
if (stopwatchRunning) {
stopwatchStartTime = millis() - stopwatchElapsedTime;
} else {
stopwatchElapsedTime = millis() - stopwatchStartTime;
}
break;
case MENU_TIME_DATE:
break;
case MENU_EDIT_TIME_DATE:
break;
case MENU_12_24_HOUR:
is24HourFormat = !is24HourFormat;
display.println(is24HourFormat ? "24 Hour Mode" : "12 Hour Mode");
break;
case MENU_BRIGHTNESS:
display.println("Adjust Brightness");
display.println("Use Set button");
break;
}
display.display();
}
void setMenuOption() {
if (settingTime) {
switch (settingField) {
case 0: // Hours
hours = (hours + 1) % 24;
break;
case 1: // Minutes
minutes = (minutes + 1) % 60;
break;
case 2: // Day
day = (day % 31) + 1;
break;
case 3: // Month
month = (month % 12) + 1;
break;
case 4: // Year
year++;
break;
}
} else {
display.clearDisplay();
display.setCursor(0, 0);
switch (currentMenu) {
case MENU_STOPWATCH:
if (!stopwatchRunning) stopwatchElapsedTime = 0;
break;
case MENU_BRIGHTNESS:
currentBrightnessIndex = (currentBrightnessIndex + 1) % 4;
analogWrite(OLED_RESET, brightnessLevels[currentBrightnessIndex]); // Simulate brightness adjustment
display.println("Brightness:");
display.print(brightnessLevels[currentBrightnessIndex]);
display.println(" %");
break;
case MENU_TIME_DATE:
break;
case MENU_EDIT_TIME_DATE:
/*settingTime = !settingTime;
if (!settingTime) {
//editing = true;
updateTime();
rtc.setTime(seconds, minutes, hours, day, month, year); // Set to 12:00:00 on 1st Jan 2023
display.println("Time/Date Set!");
delay(1000);
}*/
break;
case MENU_12_24_HOUR:
break;
case MENU_TEMPERATURE_HUMIDITY:
display.println("Adjustments not implemented");
break;
}
display.display();
}
}
void displayMenu() {
display.clearDisplay();
display.setCursor(0, 0);
switch (currentMenu) {
case MENU_TIME_DATE:
display.println("Menu: Time/Date");
break;
case MENU_EDIT_TIME_DATE:
display.println("Menu: Edit Time/Date");
break;
case MENU_STOPWATCH:
display.println("Menu: Stopwatch");
break;
case MENU_12_24_HOUR:
display.println("Menu: 12/24 Hour");
break;
case MENU_BRIGHTNESS:
display.println("Menu: Brightness");
break;
case MENU_TEMPERATURE_HUMIDITY:
display.println("Menu: Temp/Humidity");
break;
}
display.display();
}
void GetDateTime() {
second = rtc.getSecond();
minute = rtc.getMinute();
hour = rtc.getHour();
}
void displayTime() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(65, 0); // Adjusted to make space for the analog clock on the left
display.printf("%02d:%02d:%02d", hour, minute, second);
display.setCursor(65, 25);
display.printf("%s", daysOfWeek[rtc.getDayofWeek()]);
display.setTextSize(1);
display.setCursor(65, 40);
display.printf("%s %02d", months[rtc.getMonth()], rtc.getDay());
display.setTextSize(1);
display.setCursor(65, 50);
display.printf("%04d", rtc.getYear());
display.display();
}
void drawAnalogClock() {
display.drawCircle(xcenter, ycenter, 30, SSD1306_WHITE);
display.drawCircle(xcenter, ycenter, 2, SSD1306_WHITE);
// Draw hour hand
angle = ((hour % 12) + minute / 60.0) * 30;
angle = angle * pi / 180;
x = xcenter + 15 * sin(angle);
y = ycenter - 15 * cos(angle);
display.drawLine(xcenter, ycenter, x, y, SSD1306_WHITE);
// Draw minute hand
angle = (minute + second / 60.0) * 6;
angle = angle * pi / 180;
x = xcenter + 20 * sin(angle);
y = ycenter - 20 * cos(angle);
display.drawLine(xcenter, ycenter, x, y, SSD1306_WHITE);
// Draw second hand
angle = second * 6;
angle = angle * pi / 180;
x = xcenter + 25 * sin(angle);
y = ycenter - 25 * cos(angle);
display.drawLine(xcenter, ycenter, x, y, SSD1306_WHITE);
// Draw clock markers
for (int i = 0; i < 12; i++) {
angle = i * 30 * pi / 180;
x = xcenter + 28 * sin(angle);
y = ycenter - 28 * cos(angle);
int x2 = xcenter + 30 * sin(angle);
int y2 = ycenter - 30 * cos(angle);
display.drawLine(x, y, x2, y2, SSD1306_WHITE);
}
display.display();
}
void displayStopwatch() {
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(2);
unsigned long elapsed = stopwatchRunning ? millis() - stopwatchStartTime : stopwatchElapsedTime;
int stopwatchSeconds = (elapsed / 1000) % 60;
int stopwatchMinutes = (elapsed / (1000 * 60)) % 60;
int stopwatchHours = (elapsed / (1000 * 60 * 60));
int stopwatchmilliseconds = elapsed % 1000;
display.print(stopwatchHours);
display.print(":");
if (stopwatchMinutes < 10) display.print("0");
display.print(stopwatchMinutes);
display.print(":");
if (stopwatchSeconds < 10) display.print("0");
display.print(stopwatchSeconds);
display.print(":");
if (stopwatchSeconds < 10) display.print("0");
display.print(stopwatchmilliseconds);
display.setTextSize(1);
display.setCursor(0, 50);
display.print(stopwatchRunning ? "Running" : "Stopped");
display.display();
}
void displayTemperatureAndHumidity() {
// float temp = dht.readTemperature();
// float hum = dht.readHumidity();
// if (isnan(temp) || isnan(hum)) {
// display.println("Failed to read sensor!");
// } else {
// display.println("Temperature:");
// display.print(temp);
// display.println(" C");
// display.println("Humidity:");
// display.print(hum);
// display.println(" %");
// }
display.println("Temp/Humidity Disabled");
display.display();
}
// Function to draw the menu on the OLED
void drawMenu() {
display.clearDisplay();
// Display current field being edited with highlight
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); // Highlight selected field
switch (selectedField) {
case 0: display.setCursor(0, 0); display.print("Editing: Hour"); break;
case 1: display.setCursor(0, 0); display.print("Editing: Minute"); break;
case 2: display.setCursor(0, 0); display.print("Editing: Day"); break;
case 3: display.setCursor(0, 0); display.print("Editing: Month"); break;
case 4: display.setCursor(0, 0); display.print("Editing: Year"); break;
}
display.setTextSize(2); // Larger size for values
display.setCursor(0, 20);
// Highlight the selected field
if (selectedField == 0) {
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Invert color
display.printf("%02d", hours);
} else {
display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
display.printf("%02d", hours);
}
display.print(":");
if (selectedField == 1) {
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
display.printf("%02d", minutes);
} else {
display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
display.printf("%02d", minutes);
}
display.setTextSize(1);
display.setCursor(0, 50);
if (selectedField == 2) {
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
display.printf("%02d", day);
} else {
display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
display.printf("%02d", day);
}
display.print("/");
if (selectedField == 3) {
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
display.printf("%02d", month);
} else {
display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
display.printf("%02d", month);
}
display.print("/");
if (selectedField == 4) {
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
display.printf("%04d", year);
} else {
display.setTextColor(SSD1306_WHITE, SSD1306_BLACK);
display.printf("%04d", year);
}
display.display();
}