(Another ) LCD and Menu question

Hi all, first post here.

I have read an awful lot on arduino.cc over the last few months. I have a little experience in coding, and had a good time figuring out the few routines I required for both of my projects. It was tough at times, but I learned a lot, and am still learning.

Then I bought an LCD, thinking it would make the input of variables easier in the long term. I figured out a lot of the problems that have been solved here in the forums, at least in theory, but my coding knowledge is letting me down. And I have tried to wrap my head around the few menu libraries there are out there.

i have the LCD working, the 1 input analog 5 key keypad working, I have my servo working, and most other parts, but because there are now so many options in my project to set or check, I working through them one by one without a menu is just plain silly.

I will tell you what I would like to do, and if anyone can tell me if it is possible to do this way, and what I should be looking for , terms functions, libraries etc, I would be extremely greatful.

I would like to have a main menu, with three menu items. In each menu there would be from two to ten or more submenu items.

The usual arrow left/right for viewing main menu items, and up/down for choosing submenu items (in fact I had this working using a few different methods)

I am stuck at the point where I can show the submenu/menu item (probably with bad code, but it works) but how do I chose the correct subroutine or function to run. I have tried an array containing subroutine names, but I couldnt get it working.

I know I am not the only one looking to solve this problem, as there are a lot of confused beginners out there.

Is there a menu library out there that works first time, that also helps to run the appropriate subroutine when the menu item has been selected (5th key press)

So.. Left Right toggles through (3) Main menu items
Up Down toggles the submenu items for the selected main menu (different numbers of submenu items)
Select button that runs the appropriate subroutine or function that is currently displayed on the menu

Just so you know I have tried using the finite state machine, and I think that is how I designed the menu system that works (up to running the appropriate subroutine)

Thanks in advance, and by the way, I havent done anything like this since 1997, thats 15 years ago, so I am fairly rusty!

(I am not posting any code since I dont mind starting from scratch, I can manage the other details like servo position/speed etc once I get the menu in order. I will post anything that you need though, if you think it will help, and sorry for the long post, I am fairly frustrated at this stage)

JL

you might find it easier implementing a "command" interface to take one-letter commands from the serial port

One of the guys here (luidr) has an LCD menu library, search his posts and you should find some info about it.


Rob

mmcp42:
you might find it easier implementing a "command" interface to take one-letter commands from the serial port

Thanks, but if you mean taking commands from the pc to the arduino, I will need to use the arduino standalone, so unless this is easily implemented after the menu is setup, I wont go this direction

Graynomad:
One of the guys here (luidr) has an LCD menu library, search his posts and you should find some info about it.

I had looked this library up, and tried to get it working, but no joy until now. I will dig out his posts and see what I can find, thanks

Ok, for anybody that is using the lcd keypad like the ones sold on babel duck, and that has had a hard time getting heads around the existing menu libraries, here is one that works for me.

It is not complete, and doesnt actually do anything other than display the menu so far. But the next step will be to check if the go has been selected from the menu, and run a subroutine/function if is has

Also, neither the timelapse or video menus are complete, as I am still waiting for some parts to get the physical part of the project finished.

#include <LiquidCrystal.h>  
//These constants are the keypad values sent to Analog pint 0
const int KeypadRight = 0;
const int KeypadDown = 328;
const int KeypadUp = 143;
const int KeypadLeft = 503;
const int KeypadSelect = 741;
const int KeypadOff = 1023;

//I might also put the default delay value in here, so I only have to change it once

//Main menu setup
char* lcdmenu[]={
  "1:Setup-", "2:Lapse-", "3:Video-"};

//I dont know a way of counting the elements in the above array, so I entered it manually
int numberofmenus = 3;

//I tried a few ways to auto find the start of the submenu items, but this works, and I want to keep the project moving along
int lcdsubmenustart[] = {
  0,2,6};
  
//I know this next array could be calculated but again, I have gotten bogged down in a lot of other areas until now  
int lcdsubmenuelements[] = {
  2,4,4};

// The submenu array
char* lcdsubmenu[] = {
  "Brightness", "Volume", "Initial Delay", " Fire Delay", " Start Point", " End Point"," Auto-Run", " Fire Delay", " Start Point", "Go"};

//The following arrays are parallel arrays to the submenu array, setting the default (start) value, step size, max and min values
int lcdsubmenuvalue[] =     {
  75, 5, 2000, 1000, 0, 100, 0, 2000, 0, 0};
int lcdsubmenustepsize[] =  {
  25, 1, 100 , 100 , 1,1, 1, 100, 1, 1} 
;
int lcdsubmenumaxsize[] =  {
  250, 10, 10000 , 1000 , 1000,1000, 1, 1000, 1000, 1} 
;
int lcdsubmenuminsize[] =  {
  0, 1, 0 , 0 , 0,0, 0, 0, 0,0 } 
;

//Initialise the LCD, i am using these pins because I have the shield version, your pinout may and probably will be different
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

//int b_light = 64; an older setting for the lcd brightness
int key = 0; //I am not using this anymore, I think
int mainmenulocation=0; //Current main menu position, within the main menu array
int submenulocation=0; //Current submenu position, this is reset every time i move to a new main menu, which is the way I want it, and easier to keep track of

//A custom character, up and down arrow
byte updownarrow[8] = {
  B00100,
  B01010,
  B10001,
  B00100,
  B10001,
  B01010,
  B00100,
};

void setup(){
  Serial.begin(9600);
  lcd.begin(16, 2); // set up lcd type
  analogWrite(10,lcdsubmenuvalue[0]); //Set the brigtness level
  lcd.createChar(0, updownarrow);// create the character
  pinMode(0, INPUT); // set the analog pin 0 as an input, i dont think i need this, but in previous versions without it the brightness wouldnt work!



}

void loop(){
  mainmenu(); //Calls and prints the main menu on the screen, see full function below
  //Next few lines are just feedback on which menu and submenu i am currently in for the serial monitor
  Serial.print("Main Menu Location - ");
  Serial.println(mainmenulocation);
  Serial.print("Sub Menu Location - ");
  Serial.println(submenulocation);
  Serial.print("Sub Menu Elements - ");
  Serial.println(lcdsubmenuelements[mainmenulocation]);

//next step is to check whether or not the go option has been selected in either the video or timelapse menu, it will be reset before return, so only one can be set at any one time
//And if it is set, then to run the subroutine that controls the modified for contiuous rotation servo, 
  
//A0 will never be >1024, so this while loop will run until I return out of it, not sure if this is normal practice, maybe there is a better way
  while (analogRead(A0)<1024){

    switch (analogRead(A0)) { // Which key was pressed, and what to do when it is pressed


    case KeypadSelect:    // The Select key, or the left most one, calls the function that changes the current submenu value
    delay(200);
      changesubmenuvalue();  

      return;
      break;

    case KeypadUp:    // Up, toggles through submenu items
      delay(400);
      if (submenulocation+1==lcdsubmenuelements[mainmenulocation]+lcdsubmenustart[mainmenulocation]){
        submenulocation= submenulocation-lcdsubmenuelements[mainmenulocation]+1;
      }
      else{
        lcd.setCursor(8,0);
        submenulocation=submenulocation+1;
 
      }
      return;     
      break;

    case KeypadDown:    // Down, toggles through submenus
      delay(200);
      Serial.print("Keypad Down - submenu start point - ");
      Serial.println(lcdsubmenustart[mainmenulocation]);
      if (submenulocation>lcdsubmenustart[mainmenulocation]){



        submenulocation=submenulocation-1;


      }
      else{
        submenulocation = lcdsubmenustart[mainmenulocation]+lcdsubmenuelements[mainmenulocation]-1;
      }
      return;
      break;

    case KeypadRight: //Right, Toggles through menus
      if(mainmenulocation==numberofmenus-1){
        mainmenu();
        mainmenulocation=0;
        submenulocation=0;
        delay(400);

        return;
      }
      mainmenulocation+=1;
      submenulocation =lcdsubmenustart[mainmenulocation];

      delay(400);
      return;
      break;

    case KeypadLeft: // Left, toggles through menu items
      if(mainmenulocation==0){
        mainmenu();
        mainmenulocation=numberofmenus-1;
        submenulocation=submenulocation =lcdsubmenustart[mainmenulocation];
        delay(400);
        return;
      }
      mainmenulocation-=1;
      submenulocation-=4;
      submenulocation=submenulocation =lcdsubmenustart[mainmenulocation];

      delay(400);
      return;
      break;
    }
  }
}



// The main menu, prints the current menu, submenu, and submenu values to the screen, also refreshes the brightness
//THe brightness doesnt need to be called this often, but it will do for now
void mainmenu(){
  analogWrite(10,lcdsubmenuvalue[0]);
  lcd.noCursor(); //reset cursor to off
  lcd.noBlink(); //reset blink to off
  lcd.clear();
  lcd.print(lcdmenu[mainmenulocation]);
  lcd.setCursor(8,0);
  lcd.print(lcdsubmenu[submenulocation]);
  lcd.setCursor(15,0);
  lcd.print(char(0));
  lcd.setCursor(1,1);
  lcd.print(lcdsubmenuvalue[submenulocation]);
}



//Changes the current submenu value
// I am nearly sure there is some way I can reuse the switch case statement from the loop function, but I dont know how to do this at the moment
void changesubmenuvalue(){
  
  lcd.cursor(); //Switch on cursor
  lcd.blink(); //Switch on blink, both of these show that you are editing the value, as nothing else on the screen has changed
  lcd.setCursor(1,1);
  
  //Again, as above, A0 will never be >=1024, so this loops runs until returned from
  while (analogRead(A0)<1024){
    

    switch (analogRead(A0)) { // Which key was pressed, and what to do when it is pressed


    case KeypadSelect:    // Clears the display for a brief moment, and returns to menu toggling
    lcd.clear();
    delay(200);

      return;
      break; // I dont think I need these breaks if there is a return!

//Keypad up checks to see that the max size of the current submenu value has been reached, if it has, do nothing
//If it hasnt, increase the submenu value by the amount stated in the submenustepsize array
//Keypad down does the same in reverse and for the minimum value

    case KeypadUp:
      delay(200);
       if (lcdsubmenuvalue[submenulocation]>=lcdsubmenumaxsize[submenulocation]){}
       else{
       lcdsubmenuvalue[submenulocation]+=lcdsubmenustepsize[submenulocation]; 
       changedisplayvalue();}
      break;

    case KeypadDown:    // Down, toggles through submenus
      delay(200);
      if (lcdsubmenuvalue[submenulocation]<=lcdsubmenuminsize[submenulocation]){}
      else{
       lcdsubmenuvalue[submenulocation]-=lcdsubmenustepsize[submenulocation]; 
       changedisplayvalue();
      }
      break;
//Keypad Right and Left return out of editing the submenu values

    case KeypadRight: //Right, Toggles through menus
                delay(400);
 
      return;
      break;

    case KeypadLeft: // Left, toggles through menu items
      return;
      break;
    }
   
  }

}


//This function changesupdates the display to reflect the changes made to the submenuvalue
void changedisplayvalue(){
  lcd.setCursor(1,1);
  lcd.print("    ");
lcd.setCursor(1,1);

  lcd.print(lcdsubmenuvalue[submenulocation]);
  analogWrite(10,lcdsubmenuvalue[0]);
}



//And thats about it