#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <AiEsp32RotaryEncoder.h>
//ENCODER
#define ENCODER_CLK 39
#define ENCODER_DT 36
#define ENCODER_SW 27
#define ENCODER_VCC -1
#define ROTARY_ENCODER_STEPS 4
#define ENCODER_STEPS 4
//TELA
#define SSD1306_NO_SPLASH
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);
//ENCODER
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(ENCODER_CLK, ENCODER_DT, ENCODER_SW, -1, ROTARY_ENCODER_STEPS);
int previousRotaryValue = 0;
int currentRotaryValue = 0;
unsigned long rotaryIdleStartTime = 0;
const unsigned long rotaryIdleDelay = 10000;
unsigned long longPressDelay = 1000;
unsigned long lastPressTime = 0;
void IRAM_ATTR readEncoderISR() {
rotaryEncoder.readEncoder_ISR();
};
struct varMenus{
int startIndex = 0;
int endIndex = 0;
int menuIndex = 0;
int numMenus = 0;
int maxVisibleItems = 4;
};
varMenus vmMenuGlobal;
varMenus vmMenuLanguage;
varMenus vmMenuPresetSettings;
varMenus vmMenuQueue;
int MenuItemSelectedIndex = 0;
String descTelasMenus[] = {
"GLOBAL MENU",
"LANGUAGE",
"CALIBRATION KNOBS",
"PRESET SETTINGS",
"FACTORY DEFAULTS"
};
String menus[] = {
"Language",
"Preset Settings",
"Calibration Knobs",
"Factory Defaults",
"Exit"
};
String subMenuLanguage[] = {
"English",
"Portuguese"
};
String subMenuPresetSettings[] = {
"WakeUp Mode",
"Queue Limit",
"Configure Queue",
"Clear",
"Back"
};
String subMenuQueue[] = {
"Queue Size",
"Order",
"Clear Queue",
"Back"
};
//MENUS E SUBMENUS SELECIONADAS
struct MenusScreen {
//OPÇÕES
const int MenuGlobal = 1;
const int Language = 2;
const int PresetSettings = 3;
const int CalibrationKnobs = 4;
const int FactoryDefaults = 5;
const int Exit = 6;
};
constexpr MenusScreen mnus;
int mnusTelaSelecionada = 1; //VARIÁVEL DE TELA SELECIONADA. PODE RECEBER OS VALORES DAS OPÇÕES ABAIXO
//PARÂMETROS DA SCROLLBAR
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;
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
vmMenuGlobal.numMenus = sizeof(menus) / sizeof(menus[0]);
vmMenuLanguage.numMenus = sizeof(subMenuLanguage) / sizeof(subMenuLanguage[0]);
vmMenuPresetSettings.numMenus = sizeof(subMenuPresetSettings) / sizeof(subMenuPresetSettings[0]);
vmMenuQueue.numMenus = sizeof(subMenuQueue) / sizeof(subMenuQueue[0]);
vmMenuGlobal.startIndex = 0;
vmMenuGlobal.endIndex = (vmMenuGlobal.maxVisibleItems - 1);
vmMenuLanguage.startIndex = 0;
vmMenuLanguage.endIndex = (vmMenuLanguage.maxVisibleItems - 1);
vmMenuPresetSettings.startIndex = 0;
vmMenuPresetSettings.endIndex = (vmMenuPresetSettings.maxVisibleItems - 1);
vmMenuQueue.startIndex = 0;
vmMenuQueue.endIndex = (vmMenuQueue.maxVisibleItems - 1);
display.display();
delay(2000);
display.clearDisplay();
Serial.begin(115200);
rotaryEncoder.areEncoderPinsPulldownforEsp32=false;
rotaryEncoder.begin();
rotaryEncoder.setup(readEncoderISR);
rotaryEncoder.setBoundaries(0, 1000, true); //minValue, maxValue, circleValues true|false (when max go to min and vice versa)
rotaryEncoder.setAcceleration(250);
displayMenu();
}
void loop() {
handle_rotary_button();
rotary_loop();
}
void rotary_loop() {
if (rotaryEncoder.encoderChanged()) {
//Serial.println("encoder Changed");
currentRotaryValue = rotaryEncoder.readEncoder();
if (currentRotaryValue > previousRotaryValue) {
//Serial.println("OnRotatingDown");
OnRotatingDown();
} else {
//Serial.println("OnRotatingUp");
OnRotatingUp();
}
previousRotaryValue = currentRotaryValue;
rotaryIdleStartTime = millis(); // Reset the idle start time
}
// if (rotaryEncoder.isEncoderButtonClicked()) {
// Serial.println("button pressed");
// }
}
void OnRotatingUp() {
Serial.println("Girou para Cima");
switch (mnusTelaSelecionada) {
case mnus.MenuGlobal:
VoltaItemMenu();
break;
case mnus.Language:
break;
case mnus.PresetSettings:
VoltaItemSubPresetSettings();
break;
case mnus.CalibrationKnobs:
break;
case mnus.FactoryDefaults:
break;
case mnus.Exit:
break;
default:
break;
}
}
void OnRotatingDown(){
Serial.println("Girou para Baixo");
switch (mnusTelaSelecionada) {
case mnus.MenuGlobal:
AvancaItemMenu();
break;
case mnus.Language:
break;
case mnus.PresetSettings:
AvancaItemSubPresetSettings();
break;
case mnus.CalibrationKnobs:
break;
case mnus.FactoryDefaults:
break;
case mnus.Exit:
break;
default:
break;
}
}
void AvancaItemMenu(){
Serial.println("Avançou Item do Menu Global");
if (vmMenuGlobal.menuIndex < vmMenuGlobal.numMenus - 1) {
vmMenuGlobal.menuIndex += 1;
} else {
vmMenuGlobal.menuIndex = 0; // Cycle back to the beginning
}
vmMenuGlobal.startIndex = vmMenuGlobal.menuIndex - 2;
vmMenuGlobal.endIndex = vmMenuGlobal.menuIndex + 1;
// Adjust start index and end index to stay within bounds
if (vmMenuGlobal.startIndex < 0) {
vmMenuGlobal.startIndex = 0;
vmMenuGlobal.endIndex = (vmMenuGlobal.maxVisibleItems - 1);
} else if (vmMenuGlobal.endIndex >= vmMenuGlobal.numMenus) {
vmMenuGlobal.endIndex = vmMenuGlobal.numMenus - 1;
vmMenuGlobal.startIndex = vmMenuGlobal.endIndex - (vmMenuGlobal.maxVisibleItems - 1);
}
displayMenu();
}
void VoltaItemMenu(){
if (vmMenuGlobal.menuIndex > 0) {
vmMenuGlobal.menuIndex -= 1;
} else {
vmMenuGlobal.menuIndex = vmMenuGlobal.numMenus - 1; // Cycle back to the end
}
vmMenuGlobal.startIndex = vmMenuGlobal.menuIndex - 1;
vmMenuGlobal.endIndex = vmMenuGlobal.menuIndex + 2;
// Adjust start index and end index to stay within bounds
if (vmMenuGlobal.startIndex < 0) {
vmMenuGlobal.startIndex = 0;
vmMenuGlobal.endIndex = (vmMenuGlobal.maxVisibleItems - 1);
} else if (vmMenuGlobal.endIndex >= vmMenuGlobal.numMenus) {
vmMenuGlobal.endIndex = vmMenuGlobal.numMenus - 1;
vmMenuGlobal.startIndex = vmMenuGlobal.endIndex - (vmMenuGlobal.maxVisibleItems - 1);
}
displayMenu();
}
void AvancaItemSubPresetSettings(){
Serial.println("Avançou Item do Submenu Preset Settings");
if (vmMenuPresetSettings.menuIndex < vmMenuPresetSettings.numMenus - 1) {
vmMenuPresetSettings.menuIndex += 1;
} else {
vmMenuPresetSettings.menuIndex = 0; // Cycle back to the beginning
}
vmMenuPresetSettings.startIndex = vmMenuPresetSettings.menuIndex - 2;
vmMenuPresetSettings.endIndex = vmMenuPresetSettings.menuIndex + 1;
// Adjust start index and end index to stay within bounds
if (vmMenuPresetSettings.startIndex < 0) {
vmMenuPresetSettings.startIndex = 0;
vmMenuPresetSettings.endIndex = (vmMenuPresetSettings.maxVisibleItems - 1);
} else if (vmMenuPresetSettings.endIndex >= vmMenuPresetSettings.numMenus) {
vmMenuPresetSettings.endIndex = vmMenuPresetSettings.numMenus - 1;
vmMenuPresetSettings.startIndex = vmMenuPresetSettings.endIndex - (vmMenuPresetSettings.maxVisibleItems - 1);
}
displaySubMenuPresetSettings();
}
void VoltaItemSubPresetSettings(){
Serial.println("Voltou Item do Submenu Preset Settings");
if (vmMenuPresetSettings.menuIndex > 0) {
vmMenuPresetSettings.menuIndex -= 1;
} else {
vmMenuPresetSettings.menuIndex = vmMenuPresetSettings.numMenus - 1; // Cycle back to the end
}
vmMenuPresetSettings.startIndex = vmMenuPresetSettings.menuIndex - 1;
vmMenuPresetSettings.endIndex = vmMenuPresetSettings.menuIndex + 2;
// Adjust start index and end index to stay within bounds
if (vmMenuPresetSettings.startIndex < 0) {
vmMenuPresetSettings.startIndex = 0;
vmMenuPresetSettings.endIndex = (vmMenuPresetSettings.maxVisibleItems - 1);
} else if (vmMenuPresetSettings.endIndex >= vmMenuPresetSettings.numMenus) {
vmMenuPresetSettings.endIndex = vmMenuPresetSettings.numMenus - 1;
vmMenuPresetSettings.startIndex = vmMenuPresetSettings.endIndex - (vmMenuPresetSettings.maxVisibleItems - 1);
}
displaySubMenuPresetSettings();
}
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
lastPressTime = millis();
}
buttonPressedTime = 0;
isLongpress = false;
}
}
void onButtonShortPress() {
Serial.println("Pressionou Rapidamente!");
//subMenuPresetSettingsIndex = (subMenuPresetSettingsIndex + 1) % numSubMenusPresetSettings; // MENU ///////////////////////////////
switch (mnusTelaSelecionada) {
case mnus.MenuGlobal:
switch (vmMenuGlobal.menuIndex) {
case 0: //Language
Serial.println("Chamei a tela Language");
mnusTelaSelecionada = mnus.Language;
break;
case 1: //Preset Settings
Serial.println("Chamei a tela Preset Settings");
mnusTelaSelecionada = mnus.PresetSettings; //DEFINE A TELA SELECIONADA
vmMenuPresetSettings.menuIndex = 0; //SELECIONA O PRIMEIRO ITEM DA LISTA
vmMenuPresetSettings.endIndex = (vmMenuPresetSettings.maxVisibleItems - 1); //VOLTA POSIÇÃO INICIAL DA PROGRESSBAR
vmMenuPresetSettings.startIndex = 0; //VOLTA POSIÇÃO INICIAL DA PROGRESSBAR
displaySubMenuPresetSettings(); //EXIBE TELA DE PRESET SETTINGS
break;
case 2: //Calibration Knobs
Serial.println("Chamei a tela Calibration Knobs");
mnusTelaSelecionada = mnus.CalibrationKnobs;
break;
case 3: //Factory Defaults
Serial.println("Chamei a tela Factory Defaults");
mnusTelaSelecionada = mnus.FactoryDefaults;
break;
case 4: //Exit
Serial.println("Chamei a tela Exit");
mnusTelaSelecionada = mnus.MenuGlobal;
break;
default:
break;
}
break;
case mnus.Language:
switch (vmMenuLanguage.menuIndex) {
case 0: //English
break;
case 1: //Portuguese
break;
default:
break;
}
break;
case mnus.PresetSettings:
Serial.println("vmMenuPresetSettings.menuIndex: " + String(vmMenuPresetSettings.menuIndex));
switch (vmMenuPresetSettings.menuIndex) {
case 0: // WakeUp Mode
break;
case 1: // Queue Limit
break;
case 2: // Configure Queue
break;
case 3: // Clear
break;
case 4: //Back
Serial.println("Voltei no Menu Global");
mnusTelaSelecionada = mnus.MenuGlobal;
displayMenu();
break;
default:
break;
}
break;
case mnus.CalibrationKnobs:
break;
case mnus.FactoryDefaults:
break;
case mnus.Exit:
break;
default:
break;
}
}
void onButtonLongPress() {
Serial.println("Pressionou e Segurou!");
}
void displayMenu() {
int16_t x1;
int16_t y1;
uint16_t width;
uint16_t height;
String Texto = descTelasMenus[0]; //GLOBAL MENU
int visibleItems = min(vmMenuGlobal.numMenus, vmMenuGlobal.maxVisibleItems); // Calcula o número total de itens visiveis
float percentVisible = (float)visibleItems / vmMenuGlobal.numMenus; // Calcula o percentual de itens visiveis
int scrollbarInnerHeight = SCROLLBAR_HEIGHT * percentVisible; // Calcula a altura da barra INNER
int scrollbarPosition = map(vmMenuGlobal.startIndex, 0, vmMenuGlobal.numMenus - visibleItems, SCROLLBAR_Y, SCROLLBAR_Y + SCROLLBAR_HEIGHT - scrollbarInnerHeight); // Calculate the scrollbar position based on the percentage of visible items
// LIMPA DISPLAY
display.clearDisplay();
//DEFINE FORMATAÇÃO
display.setTextSize(1);
display.setTextWrap(false);
display.setTextColor(SSD1306_WHITE);
// ESCREVE O TÍTULO DA TELA
display.getTextBounds(Texto, 0, 0, &x1, &y1, &width, &height);
display.setCursor((SCREEN_WIDTH - width) / 2, 0);
display.print(Texto);
//DESENHA A SCROLLBAR
display.drawRoundRect(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, 1, SSD1306_WHITE); // DESENHA A SCROLLBAR outline
display.fillRect(SCROLLBAR_INNER_X, scrollbarPosition, SCROLLBAR_INNER_WIDTH, scrollbarInnerHeight, SSD1306_WHITE); // DESENHA A SCROLLBAR inner part
// ESCREVE OS ITENS DA LISTA
for (int i = vmMenuGlobal.startIndex; i <= vmMenuGlobal.endIndex; i++) {
//DESENHA O QUADRO DE SELEÇÃO DO MENU
if (i == vmMenuGlobal.menuIndex) {
display.drawRect(0, (i - vmMenuGlobal.startIndex) * 12 + 12, 120, 11, SSD1306_WHITE);
//display.drawRoundRect(0, (i - vmMenuGlobal.startIndex) * 12 + 12, 120, 11, 3, SSD1306_WHITE);
}
//ESCREVE O ITEM
display.setCursor(2, (i - vmMenuGlobal.startIndex) * 12 + 14);
display.print(menus[i]);
}
//EXIBE NA TELA
display.display();
}
void displaySubMenuPresetSettings() {
int16_t x1;
int16_t y1;
uint16_t width;
uint16_t height;
String Texto = descTelasMenus[3]; //PRESSET SETTINGS
int visibleItems = min(vmMenuPresetSettings.numMenus, vmMenuPresetSettings.maxVisibleItems); // Calcula o número total de itens visiveis
float percentVisible = (float)visibleItems / vmMenuPresetSettings.numMenus; // Calcula o percentual de itens visiveis
int scrollbarInnerHeight = SCROLLBAR_HEIGHT * percentVisible; // Calcula a altura da barra INNER
int scrollbarPosition = map(vmMenuPresetSettings.startIndex, 0, vmMenuPresetSettings.numMenus - visibleItems, SCROLLBAR_Y, SCROLLBAR_Y + SCROLLBAR_HEIGHT - scrollbarInnerHeight); // Calculate the scrollbar position based on the percentage of visible items
// LIMPA DISPLAY
display.clearDisplay();
//DEFINE FORMATAÇÃO
display.setTextSize(1);
display.setTextWrap(false);
display.setTextColor(SSD1306_WHITE);
// ESCREVE O TÍTULO DA TELA
display.getTextBounds(Texto, 0, 0, &x1, &y1, &width, &height);
display.setCursor((SCREEN_WIDTH - width) / 2, 0);
display.print(Texto);
//DESENHA A SCROLLBAR
display.drawRoundRect(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH, SCROLLBAR_HEIGHT, 1, SSD1306_WHITE); // DESENHA A SCROLLBAR outline
display.fillRect(SCROLLBAR_INNER_X, scrollbarPosition, SCROLLBAR_INNER_WIDTH, scrollbarInnerHeight, SSD1306_WHITE); // DESENHA A SCROLLBAR inner part
// ESCREVE OS ITENS DA LISTA
for (int i = vmMenuPresetSettings.startIndex; i <= vmMenuPresetSettings.endIndex; i++) {
//DESENHA O QUADRO DE SELEÇÃO DO MENU
if (i == vmMenuPresetSettings.menuIndex) {
display.drawRect(0, (i - vmMenuPresetSettings.startIndex) * 12 + 12, 120, 11, SSD1306_WHITE);
//display.drawRoundRect(0, (i - vmMenuPresetSettings.startIndex) * 12 + 12, 120, 11, 3, SSD1306_WHITE);
}
//ESCREVE O ITEM
display.setCursor(0, (i - vmMenuPresetSettings.startIndex) * 12 + 14);
display.print(subMenuPresetSettings[i]);
}
//EXIBE NA TELA
display.display();
}