MENWIZ: yet another character lcd menu wizard library

@Khalid,
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.

MENWIZ.zip (159 KB)

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:

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)
  if (READ_RIGHT_LDR>READ_LEFT_LDR)
  {
    Diff=READ_RIGHT_LDR-READ_LEFT_LDR;
   }
   else {
     Diff=READ_LEFT_LDR-READ_RIGHT_LDR;
     }   
  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??
 
 // CALCULATING MINUTES AND SECOND
  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
  menu.drawUsrScreen(menu.sbuf);
  }

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

byte M_PWM=78; //is it correc?t

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

Okay Brunalti,
I tested the new functionalities and here is my review:
GOOD THING:
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.
BAD THING:
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:
MAIN MENU
Basic Parameters
Move East
Save
Load

Basic Parameters have terminal nodes like following
Basic Parameters
Manual (This is List of 5)
Motor PWM
Sensitivity
NightRetHr
RTimeSec
LightPoint
SampleTimein

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....

Regards

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:

//MOON
byte  Moon[8] = {
	B00100,
	B01000,
	B11000,
	B11001,
	B11001,
	B11111,
	B01110,
	B00000
};

byte Sun[8] = {
	B00000,
	B00100,
	B10101,
	B01010,
	B11111,
	B11111,
	B11111,
	B11111
};

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.

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).

brunialti:
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.

Okay,
First i defined this in Setup();

byte  Moon[8] = {
	B00100,
	B01000,
	B11000,
	B11001,
	B11001,
	B11111,
	B01110,
	B00000
};

then i used in the Setup();

 lcd.createChar(6, Moon);

and after that how can i use this character in user screen in the following function, last 4th line of LCD??

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)
  menu.drawUsrScreen(menu.sbuf);
  }
  */

Please can you help me.

brunialti:
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

Yes, i introduced 250millis delay and its work fine. Thank you:)

brunialti:
... 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).

Actually i am making Sun Tracking System for the Solar Panel.. The sensors are East and West LDRs.. Now all is okay..
I am very very satisfied with your MENWIZ.. you are great programmer...

I think MENWIZ is one of the complete and simple MENU wizard for Arduino platform. It should be the part of Arduino distributable. I really like the PDF tutorial of the menu. Thank you brunitali.. I hope you shall keep improving this MENWIZ...

to display user defined characters yo have to use the write method:
lcd.write(6);

p.s. version 1.2.0 is now available on github

Unable to write the user definable character on the USER DISPLAY SCREEN... 8)

if you use drawUsrScreen, it overwrites the screen with space padding. In this case you have to insert the character into the buffer you pass to drawUsrScreen with explicit assignment. something like the following.

menu.sbuf[...]=6;

If you do not use drawUsrScreen the write statement should work fine.

Thank You Brunialti,
i want tp write the character in drawUsrScreen ,Can you please provide a small demo example?.. Actually i am unable to do.. I made a small video on previous MENWIZ 1.1 version here its link. My sketch compilation size exceeded 22Kb... Still need to implement the code of the dual axis , i hope i will be able to complete within the 32K limit...

Following is a small video of MENWIZ:

Nice video!
this should print your char after the letter V of the first line. You have to put the code chunks at the proper place...

byte  Moon[8] = {
	B00100,
	B01000,
	B11000,
	B11001,
	B11001,
	B11111,
	B01110,
	B00000
};
.....
lcd.createChar(6, Moon);
.....
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.sbuf[13]=6; 
menu.addSplash((char *) menu.sbuf, 5000);

wow..Thank You...thank you for helping me and thank you for the MENWIZ:).. i shall make some more videos and post here... I am currently making controller for the suntracker..once completed in a good box, i shall make the working video.

Okay,
The symbol of Moon is working now:)..but as the parameters changes it changes it location... Now i want it to fixed place at the end of the buffer. Can we measure the size of buffer sbuf?

EDIT:
If you tell me a method then its good..else the sun symbol moving back and forth looks good:)

khalid, you can count...
use strlen and put the character at the returned position-1

okay got it:

int x=strlen(menu.sbuf);
then

i will place the Moon at x-1.
Regards

nice work khalid.
I suggest to submit to arduino blog.

brunialti:
nice work khalid.
I suggest to submit to arduino blog.

Sure, i shall put that once it complete...

Ciao a tutti,
I am a very beginner (it's my first week on Arduino), no experience in programming, I already read the guide and the book "Getting Started With Arduino 2ndEd" ..... but .... it's still a long way ....
Anyway, what I am trying to do is to make an actuator with a stepper (and a relative mechanical transmission) to move the height of a the saw in a table saw. I need to set the height to a certain amount of mm, or to move of a certain amount on mm.
Now my starting problem is to use Menwiz with the classic LCD Keypad Shield (16x2) that have 5 analog buttons (it's perfectly ok to use only 4).
(From this tutorial which is perfecly working I took some setup:

http://tutorial.cytron.com.my/2011/08/12/project-1-“hello-world”-on-lcd/)

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //this is the working pins

[sup]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 //this is what I replaced
[/sup]

and from Backbone post « Reply #188 on: September 27, 2012, 07:17:31 PM » I took his example changing the button parameters:

// +++++++ Libaries included
#include <Wire.h>
#include <LiquidCrystal.h>
//INSERT ALL THE LIBARIES AFTER INCLUDING WIRE LIB (MENWIZ request)
#include <LiquidCrystal_I2C.h>
#include <buttons.h>
#include <MENWIZ.h>
#include <EEPROM.h>

// Create global object for menu and lcd
menwiz menu;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
/*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
*/
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

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

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("SET1"));                        //add a submenu node 1 to the root menu
  s2=menu.addMenu(MW_VAR,s1,F("MD"));                         //add a terminal node in the menu tree (that is "variable");
  s2=menu.addMenu(MW_VAR,s1,F("BR"));                         //add a terminal node in the menu tree (that is "variable");
  s2=menu.addMenu(MW_VAR,s1,F("RH"));                         //add a terminal node in the menu tree (that is "variable");

  //---------------
  s1=menu.addMenu(MW_SUBMENU,r,F("SET2"));                    //add a submenu node 2 to the root menu
  s2=menu.addMenu(MW_VAR,s1,F("CP"));                         //add a terminal node in the menu tree (that is "variable");
  s2=menu.addMenu(MW_VAR,s1,F("DB"));                         //add a terminal node in the menu tree (that is "variable");
  s2=menu.addMenu(MW_VAR,s1,F("SQ"));                         //add a terminal node in the menu tree (that is "variable");

  //---------------
  s1=menu.addMenu(MW_SUBMENU,r,F("SET3"));                    //add a submenu node 3 to the root menu
  s2=menu.addMenu(MW_VAR,s1,F("RS"));                         //add a terminal node in the menu tree (that is "variable");
  s2=menu.addMenu(MW_VAR,s1,F("RCALIB"));                     //add a terminal node in the menu tree (that is "variable");
  s2=menu.addMenu(MW_VAR,s1,F("SCALIB"));                     //add a terminal node in the menu tree (that is "variable");

/*
  //++++++ 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
   */
  readButtons();
}

//  ++++++ 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 >= 700)
  {
    buttonPressed = 0;
    noButtonPressed(); // is calling an extra fucntion
  }
  else  if(buttonValue >= 470 & buttonValue <= 600)
  {
    buttonPressed = 4;
    buttonAct = 1; // set the menu flag1
  }
  else if(buttonValue >= 300 & buttonValue <=420)
  {    
    buttonPressed = 3;
    buttonAct = 1; // set the menu flag1
  }
  else if(buttonValue >= 100 & buttonValue <=200)
  {    
    buttonPressed = 1;
    buttonAct = 1; // set the menu flag1
  }
  else if(buttonValue >= 10 & buttonValue <=50)
  {    
    buttonPressed = 2;
    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  + 2000; //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);
}

that was the code for the buttons on the "helloWorld" example (it's a completely different approach from the Backbone one:

int analogPin = A0;
int adc_key_old;
int adc_key_in;
int NUM_KEYS = 5;
int key=-1;
int adc_key_val[5] ={30, 150, 360, 535, 760 };  

//omitted some functions , then ...

int get_key(unsigned int input)
{
	int k;
    
	for (k = 0; k < NUM_KEYS; k++)
	{
		if (input < adc_key_val[k])
		{
           
    return k;
        }
	}
    
    if (k >= NUM_KEYS)
        k = -1;     // No valid key pressed
    
    return k;
}

I am not asking to check all the code, but can somebody check if I did some noticeable error in manipulating the code ?
Thanks from a beginner.
Sergio

I'm planning on having a play with this as it could be the solution to a manu system we need to provide user settings saved to EEPROM.

Reading through it seems that it can use a 4 button or 6 button set up.

6 Buttons - Up, Down, Left, Right, Select, Back
4 Buttons - Up, DOwn, Select, Back

What about 5 Button option, like what you'd have on some default shields:-

5 Buttons - Up, Down, Left, Right, Select

This doesn't have a specific button for the Back option, but couldn't the library incorporate a visual replacement. When in Menu's or on items you'd have a last option of 'BACK' which you just Select as with any other.

Actually, I guess this could also provide you a 3 button option too, along the lines of your existing 4 button plus the 'BACK' display for selection.