#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <AiEsp32RotaryEncoder.h>
#define ENCODER_CLK 25
#define ENCODER_DT 26
#define ENCODER_SW 27
#define ENCODER_VCC -1
#define ENCODER_STEPS 4
const int SCREEN_WIDTH = 128;
const int SCREEN_HEIGHT = 64;
const int OLED_RESET = -1;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(ENCODER_CLK, ENCODER_DT);
int previousRotaryValue = 0;
int currentRotaryValue = 0;
unsigned long rotaryIdleStartTime = 0;
const unsigned long rotaryIdleDelay = 10000;
unsigned long rotationDebounceDelay = 1000;
unsigned long sensorDebounceDelay = 500;
unsigned long longPressDelay = 1000;
unsigned long syncTimeout = 10000;
unsigned long syncFailTime = 0;
unsigned long manualSyncDisableTime = 300000;
unsigned long lastPressTime = 0;
void IRAM_ATTR readEncoderISR() {
rotaryEncoder.readEncoder_ISR();
}
/* /////////////////////////////// Menu setup from here... /////////////////////////////// */
int menuIndex = 0;
int subMenuIndex = 0;
int numMenus = 6;
int maxVisibleItems = 4;
int numSubMenus = 2;
int startIndex;
int endIndex;
bool rotatingDown = false;
String menus[] = {"Menu 1", "Menu 2", "Menu 3", "Menu 4", "Menu 5", "Menu 6"};
String subMenus[] = {"Submenu 1", "Submenu 2"};
const int SCROLLBAR_X = 124;
const int SCROLLBAR_Y = 12;
const int SCROLLBAR_WIDTH = 4;
const int SCROLLBAR_HEIGHT = 51;
const int SCROLLBAR_INNER_X = 125;
const int SCROLLBAR_INNER_Y = 14;
const int SCROLLBAR_INNER_WIDTH = 2;
const int SCROLLBAR_INNER_HEIGHT = 13;
/* /////////////////////////////// ...to here. /////////////////////////////// */
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.display();
delay(2000);
display.clearDisplay();
rotaryEncoder.begin();
rotaryEncoder.setup(readEncoderISR);
rotaryEncoder.setBoundaries(-99999, 99999, true);
rotaryEncoder.disableAcceleration();
}
void loop() {
rotary_loop();
displayMenu();
}
/* /////////////////////////////// Menu display functions from here... /////////////////////////////// */
void displayMenu() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0);
if (rotatingDown) {
startIndex = menuIndex - 2;
endIndex = menuIndex + 1;
} else {
startIndex = menuIndex - 1;
endIndex = menuIndex + 2;
}
// Adjust start index and end index to stay within bounds
if (startIndex < 0) {
startIndex = 0;
endIndex = (maxVisibleItems - 1);
} else if (endIndex >= numMenus) {
endIndex = numMenus - 1;
startIndex = endIndex - (maxVisibleItems - 1);
}
// Calculate the total number of visible items
int visibleItems = min(numMenus, maxVisibleItems);
// Calculate the percentage of visible items
float percentVisible = (float)visibleItems / numMenus;
// Calculate the height of the scrollbar inner part
int scrollbarInnerHeight = SCROLLBAR_HEIGHT * percentVisible;
// Draw the scrollbar outline
display.drawRoundRect(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, 1, SSD1306_WHITE);
// Calculate the scrollbar position based on the percentage of visible items
int scrollbarPosition = map(startIndex, 0, numMenus - visibleItems, SCROLLBAR_Y, SCROLLBAR_Y + SCROLLBAR_HEIGHT - scrollbarInnerHeight);
// Draw the scrollbar inner part
display.fillRect(SCROLLBAR_INNER_X, scrollbarPosition, SCROLLBAR_INNER_WIDTH, scrollbarInnerHeight, SSD1306_WHITE);
for (int i = startIndex; i <= endIndex; i++) {
if (i == menuIndex) {
display.drawRoundRect(0, (i - startIndex) * 12 + 12, 120, 11, 3, SSD1306_WHITE);
}
display.setCursor(4, (i - startIndex) * 12 + 14);
display.print(menus[i]);
}
display.display();
}
void displaySubMenu() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0);
display.println("Sub Menu");
for (int i = 0; i < numSubMenus; i++) {
if (i == subMenuIndex) {
display.drawRect(0, i * 8, SCREEN_WIDTH, 8, SSD1306_WHITE);
}
display.setCursor(0, i * 8);
display.print(subMenus[i]);
}
display.display();
}
/* /////////////////////////////// ...to here. /////////////////////////////// */
void rotary_loop() {
if (rotaryEncoder.encoderChanged()) {
currentRotaryValue = rotaryEncoder.readEncoder();
if (currentRotaryValue > previousRotaryValue) {
rotatingDown = true;
if (menuIndex < numMenus - 1) {
menuIndex += 1;
} else {
menuIndex = 0; // Cycle back to the beginning
}
} else {
rotatingDown = false;
if (menuIndex > 0) {
menuIndex -= 1;
} else {
menuIndex = numMenus - 1; // Cycle back to the end
}
}
Serial.print("Rotating down: ");
Serial.println(rotatingDown);
previousRotaryValue = currentRotaryValue;
rotaryIdleStartTime = millis(); // Reset the idle start time
}
handle_rotary_button();
}
void handle_rotary_button() {
static unsigned long buttonPressedTime = 0;
static bool isLongpress = false;
bool isEncoderButtonDown = rotaryEncoder.isEncoderButtonDown();
if (isEncoderButtonDown) {
if (!buttonPressedTime) {
buttonPressedTime = millis();
}
if (!isLongpress && (millis() - buttonPressedTime >= longPressDelay)) {
onButtonLongPress();
isLongpress = true;
rotaryIdleStartTime = millis(); // Reset the idle start time
}
} else {
if (buttonPressedTime && !isLongpress) {
onButtonShortPress();
rotaryIdleStartTime = millis(); // Reset the idle start time
}
buttonPressedTime = 0;
isLongpress = false;
}
}
void onButtonShortPress() {
lastPressTime = millis();
Serial.println("PRESS");
subMenuIndex = (subMenuIndex + 1) % numSubMenus; // MENU ///////////////////////////////
displaySubMenu(); // MENU ///////////////////////////////
delay(500);
}
void onButtonLongPress() {
}