Go Down

Topic: MENWIZ: yet another character lcd menu wizard library (Read 79 times) previous topic - next topic


Next version  :)
As behaviour of the MW_ROOT _menu

I will be the one who shall wait forever :D


Hi, I plan to use Menwiz for menu input in a temperature control system with two pid processes and 8-10 one-wire sensors.

I will use input via ISR. Here is my test code based on the quick_tour.ino example. I hope this is useful and helps someone.
Code: [Select]
/* Sketch for Arduino/IDE 1.x (quick_tour_rotEnc.ino). Based on MENWIZ quick_tour.ino
   Detent rotary encoder with two buttons and 16x2 LCD example.
   Input signal using interrupts on pin A0/1 for encoder and A2/3 for buttons.
   Based on BenF forum sketch 25may2011 and debounce from delphino-999 forum sketch 19oct2012.
   HW: Roboduino (http://arduino-direct.com/sunshop) and rotary encoder with pushbutton (Panasonic 30 PPR, audiosector, www.ebay.com)
   Desember 2012, Hans Fredrik Sandberg
Pin allocation on Uno
  D4      LCD DB4
  D5      LCD DB5
  D6      LCD DB6
  D7      LCD DB7
  D8      LCD RS 
  D9      LCD Enable
  D10     LCD backlit control
  A0 D14  Rotary A left 1
  A1 D15  Rotary B right 2
  A2 D16  Button 1 confirm 6
  A3 D17  Button 2 escape 5
#include <Wire.h>
#include <LiquidCrystal.h>
#include <buttons.h>
#include <MENWIZ.h>
#include <EEPROM.h>

volatile byte virtualButton = 0;     // which button was pressed
volatile int push[2] = {0,0};        // Variable to pass button press back to main loop
long debouncing_time = 40;           // Debouncing Time in Milliseconds
volatile unsigned long last_micros;  // Variable to keep time while debounce

menwiz menu;
LiquidCrystal lcd( 8,  9, 4, 5, 6, 7, 10, POSITIVE );
//LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Addr, En, Rw, Rs, d4, d5, d6, d7, backlightpin, polarity 4 x 20 LCD
extern byte MW_navbtn;        // needed to run the software key navigation in Menwiz
int  list,setp=110;

void setup()
  PORTC |= _BV(PORTC0) | _BV(PORTC1) | _BV(PORTC2) | _BV(PORTC3);   // enable pullup for pins
  PCMSK1 = _BV(PCINT8) | _BV(PCINT9) | _BV(PCINT10) | _BV(PCINT11); // enable button pin change interrupt A0 A1 A2 A3
  PCICR = _BV(PCIE1);                                               // C-port interrupt enable

  _menu *r,*s1,*s2;
  MW_navbtn=4;  // force 4 or 6 buttons mode for menu navigation -> MW_navbtn=4; or MW_navbtn=6;

    s1=menu.addMenu(MW_SUBMENU,r, F("Node1-submenu"));
      s2=menu.addMenu(MW_VAR,s1, F("Node3-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("Node4-int"));
        s2->setBehaviour(MW_ACTION_CONFIRM,false);  // MW_ACTION_IMMEDIATE described in MENWIZ_0-6-0_QUICK TOUR.pdf 
    s1=menu.addMenu(MW_VAR,r, F("Node2-action"));

  sprintf(menu.sbuf,"Test MENWIZ\nRotEnc SwBtn",1);  // Splash screen shown at startup time
  menu.addSplash((char *) menu.sbuf,4000);           // Shown for msecs
  menu.addUsrScreen(msc,3000);                       // Default screen function shown after msecs since last button push
  digitalWrite(10, HIGH);                            // LCD backlit on

void loop()     
  menu.draw();                    // NAVIGATION MANAGEMENT & DRAWING ON LCD. NOT BLOCKING has to be the first in the void loop
  put your regular code here
  if ( push [0] > 0 ) { // confirm
    virtualButton = 6;
    push[0] = 0;
  if ( push [1] > 0 ) { // escape
    virtualButton = 5;
    push[1] = 0;

ISR(PCINT1_vect) // handle pin change interrupt for A0 to A5  here
  static uint8_t AB = 0x03;     //lookup table index
  static long encval = 0;           //encoder value
  static const int8_t enc_states[] =   {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
  static uint8_t btn = _BV(PINC2) | _BV(PINC3);
  uint8_t t = PINC;  // read port status

  // check for rotary2 state change on pin A0 A1
  AB <<= 2;                  // save previous state
  AB |= ( t & 0x03 );       // add current state, from lower port pin pair
  encval += enc_states[AB & 0x0f];
  if( encval > 1 ) {         // step forward
    virtualButton = 2; encval = 0;
  else if( encval < -1 ) {   // step backwards
    virtualButton = 1; encval = 0;

  // check if buttons are pushed
  t &= _BV(PINC2) | _BV(PINC3); // A2 A3
  if (t != btn) {  // activity on pins, we're only interested in high to low transitions
    if((long)(micros() - last_micros) >= debouncing_time * 1000) { 
      push[0] |= !(t & _BV(PINC2));
      push[1] |= !(t & _BV(PINC3));
      btn = t;
      last_micros = micros();  // start/restart bounce guard timer (covers make and break)

void myfunc(){Serial.println("ACTION FIRED");}

int navMenu() // called from menu.draw
  switch (virtualButton)
  case 5: // Escape
    virtualButton = 0;
    return MW_BTE;
  case 6: // Confirm
    virtualButton = 0;
    return MW_BTC;
  case 1: // Up
    virtualButton = 0;
    return MW_BTU;
  case 2: // Down
    virtualButton = 0;
    return MW_BTD;
  case 0: // no button
    return MW_BTNULL;

void escapeFunct() {
  Serial.println("Back Called");
  virtualButton = 5;  //simulate escape button press, for use in some menus

// user defined default screen.
void msc()

I will gladly do testing of new library version 1.1.0 beta!

I have downloaded the zip from 18des. Will these steps do as verifiation of the new library? (I have never done this before, that is why so detailed...)

  • Stop the IDE

  • Move directory C:\arduino-1.0.2\libraries\MENWIZ to another place

  • Create a new directory C:\arduino-1.0.2\libraries\MENWIZ.

  • Copy the two new files MENWIZ.cpp and MENWIZ.h

  • Start the IDE, open sketch and upload to board

  • Test all the menu posibilities with the four button actions

Another question: Is the version noted somewere inside the files?


Hi brunitali,
How can we load the variable from EEPROM automatically after powering up of Arduino..i tried loadvar(); at the end of Setup(); This trick load the variable parameters but when i enter into Basic Parameters, the last parameters description always double..
Basic Parameters:

You see the -Farooq double.. why?
Anyway, to load the parameters from EEPROM at startup so that programm can run automatically based on those values and user not to Load the parameters manually.


Dec 23, 2012, 10:21 am Last Edit: Dec 23, 2012, 11:26 am by brunialti Reason: 1
The steps are:
- create a library directory anywhere (e.g. c:/mysketches)
- enter the ide menu File/Preferences and enter the c:/mysketches as default sketches directory
- create the  libraries MENWIZ subdirectory directory inside c:/mysketches
- move into MENWIZ the MENWIZ.h, MENWIZ.cpp, keywords.txt files and the examples directory
- start IDE again

WARNING: addUsrNav requires now two parameters, and it is not any more necessary to manually force MW_navbtn parameter.

The 1.1.0 is available on github.
I suggest to subscribe for notificazion on github, so you'll b enotified as new versione will be available (1.2.0 quite soon).

EDIT: in the 1.1.0 MENWIZ  the version is available calling menu.getVer(). In version 1.2.0 the getVer will be stripped off the menwiz class and will became a pseudofunction (a function declared via a #define).

The setup invocation should be correct. I  cannot undestand exactly what do you mean with  doubling parameter description... do you mean you find a two lines in the menus instead of one? let me see the setup code


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


I shall soon make the video and post..Youtube service is unavailable in my country..
However, my question is:
How can we load the variable from EEPROM at the start of program?


Dec 23, 2012, 12:08 pm Last Edit: Dec 23, 2012, 12:16 pm by Khalid Reason: 1

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

Just see i called the loadvar(); in the end of setup after creations of all menuobjects. Which causes duplication in collapsed menu.

Code: [Select]
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <buttons.h>
#include <MENWIZ.h>
#include <EEPROM.h>    // to be included only if defined EEPROM_SUPPORT

#define UP_BUTTON_PIN       9
#define DOWN_BUTTON_PIN     10
#define LEFT_BUTTON_PIN     7
#define RIGHT_BUTTON_PIN    8
#define ESCAPE_BUTTON_PIN   11

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

//instantiate global variables to bind to menu
int      tp=0;
float    f=26.0;
boolean  bb=0;
byte     b=50;

void setup(){
 _menu *r,*s1,*s2;
 _var *v;
 int  mem;

 // have a look on memory before menu creation
 // inizialize the menu object (20 colums x 4 rows)

 //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("MEASURE SUBMENU"));     //add a child (submenu) node to the root menu
     s2=menu.addMenu(MW_VAR,s1,F("list"));            //add a terminal node (that is "variable");
         s2->addVar(MW_LIST,&tp);                          //create a variable of type "option list"..
         s2->addItem(MW_LIST,F("option 1"));               //add option to the OPTION LIST
         s2->addItem(MW_LIST,F("option 2"));               //add option to the OPTION LIST
         s2->addItem(MW_LIST,F("option 3"));               //add option to the OPTION LIST
         s2->addItem(MW_LIST,F("option 4"));               //add option to the OPTION LIST
         s2->addItem(MW_LIST,F("option 5"));               //add option to the OPTION LIST
//          s2->setBehaviour(MW_SCROLL_HORIZONTAL,true);    
//          s2->setBehaviour(MW_LIST_2COLUMNS,true);          
//          s2->setBehaviour(MW_LIST_3COLUMNS,true);          

     s2=menu.addMenu(MW_VAR,s1,F("float var"));       //add a terminal node (that is "variable");
         s2->addVar(MW_AUTO_FLOAT,&f,11.00,100.00,0.5); //create a variable of type "float number"...
                                                        //...associated to the terminal node and bind it to the app variable "f" of type float
     s2=menu.addMenu(MW_VAR,s1,F("byte var"));        //add a terminal node (that is "variable");
         s2->addVar(MW_AUTO_BYTE,&b,25,254,10);         //create a variable of type "byte"...
                                                        //...associated to the terminal node and bind it to the app variable "b" of typr byte
     s2=menu.addMenu(MW_VAR,s1,F("boolean var"));     //add a terminal node (that is "variable");
         s2->addVar(MW_BOOLEAN,&bb);                    //create a variable of type "boolean"
                                                        //...associated to the terminal node and bind it to the app variable "bb" of type boolean
   s1=menu.addMenu(MW_VAR,r,F("WRITE TO SERIAL"));             //add a terminal node (that is "variable") create an "action" associated to the terminal node...
     s1->addVar(MW_ACTION,act);                         //the act function as default will be called when enter button is pushed
//      s1->setBehaviour(MW_ACTION_CONFIRM,false);         //...if you don't need action confirmation

   s1=menu.addMenu(MW_VAR,r,F("SAVE TO EPROM"));           //add a terminal node (that is "variable") create an "action" associated to the terminal node...
     s1->addVar(MW_ACTION,savevar);                     //the act function as default will be called when enter button is pushed

   s1=menu.addMenu(MW_VAR,r,F("LOAD FROM EEPROM"));        //add a terminal node (that is "variable") create an "action" associated to the terminal node...
     s1->addVar(MW_ACTION,loadvar);                     //the act function as default will be called when enter button is pushed

 //declare navigation buttons (required)

 //(optional)create a user define screen callback to activate after 10 secs (10.000 millis) since last button push

 //(optional) create a splash screen (duration 5.000 millis)with some usefull infos the character \n marks end of LCD line
 //(tip): use preallocated internal menu.sbuf buffer to save memory space!
//  sprintf(menu.sbuf,"MENWIZ TEST V %s\n.Free mem. :%d\n.Used mem  :%d\n.Lap secs  :%d",menu.getVer(),menu.freeRam(),mem-menu.freeRam(),5);
//  menu.addSplash((char *) menu.sbuf, 5000);

void loop(){

// user defined callbacks
// WARNING avoid sprintf usage: it requires > 1.5 kbytes of memory!
void msc(){
 static  char buf[7];
 strcpy(menu.sbuf,"User screen"); //1st lcd line
 strcat(menu.sbuf,"\nUptime (s): ");strcat(menu.sbuf,itoa((int)(millis()/1000),buf,10));//2nd lcd line
 strcat(menu.sbuf,"\nFree mem  : ");strcat(menu.sbuf,itoa((int)menu.freeRam(),buf,10));//3rd lcd line
 strcat(menu.sbuf,"\n"); //4th lcd line (empty)
void act(){
 Serial.println("FIRED ACTION!");

void savevar(){
void loadvar(){

if i comment the loadvar(); function duplication not happen...

for close lookup, just see the following sketch where i loaded the EEPROM in void Setup();
Code: [Select]
    s1=menu.addMenu(MW_VAR,r,F("LOAD FROM EEPROM"));        //add a terminal node (that is "variable") create an "action" associated to the terminal node...
      s1->addVar(MW_ACTION,loadvar);                     //the act function as default will be called when enter button is pushed

  //declare navigation buttons (required)
//Loading variable from EEPROM


Hi again,
I want to display the byte variable on LCD in void msc();
How can i convert byte to string?

You posted itoa for integer conversion to string , do you help me in byte to string?



Khalid, this is not the place where to post general questions about c language and programming.
There are other threads and places where you can get help about that.
By the way the itoa works fine also with byte, without casting.

  byte a=78;
  char buf[4];
  Serial.print(itoa(a, buf,10)); 


Dec 24, 2012, 07:38 pm Last Edit: Dec 24, 2012, 07:50 pm by brunialti Reason: 1
some bugs found in the 1.1.0 in boundary conditions (e.g. using some behaviours with root menus). Neverthless I could'nt replicate the loadvar error. As I worked to the 1.2.0 please use it (here attached in beta). Any test is wellcome. This is the last version (this year  8))

Solved bugs
Solved few bug occourring when a root menu is declared as collapsed menu in ver 1.1.0 .
Internal  changes
It is possible to disable button.h support in order to save space.
New and modified functions
New behaviours added:  MW_MENU_INDEX (applies to objects of class menwiz).
The getVer() function is now declared as #define pseudofunction, outside menwiz class.


Dec 24, 2012, 08:19 pm Last Edit: Dec 24, 2012, 08:29 pm by Khalid Reason: 1
Thanks brunialti Roberto,
I shall check and tell you tommorrow..
I am stuck here , i have defined M_PWM as a byte variable. This store motor PWM value in the range 0~100 in the EEPROM. I was unable to make user screen in MENWIZ.. you can see following code, especially below the  //2nd LCD Line:
Code: [Select]
void msc(){
 static  char buf[7];
 int Diff=0;
 //1st lcd line
 strcpy(menu.sbuf,"E:"); strcat(menu.sbuf,itoa((int)(READ_RIGHT_LDR),buf,10)); strcat(menu.sbuf,"W:"); strcat(menu.sbuf,itoa((int)(READ_LEFT_LDR),buf,10));  //integer variable READ_RIGHT_LDR and READ_LEFT_LDR getting values from Analog pins (1~1023)
  else {
 strcat(menu.sbuf,"Diff:"); strcat(menu.sbuf,itoa((int)(Diff),buf,10));
 //2nd LCD Line
 strcat(menu.sbuf,"\nMpwm:");strcat(menu.sbuf,itoa(M_PWM,buf,10));  //M_PWM is a byte range any value  from 0 to 100. Here i am stuck....how can i use strcat??

 int seconds = (int) ( CountdownTime/ 1000) % 60 ;  //CountdownTime is unsigned long  and a global variable suppose its getting values from Millis()
 int minutes = (int) ((CountdownTime / (1000*60)) % 60);
 strcat(menu.sbuf," CNTD:");strcat(menu.sbuf,itoa((int)(minutes),buf,10)); strcat(menu.sbuf,":");strcat(menu.sbuf,itoa((int)(seconds),buf,10));
 //3rd LCD Line
 strcat(menu.sbuf,"\nSamplingTime: ");strcat(menu.sbuf,itoa((int)(SampleTime),buf,10));//SampleTime is integer variable
   //4th LCDLine
 strcat(menu.sbuf,"\nSensitivity: ");strcat(menu.sbuf,itoa((int)(Sensitivity),buf,10));//Sensitivity is also integer


I have not enough information, Anyway the following code (extracted by your example) works correctly.
Check actual values of your variable in msc....

Code: [Select]
byte M_PWM=78; //is it correc?t

void setup(){
 static  char buf[7],sbuf[25];
 int Diff=0;
 //2nd LCD Line
 strcat(sbuf,"Mpwm:");  strcat(sbuf,itoa(M_PWM,buf,10));  
void loop() {}


Dec 25, 2012, 06:18 am Last Edit: Dec 25, 2012, 07:53 am by Khalid Reason: 1
Okay Brunalti,
I tested the new functionalities and here is my review:
1- Menu index (MW_MENU_INDEX) appears on the LCD screen and looks good
2- The new library function good when serial.println function is not in use in the USER DISPLAY SCREEN.
1- The new library is unstable, if the serial monitor is ON and Serial.Println function working in the USER DISPLAY SCREEN:

 a)I have 04 nodes in the MAIN MENU,  If the Serial.Println(); i.e. serial communication is on then when i reach to 4/4, the arduino reset itself and shows weired characters on LCD. It always reset..

Just see the MenuStructure:
 Basic Parameters
 Move East

   Basic Parameters have terminal nodes like following
   Basic Parameters
        Manual   (This is List of 5)
        Motor PWM

b) if the serial communication is on, When i go to Node 3 of the Menu (Save), and press the confirm button, the next screen appears but all things are mixed up and nothing happen.
c)if the serial communication is on, I can't go to Node-4 (Load) of the menu, the LCD shows weired character and hang..

Previously all working fine for me....



I disabled the Serial function in USER DISPLAY SCREEN and all things are working fine to me. Have few questions:
1- How can i display the following two cutom characters on my USER DISPLAY SCREEN:
Code: [Select]
byte  Moon[8] = {

byte Sun[8] = {

2- How can i increase the delay time in USER DISPLAY SCREEN. actually my analog sensors readings are too fast on USER SCREEN unable to read.


Dec 25, 2012, 10:43 am Last Edit: Dec 25, 2012, 10:54 am by brunialti Reason: 1
About Serial problems in user screen It is obscure for me. I'll try to replicate. Maybe serial connection introduces a delay time and/or has conflicts with your current arduino attached sensors/devices.

About characters, you can use createChar (this is not a MENWIZ char, it is a LiquidCrystal method instead, look at menwiz.cpp). Be carefull: it may conflict with MEWNWIZ, as it also create characters. Use char indexes higher than 3.

The user display elapsed time (the idle time before user screen appears) is the last parameter of addusrscreen. But perhaps I did not uderstand your problem.
If you refer to rapidly changes to values in user screen ... there are no  ways other than to introduce a delay ... but it will slow down the interface responsivity. Try values around max 100 millis ... It sound quite strange as in my experience sensors values never changed at such a dramatic pace to let them unreadable on a LCD ... You can also and preferably set a user software implemented delay time before refresh values: store the latest read time and let the value be refreshed after that value (I suggest 500 millis).

Go Up