Multiple menus can be created by defining different sets of definitions and variables for each menu, along with different versions of the two menu functions. I use a naming convention of mnuName... where Name where NAME specifies which menu.
For the 'Test Gadget' there might be a mnuMain..., mnuVoltmeter...,
mnuSigGen... .
Each menu uses a block of global definitions and variables, with each member using the stated naming convention. For my main menu they are.
#define mnuMainNum 3 // number of menu items 0 referenced
#define mnuMainX 20 // menu x and y position
#define mnuMainY 20
#define mnuMainTSize 3 // text size
#define mnuMainTCol YELLOW // text color
#define mnuMainTBak BLUE // text background color
int mnuMainSel = 1 ; // selected menu item
// menu looks better if you pad items with spaces to make lengths equal
String mnuMainItems[] = {" Meters ", " Waveform ", " LCD "," Sub Menu "};
For this test example I also have a sub menu that is called from the main menu. Its global values are.
#define mnuSubNum 2 // number of menu items 0 referenced
#define mnuSubX 120 // menu x and y position
#define mnuSubY 100
#define mnuSubTSize 3 // text size
#define mnuSubTCol BLUE // text color
#define mnuSubTBak CYAN // text background color
int mnuSubSel = 1 ; // selected menu item
// menu looks better if you pad items with spaces to make lengths equal
String mnuSubItems[] = {" sub1 ", " sub2 ", " sub3 "};
I find it really easy to just do a copy and paste, and then using the Arduino IDE find and replace function to quickly make changes to the names.
Most of the work in the menu function is a function called
showNAMEMenuItems(), as with the definitions and variables you will have to change the name for each menu. For the Main menu it looks like this. .
void showMainMenuItems() {
tft.setTextSize(mnuMainTSize);
for (int i = 0; i <= mnuMainNum ; i++) {
if (i == mnuMainSel )
tft.setTextColor( mnuMainTBak , mnuMainTCol ); // inverted colors
else
tft.setTextColor(mnuMainTCol, mnuMainTBak ); // normal colors
// calculate proper x y position and set cursor
tft.setCursor( mnuMainX , mnuMainX + (i * 8 * mnuMainTSize));
tft.println(mnuMainItems[i]);
}
}
It uses the global values and variables to show the menu items at the correct x,y position on the screen. Each menu item is tested to see if it is the selected one, and the text/background colors are inverted from the non selected item.
The other function in the menu system is a function called
ShowMenuNAME() . It uses a local variable inMenu to control a while loop that reads the joystick and checks the vertical axis value to select the desired menu item. It calls showNAMEMenuItems()to update the menu after any change. It also tests the joystick switch for a 'LongPress' which causes the while loop to end.
void ShowMenuMain() {
boolean inMenu = true;
showMainMenuItems();
delay(200);
while (inMenu) {
ReadJoyStick();
delay(20);
if (jstick) {
if (jstickV ) {
mnuMainSel += jstickV ;
if ( mnuMainSel < 0) mnuMainSel = 0;
if (mnuMainSel >= mnuMainNum ) mnuMainSel = mnuMainNum;
showMainMenuItems();
}
if (jstickSW == LongPress)
inMenu = false; // long press gets you out of the menu loop
}
jstick = false;
}
After the while(inMenu) loop ends you can use either a series of if statements , or a switch- case function to select what is done for each menu item. For this test example I used a series of if statements , mainly because I was just copying existing code for testing. If I had already written specific functions for each menu item, I would probably have gone with a switch-case instead.
if (mnuMainSel == 0) {
tft.fillScreen(BLACK);
drawMeter(MTR1_X, MTR1_Y, MTR1_W , MTR1_H, WHITE, WHITE, BLACK,
" 1 2 3 4 5 6 7 8 9 10");
drawMeter(MTR2_X, MTR2_Y, MTR2_W , MTR2_H, WHITE, BLUE, YELLOW,
" 1 2 3 4 5 6 7 8 9 10");
plotNeedle(MTR1_X, MTR1_Y, MTR1_W , MTR1_H, 150, 300, BLACK);
plotNeedle(MTR2_X, MTR2_Y, MTR2_W , MTR2_H, 250, 400, BLUE);
}
if (mnuMainSel == 1) {
tft.fillScreen(BLACK);
DrawWaveformScreen(BLUE, CYAN, 10);
DrawYScale(0 , 10, 10);
}
if (mnuMainSel == 2) {
tft.fillScreen(BLACK);
lcd_begin(10, 60, 16, 4, 3, BLUE, YELLOW) ; // text and background color
tft.setCursor(0, 0);
tft.setTextColor(lcdTxtColor);
tft.setTextSize(lcdTxtSize);
for (int i = 0; i < lcdRow; i++) {
lcd_setCursor(0, i);
lcd_print("12345");
lcd_setCursor(5, i);
lcd_printNum(6, 0);
lcd_print("7890123456");
}
}
if (mnuMainSel == 3) {
ShowMenuSub();
}
}
This test code this shows a simple menu, which lets you select one of the test graphic display types from the last blog entry. The other option is a sub menu that only exits when you do a 'LongPress'.
In each case the display stays around for 5 seconds then clears the screen and shows the main menu again.The two functions for the sub menu are basically the same as the main menu code with the names changed.
void ShowMenuSub() {
boolean inMenu = true;
showSubMenuItems();
delay(200);
while (inMenu) {
ReadJoyStick();
delay(20);
if (jstick) {
if (jstickV ) {
mnuSubSel += jstickV ;
if ( mnuSubSel < 0) mnuSubSel = 0;
if (mnuSubSel >= mnuSubNum ) mnuSubSel = mnuSubNum;
showSubMenuItems();
}
if (jstickSW == LongPress)
inMenu = false; // long press gets you out of the menu loop
}
jstick = false;
}
// out of menu item selection now do selected item
// you can do as multiple if statements
// or a switch - case
// FOR THIS DEMO JUST EXIT
}
void showSubMenuItems() {
tft.setTextSize(mnuSubTSize);
for (int i = 0; i <= mnuSubNum ; i++) {
if (i == mnuSubSel )
tft.setTextColor( mnuSubTBak , mnuSubTCol ); // inverted colors
else
tft.setTextColor(mnuSubTCol, mnuSubTBak ); // normal colors
tft.setCursor( mnuSubX , mnuSubX + (i * 8 * mnuSubTSize));
tft.println(mnuSubItems[i]);
}
}
Here is a picture of what the menu looks
like after the sub menu was selected.
The same code can be easily modified to use a rotary encoder with switch instead of the joystick. I have found this to be a relatively simple menu system to implement. It may use more program space than some of the complete menu management sytems, but most of my applications have only need one or two menu selections and in that case may be smaller.