Cosa/LCD Menu System

The latest addition to Cosa is a LCD menu system. It ties into the Cosa LCD and Keypad device drivers.

The design is data-driven and uses program memory for all static data, e.g. menu items, lists, strings, etc. This gives a very compact design. The Menu support also allows simple access and change of program state such as enumeration, bitset and integer range variables. A supporting macro set hides all of the details of creating the data structures in program memory. Below are some snippets to give a feel for the declarative programming style.

First, creating a menu item with an action function. As most of the Cosa design this is object-oriented with a support class Menu::Action. The object can hold the state necessary for the implementation of the action.

#include "Cosa/LCD/Driver/HD44780.hh"
#include "Cosa/Menu.hh"

// Use the HD44780 LCD driver with 4-bit parallel port and keypad shield
HD44780::Port port;
HD44780 lcd(&port);

// Menu Action ---------------------------------------------------------------
// Menu item with binding to action function and state (object)
// 1. Create an action handler by sub-classing Menu::Action

class FileOpenAction : public Menu::Action {
public:
  virtual void run(Menu::item_P item) 
  {
    lcd.display_clear();
    lcd.puts_P(PSTR("opening file..."));
    SLEEP(2);
  }
};

// 2. Create an instance and bind to a menu item.
FileOpenAction do_open;
MENU_ACTION(open_action,"Open",do_open)

// 3. Add to a menu item list.
MENU_BEGIN(file_menu,"File")
MENU_ITEM(open_action)
MENU_END(file_menu)

Selecting "File" from the "Demo" root menu the first item, "Open", in the sub-menu is shown as the title.

Selecting this item will execute the action function.

Second snippet creates enumeration symbols and mapping from a menu item to program state. When the item is selected the up/down buttons will allow change of the associated value. The current value is shown as the enumeration item string. These configuration/control variables are collected in an "Options" sub-menu in "Edit". Please note that the menu system data structures handle the mapping between the binary and textual representation of the value.

// Menu Enumeration Variable -------------------------------------------------
// Menu item with enumeration to change program state
// 1. Define the symbols needed for the enumeration type
MENU_SYMB(on_symb,"On")
MENU_SYMB(off_symb,"Off")

// 2. Define the enumeration type
MENU_ENUM_BEGIN(onoff_enum_t)
  MENU_ENUM_ITEM(off_symb)
  MENU_ENUM_ITEM(on_symb)
MENU_ENUM_END(onoff_enum_t);

// 3. Create a menu item with reference to program state to control
uint16_t tracing = 1;
MENU_ENUM(onoff_enum_t,tracing_enum,"Tracing",tracing)

Selecting the "Edit" and "Options" will take us to the above settings.

Selecting the "Tracing" item will allow us to change the tracing state. In modify mode the menu item title is prefixed with an asterisks.

Third snippet shows mapping from a menu item to an integer range variable. When selected the up/down keys will allow the variable to be changed within the given range.

// Menu Integer Range Variable -----------------------------------------------
// Menu item with integer range(low..high) to change program state change
// 1. Create menu item with integer range and reference to variable
int16_t limit = 42;
MENU_RANGE(limit_range,"Limit",-10,100,limit)

// 2. Add menu item to menu item list
MENU_BEGIN(options_menu,"Options")
  MENU_ITEM(tracing_enum)
  MENU_ITEM(state_enum)
  MENU_ITEM(limit_range)
MENU_END(options_menu)

The integer range menu item will show the lower and upper value in modification mode. The up/down keys will handle decrement/increment of the variable.

The enumeration symbol lists may also be used for bitset, i.e. to create configuration variables with one-or-many selection.

Below is a snippet that allows setting of a number of debug attributes. Selecting the menu item will allow adding/removing attributes for the set.

// Menu Bitset Variable ------------------------------------------------------
// Menu item with bitset to change program state
// 1. Define the symbols needed for the enumeration type
MENU_SYMB(break_symb,"Break")
MENU_SYMB(mockup_symb,"Mockup")
MENU_SYMB(profile_symb,"Profile")
MENU_SYMB(trace_symb,"Trace")

// 2. Define the enumeration type. Symbol order is the value set (0.3).
MENU_ENUM_BEGIN(debug_enum_t)
  MENU_ENUM_ITEM(break_symb)
  MENU_ENUM_ITEM(mockup_symb)
  MENU_ENUM_ITEM(profile_symb)
  MENU_ENUM_ITEM(trace_symb)
MENU_ENUM_END(debug_enum_t);

// 3. Create a menu item with reference to program state to control
// The enumeration type values are bit positions. The initial value is
// Break | Mockup.
uint16_t debug = 3;
MENU_BITSET(debug_enum_t,debug_enum,"Debug",debug)

// The Edit sub-menu with Options and Debug settings
MENU_BEGIN(edit_menu,"Edit")
  MENU_ITEM(options_menu)
  MENU_ITEM(debug_enum)
MENU_END(edit_menu)

Selecting an item in the bitset with toggle the corresponding bit in the control variable.

A Menu::Walker class handles all of the details of stepping through the menu tree and decoding the key events. The walker keeps track of the path through the menu tree and handles the select/up/down/left/right keypad button events. Below is the setup/loop snippet. It is pure event driven.

// The walker will receive key events from the keypad
Menu::Walker walker(&lcd, &root_menu);

void setup()
{
   Watchdog::begin(16, SLEEP_MODE_IDLE, Watchdog::push_timeout_events);
   lcd.begin();
   lcd.puts_P(PSTR("CosaLCDmenu: started"));
   SLEEP(2);
   walker.begin();
}

void loop()
{
  Event event;
  Event::queue.await(&event);
  event.dispatch();
}

See the full example sketch on github (or in the Cosa/examples directory):
https://github.com/mikaelpatel/Cosa/blob/master/examples/Sandbox/CosaLCDmenu/CosaLCDmenu.ino

The LCD menu class documentation: http://dl.dropboxusercontent.com/u/993383/Cosa/doc/html/dc/db6/Menu_8hh.html

Cheers!

Updated with some new pictures and the new bitset variable menu item type.

Cheers!

What are you talking about? The title sounded interesting to me.
Maybe some people on this forum know, but I and a lot have no clue what you are talking about.
How about a little more info, a url would probably be helpful to.

Thanks, Jack

jackwp:
What are you talking about? The title sounded interesting to me.
Maybe some people on this forum know, but I and a lot have no clue what you are talking about.
How about a little more info, a url would probably be helpful to.

@jackwp

Sorry about that. Here are a few links to the Cosa project.

http://forum.arduino.cc/index.php?topic=150299.0
http://dl.dropboxusercontent.com/u/993383/Cosa/doc/html/index.html

Please let me know if you need more info or if there is something I can explain more in detail.

Cheers!

Thanks for the info. It is a really interesting project. I will give it a try in the near future.

Thanks, Jack

The latest update of the Cosa LCD Device Driver and Menu System contains some improvement that might be of interest.

  1. The menu controller has been separated from the menu walker class. This is more in line with the Model-View-Control design pattern, Model–view–controller - Wikipedia, and allows multiple and replaceable controllers.

  2. The keypad handler is now a controller.

  3. A controller for rotary encoder with push button has been added.

  4. A shift register (74HC595) based LCD port adapters (SR3W) have been introduced. Cosa: An Object-Oriented Platform for Arduino programming - #167 by kowalski - Libraries - Arduino Forum.

Below is Arduino Nano running the CosaLCDmenu example sketch with a 16X2 LCD and a rotary encoder. The sketch uses a total of 6 pins; D2..D4 for the rotary encoder (input clock, data and push button), D5..D7 for the LCD SR3W adapter (output clock, data and enable).

The menu declaration is as before. Below is the new setup() with both the LCD port adapter for SR3W and the rotary encoder menu controller.

...
HD44780::SR3W port;
HD44780 lcd(&port);
...
// The menu handler ----------------------------------------------------------
Menu::Walker walker(&lcd, &root_menu);
Menu::RotaryController rotary(&walker);

void setup()
{
  Watchdog::begin(16, SLEEP_MODE_IDLE, Watchdog::push_timeout_events);
  lcd.begin();
  lcd.puts_P(PSTR("CosaLCDmenu: started"));
  SLEEP(2);
  walker.begin();
  rotary.begin();
}

Turning the rotary encoder will step through the menu items or values as expected. The push button is used to select and deselect items and values.

The LCD menu system is part of the Cosa Arduino programming framework. See github for the download and installation instructions. GitHub - mikaelpatel/Cosa: An Object-Oriented Platform for Arduino/AVR

Cheers!