/**************************************************************************************************
FileName : Menu_DFR_LCD_I2CLCD_V1.ino
Menu de base pour ShieldsLab
I2C_LCD2004 display simple none blocking menu System - i2c version SSD1306 - 30mar22
This sketch is on Github: https://github.com/alanesq/BasicOLEDMenu
https://wokwi.com/projects/323967017900048980 "sketch Source"
**************************************************************************************************
The sketch displays a menu on the oled and when an item is selected it sets a
flag and waits until the event is acted upon. Max menu items on a 128x64 oled
is four.
Notes: text size 1 = 21 x 8 characters on the larger oLED display
text size 2 = 10 x 4
For more oled info see: https://randomnerdtutorials.com/guide-for-oled-display-with-arduino/
See the "menus below here" section for examples of how to use the menus
Note: If you get garbage on the display and the device locking up etc. it may just be a poor connection
to the rotary encoder
Status : OK
**************************************************************************************************/
/*
LCD Keypad Shield Menu System
learnelectronics
18 Sept 2017
adapted from an original sketch by Paul Siewert
www.youtube.com/c/learnelectronics
[email protected]
*/
#include "Affiche_I2C_LCD.h"
// Creates 3 custom characters for the menu display
byte downArrow[8] = {
0b00100, // *
0b00100, // *
0b00100, // *
0b00100, // *
0b00100, // *
0b10101, // * * *
0b01110, // ***
0b00100 // *
};
byte upArrow[8] = {
0b00100, // *
0b01110, // ***
0b10101, // * * *
0b00100, // *
0b00100, // *
0b00100, // *
0b00100, // *
0b00100 // *
};
byte menuCursor[8] = {
B01000, // *
B00100, // *
B00010, // *
B00001, // *
B00010, // *
B00100, // *
B01000, // *
B00000 //
};
const int iLED = 13; // onboard indicator led gpio pin
// int stringStart = 0;
// int stringEnd = 39;
// int scrollCursor = 0; //lcd_screenWidth;
// String line1 = "STARTED"; // stationary
// String line2 = "Presse Button 'SELECT' to START Main_menu"; // scroll this line
// You can have up to 10 menu items in the menuItems[] array below
// without having to change the base programming at all. Name them however you'd like.
// Beyond 10 items, you will have to add additional "cases" in the switch/case
// section of the operateMainMenu() function below.
// You will also have to add additional void functions (i.e. menuItem11, menuItem12, etc.) to the program.
String menuItems[] = {"Yellow", "Green", "Red", "Purple", "Blink", "Fade"};
// Navigation button variables
int readKey;
int savedDistance = 0;
// Menu control variables
int menuPage = 0;
int maxMenuPages = round(((sizeof(menuItems) / sizeof(String)) / 4) + .5);
int cursorPosition = 0;
void setup() {
// Initializes serial communication
Serial.begin(115200); while (!Serial); delay(50); // start serial comms
Serial.println("Starting SETUP\n");
delay(1000);
pinMode(iLED, OUTPUT); // onboard indicator led
// --- initialise les affichages
// Initializes and clears the LCD1602 screen
// init_lcd1602();
// Initializes and clears the I2C_LCD2004 screen
init_I2C_lcd2004();
// Initializes and clears the OLED screen
// init_oLED();
// Serial.println();
// --- Creates the byte for the 3 custom characters
I2C_lcd.createChar(0, menuCursor);
I2C_lcd.createChar(1, upArrow);
I2C_lcd.createChar(2, downArrow);
pinMode(13, OUTPUT); // Yellow
pinMode(12, OUTPUT); // Green
pinMode(11, OUTPUT); // Red
pinMode(3, OUTPUT); // Purple
// --- display greeting message - pressing button 'SELECT' will start menu
// *****************************
// greeting on Serial Monitor
// *****************************
// Serial.println("STARTED");
// Serial.println("Presse Button 'SELECT'\nto START Main_menu\n");
// *****************************
// greeting on LCD 1602
// *****************************
// greeting_lcd1602(); // scrolling line2 to left
// *****************************
// greeting on I2C_LCD2004
// *****************************
greeting_I2C_lcd2004();
// *****************************
// greeting on oLED
// *****************************
// greeting_oLED();
Serial.println("SETUP done\n");
Serial.println("Starting Main menu\n");
Serial.print("maxMenuPages :"); Serial.println(maxMenuPages);
}
void loop() {
mainMenuDraw();
drawCursor();
operateMainMenu();
// flash onboard led
// static uint32_t ledTimer = millis();
// if ( (unsigned long)(millis() - ledTimer) > 500 ) {
// digitalWrite(iLED, !digitalRead(iLED));
// ledTimer = millis();
// }
}
// This function will generate the 2 menu items that can fit on the screen.
// They will change as you scroll through your menu.
// Up and down arrows will indicate your current menu position.
void mainMenuDraw() {
// debug
// Serial.print("menuPage : "); Serial.println(menuPage);
I2C_lcd.clear();
I2C_lcd.setCursor(1, 0); // yellow
I2C_lcd.print(menuItems[menuPage]);
I2C_lcd.setCursor(1, 1); // green
I2C_lcd.print(menuItems[menuPage + 1]);
I2C_lcd.setCursor(1, 2); // Red
I2C_lcd.print(menuItems[menuPage + 2]);
I2C_lcd.setCursor(1, 3); // purple
I2C_lcd.print(menuItems[menuPage + 3]);
if (menuPage == 0) {
I2C_lcd.setCursor(19, 3);
I2C_lcd.write(byte(2));
} else if (menuPage > 0 and menuPage < maxMenuPages) {
I2C_lcd.setCursor(19, 3);
I2C_lcd.write(byte(2));
I2C_lcd.setCursor(19, 0);
I2C_lcd.write(byte(1));
} else if (menuPage == maxMenuPages) {
I2C_lcd.setCursor(19, 0);
I2C_lcd.write(byte(1));
}
}
// When called, this function will erase the current cursor and
// redraw it based on the cursorPosition and menuPage variables.
void drawCursor() {
for (int x = 0; x < 4; x++) { // Erases current cursor
I2C_lcd.setCursor(0, x);
I2C_lcd.print(" ");
}
// The menu is set up to be progressive
// menuPage 0 = Item 1 & Item 2 & Item 3 & Item 4,
// menuPage 1 = Item 2 & Item 3 & Item 4 & Item 5,
// menuPage 2 = Item 3 & Item 4 & Item 5 & Item 6,
// so in order to determine where the cursor should be you need to see
// if you are at an odd or even menu page and an odd or even cursor position.
// debug
// Serial.print("cursorPosition "); Serial.println(cursorPosition);
if (menuPage == 0) {
if (cursorPosition == 0) { // If the menu page is even and the cursor position is even
// that means the cursor should be on line 0
I2C_lcd.setCursor(0, 0);
I2C_lcd.write(byte(0));
}
if (cursorPosition == 1) { // If the menu page is even and the cursor position is even
// that means the cursor should be on line 1
I2C_lcd.setCursor(0, 1);
I2C_lcd.write(byte(0));
}
if (cursorPosition == 2) { // If the menu page is even and the cursor position is even
// that means the cursor should be on line 2
I2C_lcd.setCursor(0, 2);
I2C_lcd.write(byte(0));
}
if (cursorPosition == 3) { // If the menu page is even and the cursor position is even
// that means the cursor should be on line 3
I2C_lcd.setCursor(0, 3);
I2C_lcd.write(byte(0));
}
}
if (menuPage == 1) {
if (cursorPosition == 4) { // If the menu page is odd and the cursor position is even
// that means the cursor should be on line 3
I2C_lcd.setCursor(0, 3);
I2C_lcd.write(byte(0));
}
if (cursorPosition == 1) { // If the menu page is odd and the cursor position is even
// that means the cursor should be on line 2
I2C_lcd.setCursor(0, 0);
I2C_lcd.write(byte(0));
}
}
if (menuPage == 2) {
if (cursorPosition == 2) { // If the menu page is odd and the cursor position is odd
// that means the cursor should be on line 1.
I2C_lcd.setCursor(0, 0);
I2C_lcd.write(byte(0));
}
if (cursorPosition == 3) { // If the menu page is odd and the cursor position is odd
// that means the cursor should be on line 1.
I2C_lcd.setCursor(0, 1);
I2C_lcd.write(byte(0));
}
if (cursorPosition == 4) { // If the menu page is odd and the cursor position is odd
// that means the cursor should be on line 1.
I2C_lcd.setCursor(0, 2);
I2C_lcd.write(byte(0));
}
if (cursorPosition == 5) { // If the menu page is odd and the cursor position is odd
// that means the cursor should be on line 1.
I2C_lcd.setCursor(0, 3);
I2C_lcd.write(byte(0));
}
}
}
void operateMainMenu() {
int activeButton = 0;
while (activeButton == 0) {
int button;
readKey = analogRead(0);
if (readKey < 790) {
delay(100);
readKey = analogRead(0);
}
button = evaluateButton(readKey);
switch (button) {
case 0: // When button returns as 0 there is no action taken
break;
case 1: // This case will execute if the "forward" button is pressed
button = 0;
switch (cursorPosition) { // The case that is selected here is dependent
// on which menu page you are on and where the cursor is.
case 0:
menuItem1(); // Yellow
break;
case 1:
menuItem2(); // Green
break;
case 2:
menuItem3(); // Red
break;
case 3:
menuItem4(); // Purple
break;
case 4:
menuItem5(); // Blink
break;
case 5:
menuItem6(); // Fade
break;
}
activeButton = 1;
mainMenuDraw();
drawCursor();
break;
case 2: // bouton UP
button = 0;
// debug
// Serial.print("cursorPosition "); Serial.println(cursorPosition);
if (menuPage == 1 and cursorPosition == 1) {
menuPage = menuPage - 1;
menuPage = constrain(menuPage, 0, maxMenuPages);
}
if (menuPage == 2 and cursorPosition == 2) {
menuPage = menuPage - 1;
menuPage = constrain(menuPage, 0, maxMenuPages);
}
cursorPosition = cursorPosition - 1;
cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));
mainMenuDraw();
drawCursor();
activeButton = 1;
break;
case 3: // bouton DOWN
button = 0;
Serial.print("cursorPosition "); Serial.println(cursorPosition);
if (menuPage == 0 and cursorPosition < 3) {
// menuPage = menuPage + 1;
menuPage = constrain(menuPage, 0, maxMenuPages - 1);
}
if (menuPage == 0 and cursorPosition == 3) {
menuPage = menuPage + 1;
menuPage = constrain(menuPage, 0, maxMenuPages);
}
if (menuPage == 1 and cursorPosition == 4) {
menuPage = menuPage + 1;
menuPage = constrain(menuPage, 0, maxMenuPages);
}
cursorPosition = cursorPosition + 1;
cursorPosition = constrain(cursorPosition, 0, ((sizeof(menuItems) / sizeof(String)) - 1));
mainMenuDraw();
drawCursor();
activeButton = 1;
break;
}
}
}
// This function is called whenever a button press is evaluated.
// The LCD shield works by observing a voltage drop across the buttons all hooked up to A0.
int evaluateButton(int x) {
int result = 0;
if (x < 50) {
result = 1; // right
} else if (x < 195) {
result = 2; // up
} else if (x < 380) {
result = 3; // down
} else if (x < 790) {
result = 4; // left
}
return result;
}
// If there are common usage instructions on more than 1 of your menu items
// you can call this function from the sub menus to make things a little more simplified.
// If you don't have common instructions or verbage on multiple menus
// I would just delete this void. You must also delete the drawInstructions()
// function calls from your sub menu functions.
void drawInstructions() {
I2C_lcd.setCursor(0, 1); // Set cursor to the bottom line
I2C_lcd.print("Use ");
I2C_lcd.write(byte(1)); // Up arrow
I2C_lcd.print("/");
I2C_lcd.write(byte(2)); // Down arrow
I2C_lcd.print(" buttons");
}
void menuItem1() { // Function executes when you select the Yellow item from main menu
int activeButton = 0;
I2C_lcd.clear();
I2C_lcd.setCursor(3, 0);
I2C_lcd.print("Yellow On");
digitalWrite(13, HIGH);
while (activeButton == 0) {
int button;
readKey = analogRead(0);
if (readKey < 790) {
delay(100);
readKey = analogRead(0);
}
button = evaluateButton(readKey);
switch (button) {
case 4: // This case will execute if the "back" button is pressed
button = 0;
activeButton = 1;
digitalWrite(13, LOW);
break;
}
}
}
void menuItem2() { // Function executes when you select the Green item from main menu
int activeButton = 0;
I2C_lcd.clear();
I2C_lcd.setCursor(3, 0);
I2C_lcd.print("Green On");
digitalWrite(12, HIGH);
while (activeButton == 0) {
int button;
readKey = analogRead(0);
if (readKey < 790) {
delay(100);
readKey = analogRead(0);
}
button = evaluateButton(readKey);
switch (button) {
case 4: // This case will execute if the "back" button is pressed
button = 0;
activeButton = 1;
digitalWrite(12, LOW);
break;
}
}
}
void menuItem3() { // Function executes when you select the Red item from main menu
int activeButton = 0;
I2C_lcd.clear();
I2C_lcd.setCursor(3, 0);
I2C_lcd.print("Red On");
digitalWrite(11, HIGH);
while (activeButton == 0) {
int button;
readKey = analogRead(0);
if (readKey < 790) {
delay(100);
readKey = analogRead(0);
}
button = evaluateButton(readKey);
switch (button) {
case 4: // This case will execute if the "back" button is pressed
button = 0;
activeButton = 1;
digitalWrite(11, LOW);
break;
}
}
}
void menuItem4() { // Function executes when you select the Purple item from main menu
int activeButton = 0;
I2C_lcd.clear();
I2C_lcd.setCursor(3, 0);
I2C_lcd.print("Purple On");
digitalWrite(3, HIGH);
while (activeButton == 0) {
int button;
readKey = analogRead(0);
if (readKey < 790) {
delay(100);
readKey = analogRead(0);
}
button = evaluateButton(readKey);
switch (button) {
case 4: // This case will execute if the "back" button is pressed
button = 0;
activeButton = 1;
digitalWrite(3, LOW);
break;
}
}
}
void menuItem5() { // Function executes when you select the Blink item from main menu
int activeButton = 0;
I2C_lcd.clear();
I2C_lcd.setCursor(0, 0);
I2C_lcd.print("Green Blinking");
Serial.println("Green is Blinking");
blink_NB();
// digitalWrite(12, HIGH);
while (activeButton == 0) {
int button;
readKey = analogRead(0);
if (readKey < 790) {
delay(100);
readKey = analogRead(0);
}
button = evaluateButton(readKey);
switch (button) {
case 4: // This case will execute if the "back" button is pressed
button = 0;
activeButton = 1;
digitalWrite(12, LOW);
break;
}
}
}
void menuItem6() { // Function executes when you select the Purple item from main menu
int activeButton = 0;
I2C_lcd.clear();
I2C_lcd.setCursor(3, 0);
I2C_lcd.print("Red Fading");
Serial.println("Red is Fading");
digitalWrite(3, HIGH);
while (activeButton == 0) {
int button;
readKey = analogRead(0);
if (readKey < 790) {
delay(100);
readKey = analogRead(0);
}
button = evaluateButton(readKey);
switch (button) {
case 4: // This case will execute if the "back" button is pressed
button = 0;
activeButton = 1;
digitalWrite(3, LOW);
break;
}
}
}