Introduction: Arduino Uno Menu Template

While working on a new Instructable (coming soon) I had decided to use an Arduino Uno with an LCD/button shield I purchased off of AliExpress. It's a knockoff of the DFRobot Shield. I knew that I needed a menu for my project but was becoming so disillusioned with the terrible menu template programs available for the Arduino Uno. Many of which were not designed to work with this shield. I decided to make my own.

Since I put a significant amount of work into making this menu template so easily modifiable I figured I would share it.

Step 1: Parts

Parts are relatively simple. All you'll need is an Arduino Uno and a LCD Keypad Shield. I purchased mine from AliExpress for a total of $6.75.

Step 2: Modifying the Program

The program attached is written with the basic Arduino libraries of Wire.h and LiquidCrystal.h so there should be nothing else for you to download besides this program.

One of the things you'll have to modify is the array of menu item names on line 27 and the programming content for each sub menu. I've started with 10 possible menu items in the program. If you want 10 or fewer just modify line 27 to create your main menu structure.

String menuItems[] = {"ITEM 1", "ITEM 2", "ITEM 3", "ITEM 4", "ITEM 5", "ITEM 6"};

For example, you could do fewer items:

String menuItems[] = {"DISTANCE", "TIME", "REPEAT", "DIRECTION", "START", "ABOUT"};

Or you can add more (up to 10 of course):

String menuItems[] = {"DISTANCE", "DELAY", "RANDOMIZE", "TIME", "REPEAT", "DIRECTION", "START", "ABOUT"};

Beyond 10 items you will have to add additional "cases" to the "switch/case" section starting on line 167. You will also have to add additional menuItemX[] (where X is 11, 12, 13, etc.) void functions at the bottom of the program.

After you have created the menu structure you want it's time to create the content for each one of those sub menus. The first menuItem void function starts on line 275. I put in some default code to print "Sub Menu X" for each menu item as well as a code to wait for a back button to be pressed. Below as an example of what you could do in the sub menu. This is the sub menu I'm using to modify and store the "savedDistance" variable. The up and down buttons are used to select the distance and when back is pressed it dumps back to the main menu.

void menuItem1() { // Function executes when you select the 1st item from main menu
  int activeButton = 0;
  lcd.clear();
  lcd.setCursor(0, 1);
  drawInstructions();
  lcd.setCursor(0, 0);
  lcd.print("DISTANCE: ");
  lcd.print(savedDistance);
  lcd.print(" in");
  while (activeButton == 0) {
    int button;
    readKey = analogRead(0);
    if (readKey < 790) {
      delay(100);
      readKey = analogRead(0);
    }
    button = evaluateButton(readKey);
    switch (button) {
      case 2:
        button = 0;
        savedDistance = savedDistance + 1;
        savedDistance = constrain(savedDistance,0,36);
        lcd.setCursor(10,0);
        lcd.print("     ");
        lcd.setCursor(10,0);        
        lcd.print(savedDistance);
        lcd.print(" in");
        break;
      case 3:
        button = 0;
        savedDistance = savedDistance - 1;
        savedDistance = constrain(savedDistance,0,36);
        lcd.setCursor(10,0);
        lcd.print("     ");
        lcd.setCursor(10,0);
        lcd.print(savedDistance);
        lcd.print(" in");
        break;
      case 4:  // This case will execute if the "back" button is pressed
        button = 0;
        lcd.clear();
        lcd.setCursor(2,0);
        lcd.print("-- VALUES --");
        lcd.setCursor(2,1);
        lcd.print("-- STORED --");
        delay(1500);
        activeButton = 1;
        break;
    }
  }
}

This function generates the distance menu shown in the picture above.

Step 3: Programming Buttons to Tasks

This LCD shield has 6 buttons, only 4 of which I'm using in my program. There is a select, up, down, left, right, and reset button. The reset is hard wired into the Arduino Uno reset so there is nothing we can do with it. The select button is left off of my program and I just used the right button instead. For some reason having the select button be so far to the left of up and down felt unnatural for menu navigation.

The function that determines which button you just pressed is called evaluateButton(). Right before you call that function you need to read the analog signal from the A0 input on the Uno and save it to the readKey variable. When you call the evaluateButton() function make it evaluateButton(readKey). This will send that function the signal from A0 and the function will determine which button that is associated with and return the button number in integer form.

Here's an example of calling for a button check:

int button;                         // Declares a fresh button variable
readKey = analogRead(0); // Reads the analog signal from A0 and saves it to readKey if (readKey < 790) { // If the signal drops below 790 that means a button was // pressed. The if statement delays the program for 100 microseconds // to debounce the button press and let voltage stabilize delay(100); readKey = analogRead(0); // Once the voltage has stabilized read the signal again } button = evaluateButton(readKey); // Call the evaluateButton function and send it the readKey data // Whatever integer comes back save it to the button variable

The evaluateButton() function looks like this:

int evaluateButton(int x) {
int result = 0; if (x < 50) { result = 1; // right } else if (x < 195) { result = 2; // up } else if (x < 380) { result = 3; // down } else if (x < 790) { result = 4; // left } return result; }

The easiest and cleanest way to use that button variable is with a switch/case. A switch/case function essentially looks at that button variable and matches it to "case". The programming inside that case is executed if it matches the button pressed. For example:

switch (button) {
case 0: // When button returns as 0 break; case 1: // When button returns as 1 lcd.clear(); lcd.print("Forward Button"); break; case 2: // When button returns as 2 lcd.clear(); lcd.print("Up Button"); break; case 3: // When button returns as 3 lcd.clear(); lcd.print("Down Button"); break; }

Ideally you will nest the button evaluation call and the switch/case inside of a do/while or while loop that will continue to execute until a button is pressed.

Comments

author
phiwong (author)2017-07-31

I believe the keypad shield (if it uses the standard Hitachi controller) already as the select, up, down, right and left arrow keys pre programmed into the permanent character set. Char code b00010111 b00011000 b00011001 b00011010 and b00011011, I believe (according to Hitachi datasheet). Nice post!

author
edgarricci (author)2017-07-24

The code is awesome! Thank you very much Paul. I modified the code for singles switches (digitalRead) and a rotary encoder instead to use the shield that you have.

I have a question: If I use less than 6 items, there is something wrong in the last item of the menu. There is something i am missing. Any clue?

Again, thanks a lot!

author
JodyC (author)2017-06-07

Fantastic Code! Just what I needed. You are a legend.

author
PaulSS (author)JodyC2017-06-07

Thanks for the kind words! I'm glad a lot of people are finding uses for it.

author
KonradGeel (author)2017-05-12

Hi Paul,

your code is exactly what I have been looking for. Clear and understandable!

Well actually it is not exactly, exactly what I am looking for;

what I want to do is split my menu tree in two = Main Menu with a choice of two modes, then have menu Items for the two modes that can be adjusted. How would I go about achieving this? or Am I missing something...

I am new to Arduino and Coding and I am working my way through a project and examples to teach myself.

Thanx a stack so far - and hope you can help me.

Cheers

Konrad

author
Robbe Leemans (author)2017-05-06

Paul,

If i upload the program on the arduino + lcd shield,

Then there will be: > ITEM 1

ITEM 2

With the > moving from top to bottom. And then comes: Sub Menu 2. Do you know what am i doing wrong?

Greetings, Robbe

author
PaulSS (author)Robbe Leemans2017-05-08

One thing I neglected is that the Adafruit library reads buttons with lcd.readButtons() while the shield I used needs to measure analog voltage drop across the buttons. You'll have to go through the code and replace my button calls with the Adafruit commands. It's just too time consuming for me to experiment without actually having the shield here.

author
Robbe Leemans (author)2017-05-05

Dear Paul,

Is it possible to say what needs to be changed? 
If you do not have time or sense for it, that does not matter.
You should not do it if you do not want to.
But I have to make a job for STEM,and I do not have so much
experience with programming yet.

Greetings, Robbe
author
PaulSS (author)Robbe Leemans2017-05-05

I took a quick look at the library that Adafruit created for this LCD screen and it looks like a simple change. Again, since I can't test it I can make a program that I THINK will work and send it to you. Try this file.

https://www.dropbox.com/s/cpma0zkvbjp6q20/AdafruitRGBLCD_Menu_Program.ino?dl=0

author
PaulSS (author)Robbe Leemans2017-05-05

Robbe,

Since I don't have one of the RGB LCD displays from Adafruit it will be impossible for me to test anything, but I will take a look and see if I can come up with something for you.

Regards,

Paul

author
Robbe Leemans (author)2017-05-03

Hi Paul,

Can the program work on the adafruit RGB lcd shield kit using the bus system?

author
PaulSS (author)Robbe Leemans2017-05-04

According to Adafruit this LCD works off of an I2C bus and not digital pins. You'd need to install an entirely different library to do the communication and do heavy modification to the code.

author
MostafaN12 (author)2017-04-30

hello paulss

i want using ps2 gamepad replace bottuns . and controlling your sketch with ps2 gamepad

i dont use lcd shield and connect lcd 16*2 direct to arduino

ps2 game pad connected to arduino and bottuns work correctly

but i want undrestand changes in your program

i used ps2.lib and added library to your sketch

i want changes your sketch with ps2 gamepad

please help me

author
vandersantc (author)2017-04-28

Hi Paul

Great work and thank you!

I was able to create a menu driven spindle controller for a brushless spindle using PWM on pin D3 (not used by LCD keypad) all in one evening thanks to the great start you provided. Much appreciated.

Regards

Coert

author
MostafaN12 (author)2017-04-23

hi paul

great work

i want to use play station 2for this sketch

i cannot to change lcd .ino

please help me

thank you

author
PaulSS (author)MostafaN122017-04-25

I do not understand what you mean by "i cannot to change lcd .ino". This sketch will not work on a PlayStation 2.

author
karlosp2 (author)2017-03-19

Hi Paul

Great work!! You helped me a lot.

I ran with some troubles with the "maxMenuPages" variable. I had 9 items to show on the menu, and the menu don't work properly after option 7.

I have solved the issue by changin the line from

maxMenuPages = round(((sizeof(menuItems) / sizeof(String)) / 2) + .5);

to

int maxMenuPages = round((sizeof(menuItems) / sizeof(String)) - 2);
author
Garth4 (author)2017-02-21

Hi Paul

Many Thanks for sharing some neat code, helps in learning new things

author
vidyale (author)2017-02-15

Hi Paul,

The code has been perfect and has taught me a lot, I understand most of it now.

Just one question. The code on page 206-208, why is this required? Isn't this operation covered by the code on page 220-221.

Many thanks for sharing, you are a legend

author
dennisfelipe (author)2017-02-12

thank you,

I've been looking for this code for over 2 years. You helped me a lot.

congratulations.

author
PaulSS (author)dennisfelipe2017-02-13

I'm glad it worked for you. I was in the same situation for years trying to find a simple scrolling menu but they were all garbage. I decided to write my own code. If you need any help let me know.

author
LeonardoL68 (author)2016-09-18

Thank You,

Really helpful for me too! I made It!

I'm with some problems starting a motor Reading a sensor, when i put the code on menuitem5, it doesn't executes the action. Could you help me, please?

Tks again!

author
PaulSS (author)LeonardoL682016-09-27

Could you send me a copy of your Adrduino sketch so I can take a look?

author
theodoreramli made it! (author)2016-09-08

Hey man, thank you very much for the Instructable. Really helpful. :D

14274584_10154442860478972_307704298_o.jpg
author
PaulSS (author)theodoreramli2016-09-08

Awesome! I'm glad it worked out for you. If you come up with some cool modifications to the code let me know.

author
AnthonyP15 (author)2016-06-15

Great tutorial. Have a project that would be able to use this.

author
PaulSS (author)AnthonyP152016-06-16

Keep me posted on the project. I'd love to see it.

author
Coert (author)2016-06-15

I save this for the future! Really a nice and usefull script,.

author
PaulSS (author)Coert2016-06-16

Thank you! I appreciate the kind words. I hope it proves helpful.