Tutorial: manage menu and LCD display with Arduino

Working on my MIDI ribbon controller I needed to implement a menu to easily configure several parameters, but    I found information on how to handle a menu, how to manage an LCD display, but very poor info on how to combine the two things. However, thanks to Alexander Brevig Menubackend library I managed to create the menu I wanted.I downloaded the  Alexander  Brevig Library version 1.4 and I  slightly modified it adding, at line 195 of the  MenuBackend.h file , immediately before the line  "private:", this method:
   

void toRoot() {
       

        setCurrent( &getRoot() );
    }

I needed this method to be able to return easily to the root of the menu.

In my sketch I then imported and the Menubackend library and  the LiquidCrystal library which is included in the Arduino IDE.

The MenuBackend library allows to manage a very flexible menu. You can create the menu structure using such methods as addRight, addLeft, Add and AddBefore  to add the entries to the right, left, below or above other menu items. To browse an item other intuitive methods are used such as moveLeft, MoveRight, moveUp and moveDown. In my example I created a structure like this.   

Main
  |
 Item1-------------------------------Item2----------------------------------------------------Item3
  |                                   |
Item1SubItem1-----Item1SubItem2     Item2SubItem1------Item2SubItem2------Item2SubItem3


MenuBackend triggers an event whenever the current menu item changes and so I used to "print"on the LCD display the current menu item. In addition another event is triggered when an item is selected by the method "use". This could be used to change the status of your application, but in this case I simply print to LCD the phrase "You used" followed by the name of the used menu item.
To navigate the menus I used 4 buttons (Left, Right, Esc and Enter).

For this project you need the following components:

  • Arduino compatible (should well be fine the old Arduino Diecimila)
  • an Hitachi HD44780 compatible LCD display
  • 4 buttons
  • 4 resistors 10k

Optionally, to facilitate connections you can use a card for prototypes (protoshield). I have used one from Nuelectronics.
The schematic is very simple. There are 12 of the 16-pin LCD display connected to the Arduino board: 6 are input pins, 2 power pins, 2are used to power the interior light ,1 to enable writing ( it is connected to ground )and 1 to adjust the contrast ( even is connected to the this ground to have always the maximum contrast). The 4 buttons are connected to four input pins of the Arduino, which also are connected to the ground through 10K resistors. The other end of the buttons is connected to the Arduino 5V.

images developed using Fritzing

To connect the LCD display to the Arduino I first soldered two 6-pin strips male on the proto pcb and then, thanks to the an idea given to me by Emanuel in the comments of another post, I used an old parallel cable for IDE hard disks.

Update: As noted by Liudr for safety reasons is better to use a very old 40 wires calble instead of the 80 wires cable used  by me in these pictures.

I think this type of structure although very simple may be useful in many situations to manage menu and LCD display with Arduino, and ican be used as a basis for other modifications.

You can download the source code here.

/*
    Copyright Giuseppe Di Cillo (www.coagula.org)
    Contact: dicillo@coagula.org
   
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
IMPORTANT: to use the menubackend library by Alexander Brevig download it at http://www.arduino.cc/playground/uploads/Profiles/MenuBackend_1-4.zip and add the next code at line 195
    void toRoot() {
        setCurrent( &getRoot() );
    }
*/
#include <MenuBackend.h>    //MenuBackend library - copyright by Alexander Brevig
#include <LiquidCrystal.h>  //this library is included in the Arduino IDE

const int buttonPinLeft = 8;      // pin for the Up button
const int buttonPinRight = 9;    // pin for the Down button
const int buttonPinEsc = 10;     // pin for the Esc button
const int buttonPinEnter = 11;   // pin for the Enter button

int lastButtonPushed = 0;

int lastButtonEnterState = LOW;   // the previous reading from the Enter input pin
int lastButtonEscState = LOW;   // the previous reading from the Esc input pin
int lastButtonLeftState = LOW;   // the previous reading from the Left input pin
int lastButtonRightState = LOW;   // the previous reading from the Right input pin


long lastEnterDebounceTime = 0;  // the last time the output pin was toggled
long lastEscDebounceTime = 0;  // the last time the output pin was toggled
long lastLeftDebounceTime = 0;  // the last time the output pin was toggled
long lastRightDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 500;    // the debounce time

// LiquidCrystal display with:
// rs on pin 2
// rw on 3
// enable on pin 6
// d4, d5, d6, d7 on pins 4, 5, 6, 7
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

//Menu variables
MenuBackend menu = MenuBackend(menuUsed,menuChanged);
//initialize menuitems
    MenuItem menu1Item1 = MenuItem("Item1");
      MenuItem menuItem1SubItem1 = MenuItem("Item1SubItem1");
      MenuItem menuItem1SubItem2 = MenuItem("Item1SubItem2");
    MenuItem menu1Item2 = MenuItem("Item2");
      MenuItem menuItem2SubItem1 = MenuItem("Item2SubItem1");
      MenuItem menuItem2SubItem2 = MenuItem("Item2SubItem2");
      MenuItem menuItem3SubItem3 = MenuItem("Item2SubItem3");
    MenuItem menu1Item3 = MenuItem("Item3");


void setup()
{
  pinMode(buttonPinLeft, INPUT);
  pinMode(buttonPinRight, INPUT);
  pinMode(buttonPinEnter, INPUT);
  pinMode(buttonPinEsc, INPUT);
 
  lcd.begin(16, 2);

  //configure menu
  menu.getRoot().add(menu1Item1);
  menu1Item1.addRight(menu1Item2).addRight(menu1Item3);
  menu1Item1.add(menuItem1SubItem1).addRight(menuItem1SubItem2);
  menu1Item2.add(menuItem2SubItem1).addRight(menuItem2SubItem2).addRight(menuItem3SubItem3);
  menu.toRoot();
  lcd.setCursor(0,0); 
  lcd.print("www.coagula.org");

}  // setup()...


void loop()
{

  readButtons();  //I splitted button reading and navigation in two procedures because
  navigateMenus();  //in some situations I want to use the button for other purpose (eg. to change some settings)
                 
} //loop()...


void menuChanged(MenuChangeEvent changed){
 
  MenuItem newMenuItem=changed.to; //get the destination menu
 
  lcd.setCursor(0,1); //set the start position for lcd printing to the second row
 
  if(newMenuItem.getName()==menu.getRoot()){
      lcd.print("Main Menu       ");
  }else if(newMenuItem.getName()=="Item1"){
      lcd.print("Item1           ");
  }else if(newMenuItem.getName()=="Item1SubItem1"){
      lcd.print("Item1SubItem1");
  }else if(newMenuItem.getName()=="Item1SubItem2"){
      lcd.print("Item1SubItem2   ");
  }else if(newMenuItem.getName()=="Item2"){
      lcd.print("Item2           ");
  }else if(newMenuItem.getName()=="Item2SubItem1"){
      lcd.print("Item2SubItem1   ");
  }else if(newMenuItem.getName()=="Item2SubItem2"){
      lcd.print("Item2SubItem2   ");
  }else if(newMenuItem.getName()=="Item2SubItem3"){
      lcd.print("Item2SubItem3   ");
  }else if(newMenuItem.getName()=="Item3"){
      lcd.print("Item3           ");
  }
}

void menuUsed(MenuUseEvent used){
  lcd.setCursor(0,0); 
  lcd.print("You used        ");
  lcd.setCursor(0,1);
  lcd.print(used.item.getName());
  delay(3000);  //delay to allow message reading
  lcd.setCursor(0,0); 
  lcd.print("www.coagula.org");
  menu.toRoot();  //back to Main
}


void  readButtons(){  //read buttons status
  int reading;
  int buttonEnterState=LOW;             // the current reading from the Enter input pin
  int buttonEscState=LOW;             // the current reading from the input pin
  int buttonLeftState=LOW;             // the current reading from the input pin
  int buttonRightState=LOW;             // the current reading from the input pin

  //Enter button
                  // read the state of the switch into a local variable:
                  reading = digitalRead(buttonPinEnter);

                  // check to see if you just pressed the enter button
                  // (i.e. the input went from LOW to HIGH),  and you've waited
                  // long enough since the last press to ignore any noise: 
               
                  // If the switch changed, due to noise or pressing:
                  if (reading != lastButtonEnterState) {
                    // reset the debouncing timer
                    lastEnterDebounceTime = millis();
                  }
                 
                  if ((millis() - lastEnterDebounceTime) > debounceDelay) {
                    // whatever the reading is at, it's been there for longer
                    // than the debounce delay, so take it as the actual current state:
                    buttonEnterState=reading;
                    lastEnterDebounceTime=millis();
                  }
                 
                  // save the reading.  Next time through the loop,
                  // it'll be the lastButtonState:
                  lastButtonEnterState = reading;
                 

    //Esc button              
                  // read the state of the switch into a local variable:
                  reading = digitalRead(buttonPinEsc);

                  // check to see if you just pressed the Down button
                  // (i.e. the input went from LOW to HIGH),  and you've waited
                  // long enough since the last press to ignore any noise: 
               
                  // If the switch changed, due to noise or pressing:
                  if (reading != lastButtonEscState) {
                    // reset the debouncing timer
                    lastEscDebounceTime = millis();
                  }
                 
                  if ((millis() - lastEscDebounceTime) > debounceDelay) {
                    // whatever the reading is at, it's been there for longer
                    // than the debounce delay, so take it as the actual current state:
                    buttonEscState = reading;
                    lastEscDebounceTime=millis();
                  }
                 
                  // save the reading.  Next time through the loop,
                  // it'll be the lastButtonState:
                  lastButtonEscState = reading;
                 
                    
   //Down button              
                  // read the state of the switch into a local variable:
                  reading = digitalRead(buttonPinRight);

                  // check to see if you just pressed the Down button
                  // (i.e. the input went from LOW to HIGH),  and you've waited
                  // long enough since the last press to ignore any noise: 
               
                  // If the switch changed, due to noise or pressing:
                  if (reading != lastButtonRightState) {
                    // reset the debouncing timer
                    lastRightDebounceTime = millis();
                  }
                 
                  if ((millis() - lastRightDebounceTime) > debounceDelay) {
                    // whatever the reading is at, it's been there for longer
                    // than the debounce delay, so take it as the actual current state:
                    buttonRightState = reading;
                   lastRightDebounceTime =millis();
                  }
                 
                  // save the reading.  Next time through the loop,
                  // it'll be the lastButtonState:
                  lastButtonRightState = reading;                 
                 
                 
    //Up button              
                  // read the state of the switch into a local variable:
                  reading = digitalRead(buttonPinLeft);

                  // check to see if you just pressed the Down button
                  // (i.e. the input went from LOW to HIGH),  and you've waited
                  // long enough since the last press to ignore any noise: 
               
                  // If the switch changed, due to noise or pressing:
                  if (reading != lastButtonLeftState) {
                    // reset the debouncing timer
                    lastLeftDebounceTime = millis();
                  }
                 
                  if ((millis() - lastLeftDebounceTime) > debounceDelay) {
                    // whatever the reading is at, it's been there for longer
                    // than the debounce delay, so take it as the actual current state:
                    buttonLeftState = reading;
                    lastLeftDebounceTime=millis();;
                  }
                 
                  // save the reading.  Next time through the loop,
                  // it'll be the lastButtonState:
                  lastButtonLeftState = reading; 

                  //records which button has been pressed
                  if (buttonEnterState==HIGH){
                    lastButtonPushed=buttonPinEnter;

                  }else if(buttonEscState==HIGH){
                    lastButtonPushed=buttonPinEsc;

                  }else if(buttonRightState==HIGH){
                    lastButtonPushed=buttonPinRight;

                  }else if(buttonLeftState==HIGH){
                    lastButtonPushed=buttonPinLeft;

                  }else{
                    lastButtonPushed=0;
                  }                 
}

void navigateMenus() {
  MenuItem currentMenu=menu.getCurrent();
 
  switch (lastButtonPushed){
    case buttonPinEnter:
      if(!(currentMenu.moveDown())){  //if the current menu has a child and has been pressed enter then menu navigate to item below
        menu.use();
      }else{  //otherwise, if menu has no child and has been pressed enter the current menu is used
        menu.moveDown();
       }
      break;
    case buttonPinEsc:
      menu.toRoot();  //back to main
      break;
    case buttonPinRight:
      menu.moveRight();
      break;     
    case buttonPinLeft:
      menu.moveLeft();
      break;     
  }
 
  lastButtonPushed=0; //reset the lastButtonPushed variable
}

 


Licenza Creative Commons
Tutorial: come gestire menu e display LCD con Arduino by Giuseppe Di Cillo - Coagula.org is licensed under a Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported License.

Comments(90)

Hi,
primarily i have to thanks you share this code! I fixed the library like you sad, but it doesn't work. Pls take a look for these links:

http://kepfeltoltes.hu/view/140519/N_vtelen_www.kepfeltoltes.hu_.png
http://kepfeltoltes.hu/view/140519/N_vtelen1_www.kepfeltoltes.hu_.png
http://kepfeltoltes.hu/view/140519/N_vtelen2_www.kepfeltoltes.hu_.png

The third picture has made from the library, cos i checked it in the IDE. I'm not a pro. in the arduino programming, but i think there is an other problem. If you know what is it please tell to me. Thanks the answer!

Hi all!

Thank you for all of your hard work.
I know this is an old thread, but i cannot get this example working and am hoping for some help.

As many others before me, I am getting an error:
"Class MenueBackend has no member named toRoot"

I modified the menubackend.h at line 195 and restarted arduino before compiling, but still get this error.

Any help would be great!

Thank you

DY

These are the erros obtained :

HelloMenu:20: error: 'MenuUseEvent' was not declared in this scope
HelloMenu:21: error: variable or field 'menuChangeEvent' declared void
HelloMenu:21: error: 'MenuChangeEvent' was not declared in this scope
HelloMenu:18: error: 'MenuBackend' does not name a type
HelloMenu:20: error: 'MenuItem' does not name a type
HelloMenu:21: error: 'MenuItem' does not name a type
HelloMenu:22: error: 'MenuItem' does not name a type
HelloMenu:23: error: 'MenuItem' does not name a type
HelloMenu:24: error: 'MenuItem' does not name a type
HelloMenu:25: error: 'MenuItem' does not name a type
HelloMenu:26: error: 'MenuItem' does not name a type
HelloMenu:27: error: 'MenuItem' does not name a type
HelloMenu:28: error: 'MenuItem' does not name a type
HelloMenu.pde: In function 'void menuSetup()':
HelloMenu:35: error: 'menu' was not declared in this scope
HelloMenu:35: error: 'settings' was not declared in this scope
HelloMenu:37: error: 'pin' was not declared in this scope
HelloMenu:39: error: 'debug' was not declared in this scope
HelloMenu:45: error: 'options' was not declared in this scope
HelloMenu:47: error: 'setDelay' was not declared in this scope
HelloMenu:49: error: 'd100' was not declared in this scope
HelloMenu:51: error: 'd200' was not declared in this scope
HelloMenu:52: error: 'd300' was not declared in this scope
HelloMenu:53: error: 'd400' was not declared in this scope
HelloMenu.pde: At global scope:
HelloMenu:69: error: variable or field 'menuUseEvent' declared void
HelloMenu:69: error: 'MenuUseEvent' was not declared in this scope

Any ideas how to fixed them ?

Thank you.

Hello!Great work!
Please tell me such a problem.
I have a menu at which there are one item,and two subitems.I need to change the value of the variable in the first subitem.But when I use the subitem and when I press the up or down the variable does not change.It simply navigates the menu left or right.

/////
void menuChanged(MenuChangeEvent changed){
MenuItem newMenuItem=changed.to; //get the destination menu
lcd.setCursor(0,1); //set the start position for lcd printing to the second row
if(newMenuItem.getName()==menu.getRoot()){
menuRoot = true;
}else if(newMenuItem.getName()=="Item1"){
menuRoot = false;
lcd.setCursor(0,0);
lcd.print(" Main Menu ");
lcd.setCursor(0,1);
lcd.print("1)Temperatura ");
}else if(newMenuItem.getName()=="Item1SubItem1"){
lcd.setCursor(0,0);
lcd.print("1)Temperatura ");
lcd.setCursor(0,1);
lcd.print("Temp. podderzh.");
}else if(newMenuItem.getName()=="Item1SubItem2"){
lcd.setCursor(0,0);
lcd.print("1)Temperatura ");
lcd.setCursor(0,1);
lcd.print("Histerezis");
}
void menuUsed(MenuUseEvent used){

if ( used.item == "Item1SubItem1" )
{
lcd.clear();
lcd.setCursor(0,6);
lcd.print("Temp=");
lcd.print(Temp);
tem();
}
else
if ( used.item == "Item1SubItem2" )
{
lcd.clear();
lcd.setCursor(0,6);
lcd.print("Histerezis=");
lcd.print(Hister);
his();
}
void tem();
{if (lastButtonPushed == buttonPinRight)
{Temp= Temp+1;}
else if (lastButtonPushed == buttonPinLeft)
{Temp= Temp-1;}
}
//////
Please help me please '(

I used your code in order to create menus for my project but i've hit a brick wall. A small description of the code/problem.

At the loop function i have set my LCD to display some text with some info and not show the menus. I have assigned 5 buttons to analog pin 0 as Menu Enable button Enter, Esc, Left, Right. When i press a button assigned as an activation button for the menus then the dispaly text/info disappears and the menus come up. when i am done with the navigation at the menus and select the exit menu to return to the display text/info. When i press again the enable menu button i enter the menus but when i press enter for the first option the code returns me back at the display text/info. If i do this again and press Esc, Right, Left i can access the menus. here is the code i wrote as simplified as possible with only the setup/loop functions

void setup(){
Serial.begin(9600);

//---menu configuration---
menu.getRoot().add(brightness);
brightness.addRight(calibration).addRight(Dew_enable).addRight(Alarms).addRight(Exit);
brightness.add(Increase).addRight(Decrease);
calibration.add(Sensor_1).addRight(Sensor_2).addRight(Sensor_1);
Sensor_1.add(Temperature).addRight(Humidity).addRight(Temperature);
Alarms.add(L_Alarm).addRight(H_Alarm);
menu.toRoot();

lcd.begin(20, 4); //initialize 20 char 4 line lcd
lcd.setCursor(0,0);
lcd.clear();
lcd.setCursor(0,0);
}

void loop(){

lcd.print("test");

read_button = analogRead(0);
if (read_button > 300 && read_button < 340){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Settings");
menus_enabled = 1;
}

while(menus_enabled == 1){
readButtons(); //I splitted button reading and navigation in two procedures because
navigateMenus(); //in some situations I want to use the button for other purpose (eg. to change some settings)
}
}

//-------------------------------------Functions-------------------------------------------------------

//---menu operations when change event happens---
void menuChanged(MenuChangeEvent changed){

MenuItem newMenuItem = changed.to; //get the destination menu
lcd.setCursor(0,1); //set the start position for lcd printing to the second row

if(newMenuItem.getName() == menu.getRoot()){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Settings");
}
else
if(newMenuItem.getName() == "Brightness"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Brightness");
}
else if(newMenuItem.getName() == "Increase"){
lcd.print("Increase Brightness");
}
else if(newMenuItem.getName() == "Decrease"){
lcd.print("Decrease Brightness");
}
else if(newMenuItem.getName() == "Calibration"){
lcd.print("Calibration");
}
else if(newMenuItem.getName() == "Sensor 1"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Sensor 1 ");
}
else if(newMenuItem.getName() == "Temperature"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Temperature ");
}
else if(newMenuItem.getName() == "Humidity"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Humidity");
}
else if(newMenuItem.getName() == "Sensor 2 Temp"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Sensor 2 Temp");
}
else if(newMenuItem.getName() == "Dew Enable"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Dew Enable");
}
else if(newMenuItem.getName() == "Alarms"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Alarms ");
}
else if(newMenuItem.getName() == "Low Alarm"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Low Alarm");
}
else if(newMenuItem.getName() == "High Alarm"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("High Alarm");
}
else if(newMenuItem.getName() == "Exit"){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Exit Menu");
}
}

//---individual menu operations---
void menuUsed(MenuUseEvent used){
if (used.item.getName() == "Exit"){
lcd.clear();
menus_enabled = 0;
}
}

//---read buttons operations---
void readButtons(){ //read buttons status
boolean ButtonEnterState = 0; //the state of the Enter button
boolean ButtonEscState = 0; //the state of the Esc button
boolean ButtonLeftState = 0; //the state of the Left button
boolean ButtonRightState = 0; //the state of the Right button

//---cases for buttons pushed---
#define buttonEnter 1
#define buttonEsc 2
#define buttonRight 3
#define buttonLeft 4

// read the state of the switch into a local variable:
read_button = analogRead(0);
delay(500);

//Enter button

if (read_button > 850 && read_button < 900) {
ButtonEnterState = 1;
ButtonEscState = 0;
ButtonRightState = 0;
ButtonLeftState = 0;
}

//Esc button

if (read_button >750 && read_button < 800) {
ButtonEnterState = 0;
ButtonEscState = 1;
ButtonRightState = 0;
ButtonLeftState = 0;
}

//Right button

if (read_button > 650 && read_button <700) {
ButtonEnterState = 0;
ButtonEscState = 0;
ButtonRightState = 1;
ButtonLeftState = 0;
}

//Left button

if (read_button > 350 && read_button < 450) {
ButtonEnterState = 0;
ButtonEscState = 0;
ButtonRightState = 0;
ButtonLeftState = 1;
}

//records which button has been pressed
if (ButtonEnterState == 1){
ButtonPushed = buttonEnter;
}
else if(ButtonEscState == 1){
ButtonPushed = buttonEsc;
}
else if(ButtonRightState == 1){
ButtonPushed = buttonRight;
}
else if(ButtonLeftState == 1){
ButtonPushed = buttonLeft;
}
else{
ButtonPushed = 0;
}

}

//---menu navigation---
void navigateMenus() {
MenuItem currentMenu = menu.getCurrent();

switch (ButtonPushed){
case buttonEnter:
if(!(currentMenu.moveDown())){ //if the current menu has a child and has been pressed enter then menu navigate to item below
menu.use();
}
else{ //otherwise, if menu has no child and has been pressed enter the current menu is used
menu.moveDown();
}
break;
case buttonEsc:
menu.toRoot(); //back to main
break;
case buttonRight: //move right
menu.moveRight();
break;
case buttonLeft: //move left
menu.moveLeft();
break;
}

ButtonPushed=0; //reset the ButtonPushed variable
}

Would it be possible to interface this menu.code with a rotary encoder that is acting on interrupts, rather than being polled?

Yes I thin it's possible. I tried but programming a rotary encode is such an head scratching problem that I gave up.

Good day!
Very good tutorial!
Prompt how to make when choosing a submenu.
Use the up and down keys i can change the value from 0-10
I would be very grateful for the help

Hi when I try to compile the program, I get the error

In file included from sketch_nov11a.ino:25:
/home/primo/sketchbook/libraries/MenuBackend/MenuBackend.h:195: error: stray ‘\342’ in program
/home/primo/sketchbook/libraries/MenuBackend/MenuBackend.h:195: error: stray ‘\200’ in program
/home/primo/sketchbook/libraries/MenuBackend/MenuBackend.h:195: error: stray ‘\250’ in program

please help I have added the library, and made the change at line 195.

This sketch does not work. Arduino Compiler v.1.05 (or v0023), Library from this link: Alexander Brevig download it at http://www.arduino.cc/playground/uploads/Profiles/MenuBackend_1-4.zip shows the following result:
menu_and_lcd_display.pde: In function 'void setup()':
menu_and_lcd_display:81: error: 'class MenuBackend' has no member named 'toRoot'
menu_and_lcd_display.pde: In function 'void menuUsed(MenuUseEvent)':
menu_and_lcd_display:132: error: 'class MenuBackend' has no member named 'toRoot'
menu_and_lcd_display.pde: In function 'void navigateMenus()':
menu_and_lcd_display:276: error: 'class MenuBackend' has no member named 'toRoot'

You have to copy,

void toRoot() {
setCurrent(&getRoot() );
}

into the library MenuBackend
open the library with the notepad and paste it below

this way

void use() {
//current->use();
if (cb_menuUse) {
MenuUseEvent mue = { *current };
cb_menuUse(mue);
}
}
void toRoot() {
setCurrent(&getRoot() );
}

and save e close and will work.

You need to make a little modification to the code. Read carefully the post, it's all there.

Hello Again Giuseppe !

Sorry to bore you but everything works perfectly!
For the novices and to give an additional explanation (if I can it allow myself) it is necessary to verify that the librarie imported contain the modifications from the line 195 !
I also deleted spaces in the bracket and give finally this : setCurrent(&getRoot())
That's all !
Thanks so much again and have a nice week-end !
Chris from FRANCIA.

I'm glad you fixed the code. I'm sorry for the late answer, but for time deficit I can answer only every 2-3 months.

Hello Giuseppe !
First of all congratulation for this remarkable work!
Nevertheless I meet strictly the same problem as "EWAS" ( previous comment). ' Class MenuBackend ' has no member named ' toRoot '
I read well and reread several times but I do not understand the error...
Could you be more explicit concerning the modifications to be made during the customization of the code.
Thank you for your return to me as well as for the other enthusiasts of Arduino.
Best regards.

Hi, thanks for this useful tutorial.

PB = pushbuttons
I would like to know if you do know how to turn off the screen after, idk 10 min of inactivity (no interation with the PB); and then turn it on by pressing a combination of PB (let's say 2 at the same time).

It would also be really fabulous if the same combination of PB used to turn the screen on could, if pressed for 2 seconds, turn the screen off.

I'm looking for a solution, if I find it before someone answer me here, i'll post it here.

Thanks to everybody.

Bye

Hi! Can you make a tutorial about how to control a menu using an IR remote control instead those 4 physical buttons?... Cheers.

I'm sorry but actually I have not the time.

This is great!!! You saved me so much time. It's gonna take some serious tweaking to make it fit my project but you saved me a lot of headaches and frustration.

Ciao sono Antonio e sto utilizzato il tutorial per il menubackend che hai sul sito; ho due domande da porti a cui, ahimè, non ho trovato risposta:

1. sto utilizzando un lcd 16x2, come nella guida, e mi appare in basso a sinistra la scritta "Menu"; volendola cambiare cosa dovrei modificare? se non erro viene generata dal menu.toRoot(), ma non capisco dove recupera il testo da stampare! Ho provato anche a guardare nella libreria!

2. sempre in merito alla scritta "Menu", non capisco come fa a capire dove posizionarla! volendola spostare su quale parte del codice dovrei agire?

Ti sarei grato se potessi darmi una mano...

Grazie

Immagino tu intenda la scritta "Main Menu". Tale testo è definito nella procedura ManeuChanged dove trovi questo codice:

lcd.setCursor(0,1); //set the start position for lcd printing to the second row
if(newMenuItem.getName()==menu.getRoot()){
lcd.print("Main Menu ");

Per cambiare posizione puoi decidere dove stampare il testo sul display tramite il codice lcd.setCursor(x, y); dove x indica la posizione del carattere sulla riga (partendo da 0) e y il numero di riga.
Naturalmente dove dopo lcd.SetCursor devi eseguire l'istruzione lcd.print con il testo che vuoi slavare.

ti ringrazio per la risposta, effettivamente avevo la soluzione sotto gli occhi.... :)

ho un'altra domandina, un pò più complessa: ho modificato la funzione menuUsed in questo modo:

void menuUsed(MenuUseEvent used){

if (used.item == menuPlafoLed)

currentStatusMode = statusPlafoLed;

ecc

così da poter intercettare nel loop il currentStatusMode tramite uno switch e poter eseguire dell'altro codice..

switch(currentStatusMode){
....

case(statusSetTime_Hour):
...

la struttura del menu è molto simile alla tua, ovvero

menu
- plafo led - imposta orario
- imposta ora - imposta minuti

il problema si pone quando, ad es, modificata l'ora si vuole modificare anche i minuti!
generalmente, quando ci si sposta tra i menu, senza entravi, la navigazione è molto agevole e si riesce a percorrere in ogni verso tutti i menu e sotto menu. questo purtroppo non accade, o non sono in grado di farlo accadere :) , quando si è all'interno di un menu come ad es. "imposta ora".
Volendo uscire dallo switch e visualizzare nuovamente "imposta ora" per riprendere la navigazione tra i menu, come potrei fare?

grazie :)

forse così ci capiamo meglio, purtroppo è andata persa la struttura del msg che avevo spedito!

il menu è così:
menu.getRoot().add(menuPlafoLed);
menuPlafoLed.addRight(menuSetTime).addRight(menuSetData);
menuSetTime.add(menuSetTime_Hour).addRight(menuSetTime_Minute);
menuSetData.add(menuSetData_Day).addRight(menuSetData_Month).addRight(menuSetData_Year);
menu.toRoot();

ecco uno stralcio del loop:

void loop(){

buttons = lcd.readButtons();
delay(50);

switch(currentStatusMode){

case(statusSetTime_Hour):

if(buttons == BUTTON_UP){

CHE CI METTO PER TORNARE A VISUALIZZARE AL MENU E VISUALIZZARE: "imposta ora"?

}

lcd.setCursor(0,1);
lcd.print("sx(-1) dx(+1) ");

if (buttons == BUTTON_LEFT){

DateTime past (now.unixtime() - 1 * 3600L );

RTC.adjust(DateTime(past.year(), past.month(), past.day(), past.hour(), past.minute(), past.second()));

lcd.setCursor(0,1);
lcd.print("hour -1 ");
delay(400);

modTime = HIGH;
}

if (buttons == BUTTON_RIGHT){

DateTime future (now.unixtime() + 1 * 3600L );

RTC.adjust(DateTime(future.year(), future.month(), future.day(), future.hour(), future.minute(), future.second()));

lcd.setCursor(0,1);
lcd.print("hour +1 ");
delay(400);

modTime = HIGH;
}

break;

spero di esser stato più chiaro :)

menu.moveUp(); dovrebbe andaqre bene. Per altri eventuali quesiti ti prego di postarli come commento all'articolo in lingua italiana. Grazie.

ok. ad ogni modo non funziona.

ciao

Non ho capito bene la struttura del tuo menù e cosa gli vuoi far fare.

Awesome tutorial. Just what i was looking for. Just one question, is there someone who can help me write or get me the code to get the menu as below :

>Menu1
Menu2
Menu3
Menu4

This is very much like Noob has suggested.

Thanks for all the hard work.

Read the code for my ribbon controller http://www.coagula.org/content/pages/coagula-midi-ribbon-controller-20-1. The menu has a more complex structure.

Thanks for the tutorial! It's exactly what I am looking for but I am encountering a problem.

This part doesn't seem to be working.

menu.getRoot().add("test1");

if (newMenuItem.getName() == "test1") {
// do stuff
}

however this works

if (newMenuItem.getName() == menu.getRoot()) {
// do stuff
}

Any pointers as to why?

I think that you can't add "test1" directly but you need to declare a varibale type menuitem

I just tried copying your code directly and it's the same problem. None of the conditions in menuChanged works except newMenuItem.getName() == menu.getRoot().

I also tested the buttons are receiving inputs properly.

I assure you that my code is tested by me and a lot of other users, so I can't figure what's happening in your setup.

hey thanks for the quick reply!

I actually did it was late over here and I wasn't thinking right.

I declared it at the top. Here's a more complete code that I made.

Menu menu = Menu(menuUsed, menuChanged);
MenuItem test1 = MenuItem("test1");

void setup() {
menu.getRoot().add(test1);
menu.toRoot();

}

void menuChanged(MenuChangeEvent changed) {
MenuItem newMenuItem = changed.to; //get the destination menu

lcd.setCursor(0, 0); //set the start position for lcd printing to the first row

// pretend only one of the following conditions are in the program when it is running
if (newMenuItem.getName() == menu.getRoot()) {
// this works
}

if (newMenuItem.getName() == "test1") {
// this DOES NOT WORK

// also even when I have a whole menu setup and I try to navigate the menus, the if clause for all other menus does not work except menu.getRoot()
// i.e. if(newMenuItem.getName() == "test2") <-- this does not work even when I've got the menu item setup and navigated to the menu
}

}

I think you need to investigate the navigation buttons code.

Hello sir, this is just what i needed! i was wondering whether or not i could use a keypad as the input source instead of the buttons?

Yes but you need to modify to code to manage the different connections.

How I can configurate the connctions to use a keypad 4x4 instead pushbuttons?

I'm very new to arduino and am trying to use the keypad to create a menu structure similar to yours. Any clue on what I should change besides the LiquidCrystal function? I know that something has to be done with buttonPinLeft, buttonPinRight, ect but not sure what to do.
Thanks!

Hi, great tutorial! I have a question about using the menu. I have functions that i want to launch when I get to the end of the SubMenu and press enter. Currently, whenever I get to the end of a menu, a message pops up and says I have used the menu. How can I make it so that it launches a code when it gets to the end of the menu so instead of just a message, it actually runs the function I have made?

You need to change the "menuUsed" procedure to verify, with an if or a switch, which menu is used and launch the function accordingly.

Could you please explain this a little bit more? Im also trying to do this but I cant get this to work.

I'm sorry but I have no time to write another post now. Maybe next year.

Dear Giuseppe Di Cillo,

How can I change the MenuBackend.h file ?
Must I do this in the arduino software?
I have try it on the microsoft visual express but it is notworking.
You say that you must change this on row 195.

This is the first time that I must change something in to the .h file.

You can use a simple text editor such as notepad for windows or TextEdit for Mac.

Dear Giuseppe Di Cillo,

i try to change your code that it go working with a I2C/TWI LCD1602 Module from DFRobot.

I have change #include //this library is included in the Arduino IDE
into

#include
#include

And also i have change
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);
in to
LiquidCrystal_I2C lcd(0x27,16,2);

The rest off the program it looks that it the same.

Thit you now wath I doing wrong if I verify the program everithing looks good also if I upload it to the arduino it looks good.
But on the display I don't see nothing.

Hi i use this code with I2C/TWI LCD1602 Module and its work

this is example

#include //MenuBackend library - copyright by Alexander Brevig
#include
#include
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27

Thanks for share!

Sorry but I haven't worked with I2C/TWI LCD1602 module so I don't know what's going on.

First and formost, Thank You!!! your menu structure is fantastic!!!

I have it working great with my Oxygen Analyzer project!!!! But I do hove one question I was hoping you could help me with.

I have 3 analog sensors that I have inserted into your menu structure under item3 and it works great the only problem is these 3 sensors only update when I refresh menu3, I assume this is because they are not part of the Loop. I have tried inserting them under the loop function but they remain on the screen no matter what menu I've selected. Is there a way or place I could insert my sensor code to allow them to loop only when menu3 is selected and clear when any other menu is selected?

Again great job!!!!!!
I thank you in advance for any help you can provide.

Dave

You're welcome :-)
You can try to put the sensor reading code in the main loop inside an if that verify using the menu.getCurrent() method that the current item is the item3.

Thank you for this tutorial Giuseppe.
I was having the same problem with an analog sensor but this corrected the issue.
Now i'm trying to figure out how to replace menu.getCurrent() in the 'if' with something like used.item.getName()
so the sensor output will only show if the menu item representing it is selected.
Any ideas?

MenuItem currentMenu=menu.getCurrent();
if(currentMenu.getName() == 'xxxx');
should do the trick!

Hi!
I have the same issue, but I don´t really can figure out where I should put the code you wrote before. I want the sensor i have in "Item1SubItem1" be rolling i a loop when current menu i shown. Any ideas how I shall solve my problem?

Otherwise very nice menu structure! You have saved med so much work! :)

The code must be in the main loop routine.

I got it to work the way I want by tethering the 'if' statment you suggested to a dummy menu item below the selection
menu item. So as your scrolling left to right you see the options but you only see the specific data when you select an option by pressing enter.
Thanks again!

hello...i have problem when i compile this code...i copy and paste that code into arduino..after compile that code, i got the error...error is about the menu.toRoot();...can anyone help me??

Have you modified the library as noted in the post?

Regarding the ribbon cable, I have a hunch your ribbon cable was not old enough. It looks like a 80-wire 40-connection IDE cable. Lots of the connections are internally connected to ground. It's dangerous to use such a cable for your purpose. Best is to find real old cables, 40-wire 40-connections. I have a blog post about these cables. Hope it helps:

http://liudr.wordpress.com/2011/06/25/ide-harddrive-cables/

This is very interesting. Thanks. I'll fix it in the post.

Hello!

You said, in a comment in your sketch, this: "I splitted button reading and navigation in two procedures because
in some situations I want to use the button for other purpose (eg. to change some settings)"

How can I use the same buttons to change (define) a parameter that is situated somewhere in the menu?
Thank you!

You can see the code for my Midi Ribbon Controller.

Hello, I have a problem with your example, I copied and pasted into arduino program, I modified the file with notepad (open unicode file) I then saved the data compiled and I get this error:

C: \ Users \ Jose Ramon \ Desktop \ arduino-1.0 \ libraries \ MenuBackend / MenuBackend.h: In function 'void setup ()':
C: \ Users \ Jose Ramon \ Desktop \ arduino-1.0 \ libraries \ MenuBackend / MenuBackend.h: 197: error: 'void MenuBackend :: toRoot ()' is private
sketch_mar19a: 80: error: within this context
C: \ Users \ Jose Ramon \ Desktop \ arduino-1.0 \ libraries \ MenuBackend / MenuBackend.h: In function 'void menuUsed (MenuUseEvent)':
C: \ Users \ Jose Ramon \ Desktop \ arduino-1.0 \ libraries \ MenuBackend / MenuBackend.h: 197: error: 'void MenuBackend :: toRoot ()' is private
sketch_mar19a: 131: error: within this context
C: \ Users \ Jose Ramon \ Desktop \ arduino-1.0 \ libraries \ MenuBackend / MenuBackend.h: In function 'void navigateMenus ()':
C: \ Users \ Jose Ramon \ Desktop \ arduino-1.0 \ libraries \ MenuBackend / MenuBackend.h: 197: error: 'void MenuBackend :: toRoot ()' is private
sketch_mar19a: 275: error: within this context

The modification is as follows:

...........................
MenuUseEvent furniture * current = {};
cb_menuUse (furniture);
}
}
toRoot void () {
setCurrent (& getRoot ());
}
private:

void setCurrent (MenuItem * next) {
if (next) {..........................

Problem is the modification of the *. H?
And of course thank you very much for the code sample.

I suppose the error is "toRoot void ()" instead of "void toRoot()".

Here the code we change to use multiples menus at the same time (without repeating each)

MenuItem *moveDown() {
if (after) { after->back = this; }
if (after->remember_parent) {
after->setBefore(this);
MenuItem *node = after->getRight();
while(node && (node != after)){
node->setBefore(this);
node = node->getRight();
}
}
return after;
}

more info on
https://github.com/CrearAyT/Guitarra-Vas-a-Llorar/tree/experimental/Menu...

https://github.com/CrearAyT/Guitarra-Vas-a-Llorar/blob/experimental/guit...

Great work! Thank you very much for sharing.

¡Great example!
Someone knows how to attach the same submenu to multiples menus. The thing is that I have to add this to more than 64 options...

Hi!
Is it possible to show many menu items in 20x4 lcd at once? Something like:
------------------------------------
| > ITEM 1 |
| ITEM 2 |
| ITEM 3 |
| ITEM 4 |
------------------------------------

After selecting one MainItem one or all subitems will show etc.

Sure but you need to modify the code.

Ok. Is necessary to restart arduino after save modification in MenuBackend.h

I think the MenuBackend.h from the link is changed. After line 195 is private: not protected:
You can post your lib?

You're right: it's "private:" instead of "protected:". Fixed. Thanks.

Hi, I am setting this up again (as my last hard drive died)
I have added the library, and made the change at line 195.
When I try to compile the program, I get the error : expected unqualified-id before string constant
and it highlights the /* right before the "IMPORTANT: to use the menubackend library by Alexander Brevig download it at..."

Any ideas what I am doing wrong?

I had the same issue, so I downloaded the library from the link in the tutorial. Then it worked.
Turns out I was using version 1.3 not version 1.4.

Thanks for share.

Maybe something has gone wrong with the modification of the code.

i was just wondering if it is possible to get rid of the first menu so it is more like this.

Item1------------------------------------------Item2----------------------------------------------------Item3
| |
Item1SubItem1-----Item1SubItem2 Item2SubItem1------Item2SubItem2------Item2SubItem3

or add more to the first menu so it is like this

menu---------------------------------------------------------------------menu1
| |
Item1------------------------------------------Item2 Item1------------------------------------------Item2
| | |
Item1SubItem1-----Item1SubItem2 Item2SubItem1 Item1SubItem1-----Item1SubItem2

I think that is poossible modifying the code.

Hi
I used your code and it is very good. I can add more subitems ans subitems in the subitems! :D
My problem is when i push the esc button it goes to the main root. I want to rewrite this part to put just to the previous menu item.
An other issue:
I want to control PWM output with this menu, If i make a Menuitem like PWM and a Subitem like PWM value, and If i add 256 PWM subitem into the PWM value menu to control every single state of the output and make 256 " if " statement to write out all the subitems, do you think is would be work?? Or can you give me some other solution? Because it is a lot of code.

Thanks :D
All the Best

You can try the menu.moveUp statement to go to the previous menu item. I suggest for the "PWM problem" to use the PWM item to enter in a state where the Up and Down buttons works to change a variable value. I used this type of code in my "MIDI Ribbon Controller" to change some settings via LCD menu.

Can somebody help me. I already made my menu using this article. But i need to incorporate my system with stepper motor. in my LCD menu i have an instruction that if i press right button the number will increment or using left button vice versa. But after i put the instruction "Stepper myStepper(200, 10,11,12,13)" The increment function did not work properly. Before i declare my stepper. The value will increase from 1 to 2 to 3 and so on. But after i declare my stepper the value increase from 1 to 3 to 5 to 7 and so on. Can you guys help me. :(

my code is in this forum
http://www.electronicslab.ph/forum/index.php?topic=29033.0

Thank you for such nice work.

I think there is small missprint at line 63:
MenuItem menuItem3SubItem3 = MenuItem("Item2SubItem3"); should be
MenuItem menuItem2SubItem3 = MenuItem("Item2SubItem3");

and

menu1Item2.add(menuItem2SubItem1).addRight(menuItem2SubItem2).addRight(menuItem3SubItem3); should be
menu1Item2.add(menuItem2SubItem1).addRight(menuItem2SubItem2).addRight(menuItem2SubItem3);

Thank you Giuseppe for this example!!!

Thank you very much! I'll fix it soon. Fortunately, the code works even with those errors but is less readable.

The code I have is only 9174 bytes out of 30720. It allows me to compile the code, but wont when I run it, the new menu options I have added don't appear. If you could check out if there is a limit, I would be very appreciative!
Thank you

I seem to be having difficulties adding more menu items. I cant seem to get more than 3 menuitems. I have no difficulties adding more subitems however. Is this something that needs to be done in the library? I want to have 5 menuitems (each with their subitems) all up.
Any assistance would be greatly appreciated!

I don't think there is a limit to the menuitems, but i need to check it out.
How much big is the code? If you run out of memory you could have strange code beahviour.

Thank you so much for this tutorial!
I want to change the structure so that after I select an subitem of Item1, it automatically puts me into Item2, to select a subitem of Item2. Any suggestions on how to accomplish this?

Unfortunately there is no direct command to accomplish this, but probably if you put the code below in every subitem1 routine, it should work:
menu.toRoot();
menu.use();
menu.moveRight();

Very good tutorial!! although , I don´t know where to input the routines selected. IE: menuitem3 selected, turns pin 10 high for 3 seconds. Very nice tut. Kudos for you.

Thanks!
You need to modify the code in the menuUsed routine. I.E:
void menuUsed(MenuUseEvent used){
if( used.item == menuItem3 ){
digitalWrite(10, HIGH);
delay(3000);
digitalWrite(10, LOW);
}
if( used.item == menuItem4 ){
//your code here
}
//etc.
}

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.