Go Down

Topic: Creating Submenus (Read 120 times) previous topic - next topic

Mar 15, 2015, 06:00 pm Last Edit: Mar 15, 2015, 06:19 pm by Angelsbane72
Hello,

I currently have a menu system accessible through an LCD button shield with the buttons used for menu navigation.

Currently, the program logic is as following:
1. Ask for user to select part family using up and down arrows.
2. Press select to choose family and display will show selected family

Now, I'd like to create a secondary submenu for each part family
EX: Able to scroll through and select CFM Parts 1-5 as individual items in CFM family.

As I am very new at programming and Arduino, I figured you guys would now what next steps to take.

My current wiring is a simple LCD button shield wired normally to Arduino MEGA with the extra analog input for the buttons.

My current code is:
Code: [Select]

#include <LiquidCrystal.h>
 
// Initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
 
//States for the menu.
int currentMenuItem = 0;
int lastState = 0;
 
void setup() {
   //Set the characters and column numbers.
   lcd.begin(16, 2);
   //Print default title.
   clearPrintTitle();
}
 
void loop() {
  //Call the main menu.
  mainMenu();
}
 
void mainMenu() {
  //State = 0 every loop cycle.
  int state = 0;
  //Refresh the button pressed.
  int x = analogRead (0);
  //Set the Row 0, Col 0 position.
  lcd.setCursor(0,0);
 
  //Check analog values from LCD Keypad Shield
  if (x < 100) {
    //Right
  } else if (x < 200) {
   //Up
    state = 1;
  } else if (x < 400){
   //Down
    state = 2;
  } else if (x < 600){
    //Left
  } else if (x < 800){
    //Select
    state = 3;
  }
 
  //If we are out of bounds on the menu, then reset it.
  if (currentMenuItem < 0 || currentMenuItem > 4) {
   currentMenuItem = 0;
  }
 
   //If we have changed Index, saves re-draws.
   if (state != lastState) {
      if (state == 1) {
         //If Up
          currentMenuItem = currentMenuItem - 1;
          displayMenu(currentMenuItem);
      } else if (state == 2) {
         //If Down
          currentMenuItem = currentMenuItem + 1; 
          displayMenu(currentMenuItem);
      } else if (state == 3) {
         //If Selected
         selectMenu(currentMenuItem);
      }
      //Save the last State to compare.
      lastState = state;
   }
   //Small delay
  delay(5);
}
 
//Display Menu Option based on Index.
void displayMenu(int x) {
     switch (x) {
      case 1:
        clearPrintTitle();
        lcd.print ("-> CFM");
        break;
      case 2:
        clearPrintTitle();
        lcd.print ("-> FAM X");
        break;
       case 3:
        clearPrintTitle();
        lcd.print ("-> FAM Y");
        break;
      case 4:
        clearPrintTitle();
        lcd.print ("-> FAM Z");
        break;
    }
}
 
//Print a basic header on Row 1.
void clearPrintTitle() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Part Family?");
  lcd.setCursor(0,1);
}
 
//Show the selection on Screen.
void selectMenu(int x) {
   switch (x) {
      case 1:
        clearPrintTitle();
        lcd.print ("Selected CFM");
        //Call the function that belongs to Option 1
        break;
      case 2:
        clearPrintTitle();
        lcd.print ("Selected FAM X");
        //Call the function that belongs to Option 2
        break;
       case 3:
        clearPrintTitle();
        lcd.print ("Selected FAM Y");
        //Call the function that belongs to Option 3
        break;
      case 4:
        clearPrintTitle();
        lcd.print ("Selected FAM Z");
        //Call the function that belongs to Option 4
        break;
    }
}

econjack

Please read Nick Gammon's two posts at the top of this Forum on the guidelines for posting, especially the use of code tags when listing your source code. It will help us help you.

#2
Mar 15, 2015, 06:21 pm Last Edit: Mar 15, 2015, 06:22 pm by Angelsbane72
Fixed according to forum rules. I felt the rest of my original post conveyed the scope, problem, and current status of my project.

Would you be able to assist me with this issue?

Qdeathstar

#3
Mar 15, 2015, 06:26 pm Last Edit: Mar 15, 2015, 06:27 pm by Qdeathstar
I would look at the menwiz library, it has all that built in...

It is also nonblocking, which is great once your program starts to get more complex.

#4
Mar 15, 2015, 06:43 pm Last Edit: Mar 15, 2015, 06:46 pm by Angelsbane72
I took a look at the menwiz already, but honestly, I'd rather use the approach I have currently only since it seems I need to 'tack on' a submenu for each part family option listed in my sketch.
I feel like I'm close to the solution I'm looking for this method!

The problem I run into, is that I am not sure how to throw secondary menus into the mix.

The additions I'd like to make to the current sketch are:
-secondary menus consisting of 5 new item options for each part family

-the ability to hit the 'Left' button on the shield and go back 1 menu
EDIT: I realize currently pressing up and down once a family is selected will return to the part family menu, but this functionally would be replaced by the "left to go back 1 menu"implementation

-Upon selecting "Part 1 of Family X" through the two submenus, pressing an external con
cave push button once activates a servo

The last bullet is the least important, not meant for immediate implementation. That'll be adressed once the menu is working
The menu system will only require two menus: "Part Family" and "Parts in Family", therefore, I don't believe it will be overly complicated if following my current approach.

Delta_G

Here's what you do.  In this function:

Code: [Select]

void displayMenu(int x) {
     switch (x) {
      case 1:
        clearPrintTitle();
        lcd.print ("-> CFM");
        break;
      case 2:
        clearPrintTitle();
        lcd.print ("-> FAM X");
        break;
       case 3:
        clearPrintTitle();
        lcd.print ("-> FAM Y");
        break;
      case 4:
        clearPrintTitle();
        lcd.print ("-> FAM Z");
        break;
    }
}


You look at an index, and decide what text to show.  Instead, put the text into an array and get the approriate text to display using the index to the array.  You can have a second array with another set of selection texts.  When you want a different menu, then you just change which array you are getting the text from. 

Ad hoc, ad loc, and quid pro quo.  So little time - so much to know!  ~Jeremy Hillary Boob Ph.D

#6
Mar 15, 2015, 07:10 pm Last Edit: Mar 15, 2015, 07:11 pm by Angelsbane72
So all I'll need to do is first create individual arrays of every possible menu item and sub menu item?
EX: Part Family-> CFM, FAM X, FAM Y,...
EX: Parts in Family -> CFM1,CFM2, CFM3.......FAMX1,FAMX2.....FAMY1,FAMY2,.....
So each individual item will have an array of its own, as will each main menu option?

This still confuses me however, as I am unsure what to code once a part family is selected.
For example, if I choose CFM, do I just tell it to print the next array as submenu1 (CFM) option1 (CFM 1)?

My issue lies with moving from one menu to the next

#7
Mar 15, 2015, 07:17 pm Last Edit: Mar 15, 2015, 07:19 pm by Angelsbane72
I have another question, however.

In my code below, upon selecting a part family, it wil display the selected family.
But my code implies I can call another function at this time as well.
Can I set a delay between the printing of "Selected _______" and that case creating a submenu for that part family?

In other words, once a part family is selected and printed to screen, a time delay occurs, and then the "parts in family" submenu appears.
The time delay I can do, but as for each case moving to it's own 'parts in family' submenu, this is where I am struggling.
I feel as if this can be achieved, but I am not sure how to go about it.

Code: [Select]

//Show the selection on Screen.
void selectMenu(int x) {
   switch (x) {
      case 1:
        clearPrintTitle();
        lcd.print ("Selected CFM");
        //Call the function that belongs to Option 1
        break;
      case 2:
        clearPrintTitle();
        lcd.print ("Selected FAM X");
        //Call the function that belongs to Option 2
        break;
       case 3:
        clearPrintTitle();
        lcd.print ("Selected FAM Y");
        //Call the function that belongs to Option 3
        break;
      case 4:
        clearPrintTitle();
        lcd.print ("Selected FAM Z");
        //Call the function that belongs to Option 4
        break;
    }
}

Delta_G

No, each menu will have an array full of items.  

Code: [Select]


char* menu1[] = {"Menu1Item1" , "Menu1Item2", "Menu1Item3"};

char* menu2[] = {"Menu2Item1" , "Menu2Item2", "Menu2Item3"};

char** menus[] = { menu1, menu2};


int currentMenu = 0;    // This line at top of program at global scope

void displayMenu(int x) {

        clearPrintTitle();
        lcd.print (menus[currentMenu][x]);
}




Or something along those lines.
Ad hoc, ad loc, and quid pro quo.  So little time - so much to know!  ~Jeremy Hillary Boob Ph.D

After playing around with the array method, here is what I have so far:
Code: [Select]

char* partfamily[] = {"CFM", "FAM X", "FAM Y", "FAM Z"};
char* CFMparts[] = {"CFM 1", "CFM 2", "CFM 3", "CFM 4"};
char* FAMXparts[] = {"FAM X 1", "FAM X 2", "FAM X 3", "FAM X 4"};
char* FAMYparts[] = {"FAM Y 1", "FAM Y 2", "FAM Y 3", "FAM Y 4"};
char* FAMZparts[] = {"FAM Z 1", "FAM Z 2", "FAM Z 3", "FAM Z 4"};
char***** menus[] ={ partfamily, CFMparts, FAMXparts, FAMYparts, FAMZparts}

int currentMenu = 0; //Current Menu begins at zero element

void displayMenu(int x){
  clearPrintTitle();
  lcd.print (menus[currentMenu][x]);
}


Does this look similar? I have never used an array before, so I am unsure as to how to proceed and how this integrates into my current code.

Also, did you take a look at my previous response as well?

Delta_G

Code: [Select]
char***** menus[] ={ partfamily, CFMparts, FAMXparts, FAMYparts, FAMZparts}

Except this line.   You want a pointer to a pointer to char.  Not a pointer to a pointer to a pointer to a pointer to a pointer to a char. 

Only two stars needed there. 
Ad hoc, ad loc, and quid pro quo.  So little time - so much to know!  ~Jeremy Hillary Boob Ph.D

OK thanks!
Here's what I have so far:
Code: [Select]

#include <LiquidCrystal.h>
 
// Initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
 
//States for the menu.
int currentMenuItem = 0;
int lastState = 0;
//Create each Menu as an array with each corresponding item.
char* partfamily[] = {"CFM", "FAM X", "FAM Y", "FAM Z"};
char* CFMparts[] = {"CFM 1", "CFM 2", "CFM 3", "CFM 4"};
char* FAMXparts[] = {"FAM X 1", "FAM X 2", "FAM X 3", "FAM X 4"};
char* FAMYparts[] = {"FAM Y 1", "FAM Y 2", "FAM Y 3", "FAM Y 4"};
char* FAMZparts[] = {"FAM Z 1", "FAM Z 2", "FAM Z 3", "FAM Z 4"};
char** menus[] ={ partfamily, CFMparts, FAMXparts, FAMYparts, FAMZparts};
 
void setup() {
   //Set the characters and column numbers.
   lcd.begin(16, 2);
   //Print default title.
   clearPrintTitle();
}
void loop() {
  //Call the main menu.
  mainMenu();
}
void mainMenu() {
  //State = 0 every loop cycle.
  int state = 0;
  //Refresh the button pressed.
  int x = analogRead (0);
  //Set the Row 0, Col 0 position.
  lcd.setCursor(0,0);
 
  //Check analog values from LCD Keypad Shield
  if (x < 100) {
    //Right
  } else if (x < 200) {
   //Up
    state = 1;
  } else if (x < 400){
   //Down
    state = 2;
  } else if (x < 600){
    //Left
  } else if (x < 800){
    //Select
    state = 3;
  }
 
  //If we are out of bounds on the menu, then reset it.
  if (currentMenuItem < 0 || currentMenuItem > 4) {
   currentMenuItem = 0;
  }
 
   //If we have changed Index, saves re-draws.
   if (state != lastState) {
      if (state == 1) {
         //If Up
          currentMenuItem = currentMenuItem - 1;
          displayMenu(currentMenuItem);
      } else if (state == 2) {
         //If Down
          currentMenuItem = currentMenuItem + 1; 
          displayMenu(currentMenuItem);
      } else if (state == 3) {
         //If Selected
         selectMenu(currentMenuItem);
      }
      //Save the last State to compare.
      lastState = state;
   }
   //Small delay
  delay(5);
}
 

//Menu options are to be displayed using arrays.
void displayMenu(int x){
  clearPrintTitle();
  lcd.print (menus[currentMenu][x]);
}


I am unsure what to do at the end of this code; how start at part family menu and then from there

Delta_G

My first guess would be that the action taken from one of those menu items in the top level menu should change the currentMenu to a new value. 
Ad hoc, ad loc, and quid pro quo.  So little time - so much to know!  ~Jeremy Hillary Boob Ph.D

I'm new at all of this, so I'm don't understand what you mean.
I can't seem to figure out the array method, so I've attempted to create submenu cases within each part family selection.

Here is my code so far. I can't seem to figure out whats wrong/how to proceed.

Code: [Select]

#include <LiquidCrystal.h>
 
// Initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
 
//States for the menu.
int currentMenuItem = 0;
int lastState = 0;
 
void setup() {
   //Set the characters and column numbers.
   lcd.begin(16, 2);
   //Print default title.
   clearPrintTitle();
}
 
void loop() {
  //Call the main menu.
  mainMenu();
}
 
void mainMenu() {
  //State = 0 every loop cycle.
  int state = 0;
  //Refresh the button pressed.
  int x = analogRead (0);
  //Set the Row 0, Col 0 position.
  lcd.setCursor(0,0);
 
  //Check analog values from LCD Keypad Shield
  if (x < 100) {
    //Right
  } else if (x < 200) {
   //Up
    state = 1;
  } else if (x < 400){
   //Down
    state = 2;
  } else if (x < 600){
    //Left
  } else if (x < 800){
    //Select
    state = 3;
  }
 
  //If we are out of bounds on the menu, then reset it.
  if (currentMenuItem < 0 || currentMenuItem > 4) {
   currentMenuItem = 0;
  }
 
   //If we have changed Index, saves re-draws.
   if (state != lastState) {
      if (state == 1) {
         //If Up
          currentMenuItem = currentMenuItem - 1;
          displayMenu(currentMenuItem);
      } else if (state == 2) {
         //If Down
          currentMenuItem = currentMenuItem + 1; 
          displayMenu(currentMenuItem);
      } else if (state == 3) {
         //If Selected
         selectMenu(currentMenuItem);
      }
      //Save the last State to compare.
      lastState = state;
   }
   //Small delay
  delay(5);
}
 
//Display Menu Option based on Index.
void displayMenu(int x) {
     switch (x) {
      case 1:
        clearPrintTitle();
        lcd.print ("-> CFM");
        break;
      case 2:
        clearPrintTitle();
        lcd.print ("-> FAM X");
        break;
       case 3:
        clearPrintTitle();
        lcd.print ("-> FAM Y");
        break;
      case 4:
        clearPrintTitle();
        lcd.print ("-> FAM Z");
        break;
    }
}
 
//Print a basic header on Row 1.
void clearPrintTitle() {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Part Family?");
  lcd.setCursor(0,1);
}
 
//Show the selection on Screen.
void selectMenu(int x) {
   switch (x) {
     
     //CASE 1: User selected CFM family
     case 1:
        clearPrintTitle();
        lcd.print ("Selected CFM");//Display selected family to user
        delay(150); //Wait some time
       
        //Switch to Parts in CFM Family Menu
        switch (x) {
          case a:
          clearPrintTitle();
          lcd.print ("CFM 1");
          break;
          case b:
          clearPrintTitle();
         lcd.print ("CFM 2");
         break;
         case c:
        clearPrintTitle();
        lcd.print ("CFM 3");
        break;
        case d:
        clearPrintTitle();
        lcd.print ("CFM 4");
        break;
       
     break;
     
      //CASE 2: User selected FAM X
      case 2:
        clearPrintTitle();
        lcd.print ("Selected FAM X");
        delay(150); //Wait some time
        //Switch to Parts in Family X Menu
        switch (x) {
          case e:
          clearPrintTitle();
          lcd.print ("FAM X 1");
          break;
          case f:
          clearPrintTitle();
         lcd.print ("FAM X 2");
         break;
         case g:
        clearPrintTitle();
        lcd.print ("FAM X 3");
        break;
        case h:
        clearPrintTitle();
        lcd.print ("FAM X 4");
        break;
       
       break;
       
       //CASE 3: User selected Family Y:
       case 3:
        clearPrintTitle();
        lcd.print ("Selected FAM Y");
        delay(150); //Wait some time
        //Switch to Parts in Family Y Menu
        switch (x) {
          case i:
          clearPrintTitle();
          lcd.print ("FAM Y 1");
          break;
          case j:
          clearPrintTitle();
         lcd.print ("FAM Y 2");
         break;
         case k:
        clearPrintTitle();
        lcd.print ("FAM Y 3");
        break;
        case l:
        clearPrintTitle();
        lcd.print ("FAM Y 4");
        break;
       
        break;
     
      //CASE 4: User selected Family Z:
      case 4:
        clearPrintTitle();
        lcd.print ("Selected FAM Z");
        delay(150); //Wait some time
        //Switch to Parts in Family Z Menu
        switch (x) {
          case i:
          clearPrintTitle();
          lcd.print ("FAM Z 1");
          break;
          case j:
          clearPrintTitle();
         lcd.print ("FAM Z 2");
         break;
         case k:
        clearPrintTitle();
        lcd.print ("FAM Z 3");
        break;
        case l:
        clearPrintTitle();
        lcd.print ("FAM Z 4");
        break;
       
        break;
    }
}


Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy