Help modifying/understanding code for Automation Project

I have a;
arduino mega 2560
20x4 LCD
DHT11 TempHum
8CH Relay
5 5v fans
esp wifi chip
analog temp
SD Card
3 digital buttons

I would like to display on the lcd a menu, and have pages beyond them.
For Example;
Display
Relays
AnalogTemp
DigTempHum
Fans
Timer
SD Card

Then the page beyond them would have adjustable settings relating to the menu item.
For Display it would be nice to choose what items to display for each line by the use of a array,
Relays can show the available channels if on/off and if selected can be turned on,
AnalogTemp displays the values of the sensor if sensor falls below certain value should it turn on a relay, DigTempHum would have the same thing as AnalogTemp, Fans would be setup like Relays,
and Timer would allow a time limit to be set for a desired function/ like turn off a relay after 5min.

I have got a tiny bit done up to the menu, I am including my code and 2 examples I really admire.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>

#define dht_apin A0 //Analog sensor pin is connected to.
#define DHTTYPE DHT11

volatile int sec, minute = 0, hour = 0;

DHT dht(dht_apin, DHTTYPE); //init dht libary
LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x27 for a 20 chars and 4 line display

int b_1 = 2; //button 1 assigned to digital pin 3
int b_2 = 3; //button 2 assigned to digital pin 4
int b_3 = 4; //button 3 assigned to digital pin 5

void setup()
{
pinMode(b_1, INPUT_PULLUP);
pinMode(b_2, INPUT_PULLUP);
pinMode(b_3, INPUT_PULLUP);
dht.begin();//initialize dht sensor
delay(500);//delay to let system boot.
lcd.init(); // Print a message to the LCD.
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print(“Garduino”);
lcd.clear();
}

void loop()
{

float h = dht.readHumidity();//reads humidity
float t = dht.readTemperature();//reads temperature as C
float f = dht.readTemperature(true);//reads temperature as F

// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t) || isnan(f)) {
lcd.println(“Failed to read from DHT sensor!”);
return;
}

// Compute heat index
// Must send in temp in Fahrenheit!
float hi = dht.computeHeatIndex(f, h);

lcd.setCursor(0, 1);
lcd.print(“Hum: “);
lcd.print(h);
lcd.print(” %”);
lcd.setCursor(0, 0);
lcd.print(“Temp: “);
lcd.print(t);
lcd.print(“C “);
lcd.print(f);
lcd.print(“F”);
//lcd.print(“Heat index: “);
//lcd.print(hi);
//lcd.println(” *F”);
lcd.setCursor(0, 2);
//sec = millis() / 1000;
lcd.print(“Time: “);
if (hour < 10) {
lcd.print(“0”);
lcd.print(hour);
lcd.print(”:”);
}
else {
lcd.print(hour);
lcd.print(”:”);
}
if (minute < 10) {
lcd.print(“0”);
lcd.print(minute);
lcd.print(”:”);
}
else {
lcd.print(minute);
lcd.print(":");
}
if (sec < 10) {
lcd.print(“0”);
lcd.print(sec);
}
else {
lcd.print(sec);
}
lcd.print(" ");
sec++;
if (sec > 800) {
minute++;
sec = 0;
}
if (minute > 59) {
hour++;
minute = 0;
}
if (hour > 12) {
hour = 1;
}

if (digitalRead(b_1) == HIGH) {
hour++;
if (hour > 12)
hour = 1;
}
if (digitalRead(b_2) == HIGH) {
minute++;
if (minute > 59)
minute = 0;
}

}

I really like the way the github version is written, it’s efficient and not long, but single page only.
Could I add a switch statement in and modify the screens variable to acquire a 2nd page?

As for the pastebin version, it’s config’d for a 16x2LCD, could the drawcursor function be rewritten in a more efficient way?

Also I have 3 buttons, 1 for select, 1 increase, and 1 decrease
Was thinking maybe use multiclick and doubleclick select for back or just add a 4th button.

Saving data from sensors to the sd card would be great as well but the menu is more important right now to me.

I know you guys have much greater experience then myself, and dont know where else to turn for guidance, any help is so greatly appreciated!

When I did a menu for an actual product for work, I had five buttons: up, down, left, right, enter double-clicking and multi-clicks I find to be troublesome

I'm thinking it may be easiest to just add a 4th button, easier to write the code for. So I can have Back,Select,Increase,Decrease.

I hope I didn't put this in the wrong section, but would prefer to use the github example I just need help with the placement of the switch statement so can access additional pages and I am hoping I can do the rest.

https://arduino.stackexchange.com/questions/34276/multi-level-button-menu

Here is another example of a menu I really like, the simplicity in the beginning is nice!

Hi, Welcome to the forum.

Please read the first post in any forum entitled how to use this forum. http://forum.arduino.cc/index.php/topic,148850.0.html . Then look down to item #7 about how to post your code. It will be formatted in a scrolling window that makes it easier to read.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Ops pic. |372x500

Thanks.. Tom.. :)

Sure that's no problem, never got used to working with cad so I can dl a pic of a breadboard and post it after editting it. I will also refer to rule 7 for posting code, thank you for not instabanning me for my ignorance in reading rules. I just like to ask questions and learn, never mean to impose.

LCD is hooked up in standard format
DHT11 is connected to negative and pos at breadboard and A0 on the Mega
Buttons are connected to digi pins 2,3,4. 10k resistor connecting one corner of button to neg of breadboard.

Sorry for my poor mspaint craft

Hi, |500x352 We need a circuit diagram. A picture of a hand drawn circuit will be fine.

Thanks.. Tom.. :)

I should of downsized my last working program down to the most efficient code for myself.

After studying the 3 menu examples I admire for different things I finally know what I believe i'd like for sure. The EEEuthusiast example code with a switch statement similiar to the pastebin switch statement in the inputAction section.

The parameter variables/ last 2 elseif statements get rewritten so when the desired menu page is selected it can refer to the switch to direct the user.

The screens variable gets rewritten to this format screens[numOfScreens] = {"Item1","item2","item3",...} printScreen along with anything else gets edited to support the extra 2 lines. Buttons 3 and 4 would be defined in the switch statement to adjust local variables

I will try my best to dry a schematic of the arduino side later on today after trying to figure this out a little bit, took me a lil long just to do the breadboard pic which unfortunately cut my diagnosing time short.

The arduino side is super simple though, just a I2C LCD connected to pins described in the program and DHT11 at A0 then the 3 buttons which I have to add another one to.

I am open to changing anything however as suggestions are nice. Been doing this in small steps so hopefully this can turn out how I wish or how I'd like.

If I had a dead set circuit I would of been sure to of include it but for now my circuit can still change, the option of putting multiple buttons to 1 analog pin is very attractive, so its not completely off the table yet since I don't know how many more pins will be getting used with other hardware that I'd like to add like the SDCard, Wifi, and other misc sensors.

#include <LiquidCrystal_I2C.h> //from newliquidcrystal library
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address

//Input & Button Logic
const int numOfInputs = 4;
const int inputPins[numOfInputs] = { 8,9,10,11 };
int inputState[numOfInputs];
int lastInputState[numOfInputs] = { LOW,LOW,LOW,LOW };
bool inputFlags[numOfInputs] = { LOW,LOW,LOW,LOW };
long lastDebounceTime[numOfInputs] = { 0,0,0,0 };
long debounceDelay = 5;

//LCD Menu Logic
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  //
};

int menuPage = 0;
int maxMenuPages = round(((sizeof(menuItems) / sizeof(String)) / 2) + .5);
const int numOfScreens = 5;
int currentScreen = 0;
String screens[numOfScreens] = {"Display","Temperature",
"Relays","Fans","Timer"};

void setup() {
	for (int i = 0; i < numOfInputs; i++) {
		pinMode(inputPins[i], INPUT);
		digitalWrite(inputPins[i], HIGH); // pull-up 20k
	}
	//Serial.begin(9600);
	lcd.begin(16, 2);
	lcd.createChar(0, menuCursor);
	lcd.createChar(1, upArrow);
	lcd.createChar(2, downArrow);
}

void loop() {
	setInputFlags();
	resolveInputFlags();
}

void setInputFlags() {
	for (int i = 0; i < numOfInputs; i++) {
		int reading = digitalRead(inputPins[i]);
		if (reading != lastInputState[i]) {
			lastDebounceTime[i] = millis();
		}
		if ((millis() - lastDebounceTime[i]) > debounceDelay) {
			if (reading != inputState[i]) {
				inputState[i] = reading;
				if (inputState[i] == HIGH) {
					inputFlags[i] = HIGH;
				}
			}
		}
		lastInputState[i] = reading;
	}
}

void resolveInputFlags() {
	for (int i = 0; i < numOfInputs; i++) {
		if (inputFlags[i] == HIGH) {
			inputAction(i);
			inputFlags[i] = LOW;
			printScreen();
		}
	}
}

void inputAction(int input) {
	button = int(input);
	switch (button) {
		case 0:
			break;
		case 1: // This case will execute if button 1 is pressed
			button = 0;
			if (currentScreen == 0) {
				currentScreen = numOfScreens - 1;
			}
			else {
				currentScreen--;
			}
			break;
		case 2:  // This case will execute if button 2 is pressed
			button = 0;
			if (currentScreen == numOfScreens - 1) {
				currentScreen = 0;
			}
			else {
				currentScreen++;
			}
			break;
		case 3:
			menuItem1();
			drawCursor();
			break;
		
	}
}

void printScreen() {
	lcd.clear();
	lcd.print(screens[currentScreen][menuPage]);
	lcd.setCursor(1, 1);
	lcd.print(screens[currentScreen][menuPage+1]);
	lcd.setCursor(1, 2);
	lcd.print(screens[currentScreen][menuPage+1]);
	lcd.setCursor(1, 3);
	lcd.print(screens[currentScreen][menuPage + 1 ]);
	if (menuPage == 0) {
		lcd.setCursor(15, 1);
		lcd.write(byte(2));
	}
	else if (menuPage > 0 and menuPage < maxMenuPages) {
		lcd.setCursor(15, 1);
		lcd.write(byte(2));
		lcd.setCursor(15, 3);
		lcd.write(byte(1));
	}
	else if (menuPage == maxMenuPages) {
		lcd.setCursor(15, 0);
		lcd.write(byte(1));
	}
}

void drawCursor() {
	for (int x = 0; x < 4; x++) {     // Erases current cursor
		lcd.setCursor(0, x);
		lcd.print(" ");
	}

	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 1
			lcd.setCursor(0, 0);
			lcd.write(byte(0));
		}
		if (cursorPosition == 1) {  // If the menu page is even and the cursor position is odd that means the cursor should be on line 2
			lcd.setCursor(0, 1);
			lcd.write(byte(0));
		}
		if (cursorPosition == 2) {  // If the menu page is even and the cursor position is odd that means the cursor should be on line 2
			lcd.setCursor(0, 2);
			lcd.write(byte(0));
		}
		if (cursorPosition == 3) {  // If the menu page is even and the cursor position is odd that means the cursor should be on line 2
			lcd.setCursor(0, 3);
			lcd.write(byte(0));
		}
	}
	if (menuPage == 1) {
		if (cursorPosition == 0) {  // If the menu page is odd and the cursor position is even that means the cursor should be on line 2
			lcd.setCursor(0, 0);
			lcd.write(byte(0));
		}
		if (cursorPosition == 1) {  // If the menu page is odd and the cursor position is odd that means the cursor should be on line 1
			lcd.setCursor(0, 1);
			lcd.write(byte(0));
		}
		if (cursorPosition == 2) {  // If the menu page is even and the cursor position is odd that means the cursor should be on line 2
			lcd.setCursor(0, 2);
			lcd.write(byte(0));
		}
		if (cursorPosition == 3) {  // If the menu page is even and the cursor position is odd that means the cursor should be on line 2
			lcd.setCursor(0, 3);
			lcd.write(byte(0));
		}
	}
}

//*******************************************************Front Page 0*******************************************
void menuItem1() { // Function executes when you select the Yellow item from main menu
	int activeButton = 0;

	lcd.clear();
	lcd.setCursor(3, 0);
	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;

	lcd.clear();
	lcd.setCursor(3, 0);
	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;

	lcd.clear();
	lcd.setCursor(3, 0);
	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;

	lcd.clear();
	lcd.setCursor(3, 0);
	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;
		}
	}
}

Been working off and on all day on this, still have to go over the code more and finish cleaning it up and having it display some menus.

Then onto editing the code inside the menu’s

Disregard any pins in the beginning, thats part of the code I need to go over, and adding a 4th button.

I just hope I can get this example working.

Tieing button to int input in the inputAction() Statement so as to use a switch statement to button.
Instead for cursorPosition or menuPages % 2 in the pasthub example;
I put the if loops to 0,1,2,3 to accommodate the extra 2 lines of screen.

#include <Wire.h> // Allows communications over I2c
#include <LiquidCrystal_I2C.h> //from newliquidcrystal library
LiquidCrystal_I2C lcd(0x27, 20, 4);  // Set the LCD I2C address

//Input & Button Logic
const int numOfInputs = 4;
const int inputPins[numOfInputs] = { 2,3,4 };
int inputState[numOfInputs];
int lastInputState[numOfInputs] = { LOW,LOW,LOW,LOW };
bool inputFlags[numOfInputs] = { LOW,LOW,LOW,LOW };
long lastDebounceTime[numOfInputs] = { 0,0,0,0 };
long debounceDelay = 5;

//LCD Menu Logic
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  //
};

int menuPage = 0;
int maxMenuPages = 10;
const int numOfScreens = 5;
int currentScreen = 0;
String screens[numOfScreens] = {"Display","Temperature","Relays","Fans","Timer"};

void setup() {
  for (int i = 0; i < numOfInputs; i++) {
    pinMode(inputPins[i], INPUT);
    digitalWrite(inputPins[i], HIGH); // pull-up 20k
  }
  //Serial.begin(9600);
  lcd.begin(16, 2);
  lcd.createChar(0, menuCursor);
  lcd.createChar(1, upArrow);
  lcd.createChar(2, downArrow);
}

void loop() {
  setInputFlags();
  resolveInputFlags();
}

void setInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    int reading = digitalRead(inputPins[i]);
    if (reading != lastInputState[i]) {
      lastDebounceTime[i] = millis();
    }
    if ((millis() - lastDebounceTime[i]) > debounceDelay) {
      if (reading != inputState[i]) {
        inputState[i] = reading;
        if (inputState[i] == HIGH) {
          inputFlags[i] = HIGH;
        }
      }
    }
    lastInputState[i] = reading;
  }
}

void printScreen() {
  lcd.clear();
  lcd.print(screens[menuPage]);
  lcd.setCursor(1, 1);
  lcd.print(screens[menuPage + 1 ]);
  lcd.setCursor(1, 2);
  lcd.print(screens[menuPage + 1 ]);
  lcd.setCursor(1, 3);
  lcd.print(screens[menuPage + 1 ]);
  if (menuPage == 0) {
    lcd.setCursor(15, 1);
    lcd.write(byte(2));
  }
  else if (menuPage > 0 and menuPage < maxMenuPages) {
    lcd.setCursor(15, 1);
    lcd.write(byte(2));
    lcd.setCursor(15, 3);
    lcd.write(byte(1));
  }
  else if (menuPage == maxMenuPages) {
    lcd.setCursor(15, 0);
    lcd.write(byte(1));
  }
}

void resolveInputFlags() {
  for (int i = 0; i < numOfInputs; i++) {
    if (inputFlags[i] == HIGH) {
      inputAction(i);
      inputFlags[i] = LOW;
      printScreen();
    }
  }
}

void menuItem1() { // Function executes when you select the Yellow item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Yellow On");

  while (activeButton == 0) {
  int button;
    switch (button) {
    case 4:  // This case will execute if the "back" button is pressed
      button = 0;
      activeButton = 1;
      break;
    }
  }
}

void menuItem2() { // Function executes when you select the Green item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Green On");

  while (activeButton == 0) {
    int button;
    switch (button) {
    case 4:  // This case will execute if the "back" button is pressed
      button = 0;
      activeButton = 1;
      break;
    }
  }
}

void menuItem3() { // Function executes when you select the Red item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Red On");

  while (activeButton == 0) {
    int button;
    switch (button) {
    case 4:  // This case will execute if the "back" button is pressed
      button = 0;
      activeButton = 1;
      break;
    }
  }
}

void menuItem4() { // Function executes when you select the Purple item from main menu
  int activeButton = 0;

  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("Purple On");

  while (activeButton == 0) {
    int button;
    switch (button) {
    case 4:  // This case will execute if the "back" button is pressed
      button = 0;
      activeButton = 1;
      break;
    }
  }
}

void drawCursor() {
  for (int x = 0; x < 4; x++) {     // Erases current cursor
    lcd.setCursor(0, x);
    lcd.print(" ");
  }

  if (menuPage == 0) {
    lcd.setCursor(0, 0);
    lcd.write(byte(0));
  }
  if (menuPage== 1) {  // If the menu page is 0 and the cursor position is 1 that means the cursor should be on line 2
    lcd.setCursor(0, 1);
    lcd.write(byte(0));
  }
  if (menuPage == 2) {  // If the menu page is 0 and the cursor position is 2 that means the cursor should be on line 3
    lcd.setCursor(0, 2);
    lcd.write(byte(0));
  }
  if (menuPage == 3) {  // If the menu page is 0 and the cursor position is 3 that means the cursor should be on line 4
    lcd.setCursor(0, 3);
    lcd.write(byte(0));
  }
}

void inputAction(int input) {
  int activeButton = 0;
  int button = input;
  while (activeButton == 0) {
    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 (menuPage) { // The case that is selected here is dependent on which menu page you are on and where the cursor is.
      case 0:
        menuItem1();
        break;
      case 1:
        menuItem2();
        break;
      case 2:
        menuItem3();
        break;
      case 3:
        menuItem4();
        break;
      }
      activeButton = 1;
      drawCursor();
      break;
    case 2:
      button = 0;
      if (menuPage == 0) {
        menuPage = menuPage - 1;
        menuPage = constrain(menuPage, 0, ((sizeof(screens) / sizeof(String)) - 1));
      }
      if (menuPage == 1) {
        menuPage = menuPage - 1;
        menuPage = constrain(menuPage, 0, maxMenuPages);
      }

      if (menuPage == 2) {
        menuPage = menuPage - 1;
        menuPage = constrain(menuPage, 0, maxMenuPages);
      }

      menuPage = menuPage - 1;
      menuPage = constrain(menuPage, 0, ((sizeof(screens) / sizeof(String)) - 1));

      drawCursor();
      activeButton = 1;
      break;
    case 3:
      button = 0;
      if (menuPage == 0) {
        menuPage = menuPage + 1;
        menuPage = constrain(menuPage, 0, maxMenuPages);
      }

      if (menuPage == 1) {
        menuPage = menuPage + 1;
        menuPage = constrain(menuPage, 0, maxMenuPages);
      }

      menuPage = menuPage + 1;
      menuPage = constrain(menuPage, 0, ((sizeof(screens) / sizeof(String)) - 1));
      drawCursor();
      activeButton = 1;
      break;
  }
  }
  }

Ok I think I did what I wanted, that is if it works, it does compile in the editor after finally rearranging the voids.

cursorPosition was removed from the piece’s I used and dedicated that to menuPages == 0,1,2,3;
So it would be easy to say where to exactly print and do what with 4 lines as to 2, least I think?

#include <Wire.h> // Allows communications over I2c
#include <LiquidCrystal_I2C.h> //from newliquidcrystal library
LiquidCrystal_I2C lcd(0x27, 20, 4);  // Set the LCD I2C address

//Input & Button Logic
const int numOfInputs = 4;
const int inputPins[numOfInputs] = { 2,3,4,5 };
int inputState[numOfInputs];
int lastInputState[numOfInputs] = { LOW,LOW,LOW,LOW };
bool inputFlags[numOfInputs] = { LOW,LOW,LOW,LOW };
long lastDebounceTime[numOfInputs] = { 0,0,0,0 };
long debounceDelay = 5;

//LCD Menu Logic
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  //
};

int menuPage = 0;
int maxMenuPages = 5;
const int numOfScreens = 5;
int currentScreen = 0;
String screens[numOfScreens] = { "Display","Temperature","Relays","Fans","Timer" };

void setup() {
	for (int i = 0; i < numOfInputs; i++) {
		pinMode(inputPins[i], INPUT);
		digitalWrite(inputPins[i], HIGH); // pull-up 20k
	}
	//Serial.begin(9600);
	lcd.begin(20, 4);
	lcd.init();
	lcd.backlight();
	lcd.createChar(0, menuCursor);
	lcd.createChar(1, upArrow);
	lcd.createChar(2, downArrow);
}

void loop() {
	setInputFlags();
	resolveInputFlags();
	drawCursor();
}

void setInputFlags() {
	for (int i = 0; i < numOfInputs; i++) {
		int reading = digitalRead(inputPins[i]);
		if (reading != lastInputState[i]) {
			lastDebounceTime[i] = millis();
		}
		if ((millis() - lastDebounceTime[i]) > debounceDelay) {
			if (reading != inputState[i]) {
				inputState[i] = reading;
				if (inputState[i] == HIGH) {
					inputFlags[i] = HIGH;
				}
			}
		}
		lastInputState[i] = reading;
	}
}

void printScreen() {
	lcd.clear();
	lcd.setCursor(1, 0);
	lcd.print(screens[0]);
	lcd.setCursor(1, 1);
	lcd.print(screens[1]);
	lcd.setCursor(1, 2);
	lcd.print(screens[2]);
	lcd.setCursor(1, 3);
	lcd.print(screens[3]);
	if (menuPage == 0) {
		lcd.setCursor(19, 0);
		lcd.write(byte(2));
	}
	else if (menuPage > 0 and menuPage < maxMenuPages) {
		lcd.setCursor(19, 0);
		lcd.write(byte(2));
		lcd.setCursor(19, 3);
		lcd.write(byte(1));
	}
	else if (menuPage == maxMenuPages) {
		lcd.setCursor(15, 0);
		lcd.write(byte(1));
	}
}

void resolveInputFlags() {
	for (int i = 0; i < numOfInputs; i++) {
		if (inputFlags[i] == HIGH) {
			inputAction(i);
			inputFlags[i] = LOW;
			printScreen();
		}
	}
}

void menuItem1() { // Function executes when you select the Yellow item from main menu
	int activeButton = 0;

	lcd.clear();
	lcd.setCursor(3, 0);
	lcd.print("Yellow On");

	while (activeButton == 0) {
		switch (int(inputAction)) {
		case 4:  // This case will execute if the "back" button is pressed
			activeButton = 1;
			break;
		}
	}
}

void menuItem2() { // Function executes when you select the Green item from main menu
	int activeButton = 0;

	lcd.clear();
	lcd.setCursor(3, 0);
	lcd.print("Green On");

	while (activeButton == 0) {
		switch (int(inputAction)) {
		case 4:  // This case will execute if the "back" button is pressed
			activeButton = 1;
			break;
		}
	}
}

void menuItem3() { // Function executes when you select the Red item from main menu
	int activeButton = 0;

	lcd.clear();
	lcd.setCursor(3, 0);
	lcd.print("Red On");

	while (activeButton == 0) {
		switch (int(inputAction)) {
		case 4:  // This case will execute if the "back" button is pressed
			activeButton = 1;
			break;
		}
	}
}

void menuItem4() { // Function executes when you select the Purple item from main menu
	int activeButton = 0;

	lcd.clear();
	lcd.setCursor(3, 0);
	lcd.print("Purple On");

	while (activeButton == 0) {
		switch (int(inputAction)) {
		case 4:  // This case will execute if the "back" button is pressed
			activeButton = 1;
			break;
		}
	}
}

void drawCursor() {
	for (int x = 0; x < 3; x++) {     // Erases current cursor
		lcd.setCursor(0, x);
		lcd.print(" ");
	}

	if (menuPage == 0) {

		lcd.setCursor(0, 0);
		lcd.write(byte(0));
	}
	else if (menuPage == 1) {  // If the menu page is 0 and the cursor position is 1 that means the cursor should be on line 2
		lcd.setCursor(0, 1);
		lcd.write(byte(0));
	}
	else if (menuPage == 2) {  // If the menu page is 0 and the cursor position is 2 that means the cursor should be on line 3
		lcd.setCursor(0, 2);
		lcd.write(byte(0));
	}
	else {  // If the menu page is 0 and the cursor position is 3 that means the cursor should be on line 4
		lcd.setCursor(0, 3);
		lcd.write(byte(0));
	}
}

void inputAction(int input) {
	int activeButton = 0;
	while (activeButton == 0) {
		switch (int(input)) {
		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
			input = 0;
			switch (int(input)) { // The case that is selected here is dependent on which menu page you are on and where the cursor is.
			case 0:
				menuItem1();
				break;
			case 1:
				menuItem2();
				break;
			case 2:
				menuItem3();
				break;
			case 3:
				menuItem4();
				break;
			}
			activeButton = 1;
			drawCursor();
			break;
		case 2:
			input = 0;
			if (menuPage = 0) {
				menuPage--;
			}
			if (menuPage = 1) {
				menuPage--;
				menuPage = 0;
			}
			menuPage--;
			menuPage = 0;
			drawCursor();
			activeButton = 1;
			break;
		case 3:
			input = 0;
			if (menuPage = 0) {
				menuPage++;
				menuPage = 0;
			}

			if (menuPage = 1) {
				menuPage++;
				menuPage = 0;
			}

			menuPage++;
			drawCursor();
			activeButton = 1;
			break;
		}
	}
}

Ok so the menu displays; >Display v
Temperature
Relays
Fans

Only 2 buttons of the 4 work, I can only go down one to Temperature or the other button refer’s me to a case 4 and says Yellow ON…

Need to fix it so the cursor can go all the way down and up and be able to select each option.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);

int upButton = 10;
int downButton = 11;
int selectButton = 12;
int menu = 1;

void setup() {
  lcd.begin(20,4);
  lcd.backlight();
  pinMode(upButton, INPUT_PULLUP);
  pinMode(downButton, INPUT_PULLUP);
  pinMode(selectButton, INPUT_PULLUP);
  updateMenu();
}

void loop() {
  if (!digitalRead(downButton)){
    menu++;
    updateMenu();
    delay(100);
    while (!digitalRead(downButton));
  }
  if (!digitalRead(upButton)){
    menu--;
    updateMenu();
    delay(100);
    while(!digitalRead(upButton));
  }
  if (!digitalRead(selectButton)){
    executeAction();
    updateMenu();
    delay(100);
    while (!digitalRead(selectButton));
  }
}

void updateMenu() {
  switch (menu) {
    case 0:
      menu = 1;
      break;
    case 1:
      lcd.clear();
      lcd.print(">MenuItem1");
      lcd.setCursor(0, 1);
      lcd.print(" MenuItem2");
      lcd.setCursor(0, 2);
      lcd.print(" MenuItem3");
      lcd.setCursor(0, 3);
      lcd.print(" MenuItem4");
      break;
    case 2:
      lcd.clear();
      lcd.print(" MenuItem1");
      lcd.setCursor(0, 1);
      lcd.print(">MenuItem2");
      lcd.setCursor(0, 2);
      lcd.print(" MenuItem3");
      lcd.setCursor(0, 3);
      lcd.print(" MenuItem4");
      break;
    case 3:
      lcd.clear();
      lcd.print(" MenuItem1");
      lcd.setCursor(0, 1);
      lcd.print(" MenuItem2");
      lcd.setCursor(0, 2);
      lcd.print(">MenuItem3");
      lcd.setCursor(0, 3);
      lcd.print(" MenuItem4");
      break;
    case 4:
      lcd.clear();
      lcd.print(" MenuItem1");
      lcd.setCursor(0, 1);
      lcd.print(" MenuItem2");
      lcd.setCursor(0, 2);
      lcd.print(" MenuItem3");
      lcd.setCursor(0, 3);
      lcd.print(">MenuItem4");
      break;
    case 5:
      menu = 4;
      break;
  }
}

void executeAction() {
  switch (menu) {
    case 1:
      action1();
      break;
    case 2:
      action2();
      break;
    case 3:
      action3();
      break;
    case 4:
      action4();
      break;
  }
}

void action1() {
  lcd.clear();
  lcd.print(">Executing #1");
  delay(1500);
}
void action2() {
  lcd.clear();
  lcd.print(">Executing #2");
  delay(1500);
}
void action3() {
  lcd.clear();
  lcd.print(">Executing #3");
  delay(1500);
}
void action4() {
  lcd.clear();
  lcd.print(">Executing #4");
  delay(1500);
}

Here we go, since my last example was confusing for even me. That led me to something that I can do in my head, and is suppose to have the desired output I wish for.

Now I just need to verify it works, then I can move onto the next part of this project.

I have only been coding off and on for maybe 7yrs. Python, C, C++,scripting
I love that the arduino is interactive, makes learning code more interesting, and drives my interest to greater heights. It’s fun to see how small you can get your code to be or make it as efficient as possible, however frustrating it may be.

Knew it could be done with 3 buttons, but if I would like to return to the mainscreen on my own input, I would have to add a 4th button and put it to update the menu.

Still a problem unfortunately!

I just dont get this.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);

int upButton = 10;
int downButton = 11;
int selectButton = 12;
int menu = 1;

void setup() {
  lcd.init();
  lcd.backlight();
  pinMode(upButton, INPUT_PULLUP);
  pinMode(downButton, INPUT_PULLUP);
  pinMode(selectButton, INPUT_PULLUP);
  updateMenu();
}

void loop() {
  if (digitalRead(downButton) == LOW) {
    menu--;
    updateMenu();
    delay(100);
    while (digitalRead(downButton) == HIGH);
  }
  if(digitalRead(upButton) == LOW) {
    menu++;
    updateMenu();
    delay(100);
    while (digitalRead(upButton) == HIGH);
  }
  if(digitalRead(selectButton) == HIGH) {
    executeAction();
    updateMenu();
    delay(100);
  }
}

void updateMenu() {
  switch (menu) {
  case 0:
    menu = 1;
    break;
  case 1:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(">MenuItem1");
    lcd.setCursor(0, 1);
    lcd.print(" MenuItem2");
    lcd.setCursor(0, 2);
    lcd.print(" MenuItem3");
    lcd.setCursor(0, 3);
    lcd.print(" MenuItem4");
    break;
  case 2:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(" MenuItem1");
    lcd.setCursor(0, 1);
    lcd.print(">MenuItem2");
    lcd.setCursor(0, 2);
    lcd.print(" MenuItem3");
    lcd.setCursor(0, 3);
    lcd.print(" MenuItem4");
    break;
  case 3:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(" MenuItem1");
    lcd.setCursor(0, 1);
    lcd.print(" MenuItem2");
    lcd.setCursor(0, 2);
    lcd.print(">MenuItem3");
    lcd.setCursor(0, 3);
    lcd.print(" MenuItem4");
    break;
  case 4:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(" MenuItem1");
    lcd.setCursor(0, 1);
    lcd.print(" MenuItem2");
    lcd.setCursor(0, 2);
    lcd.print(" MenuItem3");
    lcd.setCursor(0, 3);
    lcd.print(">MenuItem4");
    break;
  case 5:
    menu = 4;
    break;
  }
}

void executeAction() {
  switch (menu) {
  case 1:
    action1();
    break;
  case 2:
    action2();
    break;
  case 3:
    action3();
    break;
  case 4:
    action4();
    break;
  }
}

void action1() {
  lcd.clear();
  lcd.print(">Executing #1");
  delay(1500);
}
void action2() {
  lcd.clear();
  lcd.print(">Executing #2");
  delay(1500);
}
void action3() {
  lcd.clear();
  lcd.print(">Executing #3");
  delay(1500);
}
void action4() {
  lcd.clear();
  lcd.print(">Executing #4");
  delay(1500);
}

I’m sticking to this code, due to its simplicity. My problem is local to the loop statement.

If I swap downButton, menu-- for upButton menu++ the menu will automatically go down or stay at the top but only the selectButton works at the moment.