Hello. I'm new to the forum, relatively inexperienced with C++, and really grateful for all the posts others have made here. They have been extraordinarily useful! I still have much to learn.
I'm trying to write a menu interface to use with a 16x2 LCD. Each Menu is a class that contains an array of pointers to MenuItems (another class), state variables, and a member function showMenu() that displays the current selected menuItem on the LCD when called.
Each MenuItem contains two strings (one for each line on the LCD), and a pointer to a callback function that is run when the MenuItem is activated (on a button press for example).
The piece I'm having trouble with is how to use the showMenu() member function of a menu as the callback for submenus (a MenuItem that is another Menu() instantiation). My failed attempt is at lines 89 and 90 of Menutest.ino (bracketed in //// below).
I've tried to strip the code down to a minimal example. Sorry if it's a little long. All the code needed to attempt compilation is attached.
compilation errors:
/home/pjohnson/Arduino/Menutest/Menutest.ino: In function 'void setup()':
Menutest:89: error: expected unqualified-id before '&' token
topMenuItem0.callbackFunction = Menu::&showMenu(subMenu1);
^
Menutest:89: error: 'showMenu' was not declared in this scope
topMenuItem0.callbackFunction = Menu::&showMenu(subMenu1);
Menutest.ino:
#include "menu.h"
boolean exampleCallback() { return false; }
Menu topMenu; // has to be global based on the way arduino loops
void setup() {
// set up the display (includes the buttons).
MyDisplay dsply;
// set up serial comm
Serial.begin(38400);
while (!Serial && (millis() <= 1000));
// set up the menu structure (main, 2-subs, 2-items/sub)
// start with the submenus and build towards the top
Menu subMenu1;
MenuItem subMenu1Item0;
MenuItem subMenu1Item1;
// populate the menuItems with data
strcpy(subMenu1Item0.displayText1, "Submenu1, Item0 ");
strcpy(subMenu1Item0.displayText2, "Press any button");
strcpy(subMenu1Item1.displayText1, "Submenu1, Item1 ");
strcpy(subMenu1Item1.displayText2, "Press any button");
// connect the callback functions to the menuItems
subMenu1Item0.callbackFunction = &exampleCallback;
subMenu1Item1.callbackFunction = &exampleCallback;
// attach the menuItems to the menu
subMenu1.menuItems[0] = subMenu1Item0; // populate the pointer array
subMenu1.menuItems[1] = subMenu1Item1;
subMenu1.numMenuItems = 2; // tell it how many there are
// connect the menu to the display and associated lcd
subMenu1.display = dsply;
subMenu1.lcd = dsply.lcd;
Menu subMenu2;
MenuItem subMenu2Item0;
MenuItem subMenu2Item1;
// populate the menuItems with data
strcpy(subMenu2Item0.displayText1, "Submenu2, Item0 ");
strcpy(subMenu2Item0.displayText2, "Press any button");
strcpy(subMenu2Item1.displayText1, "Submenu2, Item1 ");
strcpy(subMenu2Item1.displayText2, "Press any button");
// connect the callback functions to the menuItems
subMenu2Item0.callbackFunction = &exampleCallback;
subMenu2Item1.callbackFunction = &exampleCallback;
// attach the menuItems to the menu
subMenu2.menuItems[0] = subMenu2Item0; // populate the pointer array
subMenu2.menuItems[1] = subMenu2Item1;
subMenu2.numMenuItems = 2; // tell it how many there are
// connect the menu to the display and associated lcd
subMenu2.display = dsply;
subMenu2.lcd = dsply.lcd;
// build the top-level menu from the lower menus
Menu topMenu;
MenuItem topMenuItem0;
MenuItem topMenuItem1;
///////
// set up the menuItems
// populate the display text for the menu items.
strcpy(topMenuItem0.displayText1, "Show Submenu1 ");
strcpy(topMenuItem1.displayText1, "Show Submenu2 ");
///////////////////////////////////////////////////////////////////////
// This is where I'm having trouble
// set the callback functions to display the submenu
topMenuItem0.callbackFunction = Menu::&showMenu(subMenu1);
topMenuItem1.callbackFunction = Menu::&showMenu(subMenu2);
////////////////////////////////////////////////////////////////////////
// Set up the menu (attach menuItems and set the number of them
topMenu.menuItems[0] = topMenuItem0;
topMenu.menuItems[1] = topMenuItem1;
topMenu.numMenuItems = 2;
// connect the menu to the display and associated lcd
topMenu.display = dsply;
topMenu.lcd = dsply.lcd;
}
void loop() {
topMenu.showMenu();
}
menu.h:
#ifndef __menu_h
#define __menu_h
#include <Arduino.h>
#include "display.h"
class MenuItem
{
public:
MenuItem();
static const uint8_t lcdCols = 16; // columns on the lcd display
char displayText1[lcdCols]; // 1st line text to display
char displayText2[lcdCols]; // 2nd line text to display
boolean (*callbackFunction)(); // pointer to callback function
// callback functions for all items should return false when
// execution is complete and control should be returned to the
// next-higher menu. For sub-menus the callback should be the
// showMenu() method for that sub-menu.
};
// the way the menu is supposed to work is that each level of menu is
// it's own menu that consists of menuitems. Each menu keeps track of
// which menuitem is currently active and whether control is supposed
// to pass to the active menu or if the display text is supposed to be
// displayed. If control is supposed to be passed, the function
// pointed to by the callbackFunction() for the selected menuItem is
// called. The callbackFunction() should be either a function to
// drill down to the next submenu, or the final function to execute
// (like starting a measurement).
class Menu
{
public:
Menu();
static const int maxMenuItems = 5;
static boolean showMenu( ); // to show the menu on the display
static MyDisplay display; // display to draw the menu item on
static LiquidCrystal lcd; // the LCD associated with the display
// state variables to keep track of where we are in the menu system
static boolean fallThrough; // pass control to a sub-menu
static boolean redrawFlag; // true if display needs updating
static uint8_t activeItem; // selected menu item (0 indexed)
static uint8_t numMenuItems; // number of sub-menu items available.
MenuItem menuItems[maxMenuItems]; // each MenuItem contains a
// displayText string and a
// pointer to the menu item's
// callback function
private:
void _buttonscan( Menu menu );
};
#endif
menu.cpp:
#include "menu.h"
// constructor for any given menu
Menu::Menu( )
{
// set default parameters
fallThrough = false;
redrawFlag = true;
activeItem = 0;
numMenuItems = 1;
}
boolean Menu::showMenu( )
{
// this function displays the current menu. It returns true when
// control is supposed to return to the next higher menu, and false
// when control is at this level or farther down the menu structure.
MenuItem active = menuItems[activeItem]; // get the active menu item
// bypass all this if fallThrough is set and go to the next level down
if (fallThrough)
{
// drill down a level and set fallThrough based on the return value
fallThrough = active.callbackFunction();
}
// otherwise display the activeItem text and look for a button press
else
{
// redraw the display if the redrawFlag is set
if (redrawFlag)
{
// send the displayText to the display
display.displayText(lcd, active.displayText1, 0);
display.displayText(lcd, active.displayText2, 1);
// reset the redraw flag until something changes
redrawFlag = false;
}
// Process the button input and set the activeItem and fallThrough
_buttonscan(menu);
}
}
display.cpp (4.74 KB)
display.h (1.94 KB)
menu.cpp (23.7 KB)
menu.h (2.91 KB)
Menutest.ino (3.22 KB)