// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// ESP32-S3 44pin, connected to OLED display, menu with 3 buttons UP DOWN SELECT
//
// Hardware: ESP32-S3 44pin, OLED Display 128x64 I2C,
// External libraries: U8g2lib.h, Bounce2.h
// Processor selection in Arduino IDE: ESP32S3 Dev Module
// Possible Touch Pins for ESP32S3 with 44 pins: GPIO #: 1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Possible Output Pins for ESP32S3 with 44 pins: GPIO #: 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 46
// 0 1 2 19 20 21 35 36 37 38 39 40 41 42 43 44 45 47 48 48 (or38?): internal RGB LED
// What it does:
// -
// -
// to be added: -
// - -
// -
// 21.01.24 last (start dec. 2023)
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/*
* Very simple menu on an OLED display (with 8 lines of text).
* Displays menu items from the array menu. Max. number of items is 7.
*
* This sketch uses the library "U8g2", "Bounce2" and uses 3 buttons (up/down/select).
*
*/
// **************************************************************************************************
// D E F I N I T I O N S *
// **************************************************************************************************
//#include "Arduino.h" // Std. library
#include <U8g2lib.h> // library for OLED, e.g. 128x64 monochrome
// ===== CONSTRUCTOR for OLED display with u8g2 library, SCL clock and SDA data pins
//#define SDA 8 // GPIO08 I2C HW! SDA data on ESP32s3/44p
//#define SCL 9 // GPIO09 I2C HW! SCL clock on ESP32s3/44p
//U8G2_SSD1306_128X64_NONAME_F_SW_I2C display(U8G2_R0, SCL, SDA, U8X8_PIN_NONE); // constructor for OLED I2C display SSD1306 (or SSD1315), display name display
//U8G2_SSD1309_128X64_NONAME0_F_SW_I2C u8g2_2(U8G2_R0, SCL2, SDA2, U8X8_PIN_NONE); // constructor for 2.42" OLED I2C display SSD1309, display name "u8g2_2"
//U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, SCL, SDA, U8X8_PIN_NONE); // constructor for 0.96" OLED I2C display 0x3C SSD1306 (or SSD1315), display name "display"
// ===== Constructor in WOKWI apparently needs ESP32s3/44p Hardware pins 8 (data) and 9 (clock):
U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled(U8G2_R0); // for WOKWI with hardware I2C pins, [full framebuffer, size = 1024 bytes], note that taking screenshow with u8g2 only works with fullscreen buffer
#include <Bounce2.h> // needed?
int upButton = 42;
int downButton = 41;
int selectButton = 40;
int menu = 1; // variable to remember which menu position has been selected
float cursor=23.245; // just for testing
// -----other more complicated/elegant option??-----
/*
byte button_pins[] = {4, 5, 6}; // button pins, 4,5 = up/down, 6 = select
#define NUMBUTTONS sizeof(button_pins)
Bounce * buttons = new Bounce[NUMBUTTONS];
#define MENU_SIZE 5
char *menu[MENU_SIZE] = { "Option 1", "Option 2", "Option 3", "Option 4", "Option 5" };
*/
// **************************************************************************************************
// S E T U P *
// **************************************************************************************************
void setup()
{
Serial.begin(115200); // was: 9600
oled.begin(); // begin U8g2lib.h named "oled" for OLED 128x64
pinMode(upButton, INPUT_PULLUP);
pinMode(downButton, INPUT_PULLUP);
pinMode(selectButton, INPUT_PULLUP);
updateMenu(); // call Function so that Menu is shown after switching ON
// -----other more complicated/elegant option??-----
/*
for (int i=0; i<NUMBUTTONS; i++) // Make input & enable pull-up resistors on switch pins
{ buttons[i].attach( button_pins[i], INPUT_PULLUP); // setup the bounce instance for the current button
buttons[i].interval(25); // interval in ms
}
oled.begin(); // begin U8g2lib.h named "oled" for OLED 128x64
oled.setPowerSave(0); // why ?
//oled.setFontMode(0); // optional: activate (1) or deactivate (0) transparent font mode (background shines through or not). Default: 0
//oled.setDrawColor(1); // optional: color index for all drawing functions (text and boxes). Default: 1. black (0: white, 2: always reversed white on black/black on white)
//oled.setBitmapMode(0); // optional: bitmap functions write background color (mode 0/solid, not transparent) or (mode 1/transparent, overlay). Default: 0
oled.clearBuffer(); // clear buffer for storing display content in RAM
//oled.setFont(u8x8_font_pxplusibmcgathin_f); // this font was in the original code! not working in Wokwi
//oled.setFont(u8g2_font_nerhoe_tr); // set font
//oled.setFont(u8g2_font_profont11_tr); // set font for small digits
oled.setFont(u8g2_font_profont10_tr); // font for the small label, e.g. "POWER"
//oled.setDrawColor(1); // ??? set color to white
//oled.drawCircle(64, 60, 59, U8G2_DRAW_UPPER_LEFT | U8G2_DRAW_UPPER_RIGHT); // draw big semicircles for gauge outline
//oled.drawCircle(64, 60, 57, U8G2_DRAW_UPPER_LEFT | U8G2_DRAW_UPPER_RIGHT); // draw big semicircles for gauge outline
//strcpy(string_buffer, "POWER"); // write label to buffer
//oled.drawStr(10, 10, string_buffer); // place label centered
oled.drawStr(0, 10, "Display test while in Setup..."); // write some stuff
oled.drawStr(0, 30, "Font: profont10_tr"); // write some stuff
oled.setFont(u8g2_font_profont11_tr); // set font for small digits
oled.drawStr(0, 50, "Font: profont11_tr"); // write some stuff
oled.setCursor(80,10);
oled.print("Value:");
oled.setCursor(80,40);
oled.print(cursor, 2);
oled.sendBuffer(); // send buffer from RAM to display controller
delay(2000);
showMenu();
*/
}
// **************************************************************************************************
// F U N C T I O N S *
// **************************************************************************************************
void updateMenu() // FUNCTION: called if UP or DOWN has been pressed
{
switch(menu) // switch the variable 'menu' for different cases
{
case 0: // helping case avoiding that the variable 'menu' can take unwanted values
menu = 1;
break;
case 1: // case with a value for the variable 'menu', followed by :
oled.clearBuffer(); // clear buffer for storing display content in RAM
oled.setFont(u8g2_font_nerhoe_tr); // set font
oled.drawStr( 0, 10, ">>case1...#of menu"); // draw String
oled.drawStr( 0, 20, " case2...#of menu"); // draw String
oled.drawStr( 0, 30, " case3...#of menu"); // draw String
oled.drawStr( 0, 40, " case4...#of menu"); // draw String
oled.setCursor(120, 64);
oled.print(menu);
oled.sendBuffer(); // send buffer from RAM to display controller
break; // waits for new action, leave switch instruction with 'break'
case 2: // case with a value for the variable 'menu', followed by :
oled.clearBuffer(); // clear buffer for storing display content in RAM
oled.setFont(u8g2_font_nerhoe_tr); // set font
oled.drawStr( 0, 10, " case1...#of menu"); // draw String
oled.drawStr( 0, 20, ">>case2...#of menu"); // draw String
oled.drawStr( 0, 30, " case3...#of menu"); // draw String
oled.drawStr( 0, 40, " case4...#of menu"); // draw String
oled.setCursor(120, 64);
oled.print(menu);
oled.sendBuffer(); // send buffer from RAM to display controller
break; // waits for new action, leave switch instruction with 'break'
case 3: // case with a value for the variable 'menu', followed by :
oled.clearBuffer(); // clear buffer for storing display content in RAM
oled.setFont(u8g2_font_nerhoe_tr); // set font
oled.drawStr( 0, 10, " case1...#of menu"); // draw String
oled.drawStr( 0, 20, " case2...#of menu"); // draw String
oled.drawStr( 0, 30, ">>case3...#of menu"); // draw String
oled.drawStr( 0, 40, " case4...#of menu"); // draw String
oled.setCursor(120, 64);
oled.print(menu);
oled.sendBuffer(); // send buffer from RAM to display controller
break; // waits for new action, leave switch instruction with 'break'
case 4: // case with a value for the variable 'menu', followed by :
oled.clearBuffer(); // clear buffer for storing display content in RAM
oled.setFont(u8g2_font_nerhoe_tr); // set font
oled.drawStr( 0, 10, " case1...#of menu"); // draw String
oled.drawStr( 0, 20, " case2...#of menu"); // draw String
oled.drawStr( 0, 30, " case3...#of menu"); // draw String
oled.drawStr( 0, 40, ">>case4...#of menu"); // draw String
oled.setCursor(120, 64);
oled.print(menu);
oled.sendBuffer(); // send buffer from RAM to display controller
break; // waits for new action, leave switch instruction with 'break'
case 5: // helping case avoiding that the variable 'menu' can take unwanted values
menu = 4;
break;
}
}
void executeAction() // FUNCTION: called if SELECT has been pressed
{
switch(menu) // switch the variable 'menu' for different cases
{
case 1: // what action has to be taken in that case?
action1();
break;
case 2: // what action has to be taken in that case?
action2();
break;
case 3: // what action has to be taken in that case?
action3();
break;
case 4: // what action has to be taken in that case?
action4();
break;
}
}
void action1() // FUNCTION: called if SELECT has been pressed
{ oled.clearBuffer();
oled.drawStr( 0, 10, ">>executing action #1"); // draw String
oled.sendBuffer();
delay(1000);
}
void action2() // FUNCTION: called if SELECT has been pressed
{ oled.clearBuffer();
oled.drawStr( 0, 10, ">>executing action #2"); // draw String
oled.sendBuffer();
delay(1000);
}
void action3() // FUNCTION: called if SELECT has been pressed
{ oled.clearBuffer();
oled.drawStr( 0, 10, ">>executing action #3"); // draw String
oled.sendBuffer();
delay(1000);
}
void action4() // FUNCTION: called if SELECT has been pressed
{ oled.clearBuffer();
oled.drawStr( 0, 10, ">>executing action #4"); // draw String
oled.sendBuffer();
delay(1000);
}
// -----other more complicated/elegant option??-----
/*
void showMenu() // FUNCTION: * Clear display and show the menu.
{ cursor=0;
oled.clearBuffer(); // was: oled.clearDisplay();
for (int i = 0; i<MENU_SIZE; i++) // show menu items:
{ //oled.drawStr(2,i,"menu[i]"); // CHECK FOR ERROR, was: oled.drawString(2,i,menu[i]);
oled.setCursor(2,i);
oled.print(menu[i]);
}
oled.setCursor(0,0);
oled.print(">"); // was: oled.print('>');
oled.sendBuffer(); // send buffer from RAM to display controller
}
void executeChoice(int choice) // FUNCTION: * Execute the task which matches the chosen menu item.
{
switch(choice)
{
case 0 :
Serial.print("Execute choice "); Serial.print(choice); Serial.print(" - "); Serial.println(menu[choice]);
break;
case 1 :
Serial.print("Execute choice "); Serial.print(choice); Serial.print(" - "); Serial.println(menu[choice]);
break;
case 2 :
Serial.print("Execute choice "); Serial.print(choice); Serial.print(" - "); Serial.println(menu[choice]);
break;
case 3 :
Serial.print("Execute choice "); Serial.print(choice); Serial.print(" - "); Serial.println(menu[choice]);
break;
case 4 :
Serial.print("Execute choice "); Serial.print(choice); Serial.print(" - "); Serial.println(menu[choice]);
break;
default :
Serial.print("Execute choice "); Serial.print(choice); Serial.print(" - "); Serial.println(menu[choice]);
break;
}
}
*/
// **************************************************************************************************
// L O O P *
// **************************************************************************************************
void loop()
{
//oled.clearBuffer(); // needed? clear buffer for storing display content in RAM
if (!digitalRead(downButton)) // was downButton pressed? waiting that button changes from HIGH to LOW
{ menu++; // raise menu by 1...
updateMenu(); // and update menu with the new 'menu' variable
delay(100); // avoid button bouncing
while (!digitalRead(downButton)); // optional trick for stability, no matter how long button was pressed
}
if (!digitalRead(upButton)) // was upButton pressed? waiting that button changes from HIGH to LOW
{ menu--; // lower menu by 1...
updateMenu(); // and update menu with the new 'menu' variable
delay(100); // avoid button bouncing
while (!digitalRead(upButton)); // optional trick for stability, no matter how long button was pressed, anti-scroll
}
if (!digitalRead(selectButton)) // was selectButton pressed? waiting that button changes from HIGH to LOW
{ executeAction(); // call function for the needed action
updateMenu(); // show the menu
delay(100); // avoid button bouncing
while (!digitalRead(selectButton)); // optional trick for stability, no matter how long button was pressed, anti-scroll
}
// -----other more complicated/elegant option??-----
/*
for (int i = 0; i<NUMBUTTONS; i++) // process button press:
{
buttons[i].update(); // Update the Bounce instance
if ( buttons[i].fell() ) // If it fell
{
if (i==2) // select
{ //oled.clearLine(7); CHECK FOR ERROR
oled.setCursor(0,7);
oled.print(">>");
oled.print(menu[cursor]);
executeChoice(cursor);
oled.sendBuffer(); // send buffer from RAM to display controller
}
else // erase previous cursor:
{ oled.setCursor(0,cursor);
oled.print(" "); // was: oled.print(' ');
if (i==0) // up
{ cursor++;
if (cursor>(MENU_SIZE-1)) cursor=0;
}
else // down
{ cursor--;
if (cursor<0) cursor=(MENU_SIZE-1);
}
oled.setCursor(0,cursor); // show cursor at new line:
oled.print(">"); // was: oled.print('>');
oled.sendBuffer(); // send buffer from RAM to display controller
}
} // end if button fell...
} // end for-loop of button check
*/
/* ===== writing to OLED for testing
oled.clearBuffer(); // clear buffer for storing display content in RAM
oled.setFont(u8g2_font_nerhoe_tr); // set font
oled.drawStr( 0, 10, "Loop i..."); // draw String
oled.setCursor(0, 30);
oled.print("i");
oled.setCursor(0, 45);
oled.print(cursor);
//oled.print(menu[cursor]);
oled.sendBuffer(); // send buffer from RAM to display controller
*/
}