OpenMoCo Menu Manager - completely automated menus for Arduino

Thank you for your support. It's working fine.
Harikesh Patil

Hello! First of all - thanks to developers for nice lib for menu! I found it very handy and simple to use. But I have one major trouble - with newer gcc (I think from 4.6 or 4.7) all PROGMEM identifers must also be const. I try to solve issues with const-ing by static_cast(const_cast(...) all works on OLDER compiler.. but I cannot find right cast for const MENU_LIST const root_list[] = {...}. without second const, that make pointer to arry elements const and PROGMEM-able it all can be compiled on older compiler, and it also WORKS! But as soon as I const root_list[] there is compiling errors about wrong cast from ‘const OMMenuItem* const ()[3]’ to type ‘void’. And without const-ing it cannot compile on ewer gcc :frowning:

HELP! PLEASE! :slight_smile:

I knew about __flash, but I prefere old-gcc compat solution and my knowelage isn't sufficient to port this lib to __flash.

Here is what I came so far
--therm_menu_const.SNIP---
float set_temp = 26.07;
float temp_delta = 0.5;

boolean missed = false;

// values to use

// TYPE MAX MIN TARGET
const MENU_VALUE set_temp_value = { TYPE_FLOAT_100, 125, -50 , MENU_TARGETO(&set_temp) };
const MENU_VALUE temp_delta_value = { TYPE_FLOAT_100, 2, 0.2 , MENU_TARGETO(&temp_delta) };

// LABEL TYPE LENGTH TARGET
const MENU_ITEM item_set_tempme = { {"Set Temperature"}, ITEM_VALUE, 0, MENU_TARGET(const_cast(&set_temp_value)) };
const MENU_ITEM item_temp_deltame = { {"Set Delta"}, ITEM_VALUE, 0, MENU_TARGET(const_cast(&temp_delta_value)) };
const MENU_ITEM item_testme = { {"Exit"}, ITEM_ACTION, 0, MENU_TARGETO(uiQwkScreen) };

// List of items in menu level
const MENU_LIST const root_list[] = { const_cast(&item_set_tempme), const_cast(&item_temp_deltame), const_cast(&item_testme) };

// Root item is always created last, so we can add all other items to it
const MENU_ITEM menu_root = { {"Root"}, ITEM_MENU, MENU_SIZE(root_list), MENU_TARGET(&root_list) };

OMMenuMgr Menu(const_cast(&menu_root));
--therm_menu_const.SNIP---

I add another macro

#define MENU_TARGETO(x) reinterpret_cast(x)
#define MENU_TARGET(x) static_cast(x)

Problem 2: I do not know how to setup the u8glib picture loop. More specific: I need to draw the screen more than once. The draw callback handler for u8glib is easy. Assuming a font with char width 4 and height 8 it is this:

void uiDraw(char* p_text, int p_row, int p_col, int len) {

u8g.setCursorPos(p_col4, p_row8); 
  for( int i = 0; i < len; i++ ) {
    if( c < '!' || c > '~' )
      u8g.write(' ');
    else 
      u8g.write(p_text[i]);
  }
}




However, the screen has to be updated several times. But i do not know how to do this. Controll is given to this lib with checkInput(), but i assume that this procedure also does the character output. Pseudocode for u8glib is this:
1. check keys
2. handle keys
3. several times: redraw screen
I looked into the code, but did not find how to do 3)

Any ideas?

Oliver

I don't see from the u8g docs that it needs to be drawn constantly, but instead only to be drawn when it changes. However, if you want to re-draw constantly, then just store the draw data in global scope, and access that as it updates... 

something like this:



struct {

char text[OM_MENU_ROWS];
  int row;
  int col;
  int len;
} screenDat;

boolean screenSet = false;

...

void setup() {

...

Menu.setDrawHandler(uiDraw);
  Menu.setExitHandler(uiExit);
}

void loop() {

Menu.checkInput();

if( screenSet )
     u8draw();

}

void uiDraw(char* p_text, int p_row, int p_col, int len) {

// clear out buffer
  memset(screenDat.text, ' ', OM_MENU_ROWS);
  memcpy(&screenDat.text, p_text, len);

screenDat.row = p_row;
  screenDat.col = p_col;
  screenDat.len = len;

screenSet = true;

}

void uiExit() {

screenSet = false;
}

I believe that OMMenuMgr is one of the simplest Menu Routines available right now but there is no ug8lib support or i cant realize how to do it.
Could u post your code?

HI, I'm really enjoying using this library however I cannot find a copy of the documentation. Can someone point me to it please.
I am looking to disable the background interrupt driven task when the menu is being used.
Thanks...

GliderMike:
... I cannot find a copy of the documentation. Can someone point me to it please.

Documentation you are looking for is included in the library .cpp and .h files. The web site had a nicely formatted version of the same.

I am looking to disable the background interrupt driven task when the menu is being used.

I think there is a function "shown()" in the OMMenuMgr.cpp that returns true if menu is displayed. If you modify the included example you can try to check the status of "Menu.shown()".

Hi Kallek, works fine thanks :slight_smile:

Nobody try to adapt the library with u8glib?

Could not find the OpenMoCo Menu Manager - completely automated menus for Arduino anywhere
openmoco web site is now closed and on the web site Dynamic Perception does not have the library any more can any one help with this library please
Cheers
Rod J

rod5858:
Could not find the OpenMoCo Menu Manager - completely automated menus for Arduino anywhere...

You can find it on the GitHub GitHub - DynamicPerception/OMLibraries: OpenMoCo Libraries for AVR

I have made the Open MoCo menu manager to work, sort of..
The problem is my menu cursor does not move up and down, instead when I press 'DOWN' the entire menu is reprinted on screen starting with menu element two and so on. I would like the menu to stay onscreen with the cursor moving up and down. Is this possible? I also would like to start printing the menu from row three on the screen, to put some kind of headers above. Is it possible to make a menulist with , let's say seven elements where the first two is a header, and the menu cursor moves up and down from row three to row seven (five menu items)?

This is obviously an old thread.
However, does anyone know how to use serial I2C graphic LCDs with these libraries.
I was going through the examples of m2tklib and it seems it only supports parallel LCDs.

Any help would be greatly appreciated.

Thanks

Watcher:
However, does anyone know how to use serial I2C graphic LCDs with these libraries...

Hi.
I am using the "New LiquidCrystal" library (https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home) and OMMenuMgr with character LCD connected to I2C bus via PCF8574* expansion board.

The meny library does not draw the characters on the LCD screen but calls your method. If your LCD library is standard LiquidCrystal compatible then the included example should work (check out the uiDraw and uiClear functions there).

Kalle

Hello,
I have downloaded this library to add a menu system to my project and when I went to compile the example I get the following errors:

example.ino:55:19: error: variable ‘sel_ign’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:56:19: error: variable ‘sel_on’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:57:19: error: variable ‘sel_off’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:59:30: error: variable ‘state_list’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:64:13: error: variable ‘state_select’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:69:12: error: variable ‘foo_value’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:70:12: error: variable ‘bar_value’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:71:12: error: variable ‘baz_value’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:72:12: error: variable ‘bak_value’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:73:12: error: variable ‘sel_value’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:76:11: error: variable ‘item_checkme’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:77:11: error: variable ‘item_barme’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:78:11: error: variable ‘item_bazme’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:79:11: error: variable ‘item_bakme’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:80:11: error: variable ‘item_state’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:81:11: error: variable ‘item_testme’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:84:21: error: variable ‘root_list’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’
example.ino:87:11: error: variable ‘menu_root’ must be const in order to be put into read-only section by means of ‘attribute((progmem))’

In a search for a solution I found a page in the github issues section titled "port to newer avr-gcc" which seemed to be about the same issue I'm having, and it had patch files in one of the responses which I applied and the errors stayed the same.

I tried fiddling around with the library code to try and see if I could fix it myself but I only ended up causing more errors.

LINKS:
I got the OMMenuMgr Library HERE
the Github issue page "port to newer avr-gcc" is HERE

Any help would be greatly appreciated

Please disregard my previous post as I have discovered the solution to my problem.

The solution was updating from Arduino for Linux x64 version 1.0.5 to version 1.0.6.

I should have checked for updates before posting.

Sorry for the needless bump, I'd sage but no field for that here.

I have convert the OMMenuMgr library for IDE 1.6.3 and can't use it !

This is my code (i want to use digital buttons):

/**  Example OMMenuMgr Sketch

 @author
 C. A. Church
 
 */

#include "OMMenuMgr.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

//#define BUTTONTYPE ANALOG
#define BUTTONTYPE DIGITAL

const byte LCD_ROWS = 4;
const byte LCD_COLS = 20;

 // button values
#ifdef BUTTONTYPE ANALOG 
 // which input is our button
const byte BUT_PIN = 14;
// analog button read values
const int BUTSEL_VAL  = 70;
const int BUTFWD_VAL  = 250;
const int BUTREV_VAL  = 450;
const int BUTDEC_VAL  = 655;
const int BUTINC_VAL  = 830;

const byte BUT_THRESH  = 60;

// mapping of analog button values for menu
int BUT_MAP_ANALOG[5][2] = {
  {BUTFWD_VAL, BUTTON_FORWARD}, 
  {BUTINC_VAL, BUTTON_INCREASE}, 
  {BUTDEC_VAL, BUTTON_DECREASE}, 
  {BUTREV_VAL, BUTTON_BACK}, 
  {BUTSEL_VAL, BUTTON_SELECT} 
};
#endif

#ifdef BUTTONTYPE DIGTAL                             
int BUT_MAP_DIGITAL[5][2] = {
  {4, BUTTON_FORWARD}, 
  {5, BUTTON_INCREASE}, 
  {6, BUTTON_DECREASE}, 
  {7, BUTTON_BACK}, 
  {8, BUTTON_SELECT} 
};
#endif

// ====== Test Menu =========== 

byte foo = 0;
byte sel = 0;
unsigned int bar = 1;
long baz  = 0;
float bak = 0.0;

  // Create a list of states and values for a select input
const MENU_SELECT_ITEM  sel_ign = { 2, {"Ignore"} };
const MENU_SELECT_ITEM  sel_on  = { 1, {"On"} };
const MENU_SELECT_ITEM  sel_off = { 0, {"Off"} };

const MENU_SELECT_LIST  const state_list[] = { &sel_ign, &sel_on, &sel_off };
                                  
  // the special target for our state input
  
                                   // TARGET VAR   LENGTH                          TARGET SELECT LIST
const MENU_SELECT state_select = { &sel,           MENU_SELECT_SIZE(state_list),   MENU_TARGET(&state_list) };

  // values to use 

                          //    TYPE            MAX    MIN    TARGET 
const MENU_VALUE foo_value = { TYPE_BYTE,       100,   0,     MENU_TARGET(&foo) };
const MENU_VALUE bar_value = { TYPE_UINT,       10000, 100,   MENU_TARGET(&bar) };
const MENU_VALUE baz_value = { TYPE_LONG,       10000, 1,     MENU_TARGET(&baz) };
const MENU_VALUE bak_value = { TYPE_FLOAT_1000, 0,     0,     MENU_TARGET(&bak) };
const MENU_VALUE sel_value = { TYPE_SELECT,     0,     0,     MENU_TARGET(&state_select) };

                          //        LABEL           TYPE        LENGTH    TARGET
const MENU_ITEM item_checkme  = { {"Byte Edit"},    ITEM_VALUE,  0,        MENU_TARGET(&foo_value) };
const MENU_ITEM item_barme    = { {"UInt Edit"},     ITEM_VALUE,  0,        MENU_TARGET(&bar_value) };
const MENU_ITEM item_bazme    = { {"Long Edit"},    ITEM_VALUE,  0,        MENU_TARGET(&baz_value) };
const MENU_ITEM item_bakme    = { {"Float Edit"},   ITEM_VALUE,  0,        MENU_TARGET(&bak_value) };
const MENU_ITEM item_state    = { {"Select Input"}, ITEM_VALUE,  0,        MENU_TARGET(&sel_value) };
const MENU_ITEM item_testme   = { {"Test Action"},  ITEM_ACTION, 0,        MENU_TARGET(uiQwkScreen) };

                   //        List of items in menu level
const MENU_LIST const root_list[]   = { &item_checkme, &item_barme, &item_bazme, &item_bakme, &item_state, &item_testme };

                  // Root item is always created last, so we can add all other items to it
const MENU_ITEM menu_root     = { {"Root"},        ITEM_MENU,   MENU_SIZE(root_list),    MENU_TARGET(&root_list) };


 // initialize LCD object
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Addr, En, Rw, Rs, d4, d5, d6, d7, backlighpin, polarity



//OMMenuMgr Menu(&menu_root);
OMMenuMgr Menu(&menu_root, MENU_DIGITAL);



void setup() {

  lcd.begin(LCD_COLS, LCD_ROWS);
  
  uiClear();
  
  Menu.setDrawHandler(uiDraw);
  Menu.setExitHandler(uiClear);
#ifdef BUTTONTYPE ANALOG
  Menu.setAnalogButtonPin(BUT_PIN, BUT_MAP_ANALOG, BUT_THRESH);
#endif
#ifdef BUTTONTYPE DIGITAL
  Menu.setDigitalButtonPins(BUT_MAP_DIGITAL);
#endif
  Menu.enable(true); 
  
  
}

void loop() {
 Menu.checkInput();//return The button pressed, one of: BUTTON_NONE, BUTTON_FORWARD, BUTTON_BACK, BUTTON_INCREASE, BUTTON_DECREASE, BUTTON_SELECT
 
}

void testAction() {

 digitalWrite(5, HIGH);  
}


void uiDraw(char* p_text, int p_row, int p_col, int len) {
  lcd.setCursor(p_col, p_row);
  
  for( int i = 0; i < len; i++ ) {
    if( p_text[i] < '!' || p_text[i] > '~' )
      lcd.write(' ');
    else  
      lcd.write(p_text[i]);
  }
}


void uiClear() {
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Enter for Menu"));
}


void uiQwkScreen() {
  lcd.clear();
  Menu.enable(false);
  
  lcd.print("Action!");
  lcd.setCursor(0, 1);
  lcd.print(F("Enter 2 return"));
  
  while( Menu.checkInput() != BUTTON_SELECT ) {
    ; // wait!
  }
//  
  Menu.enable(true);
  lcd.clear();
}

If i add a delay into the uiDraw function, i can see the menu that change again and again after a push button !!!
Thanks for your help !!!

OMLibraries.zip (752 KB)

Hi Drone,

As a new user to OMMenuMgr I must say its a great piece of code.

It took me a while to work out what was going on and how to customize it for my own needs.

But finally I have it working and I am very happy.

I have two small question that would help me improve my code.

I was wondering if you could help me.

Q1. Relates to this quoted text from the 'OMMenuMgr.h' file.

@code
byte foo = 0;

// Data Type Max Min Target
MENU_VALUE value_foo = { TYPE_BYTE, 100, 0, MENU_TARGET(&foo) };

// LABEL TYPE LENGTH TARGET
MENU_ITEM item_foo = { {"Foo Edit"}, ITEM_VALUE, 0, MENU_TARGET(&value_foo) };
@endcode

Here, we see that we can specify the type of data the item points to, a maximum value, a minimum
value, and the target variable (by address) to be edited. Minimum and Maximum values are limited
to the range of a signed long, no matter the data type, and are not used for select lists.

Now specifically the Max and Min value and data type for 'MENU_VALUE value_foo'
I am trying to replace '100' and '0' with a variable.

The help file says

Minimum and Maximum values are limited to the range of a signed long, no matter the data type

But I have tried a rang of data types with no luck.

Could you please get back to me with you to solve this.

Below is a sample of what I have tried

int z_max = 100;
int z_min = 100;
MENU_VALUE lst_ang_z_s_value = { TYPE_UINT,z_max, z_min,MENU_TARGET(&lst_ang_z_s), Adr_zs_lstread };
MENU_ITEM item_zoom = { {"Zoom"},ITEM_VALUE,0,MENU_TARGET(&lst_ang_z_s_value)};

End Q1.

Q2. When you are exiting the value of a 'ITEM_VALUE' using the LCD and you are increasing or decreasing the values.

Is it possible for it to run some method to do something with the value on the screen.
Currently it appears to me that is only saves the variable once you press the 'BUTTON_SELECT'

The reason I ask is I am trying to set the position of a servo and if I use a 'ITEM_VALUE' menu item I can not get the servo to update as the value changes.

It only updates one I press the 'BUTTON_SELECT' which I assume is as designed.

So at this stage I have written a method that is called by the 'ITEM_ACTION' menu item.
this works but I don't think its the best solution.

Below is a sample of the code I wrote for my method.

void setHeightServo() {
 servo_adjustment_State = true;

  lcd.clear();
  Menu.enable(false);

  String Mnu_lbl = menuLabel();


  lcd.print("<- Adj " + Mnu_lbl + " ->");

  lcd.setCursor(0, 1);

  String str_new_lst_ang_ls_s = String(lst_ang_ls_s);
  lcd.print(str_new_lst_ang_ls_s);

  int org_lst_ang_ls_s = lst_ang_ls_s;
  unsigned int  old_lst_ang_ls_s = lst_ang_ls_s;


  while( Menu.checkInput() != BUTTON_SELECT ) {
    //; // wait!
 uint8_t btnPressed =Menu.checkInput();

  currentTime = millis();

 if (currentTime - previousScreenOnTime > screenTimeUntilSleep ){
 if (servo_OnOff_ControlState == 1)sleepServos();
 if (LCD_OnOff_ControlState == 1)sleepDisplay();
 }

 if(btnPressed != BUTTON_NONE){

 setPreviouseTime();

 if (btnPressed == BUTTON_INCREASE)lst_ang_ls_s = lst_ang_ls_s--;
 if (btnPressed == BUTTON_DECREASE)lst_ang_ls_s = lst_ang_ls_s++;

 if (lst_ang_ls_s != old_lst_ang_ls_s){
 if (servo_OnOff_ControlState == 0)wakeServos();
 if (LCD_OnOff_ControlState == 0)wakeDisplay();

 old_lst_ang_ls_s = lst_ang_ls_s;
 lense_shift_Servo.write(lst_ang_ls_s);

 lcd.setCursor(0, 1);
 lcd.print(String(lst_ang_ls_s));
 }


 if (btnPressed == BUTTON_BACK){
 lst_ang_ls_s = org_lst_ang_ls_s;
 lense_shift_Servo.write(org_lst_ang_ls_s);

  if (servo_OnOff_ControlState == 1)sleepServos();

  Menu.enable(true);
  lcd.clear();
  servo_adjustment_State = false;
  return;
  }

 }

  }

I also changed on of your variables from private to public so I could
get the current menu item.

below is the code I used to get the menu item on the LCD.

String menuLabel()
{

    //Serial.println("getMenuLabel");

    int i;    // counter variable
    char myChar;
    String menuLabel = "";

    // read back a char
      int len = strlen_P(Menu.m_curSel->label);

      for (i = 0; i < len; i++)
      {
        myChar =  pgm_read_byte_near(Menu.m_curSel->label + i);
        //Serial.print(myChar);
        menuLabel = menuLabel + myChar;
      }

      //Serial.print(menuLabel);
      return menuLabel;

 }

End Q2.

Thanks again for providing such a awesome library for us to use.

Cheers.

Stuart

This is an interesting menu starting point. I am running the sample sketch. If anyone knows something about this set of routines, how to use them, I have a question. Is it possible to have the menu elements appear on four lines when using a 4 line display?

The LCD setup information is set to 20x4 but it only displays the menu on the first two lines. Scrolling up or down works properly but it still only uses the top two lines. I would like the menu to make use of all four lines.

If anyone has any experience with OMMenuMgr I would appreciate any information that you have. Be it a sketch that uses OMMenuMgr. Or any insight in how to use the routines.

Rudy216:
The LCD setup information is set to 20x4 but it only displays the menu on the first two lines. Scrolling up or down works properly but it still only uses the top two lines.

Have you defined the number of lines and columns both in OMMenuMgr.h and in your sketch "lcd.begin" line?

If anyone has any experience with OMMenuMgr I would appreciate any information that you have. Be it a sketch that uses OMMenuMgr. Or any insight in how to use the routines.

You can check out one example here GitHub - woodyj21/patchedOMMenu: patches to Open MOCO to support newer gcc that includes the patched version for a new compiler.
Useful feature is that the menu system supports saving variables to EEPROM. You can combine the variables that you change in the menu to a " struct" and load them all from EEPROM in a "setup".

Thanks.

I didn't have it changed in OMMenuMgr.h It does display the menu on the four lines but when it edits a selection it leaves the menu items on the bottom two rows visible. Hey but at least I have something to work with now.

I haven't tried the example yet. I am going to update to that library first.

Once again. Thank you. :slight_smile:

Deleted post. Me whining.