MD_Menu Library  2.1
Library for 1 or 2 line display menu management
Using the Library

The MD_Menu library allows definition and navigation of a menu system by moving between the menu nodes. At a leaf node, MD_Menu can either manage editing values or call user code.

Menu structures are defined in PROGMEM memory. They are linked into tree structures relationships using the IDs of other menu nodes nodes or leaf nodes. Leaf nodes are always associated with a user variable.

Extensive use is made of callbacks to user code to manage user input, display output and getting/setting values for variables.

User Input for Menu Navigation

Menu navigation is carried out under the control of user code invoked as a callback routine. The code must comply with the cbUserNav function prototype. This callback routine implementation is dependent on the type of input hardware, but the return codes for the required actions must be one of the standardized userNavAction_t enumerated type.

Navigation is carried out with 4 keys:

  • INCREMENT (NAV_INC). Move down a menu, move to the next value in a pick list or increment a numeric value being edited.
  • DECREMENT (NAV_DEC). Move up a menu, move to the previous value in a pick list or decrement a numeric value being edited.
  • SELECT (NAV_SEL). Select the current menu or pick list item, or confirm an edited numeric value.
  • ESCAPE (NAV_ESC). Escape the current menu (back up one level) or cancel changes to an edited value.

A variety of input hardware setups are demonstrated in the Test example code provided.

Menu Display

Menu display is enabled by user code as a callback routine from the library. The callback must comply with the cbUserDisplay function prototype.

The callback is provided with a request of type userDisplayAction_t and a message to display.

Display hardware must be able to display one or two lines for the menu display. All menu screens are structured with the first line as title and the second as the current menu selection or currently edited value, as appropriate. If the display can only support one line, the first line is discarded and only the second line displayed.

A variety of display hardware setups are demonstrated in the Test example code provided.

Memory Footprint

The limited amount of RAM available in micro controllers is a challenge for menu systems, as they often contain large amounts of 'static' data as text labels and other status information.

The MD_Menu library uses statically allocated data located in PROGMEM for the menu system and only copies the current menu record into RAM. All user values reside in user code and are not duplicated in the library.

Menu Management

As shown in the figure above, the library uses three types of objects, each identified with a unique id within the object type. A menu header (of type mnuHeader_t) defines a menu. The header contains a label for the title and the range of menu items (of type mnuItem_t) that should be displayed for the menu. Menu item ids between the start and end id locations include all the ids in locations in between, and should be in number sequence.

A menu item may lead to another menu (MNU_MENU, if it is a node in the menu tree) an input item (of type mnuInput_t) if it is a leaf of the menu system (MNU_INPUT), or an input item with real-time feedback (MNU_INPUT_FB) that reports the value each time with every change of value. The depth of the menu tree is restricted by the defined MENU_STACK_SIZE constant. When this limit is exceeded, the library will just ignore requests that cause additional menu depth but continues to run.

Menu input items define the type of value that is to be edited by the user and parameters associated with managing the input for that value. Before the value is edited a callback following the cbValueRequest prototype is called to 'get' the pointer to the variable with the current value. The input item id is provided to identify which value is being requested. The data must be loaded into a value_t data structure that remains in scope while the data is being edited, and the pointer to the structure passed back to the library. This copy of the user variable is used for editing and a second cbValueRequest (conceptually a 'set') is invoked after the value is updated, enabling the user code to take action on the change. If the variable edit is cancelled, the second cbValueRequest 'set' call does not occur and no further action is required from the user code. If the edit is specified with real-time feedback, the value is 'set' for each change in value.

Variable data input may be of the following types:

  • Pick List specifies a PROGMEM character string with list items separated by the '|' character (defined as INPUT_SEPARATOR), for example "Apple|Orange|Pear". The list is specified as the pList parameter and the get/set value callback expects a value that is the index of the current selection (zero based).
  • Boolean for Input of boolean (Y/N) values. As the user makes changes, the value changes between displays of 'Y' and 'N' (defined as INP_BOOL_T and INP_BOOL_F). The get/set callback expects a 0/1 value.
  • Integer values can 8, 16 or 32 bits in size, with the get/set callback expecting int32_t (note all signed values). The input specification also allows a lower and upper bound to be set, as well as the number's base (2 through 16) to be specified. Numeric values that overflow the specified field with are prefixed by the '#' character (defined as INP_NUMERIC_OVERFLOW) to indicate that this has occurred.
  • Floating point where the library uses a 32 bit long integer and assumes the last 2 digits (defined by FLOAT_DECIMALS) to be the fraction after the decimal point (character defined as DECIMAL_POINT). Specification allows lower and upper bound to be set. The base specification field is used to represent the minimum increment or decrement of the fractional component of value input (ie, with 2 decimals, 1 is .01, 5 is .05, 50 is 0.50, etc).
  • Engineering Units where the library uses a 32 bit long integer and assumes the last 3 digits (defined by ENGU_DECIMALS) to be the fraction after the decimal point (character defined as DECIMAL_POINT). Specification allows lower and upper bound for power of 10. Units are defined in the pList parameter. The base specification field is used to represent the minimum increment or decrement of the fractional component of value input (ie, with 3 decimals, 1 is .001, 5 is .005, 50 is 0.050, etc).
  • Run Code specifies input fields that are designed to execute a user function when selected. The 'get' in the callbask determines whether the operation requires confirmation. Returning a null pointer implies confirmation, anything else is a direct execution of the user code. User code is only ever executed as part of the 'set' invocation.
  • External Input specifies that the input value is provided by external user code. The value callback 'get' function is invoked until the value is confirmed using the normal method for the menu. All values are 32 bit signed integers.