Creating simple menu Struktures

Hi guys,
i am trying to get some guidance for my current Projekt

The Projekt:
I am building a little Test Box for different Types of mechanical structures.
The Box is able to read and surveil some States of micro-switches and
activate some drives and stuff.

The Hardware:
-Arduino Mega 2560;
-8/2 LCD Display
-Rotary Encoder with Push Button
-several other Buttons for FullHand Mode

The Funktionalities:
First i want to Create 3 Main Funktions.
1: FullHand Mode, wich means every Output can be activated manually

2: SemiAuto Mode, wich means the Operator initiates a single sequence by pressing a button.

3: FullAuto Mode, wich means the Operator selects a various Number of Cycles and then initiates
them with a single button press

The Hardware works fine!

Now i need to create a simple Menu Strukture (See attached jpg)operated by the Rotary Encoder and his Push Button.

This is my first complex Projekt, i know the rudimental basics of coding but please dont be mad at me for little mistakes. I do my best =D

Thanks for Atttention and
greets from Germany, Mike

there are differnt way you can do it.

there are many differnt librarys that can do it.
but i also made my own library because, or i did not understand how its worked or they used to many memory.

and i made it useing the if statement when page == val show content on lcd.
you can also use the switch statement.

and ofcourse i made all kind of diffrent funtions. so i dont end up with a 10000 line code. which is realy hard to maintain. like submenu, LCDpage, scrollfunction, togglefunction, flashfunction, RTC output, Encoder or buttons. I2C lcd or 4/8bit LCD

@spirit

Thx for the quick reply, i failed with the library´s too.
They are way more complex than i actualy need it.

I think a System with switch Statements is the right way to go.
Thx for the Tip =)

As you have not posted your code I don't know if this is relevant.

It will make your selection system much easier to implement if the program code is well structured with separate functions for different things rather than 500 lines of spaghetti code. Have a look at Planning and Implementing a Program

If you need texts for several menus suggest you store them in an array so that the different messages can be displayed by the same few lines of code just by changing the array index.

I also recommend the concept of "states" or "state machine" to keep track of what the program is doing.

...R

Hi Mike,

I recently posted in the German speaking forum a simple menu, based on a rotary encoder and its button to scroll through a "flat = 1 layer" menu. The TO didn't react so far, maybe he found something else. But I think that is pretty simple and a good starting point for you.

The comments are in English, so the English speaking people won't be lost.
I am sure that some more experienced guys here could improve the sketch, but for me - it's doing what I wanted to achieve.

So have a look at it and replace the "cocktails" by your own applications. If you have to go one or more layers deeper, you might have to introduce, as @Robin2 said, a state machine with states to keep track where you are in the menu. If not - this will do for you.

Here is the link to the project (my code is in post #6).

In that post I have attached Ben Buxton's rotary encoder library, which, after extensive testing of numerous other libraries or direct codings, turned out to be by far the best one. My rotary encoder is a very cheap and simple one, with that library you don't need interrupt pins (in the sketch I used them - but they are not actually needed, if you have to use them for other activities in your code).

The original library of Ben is available on github plus there you can find a modified one. But both don't come with the button supported as in "my" library. In fact it is a library which I found last year somewhere in the I-net and it is actually the same algorithm as Ben is using in the library on his blog, but - I have no idea where I found the attached one with the implemented button support (just saves you the code to debounce the button - nothing fancy I admit, but convenient).

So best luck and good start. Let us know when you are a step further.

This is a simple menu system I've used for a volt/ammeter project and use it to calibrate and store the values, It's not fully commented (as I did not have time) but it should be easy to use and learn and understand. it's a quick stripped down system from my main program
It may be of use or just for learning there are plenty of other ways

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

//**************************************************************************************|

const int  menubuttonPin = 11;//pin to which the button is conected to
const int downbuttonpin = 12;//Pin to which downbutton is conected to.
const int upbuttonpin = 13;//Pin to which upbutton is conected to.
int menubuttonPushCounter = 0;
int menubuttonState = 0;
int lastmenuButtonState = 0;
boolean menubuttonbool = false;
long currentMillis = 0;
long tpreviousMillis = 0;
long tinterval = 1000;           // interval at which to update time
long tcurrentMillis = 0;
int upbuttonState;
int lastupbuttonState;
int upbutton;
int downbuttonState;
int lastdownbuttonState;
int downbutton;
int menucounter = 0;
long buttontimer;
long synctimer;
float counter =0;
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change :
const long interval = 1000;           // interval at which to blink (milliseconds)

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
void setup() {
  lcd.begin (20, 4); // for 16 x 2 LCD module
  lcd.setBacklight(HIGH);
  pinMode(menubuttonPin, INPUT);
  digitalWrite(menubuttonPin, HIGH);
  pinMode(downbuttonpin, INPUT);
  digitalWrite(downbuttonpin, HIGH);
  pinMode(upbuttonpin, INPUT);
  digitalWrite(upbuttonpin, HIGH);
 
}
//------------------------------------Main Program-------------------------------------
void loop() {
  // here is where you'd put code that needs to be running all the time.
  //**********************************************************************************
  // This is just a added sample to show the program running, needs to be be removed *
  //**********************************************************************************
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
   counter = counter + 1;
  }
  //*****************************************
  // continues to run mian program from her *
  //*****************************************
  // display the counter
  lcd.setCursor(0, 0);
  lcd.print(counter);
  
  menubuttonState = digitalRead(menubuttonPin); // checks if the menu button is pressed
  if (menubuttonState != lastmenuButtonState)
  {
    if (menubuttonState == LOW)
    {
      delay(100);
      menubuttonPushCounter = 3; //change this to how many Sub menus are required
      menubuttonbool = true;
    }
    if (menubuttonState == HIGH)
    {
      menubuttonbool = false;
    }
  }


  if (menubuttonbool == true && menubuttonPushCounter == 3) // Max number of Sub Menus
  {
    menucounter = 0;
    menu();
  }

  lastmenuButtonState = menubuttonState;

}


int menu()
{
  lcd.clear(); // clear the LCD
  delay(1000);
  while (menucounter < 3) ///while Menu equals less than max number of menus
  {
    menubuttonState = digitalRead(menubuttonPin);
    upbutton = digitalRead(upbuttonpin);
    downbutton = digitalRead(downbuttonpin);

    if (menubuttonState != lastmenuButtonState)
    {
      lcd.clear();

      if (menubuttonState == LOW)
      {
        delay(200);
        menucounter++;

      }
    }
   
    lastmenuButtonState = menubuttonState;
    
// show the menu's
    switch (menucounter) {
      case 0: //menu 1
       // add your code here if you need to adjust or alter any values
        lcd.setCursor(0, 0);
        lcd.print("** MAIN MENU 1 ***");
        lcd.setCursor(0, 1);
        lcd.print(" Display dtata here ");
        lcd.setCursor(0, 2);
        lcd.print(" press enter to ");
        lcd.setCursor(0, 3);
        lcd.print(" move to next screen ");
        break;

      case 1://menu 2
       // add your code here if you need to adjust or alter any values
         lcd.setCursor(0, 0);
        lcd.print("** MAIN MENU 2 ***");
        lcd.setCursor(0, 1);
        lcd.print(" Display dtata here ");
        lcd.setCursor(0, 2);
        lcd.print(" press enter to ");
        lcd.setCursor(0, 3);
        lcd.print(" move to next screen ");
        break;
      

      case 2:// menu 3
       // add your code here if you need to adjust or alter any values
        lcd.setCursor(0, 0);
        lcd.print("** MAIN MENU 3 ***");
        lcd.setCursor(0, 1);
        lcd.print(" Display dtata here ");
        lcd.setCursor(0, 2);
        lcd.print(" press enter to ");
        lcd.setCursor(0, 3);
        lcd.print(" move to next screen ");
        break;

      case 3: //menu 4, This is where you save the values, this restarts the aruino form loop so it needs to be last
        lcd.setCursor(0, 0);
        lcd.print("** MAIN MENU 4 ***");
        lcd.setCursor(0, 1);
        lcd.print(" this is where you ");
         lcd.setCursor(0, 2);
        lcd.print(" save any values ");
         lcd.setCursor(0, 3);
        lcd.print(" to eeprom ");
        delay(4000);
        lcd.clear(); 
        menucounter = 5;
        menubuttonPushCounter = 0;
        break;
        /* NOTE THIS IS ONLY LEFT IN FOR AN EAXMPLE
         * lcd.setCursor(0, 0);
        lcd.print(" Saving Values ");
        lcd.setCursor(0, 1);
        lcd.print(" *****....***** ");
        EEPROM.writeFloat(0, volts_scale);
        delay(200);
        EEPROM.writeFloat(10, amps_scale);
        delay(200);
        delay(2000);
        lcd.setCursor(0, 0);
        lcd.print(" Setting Values ");
        lcd.setCursor(0, 1);
        lcd.print(" *****....***** ");
        volts_scale = EEPROM.readFloat(0);
        amps_scale = EEPROM.readFloat(10);
        delay(1000);
        menucounter = 5;
        menubuttonPushCounter = 0;
        break;
        */
       
    }
  }
}

Another project where I set values in the setup loop and stays there until I press enter

  while (digitalRead(manual_pin) == HIGH) { //keep in the setup, once you press enter it will contiue to main loop
       lcd.setCursor(4, 1);
    lcd.print("SET CAPACITY ");
    manual = true;
// other code goes here 
like change menu/values
 if (digitalRead(upbuttonpin) ==LOW){
      capacity--;
      if (capacity <0)
      capacity= 0;
    }
    
    if (digitalRead(downbutton) == LOW) {
      capacity++;     //If down up button is pressed increase value
      if (capacity >400)
      capacity = 400;
      
    }
	capacity_math=capacity;
    lcd.setCursor(8, 2);
    lcd.print(capacity_math,0);
    lcd.print("AH  ");
    delay(100);
    
  }

  

  lcd.clear();
}

Thx for all your Help, especially @rpt007 and @Steveiboy.
I wil have a look on your codes and will let you know what
works out the best for my Projekt!
but now i´m really confident again :slight_smile:

Rpt007 I've had a quick look at your menu system looks good I shall play with that better using a rotary encoder

@Stevieboy:

The biggest advantage with the rotary is the simplicity of use and the speed scrolling through a menu, having the scroll function plus the trigger button in one hand.

After intense tests the used library of Ben Buxton is the best I have ever tested with my cheap ($2 !!) rotary encoders. All other rotary codes failed in either speed, giving strange feedback or not working at all.

If you want to include some graphic animated gimmicks, have a look at my test sketch.

Warning: the sketch uses delays in the "gimmick section" - just wanted to test the user defined characters. So if you want to use it in a project, you better go with the "blink without delay" approach as you did in your menu sketch here.

Hey guys,
i have finaly found a way to manage my Menu-Strukture. Its definatly not the elegantest way, but it works just fine.

The Basic idea was creating 2 variables:
var menu1 for menu1, menu2, menu3,.... --> based on counting witch my rotary encoder

var enc_buttonCount for menu1.1, menu1.2, menu 2.1, menu 2.2, ... --> based on enc_button Counter

Then i set the max values, that if the last screen is displayed and you move on it goes back to the first one.

After that i just created a scitchCase like this:

 switch(menu1){
  case 1:
    if (enc_buttonCount == 0){
    lcd.setCursor(0, 0);
    lcd.print("Mode   1");
    lcd.setCursor(0, 1);
    lcd.print("FullHand");
    }
    if (enc_buttonCount == 1){
    lcd.setCursor(0, 0);
    lcd.print("FullHand");
    lcd.setCursor(0, 1);
    lcd.print("Running!");
    FullHand = 1;
    }
    else{
      FullHand = 0;
    }
    
    break;

  case 2:
    if (enc_buttonCount == 0){
    lcd.setCursor(0, 0);
    lcd.print("Mode   2");
    lcd.setCursor(0, 1);
    lcd.print("SemiAuto"); 
    }
    if (enc_buttonCount == 1){
       lcd.setCursor(0, 0);
    lcd.print("Ensure  ");
    lcd.setCursor(0, 1);
    lcd.print("HomePos!"); 
    }
    if (enc_buttonCount == 2){
       lcd.setCursor(0, 0);
    lcd.print("SemiAuto");
    lcd.setCursor(0, 1);
    lcd.print("Running!"); 
    SemiAuto = 1;
    }
    else{
      SemiAuto = 0;
    }
    
    break;
   case 3:
    if (enc_buttonCount == 0){
    lcd.setCursor(0, 0);
    lcd.print("Mode   3");
    lcd.setCursor(0, 1);
    lcd.print("FullAuto");
    }
    if (enc_buttonCount == 1){
    lcd.setCursor(0, 0);
    lcd.print("Cykles? ");
        if (Auto_Cykles == 0){
          lcd.setCursor(0, 1);
          lcd.print("        ");
        }
        else{
          lcd.setCursor(0, 2);
          lcd.print(Auto_Cykles/4);
        }
    }
   
    if (enc_buttonCount == 2){
    lcd.setCursor(0, 0);
    lcd.print("Ensure  ");
    lcd.setCursor(0, 1);
    lcd.print("HomePos!"); 
    }
    if (enc_buttonCount == 3){
    lcd.setCursor(0, 0);
    lcd.print("FullAuto");
    lcd.setCursor(0, 1);
    lcd.print("Running!"); 
    FullAuto = 1;
    }
    else{
      FullAuto = 0;
    }
    break;
    
 }//end of menu1

Thats was the basic Idea and it works just fine for my youse. :slight_smile:

Thank you for all the replies and your help!

best regards Mike

Of course, I have yet another system:

http://inmojo.com/store/liudr-arduino-and-physics-gadgets/item/rotary-encoder-keypad-kit-16x2-lcd/

The benefit is the lcd backpack renders the menu for you and takes care of the buttons and rotary encoder. You just send a plain text menu content to it.