MENWIZ: yet another character lcd menu wizard library

I tried looking at this yesterday but, unfortunately, it only seems to work with a particular implementation of LiquidCrystal_I2C, the same one as compatible in the New LiquidCrystal Library.

My I2C backpacks work with the other I2C implementation, the one that controls the backlight through the I2C bus, rather than a dedicated arduino pin.

Are thee any plans to make MENWIZ compatible with more than one LCD connection method, as it rather limits scope for take up in this guise, but otherwise looks like it could be an excellent library.

If it were to be included as core library then it would need to work with any LCD connection, certainly with the shields available.

Many thanks.

Menwiz works on any interface method supported by (new) LiquidCrystal.
(new) liquidCrystal library works with the following interfaces (as in https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home)

  • 4 bit parallel LCD interface
  • 8 bit parallel LCD interface
  • I2C IO bus expansion board with the PCF8574* I2C IO expander ASIC such as I2C LCD extra IO.
  • ShiftRegister adaptor board as described Shift Register project home or in the HW configuration described below, 2 and 3 wire configurations supported.
  • ShiftRegister 3 wire latch adaptor board as described ShiftRegister 3 Wire Home
  • Support for 1 wire shift register ShiftRegister 1 Wire

some users adapted Menwiz library to other libraries (to support devices from webrobots , fugaku etc). I would like to include a summary by the author of such mods in the doc.
I think it is quite enough ... 8)

The New LiquidCrystal library is not limited to any aparticular HW wiring or driver as far as pin out is concerned. The base constructor does assume a particular wiring for all the drivers it is capable of handling, but every class has constructors that enable users to configure the pin mapping of their LCD.

You just need to look at the header files for each driver and see the flexibility.

As far as the backlight is concerned, once again, the default constructor does not assume support for the backlight. However, the I2C flavour of the library does support the control of the backlight, you just need to invoke the correct constructor when crating the LCD object. Again, you need to look in the header files to see which method suits your needs.

Will it be extened to support other I2C IO extension ASICs, it will. Currently it supports the PCF8574 and compatibles which is very much standard.

The other thing that is very nice about the library is that you just need to add a new class, with a few methods to support any other driver you can think off (just need to pass how to write to the device and control the backlight). The rest is done for you.

I forgot to mention, that the adaptions that #brunialti mentions are just one line of code: the creation of the object that his library uses, a simple pin mapping call in the constructor. He then uses a reference to the abstract class "LCD" throughout his menu library regardless of the LCD or driver used.

@bruinalti - great work. Every time I look it improves by the hour!!!

Very much appreciate the superb support you guys give in responding so quickly.

I will delve a little deeper into what you've said.

It would be great to switch to using the new library and give MENWIZ a proper try out too.

Many thanks again.

brunialti:
@Khalid
May be i found a bug. Do the line duplication occours when you set root menu as collapsed menu?
If it is true that is a bug I detected and solved. Probably I need to anticipate the release of 1.2.0. version to fix the problem

Salute brunialti,
Due to big size of my code of suntracker, i revert to your version MenWiz1.1, Can you also rectify the above line duplication bug in MENWIZ1.1?
Thank you for the support.
Regards

Khalid, what's the problem with size? The 1.2 should'nt differ from 1.1.

brunialti:
Khalid, what's the problem with size? The 1.2 should'nt differ from 1.1.

The v1.1 library is 28K whereas v1.2 library is 29K... However, i still have 3K programm memory in hand.. Your MENWIZ library is awesome :slight_smile:

brunialti:
Hi.
for all people interested in "odd" button models (that is 3 or 5 button models) I give you a very simple patch to implement it now. The next version will consolidate the change.

Inside the library is implemented a "logical level" where are defined all actions performed after a button push.
Those functions are private members of class menwiz, so it is necessary to make just one change to the MENWIZ.h file, moving the definition of actBTE() from private to public. That is all.

In your sketch you can implement a menu entry acting exactly as the escape button as following (example):

void bte(){

menu.actBTE();}

I'm also trying to use a 5 button analog and can't make the above line compile. It does work if I leave this in Private

Private:
 void     actBTE();

and use this in Public: with the menu.actBTE commented out. Also BTE needs to be all capitols.

Public:
void     BTE();
 /* {
	  menu.actBTE();
	}
	*/

When the menu.actBTE is not commented I get these errors :
C:\arduino-1.0.3\libraries\MENWIZ/MENWIZ.h: In member function 'void menwiz::BTE()':
C:\arduino-1.0.3\libraries\MENWIZ/MENWIZ.h:202: error: 'menu' was not declared in this scope

I don't have my display handy so I can't test the compiled code to see if it works.

I'm no c++ expert so I'm not quite sure what to do with this.

After getting the new LiquidCrystal library owing with my I2C expanders I had a quick try of MENWIZ examples last night.

It looks very good and I'll try some altering of menu code in a short while.

Incidentally I'm testing with a 16x2 and it seems fine on two line displays.

One question though:-

The way I would want to use this is as a settings menu that only appears when entered on startup. It will set parameters for a game.

I can see that I'd do this by setting a variable for menu=1 and enter a while loop with the me u worker process running. When I exit the menu then I'll set menu=0 and drop into my main loop.

It looks like there is no lcd.begin() in the main sketch, but rather controlled in the library. This seems to assume that the menu has exclusive use of the LCD.

I will use the LCD for other things too, with the menu only used when required. At start I will have a splash screen, then an optional key combination to enter setup menu. This will be followed by screens showing the current game settings, before a countdown time to start the game, at which point the display will then be a scoreboard display.

Settings will only be able to be hanged at startup, analogous to entering PC BIOS settings.

Can I do this with MENWIZ? Since the LCd is initialized with the menu, it seems that if I don't enter the menu I wouldn't need to do so. But, if I do go into the menu then I might end up with trying to initialize the same lcd object twice.

@khalid
the difference should be less than 1k. probably around 4-500bytes. btw, did you commented the button support (As you use your own analog button management). That should save some space.
Frankly i would'nt work on an "old version" to fork a new branch...

@stretched59
the following code (from menwiz.h) does compile on my ide and allows you to call actBTE directly from your sketch. this changes will be in the next version.

class menwiz{

public:
           menwiz();
  void     begin(void *,int, int);
  void     addSplash(char *,int);
  void     addUsrScreen(void (*f)(), unsigned long);
  void     addUsrNav(int (*f)(), int);
  void     setBehaviour(MW_FLAGS,boolean);
  _menu*   addMenu(int, _menu *, MW_LABEL);
  void     draw();
  void     drawUsrScreen(char *);       //draw user screen(s)
  int      getErrorMessage(boolean); 	//if arg=true, err message is printed to the default Serial terminal, otherwise the function returns error code only
  int      freeRam();

#ifdef EEPROM_SUPPORT
  void     writeEeprom();
  void     readEeprom();
#endif

#ifdef BUTTON_SUPPORT 
  _nav*    btx;
  void     navButtons(int,int,int,int,int,int);
  void     navButtons(int,int,int,int);
  int      scanNavButtons();
#endif

  MW_FLAGS flags;
  MW_LCD*  lcd;
  char*    sbuf;                        //lcd screen buffer (+ 1 for each line) 
  _cback   usrScreen;	        	//callback
  _cback   usrNav;    
  byte     idx_m;
  _menu    m[MAX_MENU];
  _menu*   cur_menu;
  _menu*   root;
  void     actBTE();

private:
  byte     row;
  byte     col;
  byte     cur_mode;
  int 	   last_button;                 //last pushed button code
  unsigned long tm_push;                //last pushed button time
  unsigned long tm_start;       	//start time (set when begin method is invocated)
  unsigned long tm_splash;      	//splash screen duration  
  unsigned long tm_usrScreen;   	//lap time before usrscreen  
  void     apply2vars(void (*f)(_menu *));
  int      actNavButtons(int);
  void     drawMenu(_menu *);
  void     drawVar(_menu *);
  void     drawList(_menu *, int);
  void     actBTU();
  void     actBTD();
  void     actBTL();
  void     actBTR();
  void     actBTC();
protected:
};

Ciao,
I made some progress on the Menu, and it's working very well with the Lcd Keypad shield .... this is the code for those with the same LCD Keypad ....

// +++++++ Libaries included
#include <Wire.h>
#include <LiquidCrystal.h>
//INSERT ALL THE LIBARIES AFTER INCLUDING WIRE LIB (MENWIZ request)
//#include <LiquidCrystal_I2C.h>               //eliminato, non più necessario
#include <buttons.h>
#include <MENWIZ.h>
#include <EEPROM.h>
#include <Stepper.h>

// Create global object for menu and lcd
menwiz menu;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

const int stepsPerRevolution = 200;  // change this to fit the number of steps per revolution for your motor
Stepper myStepper(stepsPerRevolution, 0,1,2,3);   // initialize the stepper library on pins 0 through 3:

/*                                                eliminato, non più necessario
//Addr, En, Rw, Rs, d4, d5, d6, d7, backlightpin, polarity 4 x 20 LCD
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 
*/

boolean wr=0;                         // Instantiate global variables to bind to menu

const int buttonPin = A0;             // 4 button switch circuit input connected to analog pin 0
const int led13Pin = 13;              // the number of the LED output pin
int stepCount = 0;                    // number of steps the motor has taken

boolean buttonBlock = 0;
boolean buttonAct = 0;                // flag 1
boolean stopMenu = 0;                 // flag 2
byte buttonPressed = 0;               // which button was pressed
byte lastButtonPressed = 0;           // prev button pressed
int buttonValue = 0;                  // read value from analog port for the 4 navigation pushbuttons
long menuOffTime = 0;                 //used for timer

int list,sp=0;                       // sp variable has 0 as default value (ABS height)

extern byte MW_navbtn;                //needed to run the software key navigation in Menwiz



void setup()
{
  Serial.begin(9600);                  // Output to serial writing

  digitalWrite((54), HIGH);            // enable the 20k internal pullup for MEGA board for analog port A0
  // digitalWrite((A0), HIGH);         // enable the 20k internal pullup for UNO based board for analog port A0

  //++++++++++++++++++++Menu and LCD  
  char b[84];
  _menu *r,*s1,*s2;

                                      // initialize the menu object ( 4 x rows x 20 colums LCD)
  menu.begin(&lcd,16,2);              //initialize the menwiz menu object passing the lcd object and the colums and rows params 
  menu.addUsrNav(navMenu,4);
  MW_navbtn=4;                        // force 4 or 6 buttons mode for menu navigation -> MW_navbtn=4; or MW_navbtn=6;

  //create the menu tree
  r=menu.addMenu(MW_ROOT,NULL,F("MAIN MENU"));                      //create a root menu at first (required)

  //---------------  
  s1=menu.addMenu(MW_SUBMENU,r,F("SAW"));                       //add a submenu node 1 to the root menu (control the heigh of my Saw)
  s2=menu.addMenu(MW_VAR,s1,F("ABS Height"));                   //add a terminal node in the menu tree (that is "variable"); (move at a certain mm height)
     s2->addVar(MW_AUTO_INT,&sp,0,100,1);                   //int type, fd binded variable, rage 0-100, step 1
  s2=menu.addMenu(MW_VAR,s1,F("REL Move"));                     //add a terminal node in the menu tree (that is "variable"); (move of a certain mm the height)
     s2->addVar(MW_AUTO_INT,&sp,0,100,1);                   //int type, fd binded variable, rage -100 +100, step 1
  s2=menu.addMenu(MW_VAR,s1,F("Zero"));                         //add a terminal node in the menu tree (that is "variable");  (set the zero)
      s2->addVar(MW_LIST,&list);
      s2->addItem(MW_LIST, F("Option1"));
      s2->addItem(MW_LIST, F("Option2"));
      s2->addItem(MW_LIST, F("Option3"));
  s2=menu.addMenu(MW_VAR,s1,F("Setup"));                        //add a terminal node in the menu tree (that is "variable");  (set the parameters)

  //---------------
  s1=menu.addMenu(MW_SUBMENU,r,F("PLANNER"));                    //add a submenu node 2 to the root menu (control the heigh of my Planner)
  s2=menu.addMenu(MW_VAR,s1,F("ABS Height"));                         //add a terminal node in the menu tree (that is "variable"); (should move at a certain mm height)
     s2->addVar(MW_AUTO_INT,&sp,0,100,1);                   //int type, fd binded variable, rage 0-100, step 1
  s2=menu.addMenu(MW_VAR,s1,F("REL Move"));                         //add a terminal node in the menu tree (that is "variable"); (should move of a certain mm the height)
     s2->addVar(MW_AUTO_INT,&sp,-100,100,1);                   //int type, fd binded variable, rage 0-100, step 1
  s2=menu.addMenu(MW_VAR,s1,F("Zero"));                         //add a terminal node in the menu tree (that is "variable"); (should set the zero)
  s2=menu.addMenu(MW_VAR,s1,F("Setup"));                        //add a terminal node in the menu tree (that is "variable");  (set the parameters)
  
  //---------------
  s1=menu.addMenu(MW_SUBMENU,r,F("SHAPER"));                    //add a submenu node 3 to the root menu
  s2=menu.addMenu(MW_VAR,s1,F("ABS Height"));                         //add a terminal node in the menu tree (that is "variable"); (should move at a certain mm height)
  s2=menu.addMenu(MW_VAR,s1,F("REL Move"));                     //add a terminal node in the menu tree (that is "variable"); (should move of a certain mm the height)
  s2=menu.addMenu(MW_VAR,s1,F("Zero"));                     //add a terminal node in the menu tree (that is "variable"); (should set the zero)
  s2=menu.addMenu(MW_VAR,s1,F("Setup"));                        //add a terminal node in the menu tree (that is "variable");  (set the parameters)
  
  /*
  //++++++ Splash screen +++++++
  
  sprintf(menu.sbuf,"TEST minimum menu\nwith 4 or 6\nanalog push buttons\n",1);
  menu.addSplash((char *) menu.sbuf, 1000);

  //++++++ Userscreen ++++++++++
  // create an user define screen callback to activate after X secs since last button push
  menu.addUsrScreen(msc,3000);
  */
  pinMode(led13Pin, OUTPUT);                // initialize the led as a output control pin
}

void loop()    
{
// NAVIGATION MANAGEMENT & DRAWING ON LCD. NOT BLOCKING has to be the first in the void loop
  menu.draw();
/*
  put your regular code here
*/
//  ++++++ functions +++++++

void readButtons()

// ++++ Control 4 buttons ++++
{
  lastButtonPressed = buttonPressed;

  /*
  Analog values representing the pushbutton value are depending on the resitor value used.
   Test the values first with the serial.monitor
   */
  buttonValue = analogRead(buttonPin); //analog value to simulate the pushed buttons
    
  if(buttonValue >= 850)
  {
    buttonPressed = 0;
    noButtonPressed(); // is calling an extra fucntion
  }
  else  if(buttonValue >= 380 & buttonValue <= 450)
  {
    buttonPressed = 4;
    buttonAct = 1; // set the menu flag1
  }
  else if(buttonValue >= 200 & buttonValue <=300)
  {    
    buttonPressed = 3;
    buttonAct = 1; // set the menu flag1
  }
  else if(buttonValue >= 0 & buttonValue <=50)
  {    
    buttonPressed = 2;
    buttonAct = 1; // set the menu flag1
  }
  else if(buttonValue >= 80 & buttonValue <=150)
  {    
    buttonPressed = 1;
    buttonAct = 1; // set the menu flag1
  }
}

int noButtonPressed()
{
  return MW_BTNULL;
}

int navMenu() // called from menu.draw
{
  /*
   As soon as a button is pushed the first time flag 1 is set to HIGH and if the buttonnumber is not 0 then a timer is started.
   The menu action then should only be performed once.
   After 2000 msecs the flag will be set to LOW and a new buttonpush can be processed.
   Menu action is blocked for 2000 msec even if the same button is being kept pressed for 2000 msecs by flag2.
   */

  long menublockTime = millis();

  if (buttonAct == 1 && buttonPressed != 0 && stopMenu == 0)  // we have a state were we process a menu navigation step once and block it for 2 secs
  {
    digitalWrite(led13Pin,HIGH);  // set timer led ON
    menuOffTime = menublockTime  + 400; //start the timer. You can change this blocking time to your convenience but do not make it lower aa 200 msecs
    stopMenu = 1;

    switch (buttonPressed)
    {
    case 1: // Up
      return MW_BTU;
      break;
    case 2: // Confirm
      return MW_BTC;
      break;
    case 3: // Down
      return MW_BTD;
      break;    
    case 4: // Escape
      return MW_BTE;
      break;
    }
  }

  if (menuOffTime != 0  &&  menublockTime  > menuOffTime)  //  Reset of the timer so a new menu action can be processed
  {
    buttonAct = 0; // resets the flag 1
    buttonPressed = 0;
    menuOffTime = 0;  // resets timer to zero
    stopMenu = 0;
    digitalWrite(led13Pin,LOW);  // set timer led OFF
  }
}

/*
// user defined default screen.
void msc()
{
  sprintf(menu.sbuf,"User screen\nUptime (s): %ld\nFree mem  : %d\n\n",millis()/1000,(int)menu.freeRam());
  menu.drawUsrScreen(menu.sbuf);
}*/

The stepper part is still to be implemented and also the menu code, but it's a "good" start (well there are maybe some errors but it works)

... And now a little question ...
This shows on the LCD a graphic format like that:
0 [10] 100

     s2->addVar(MW_AUTO_INT,&sp,0,100,1);                   //int type, fd binded variable, rage 0-100, step 1

Is it possible to change that format ?
I would like to display something like:
10 mm without the limit 0 - 100.
thanks

currently it is not possible. But is quite easy to add in next versions an option to avoid limits. It is not planned to add text label on the same variable value row.

Thanks Roberto, just to know if it was possible ... so many things I don't know.

Due to my complete ignorance it took me a while to figure this 3 or 5 button thing out completely. But that is part of the learning process is trying to figure out where my mistakes are.

I have it compiled but not tested. Program is at 30.7k and I have to get my double PID data converted to float for menwiz.h and then converted back to float for the PID_V1.h.

One question I have: does the new "BACK" menu option need to be in each terminal node?

OK, make it 2 questions: Can it be a menu.addItem instead of menu.addMenu? I'll find out in testing for sure.

 s2=menu.addMenu(MW_VAR,s1,F("Back"));     //add a terminal node (that is "variable"); 
          s2->addVar(MW_ACTION,&bte);                    //create a variable of type "action" 
          s2->setBehaviour(MW_ACTION_CONFIRM,false);         // you don't need action confirmation to emulate Escape button!!!

BACK: yes. as far as you do not want use a button for escape, BACK action should be the last option for each submenu. I can work on Menwiz in order to implemnet an "immediate" mode, that is any change is immediately consolidated, with CONFIRM button the only way to go back. But the code changes are not so easy ...

Unfortunately I can associate an action only to a menu node, not to a list item ... that is memory consuming, I know...

The good new: I found many serial debug tracing statements. That is consuming quite a lot of memory. Deleted. Some minor changes to the code: 180 code lines affected just to spare few bytes ....It' a dirty work, but someone has to do it .... :slight_smile:

I'll have to get back to this. I melted my new I2C display due to a wiring mistake. Another learning opportunity.

The use of custom button navigation using addUsrNav is giving me a bit of a headache. I can't see why it doesn't work.

I'm using MENWIZ Version 1.2.0 on IDE 1.0.1 with a breadboarded 328P and Arduino Uno selected as the Board.

What I am doing is basing all my input on a 4x4 Keypad, theKeypad and Keypad_I2C libraries. When I use my own buttons I just emulate a particular position on the keypad by connecting across the appropriate row/col; i.e I have 3 buttons on my device that emaulate the keys 4, 6 and #. Thus the Keypad library takes care of all my edge triggering and debouncing for me and I get up to 16 buttons sharing teh I2C lines via a PCF8574 expander.

For this menu I am choosing 2-UP, 4-LEFT, 6-RIGHT, 8-DOWN, *-ESCAPE, #-CONFIRM, so my custom function looks for these particular keypresses and returns the approrpiate MW_BTU, MW_BTL, MW_BTR, MW_BTD, MW_BTE and MW_BTC. It returns MW_BTNULL if no key is pressed when scanned.

I removed all the defines for the buttons in my sketch:-

// DEFINE ARDUINO PINS FOR THE MENU NAVIGATION BUTTONS
//#define UP_BOTTON_PIN       16
//#define DOWN_BOTTON_PIN     17
//#define LEFT_BOTTON_PIN     18
//#define RIGHT_BOTTON_PIN    19
//#define CONFIRM_BOTTON_PIN  20
//#define ESCAPE_BOTTON_PIN   21

I commented out the navButtons declaration and added a line for addUsrNav

    //menu.navButtons(UP_BOTTON_PIN,DOWN_BOTTON_PIN,LEFT_BOTTON_PIN,RIGHT_BOTTON_PIN,ESCAPE_BOTTON_PIN,CONFIRM_BOTTON_PIN);
    menu.addUsrNav(menuKeys,6);

I created my new function to read the keypad and just check for the keys we are using for menu items

int menuKeys()
{
  char keyPressed = keypad1.getKey();
  
  if (keyPressed != NO_KEY)
  {
    if (keyPressed == '2')
    {
      return MW_BTU;
    }
    else if (keyPressed == '8')
    {
      return MW_BTD;
    }
    else if (keyPressed == '4')
    {
      return MW_BTL;
    }
    else if (keyPressed == '6')
    {
      return MW_BTR;
    }
    else if (keyPressed == '*')
    {
      return MW_BTE;
    }
    else if (keyPressed == '#')
    {
      return MW_BTC;
    }
    else
    {
    return MW_BTNULL;
    }
  }

I also comment out the BUTTON_SUPPORT define in MENWIZ.h

#define EEPROM_SUPPORT     //uncomment if you want to use the readEeprom and writeEeprom methods!
//#define BUTTON_SUPPORT     //uncomment if you want to use the readEeprom and writeEeprom methods!!

(Incidentally, note the incorrect comment, I found the reference to this define and was looking for it up and down the code, but scanning comments too made it look like this section was just for EEPROM)

From what I've seen in previous posts, this should now work and let me navgate the menu structure with the keypad. I can call the function from loop() and Serial.print the returned values to Serial Monitor and I get the values 2,3,4,5,6,7 returned for U,D,L,R,E,C so the function is working fine, it just seems that MENWIZ isn't calling it.

Full sketch code below

//The full code is in library example file Quick_tour.ino
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <buttons.h>
#include <MENWIZ.h>
#include <EEPROM.h>
#include <Keypad_I2C.h>
#include <Keypad.h>

#define keypad1_addr 0x22
#define lcd1_addr 0x27

// DEFINE ARDUINO PINS FOR THE MENU NAVIGATION BUTTONS
//#define UP_BOTTON_PIN       16
//#define DOWN_BOTTON_PIN     17
//#define LEFT_BOTTON_PIN     18
//#define RIGHT_BOTTON_PIN    19
//#define CONFIRM_BOTTON_PIN  20
//#define ESCAPE_BOTTON_PIN   21

int GAMETIME=30, POINTPERIOD=10, POINTSPERPERIODRED=5, POINTSPERPERIODBLUE=5;
int RESETSCOREPERIOD=0, COUNTDOWNTIMER=3, SPLASHSCREEN=1, WIRELESS = 1, DEBUG = 1;
int LEDBLINK =1, LEDBLINKON = 500, LEDBLINKOFF = 500, CURSORBLINKRATE = 500, BACKLIGHTDELAY = 1;
int list, sp;

// Keypad setup
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the symbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {3, 2, 1, 0}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {7, 6, 5, 4}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad_I2C keypad1( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS, keypad1_addr); 

menwiz menu;
// create lcd obj using LiquidCrystal lib
//LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
LiquidCrystal_I2C lcd(lcd1_addr, 4, 5, 6, 0, 1, 2, 3, 7, NEGATIVE); // addr, EN, RW, RS, D4, D5, D6, D7, Backlight, POLARITY

void setup(){
  _menu *r,*s1,*s2,*s3;
  
  keypad1.begin();

  Serial.begin(115200);    
  menu.begin(&lcd,16,2); //declare lcd object and screen size to menwiz lib
  
  menu.setBehaviour(MW_MENU_INDEX,false); // turn off the menu index - 1/3 etc

  r=menu.addMenu(MW_ROOT,NULL,F("MAIN MENU"));
 
    s1=menu.addMenu(MW_SUBMENU,r, F("General"));
      s2=menu.addMenu(MW_SUBMENU,s1, F("Game Setup"));
        s3=menu.addMenu(MW_VAR,s2, F("Game Time (m)"));  
          s3->addVar(MW_AUTO_INT,&GAMETIME,0,5999,1);
        s3=menu.addMenu(MW_VAR,s2, F("Pts Period (s)"));
          s3->addVar(MW_AUTO_INT,&POINTPERIOD,0,3600,1);
        s3=menu.addMenu(MW_VAR,s2, F("Red Points"));
          s3->addVar(MW_AUTO_INT,&POINTSPERPERIODRED,0,1000,5);
        s3=menu.addMenu(MW_VAR,s2, F("Blue Points"));
          s3->addVar(MW_AUTO_INT,&POINTSPERPERIODBLUE,0,1000,5); // min, max, increment
        s3=menu.addMenu(MW_VAR,s2, F("Reset Period"));  
          s3->addVar(MW_LIST,&RESETSCOREPERIOD);
          s3->addItem(MW_LIST, F("No")); // returns index 0
          s3->addItem(MW_LIST, F("Yes")); // returns index 1
        s3=menu.addMenu(MW_VAR,s2, F("Countdown (s)"));
          s3->addVar(MW_AUTO_INT,&COUNTDOWNTIMER,0,30,1);
      s2=menu.addMenu(MW_SUBMENU,s1, F("Device Setup"));
        s3=menu.addMenu(MW_VAR,s2, F("Splash Screen"));  
          s3->addVar(MW_LIST,&SPLASHSCREEN);
          s3->addItem(MW_LIST, F("Off")); // returns index 0
          s3->addItem(MW_LIST, F("On")); // returns index 1

    s1=menu.addMenu(MW_SUBMENU,r, F("Advanced"));
        s2=menu.addMenu(MW_VAR,s1, F("Wireless"));  
          s2->addVar(MW_LIST,&WIRELESS);
          s2->addItem(MW_LIST, F("Off")); // returns index 0
          s2->addItem(MW_LIST, F("On")); // returns index 1 
        s2=menu.addMenu(MW_SUBMENU,s1, F("LED Control"));
          s3=menu.addMenu(MW_VAR,s2, F("LED Flashing"));  
            s3->addVar(MW_LIST,&LEDBLINK);
            s3->addItem(MW_LIST, F("Off")); // returns index 0
            s3->addItem(MW_LIST, F("On")); // returns index 1
          s3=menu.addMenu(MW_VAR,s2, F("On Time (ms)"));  
            s3->addVar(MW_AUTO_INT,&LEDBLINKON,0,1000,100);
          s3=menu.addMenu(MW_VAR,s2, F("Off Time (ms)"));  
            s3->addVar(MW_AUTO_INT,&LEDBLINKOFF,0,1000,100);
         s2=menu.addMenu(MW_VAR,s1, F("Cursor Flash"));  
           s2->addVar(MW_AUTO_INT,&CURSORBLINKRATE,0,1000,100);
 
    s1=menu.addMenu(MW_SUBMENU,r, F("Developer"));
      s2=menu.addMenu(MW_VAR,s1, F("Serial Debug"));  
        s2->addVar(MW_LIST,&DEBUG);
        s2->addItem(MW_LIST, F("Off")); // returns index 0
        s2->addItem(MW_LIST, F("On")); // returns index 1 

    s1=menu.addMenu(MW_VAR,r, F("SAVE & EXIT"));
      s1->addVar(MW_ACTION,save_settings);
      s1->setBehaviour(MW_ACTION_CONFIRM,false); // turn confirmation off

    s1=menu.addMenu(MW_VAR,r, F("EXIT NO SAVE"));
      s1->addVar(MW_ACTION,exit_menu);
      s1->setBehaviour(MW_ACTION_CONFIRM,false); // turn confirmation off

    //menu.navButtons(UP_BOTTON_PIN,DOWN_BOTTON_PIN,LEFT_BOTTON_PIN,RIGHT_BOTTON_PIN,ESCAPE_BOTTON_PIN,CONFIRM_BOTTON_PIN);
    menu.addUsrNav(menuKeys,6);
      
    Serial.println(menu.freeRam());
  }

void loop()
{
  menu.draw(); 
  
  int key = menuKeys();
 
  if (key != MW_BTNULL)
  {
    Serial.println(key);
  } 
  
  //char customKey = keypad1.getKey();
  
  /* if (customKey != NO_KEY){
    Serial.println(customKey);
    if(customKey == '2')
    {
      Serial.println(F("Did the comparison"));
    }
  } */
}
 
 void save_settings()
 {
     Serial.println(F("SAVE SETTINGS TO EEPROM"));
 }
 
 void exit_menu()
 {
     Serial.println(F("EXIT MENU WITHOUT SAVING TO EEPROM"));
 }
 
void myfunc()
{
  Serial.println(F("ACTION FIRED"));
}

int menuKeys()
{
  char keyPressed = keypad1.getKey();
  
  if (keyPressed != NO_KEY)
  {
    if (keyPressed == '2')
    {
      return MW_BTU;
    }
    else if (keyPressed == '8')
    {
      return MW_BTD;
    }
    else if (keyPressed == '4')
    {
      return MW_BTL;
    }
    else if (keyPressed == '6')
    {
      return MW_BTR;
    }
    else if (keyPressed == '*')
    {
      return MW_BTE;
    }
    else if (keyPressed == '#')
    {
      return MW_BTC;
    }
    else
    {
    return MW_BTNULL;
    }
  }
}

what is happening? The steps seems ok.
first aid: do not call inside loop your function. leave loop with only draw call. I suspect that double call (remember that inside draw method a call to your function is done each loop) to your function could affect the keyboard driver