MENWIZ: yet another character lcd menu wizard library

sorry, no serial simulation available.
as the lib displays for each draw call, it could produce a lot of serial output.
As far as i understand that library has e different implementation

Hi all,

Looking for a nice and simple library I have found MENWIZ and hence would like to test it for my project. Unfortunately I face some issues setting up my LCD. Currently I still use the included LyquidCrystal library with my DFRobot LCD Shield.

This is what I use for initialization:

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

with

  • 8 for RS
  • 9 for E
  • 4 for D4
  • 5 for D5
  • 6 for D6
  • 7 for D7

What would be the equivalent for the New LiquidCrystal library?

LiquidCrystal_I2C lcd(???);

Do I need to remove the LyquidCrystal library from my libraries folder or can they coexist?

Many thanks in advance!

I have the same shield I think and use LyquidCrystal with no problems.
what is your problem ?

Hi,

Sorry if that was unclear.

At the moment I am using the standard LiquidCrystal library with the following statement:

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

In the MENWIZ quicktour example the standard LiquidCrystal library is replaced with the New LiquidCrystal library from https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home

The statement for initialization in the example looks as follows:

LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3,POSITIVE);

My question now is, how should the statement look like for my LCD?

Aufruf,
I use exacly your first statement an the calssic Liquidcrystal library:

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

You don't need to use LiquidCrystal_I2C to use MENWIZ.
I use flawlessly MENWIZ menu and library.
the olnly thing I did is to change from digital buttons to analog buttons.

Unfortunately I can't get it to work properly. Here is my code:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <MENWIZ.h>
#include <EEPROM.h>


LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

menwiz tree;

int  list, sp=110;


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


void setup(){
	
	//_menu *r,*s1,*s2;
	
	tree.begin(&lcd, 16, 2);
	
	tree.addSplash("Library running", 2000);
}


void loop(){
	
  tree.draw();
}

But I get the following error message:

In file included from Splash.ino:3:
/Users/MyName/Documents/Arduino/libraries/MENWIZ/MENWIZ.h:214: error: ISO C++ forbids declaration of 'LCD' with no type
/Users/MyName/Documents/Arduino/libraries/MENWIZ/MENWIZ.h:214: error: expected ';' before '*' token

Would it be possible to fix that if I would you the new LiquidCrystal library? If yes, I would need to ask the same question as before as to how my statement for initialization would need to look like :slight_smile:

Many thanks for your help!

I this:

  menu.begin(&lcd,16,2);              //initialize the menwiz menu object passing the lcd object and the colums and rows params

put it after:
_menu *r,*s1,*s2,*s3;

In this thread at post 316 there is my code that is working on your hardware ....

Re: MENWIZ: yet another character lcd menu wizard library
« Reply #316 on: January 09, 2013, 09:59:40 PM »

(is on page 22)
you can try it then start to modify it for your convenience you will have the buttons working too ...

It looks to me like you need to uncomment that _menu line and change it to _tree :-

#include <Wire.h>
#include <LiquidCrystal.h>
#include <MENWIZ.h>
#include <EEPROM.h>


LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

menwiz tree;

int  list, sp=110;


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


void setup(){
	
	_tree *r,*s1,*s2; // <<<<<<<<<<<<<<<<<< CHANGE HERE <<<<<<<<<<<<<<<<<
	
	tree.begin(&lcd, 16, 2);
	
	tree.addSplash("Library running", 2000);
}


void loop(){
	
  tree.draw();
}

Silverdog63:
In this thread at post 316 there is my code that is working on your hardware ....

Re: MENWIZ: yet another character lcd menu wizard library
« Reply #316 on: January 09, 2013, 09:59:40 PM »

(is on page 22)
you can try it then start to modify it for your convenience you will have the buttons working too ...

Thanks for the offer. I have copied the code over but still get the same error message :frowning:

In file included from Splash.cpp:7:
/Users/MyName/Documents/Arduino/libraries/MENWIZ/MENWIZ.h:214: error: ISO C++ forbids declaration of 'LCD' with no type
/Users/MyName/Documents/Arduino/libraries/MENWIZ/MENWIZ.h:214: error: expected ';' before '*' token

In order to let MENWIZ manage different lcd interfaces the internal variable holding the lcd object pointer is of type LCD.
LCD is a base class of new liquidCrystal, alllowing to "virtualize" the interface, that is to work with all the supported interfaces.
That is the reason why new liquidCrystal is needed.

Liquid crystal is a collection of classes based on the virtual base class LCD. Each subclass implement a different interface: LiquidCrystal_I2C, LiquidCrystal_SR, LiquidCrystal_SR2,LiquidCrystal_SR2W. To use one interface all you need to do is to change the name of the library directory matching that of the desired interface class. Arduino IDE will use the proper source file andcompile it.

So I'm quite confident liquidCrystal is able to drive your LCD. Unfortunately I cannot tell the proper constructor statement as I have not the device.

You could use an other "external" liquidCrystal library with small changes, in your case, in MENWIZ.H change:
#define MW_LCD LCD to #define MW_LCD LiquidCrystal
#include <LCD.h> to #include <LiquidCrystal.h>

If I'm not forgetting something, it should work. Again, I suggest to use new LiquidCrystal instead.

I have deinstalled everything and reinstalled afterwards. I don't know what it made not working but now it works - oh dear :slight_smile:

Many thanks for your help and patience.

Cheers!

Hi there,

Got another problem which is that the input seems to be too fast so that I flip through the menu without the possibility to navigate one by one.

This is my code:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <MENWIZ.h>
#include <EEPROM.h>

const int btnNONE 	=	0;
const int btnUP    	= 	1;
const int btnDOWN  	= 	2;
const int btnLEFT  	= 	3;
const int btnRIGHT 	= 	4;
const int btnSELECT = 	5;

menwiz menu;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

int adc_key_in = 0;

int buttonPressed = 0;
int lastButtonPressed = 0;

long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 100;    // the debounce time; increase if the output flickers

int list,sp = 0;

extern byte MW_navbtn;


void setup() {
	
	_menu *r,*s1,*s2;

	menu.begin(&lcd,16,2);
	menu.addUsrNav(navMenu,4);
	MW_navbtn=4;
	
	r=menu.addMenu(MW_ROOT,NULL,F("Root"));
	s1=menu.addMenu(MW_SUBMENU,r, F("Node1"));
		s2=menu.addMenu(MW_VAR,s1, F("Node3"));
			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("Node4"));
			s2->addVar(MW_AUTO_INT,&sp,0,120,10);
			
	menu.addSplash("LiPanCo\n2012 v01\n", 2000);
	menu.addUsrScreen(&myMenuCall, 5000);
}

void loop() {

  menu.draw();
}

void myMenuCall() {
 sprintf(menu.sbuf,"Uptime (s): %ld\nFree mem  : %d\n\n",millis()/1000,(int)menu.freeRam());
  menu.drawUsrScreen(menu.sbuf); 
  
}


int read_LCD_buttons() {
	
	adc_key_in = analogRead(0);

	if (adc_key_in < 50)   return btnRIGHT;  
	if (adc_key_in < 200)  return btnUP; 
	if (adc_key_in < 400)  return btnDOWN; 
	if (adc_key_in < 600)  return btnLEFT; 
	if (adc_key_in < 800)  return btnSELECT;   
	if (adc_key_in > 1000) return btnNONE;
	
	return btnNONE;
}

int navMenu() {
	buttonPressed = read_LCD_buttons();
	
	if (buttonPressed != lastButtonPressed) {
		
		lastDebounceTime = millis();
	}
	
	if ((millis() - lastDebounceTime) > debounceDelay) {
			
    	switch (buttonPressed) {
    		case btnUP:
      			return MW_BTU;
      			break;
			case btnDOWN:
	      		return MW_BTD;
	      		break;
    		case btnSELECT:
      			return MW_BTC;
      			break;
	    	case btnLEFT:
	      		return MW_BTL;
	      		break;
		   	case btnRIGHT:
				return MW_BTR;
				break;
			case btnNONE:
	      		return MW_BTNULL;
	      		break;
    	}
	}
	
	lastButtonPressed = buttonPressed;  
}

I would have expected that

if (buttonPressed != lastButtonPressed) {
		
		lastDebounceTime = millis();
	}
	
	if ((millis() - lastDebounceTime) > debounceDelay) {

...

prevents this as it does in other sketches but it doesn't seem to work here. Any ideas?

Many thanks for any help in advance. Cheers!

	if (adc_key_in < 50)   return btnRIGHT;  
	if (adc_key_in < 200)  return btnUP; 
	if (adc_key_in < 400)  return btnDOWN; 
	if (adc_key_in < 600)  return btnLEFT; 
	if (adc_key_in < 800)  return btnSELECT;

Uhmmm ...
As far I understant ... you have:
if pressed button brnRIGHT true all the statement below, because ex.30 is <50<200<400<600<800 ....
I think you shoud put something like:

     	if (60 <adc_key_in < 200)  return btnUP;
        if (210< adc_key_in < 400)  return btnDOWN;

ecc. isn'it ?

If I debug my code with Serialprints it is giving me the correct button.

If I put my code like this:

int read_LCD_buttons() {
	
	adc_key_in = analogRead(0);

        if (adc_key_in < 50)   return btnRIGHT;  
	if (adc_key_in < 200)  return btnUP; 
	if (adc_key_in < 400)  return btnDOWN; 
	if (adc_key_in < 600)  return btnLEFT; 
	if (adc_key_in < 800)  return btnSELECT;
	
	return btnNONE;
}

int navMenu() {
	
	buttonPressed = read_LCD_buttons();
	
	if(buttonPressed != lastButtonPressed) {
			
    	switch (buttonPressed) {
    		case btnUP:
      			return MW_BTU;
      			break;
		case btnDOWN:
	      		return MW_BTD;
	      		break;
    		case btnSELECT:
      			return MW_BTC;
      			break;
	    	case btnLEFT:
	      		return MW_BTL;
	      		break;
		   case btnRIGHT:
			return MW_BTR;
			break;
		case btnNONE:
	      		return MW_BTNULL;
	      		break;
    	}
	}
	
	lastButtonPressed = buttonPressed;  
}

I would have thought, that if(buttonPressed != lastButtonPressed) { ensures that a button can't be pressed more than once, can it?

Hi, brunialti. With reference to your request in post #302 here is an example of addUsrNav. You are free to use this if you find it beneficial.

MENWIZ is controlled via addUsrNav call to function navMenu. This function check the value of "virtualButton". Values of virtualButton is set in one of two ISR functions. The reason to use ISR is that both pid calculation and temperature sensor communication is time consuming, and I was worried that button presses would be missed.

One ISR is handling the rotary encoder (up/down) and the other ISR two push buttons (confirm/escape).

Please ask if anything is unclear.

(only fragment of code is pasted due to 9500 char limit... Se full .ino below.)

ISR(PCINT0_vect) // handle pin change interrupt for PCINT0 to PCINT7 here, pin D53-D50 and D10-D13
{
  static uint8_t btn = _BV(PB5) | _BV(PB6);
  uint8_t t = PINB;  // read port status

  // check if buttons are pushed
  t &= _BV(PB6) | _BV(PB5); // D11 D12
  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(PB5));
      push[1] |= !(t & _BV(PB6));
      btn = t;
      last_micros = micros();  // start/restart bounce guard timer (covers make and break)
    }
 }  
}

ISR(PCINT2_vect) // handle pin change interrupt for PCINT 16 to PCINT23 here, pin D62-D69/A8-A15
{
  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};
  uint8_t t = PINK;          // read port status

  // check for rotary2 state change on pin D5 D6
  AB <<= 2;                  // save previous state
  AB |= (t >> 5) & 0x03;     // add current state
  encval += enc_states[AB & 0x0f];
  if( encval > 1 ) {         // step forward
    virtualButton = 2; encval = 0;
  }
  else if( encval < -1 ) {   // step backwards
    virtualButton = 1; encval = 0;
  }
}
int navMenu() // called from menu.draw 
{ 
  switch (virtualButton) 
  {
  case 5: // Escape
    virtualButton = 0;
    return MW_BTE;
    break;
  case 6: // Confirm
    virtualButton = 0;
    return MW_BTC;
    break;
  case 1: // Up
    virtualButton = 0;
    return MW_BTU;
    break; 
  case 2: // Down
    virtualButton = 0;
    return MW_BTD;
    break;
  case 0: // no button
    return MW_BTNULL;
    break;
  }
}
void setup() {
  Serial.begin(9600);
  _menu *r,*s1,*s2;
  menu.begin(&lcd,20,4);
  menu.addUsrNav(navMenu,4);
  r=menu.addMenu(MW_ROOT,NULL,F("BC Main menu"));
    s1=menu.addMenu(MW_VAR,r, F("Set BC On/Off"));
      s1->addVar(MW_BOOLEAN,&running);
    s1=menu.addMenu(MW_VAR,r, F("Set pump On/Off"));
      s1->addVar(MW_BOOLEAN,&mashPump);
    s1=menu.addMenu(MW_SUBMENU,r, F("Select state"));
      s2=menu.addMenu(MW_VAR,s1, F("Prep1"));
        s2->addVar(MW_ACTION,selStatePrep1);
      s2=menu.addMenu(MW_VAR,s1, F("Prep2"));
        s2->addVar(MW_ACTION,selStatePrep2);
      s2=menu.addMenu(MW_VAR,s1, F("Mash"));
        s2->addVar(MW_ACTION,selStateMash);
      s2=menu.addMenu(MW_VAR,s1, F("Boil"));
        s2->addVar(MW_ACTION,selStateBoil);
      s2=menu.addMenu(MW_VAR,s1, F("Cool"));
        s2->addVar(MW_ACTION,selStateCool);
    s1=menu.addMenu(MW_SUBMENU,r, F("Change T SetP"));
      s2=menu.addMenu(MW_VAR,s1, F("HLT SetP"));
        s2->addVar(MW_AUTO_INT,&selectHltSetp,25,98,1);
      s2=menu.addMenu(MW_VAR,s1, F("Boil SetP"));
        s2->addVar(MW_AUTO_INT,&selectBoilSetp,25,102,1);
      s2=menu.addMenu(MW_VAR,s1,F("back"));
        s2->addVar(MW_ACTION,escapeFunct);
        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("View details"));
      s1->addVar(MW_LIST,&selectView);
      s1->addItem(MW_LIST, F("(no detail)"));
      s1->addItem(MW_LIST, F("HLT"));
      s1->addItem(MW_LIST, F("Boiler"));
  menu.addUsrScreen(lcdUpdate,3000);                 // Default screen function shown after msecs since last button push
}

//==========================================================================================
//                                Arduino Loop
//==========================================================================================

void loop() {
  menu.draw();                            // NAVIGATION MANAGEMENT & DRAWING ON LCD. NOT BLOCKING has to be the first in the void loop
  if (running) {
    hltPID.SetMode(AUTOMATIC);            //turn the PID on
    boilPID.SetMode(AUTOMATIC);           //turn the PID on
  } else {
    hltPID.SetMode(MANUAL);               //turn the PID off
    boilPID.SetMode(MANUAL);              //turn the PID off
    Timer3.setPwmDuty(pin_PWM_a, 0);      // set the pwm with the output of the pid output to control the SSR:
    Timer3.setPwmDuty(pin_PWM_b, 0);      // set the pwm with the output of the pid output to control the SSR:
  }
  if ( hltSet != selectHltSetp ) { hltSet = selectHltSetp; }
  if ( boilSet != selectBoilSetp ) { boilSet = selectBoilSetp; }
  switch(selectView) {
    case 0: viewActive = viewNormal; break;
    case 1: viewActive = viewHltD; break;
    case 2: viewActive = viewBoilD; break;
  }
  if ( push [0] > 0 ) { // confirm
    virtualButton = 6;
    push[0] = 0;
  }
  if ( push [1] > 0 ) { // escape
    virtualButton = 5;
    push[1] = 0;
  }

  // do brew process activity
  brewProcess();

  // do data logging if it is time for it
  if(millis() - logTime>LOG_DELAY) {
    logTime += LOG_DELAY;
    Serial.print("brewController loop, do dataLogger");
    dataLogger();
  }
}

I have included two pictures, showing wall mounted controller and a look inside the box.

BrewCtrl_09_06.ino (27.4 KB)

Roberto,
just two more questions ....
The fist, is about the Max Menu number I know by default set to 15 .... increasing that number affect memory ... does it increase a lot ? I mean if I change the limit to 30 but use only 15 menus ... I use the same memory amount of using 15 with a limit of 15 ?
The second ... I would like to move my height in 0.1mm step ...

     s3->addVar(MW_AUTO_INT,&HeightZ,0,100,1);

if I set 0.1 step in my var, it does not the decimal step just the integer ... any suggestion ?
thanks

Declare your variable as Float and

s3->addVar(MW_AUTO_FLOAT,&HeightZ,0,100,0.1);

Thanks Khalid,
I had doubt because they told me to not use float because sometimes gives erratic rounding values ....
don't know why ....

Grumpy_Mike:

changing the variable from INT to FLOAt is ok ?

Sorry I dnon't know how many times you need telling
DO NOT USE A FLOAT!!!!!
You will suffer from rounding errors. Mathematical operations will not be commutative.
What do you not understand about using an int with the numbers greater than your resolution?

Silverdog63:
Thanks Khalid,
I had doubt because they told me to not use float because sometimes gives erratic rounding values ....
don't know why ....

I am using it without any trouble...Without the Float you will unable to do step increment like 0.1mm etc...