MENWIZ: yet another character lcd menu wizard library

Bruno,

Good to see the new 1.0 version and the new extra's.
I have to appologize for not creating yet a new bie manual.
Having heavy time issues on several projects.

Paco

@dannix

It is hard to say why. I would need to setup the analog buttons as yours, but I have not some of the components.
I'll try to make an old fashioned code inspection ... With the new lib v1.0.0 you do not need to apply the patch I sent.
Did you use the errorMessage during debug after each instruction? Did you get some message?
I'm committed to solve it before holyday :slight_smile:

@backbone

nice to read you again. If you had a practical application of Menwiz (and it works :slight_smile: ), it could be interesting share it with the communiy, even criticism,

Paco, by the way, are you using custom buttons or devices using addUsrNav method?
You could help me to test/debug setBehaviour in such a condition!

brunialti:
@dannix

It is hard to say why. I would need to setup the analog buttons as yours, but I have not some of the components.
I'll try to make an old fashioned code inspection ... With the new lib v1.0.0 you do not need to apply the patch I sent.
Did you use the errorMessage during debug after each instruction? Did you get some message?
I'm committed to solve it before holyday :slight_smile:

Hi,I'd not used errorMessage but have tried again using it, I get no errors. Code below and using latest library.

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_SR.h>
#include <buttons.h>
#include <EEPROM.h>
#include <MENWIZ.h>

//INTERRUPT BUTTONS
const int pin = 13;  // test for interrupt
const int buttonPin = A0; // buttons can be read here
volatile byte buttonAct = 0; // flag, follow button press
volatile int buttonValue = 0; // button pressed analog value 
volatile byte buttonPressed = 0;  // which button was pressed
volatile byte lastButtonPressed = 0; // prev button pressed
volatile byte state = LOW; // state of led on pin 13

/*
// DEFINE ARDUINO PINS FOR THE NAVIGATION BUTTONS
 #define UP_BOTTON_PIN       9
 #define DOWN_BOTTON_PIN     5
 #define LEFT_BOTTON_PIN     7 
 #define RIGHT_BOTTON_PIN    8
 #define CONFIRM_BOTTON_PIN  3
 #define ESCAPE_BOTTON_PIN   4
 */

menwiz tree; //create menwiz object
// create lcd obj using LiquidCrystal lib
LiquidCrystal_SR lcd(10, 11, 12); //Data,Clk,Enable.

int  list,sp=110;  

void setup(){
  Serial.begin(19200); 
  Serial.println(F("STARTED"));
  pinMode(pin, OUTPUT);
  attachInterrupt(0, readButtons, RISING);  


  _menu *r,*s1,*s2;



  tree.begin(&lcd,20,4); //declare lcd object and screen size to menwiz lib
  tree.addUsrNav(navMenu); 
  r=tree.addMenu(MW_ROOT,NULL,F("Root"));
  s1=tree.addMenu(MW_SUBMENU,r,F("SUBMENU"));
  s2=tree.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=tree.addMenu(MW_VAR,s1,F("Node4"));
  s2->addVar(MW_AUTO_INT,&sp,0,120,10);
  s2=tree.addMenu(MW_VAR,s1,F("Exit"));
  s2->addVar(MW_ACTION,exitFunct); 
  s2->setBehaviour(MW_ACTION_CONFIRM,false);


  //tree.navButtons(UP_BOTTON_PIN,DOWN_BOTTON_PIN,LEFT_BOTTON_PIN,RIGHT_BOTTON_PIN,ESCAPE_BOTTON_PIN,CONFIRM_BOTTON_PIN);
}

int i = 0;
void loop(){

  if(i > 1000){
    Serial.print(F("freeRam() reports "));
    Serial.println( tree.freeRam() );
    i = 0;
  }
  else{
    i++;
  } 
  tree.draw(); 
  int err = tree.getErrorMessage(true);
  if(err){
    Serial.println(err);
  }
}

void readButtons(){  //fired by interrupt 0
  state = !state;
  digitalWrite(pin, state);  // flip pin 13 led state
  /*   511 : none | 614 : up | 768 : down | 1023 : select   */
  lastButtonPressed = buttonPressed;

  buttonValue = analogRead(buttonPin);
  if(buttonValue >= 900) {
    buttonPressed = 3; // select
  }
  else if(buttonValue >= 700){    
    buttonPressed = 2;  // down
  }
  else if(buttonValue >= 600){    
    buttonPressed = 1;  // up
  }
  buttonAct = 1; 
  Serial.print(buttonPressed);
  Serial.print(" XX ");
  Serial.println(buttonValue);
}

void exitFunct(){
  Serial.println("EXIT Called");
  buttonAct = 1;
  buttonPressed = 4;  //simulate exit button press
}

int navMenu(){ //called by menwiz.draw()
  int err = tree.getErrorMessage(true);
  if(err){
    Serial.println(err);
  }
  
  if(buttonAct){ //true if button press not processed
    buttonAct = 0; // run once per button press
    switch (buttonPressed){
    case 4: //simulated escape
      return MW_BTE;
      break; 
    case 3: //select     
      return MW_BTC;
      break;
    case 2: //down
      return MW_BTD;
      break;     
    case 1: //up
      return MW_BTU;
      break;     
    }
  }
  else{
    return MW_BTNULL;
  }

}

The usernav function (same as the builtin scanNavButtons) is called just and only in one point in the code, at the beginning of draw() method:

  ret=usrNav.fl?usrNav.fi():scanNavButtons();    	//internal method or user defined callback?

as you can see the builtin xor your function, internally coded as usrNav.f(), returns a button code that is therefore processed by the following code.
This imply that if your function returns the same code as the builtin one, the executed code after the call is exactly the same. There is no other code that is performed by builtin function (no initialization, no global or local variables assignment). This is the reason why I cannot understand why the behaviour is different, provided the two functions return the same values for equivalent actions.

So, in a pure deductive way 8) ... you need to check if the value returned by your function (the value of ret variable) is the one you expected (e.g. is equal to MW_BTC after pushing Confirm button). Try to put a Serial.println trace after that. Of course as the call to draw() are many in the loop you must quickly unplug the usb cable (or put a conditional printing checking for the expected code).

Regarding the use of tree.getErrorMessage(true), if no error occourred in the last executed MENWIZ method call, it does not print to Serial. If an error occourret it print to Serial both the error code and an error message.
I suggest to use it in the menu structure declarations in such a way:

  s2=tree.addMenu(MW_VAR,s1,F("Exit"));
  s2->addVar(MW_ACTION,exitFunct); 
  s2->setBehaviour(MW_ACTION_CONFIRM,false);
  tree.getErrorMessage(true);

if no message is printed out in the serial terminal, it means no error occourred.

I hope this can help

Paco, by the way, are you using custom buttons or devices using addUsrNav method?
You could help me to test/debug setBehaviour in such a condition!

Do not know exactly what you mean.
I burned up my project shield and Mega.
Currently rebuilding the set up with shield and a new Mega.
Used 4 pushbuttons on the digital ports. Do not know if you call that custom buttons.
My code does not use any addUsrNav methods as far as CTRL+f can find......... :slight_smile:

Let me know how I can assist in current situation.

If you need the bare code of the MENWIZ I currently use no problem.

Paco

Hi

I've added the trace calls and It returns as expected. This is without setBehaviour declared, so run confirm expected. my funct passed MW_BTC but isn't followed by "EXIT called" as if my custom function ins't called.

STARTED
3 XX 1016
SELECT
2 XX 762
DOWN
freeRam() reports 898
2 XX 763
DOWN
3 XX 1016
SELECT
3 XX 1017
SELECT   <---- my funct passed MW_BTC 
freeRam() reports 898
freeRam() reports 898
freeRam() reports 898

This is with s2->setBehaviour(MW_ACTION_CONFIRM,false);

STARTED
freeRam() reports 898
3 XX 1017
SELECT
2 XX 763
DOWN
2 XX 764
DOWN
3 XX 1015
SELECT
EXIT Called
ESCAPE
freeRam() reports 898
freeRam() reports 898

yet this time it works, when select (MW_BTC) is passed, my function returns EXIT Called as expected sets the button values and MW_BTE is passed as can be seen by the ESCAPE. menwiz goes back a level, except in the new version of the library the line below isn't cleared. It was before.

When I press a button the interrupt fires "3 XX 1015" then I see its corresponding value sent to menwiz SELECT/MW_BTC if it was an exit i see the simulated escape being passed ESCAPE.

I have some buttons, What i'll do is hook them up to digital pins thereby removing the custom button function and see if the same thing happens. I'm certain it's not my button implementation though.

Thanks for your help

I'll do the same, using digital button with your code!
thanks for your patience.

Hi, I've tried using 4 digital buttons. Its exactly the same. Confirm to run does not actually run the custom function. If confirm disabled it runs the custom function once selected.

Using the digital escape button goes back a level but line 3 of the lcd isn't cleared (Node4) and can't be selected. Again, it didn't do this in the previous library version, this is a regression in the latest code.

Here is the code I ran with little changes, buttons defined and addUsrNav commented out

#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_SR.h>
#include <buttons.h>
#include <EEPROM.h>
#include <MENWIZ.h>

//INTERRUPT BUTTONS
const int pin = 13;  // test for interrupt
const int buttonPin = A0; // buttons can be read here
volatile byte buttonAct = 0; // flag, follow button press
volatile int buttonValue = 0; // button pressed analog value 
volatile byte buttonPressed = 0;  // which button was pressed
volatile byte lastButtonPressed = 0; // prev button pressed
volatile byte state = LOW; // state of led on pin 13


// DEFINE ARDUINO PINS FOR THE NAVIGATION BUTTONS
 #define UP_BOTTON_PIN       8
 #define DOWN_BOTTON_PIN     7
/*
 #define LEFT_BOTTON_PIN     7 
 #define RIGHT_BOTTON_PIN    8
 */
 #define CONFIRM_BOTTON_PIN  4
 #define ESCAPE_BOTTON_PIN   3
 

menwiz tree; //create menwiz object
// create lcd obj using LiquidCrystal lib
LiquidCrystal_SR lcd(10, 11, 12); //Data,Clk,Enable.

int  list,sp=110;  

void setup(){
  Serial.begin(19200); 
  Serial.println(F("STARTED"));
  pinMode(pin, OUTPUT);
  attachInterrupt(0, readButtons, RISING);  


  _menu *r,*s1,*s2;
anyErrors();


  tree.begin(&lcd,20,4); //declare lcd object and screen size to menwiz lib
  anyErrors();
  //tree.addUsrNav(navMenu); 
  anyErrors();
  r=tree.addMenu(MW_ROOT,NULL,F("Root"));
  anyErrors();
  s1=tree.addMenu(MW_SUBMENU,r,F("SUBMENU"));
  anyErrors();
  s2=tree.addMenu(MW_VAR,s1,F("Node3"));
  anyErrors();
  s2->addVar(MW_LIST,&list);
  anyErrors();
  s2->addItem(MW_LIST,F("Option1"));
  anyErrors();
  s2->addItem(MW_LIST,F("Option2"));
  anyErrors();
  s2->addItem(MW_LIST,F("Option3"));
  anyErrors();
  s2=tree.addMenu(MW_VAR,s1,F("Node4"));
  anyErrors();
  s2->addVar(MW_AUTO_INT,&sp,0,120,10);
  anyErrors();
  s2=tree.addMenu(MW_VAR,s1,F("Exit"));
  anyErrors();
  s2->addVar(MW_ACTION,exitFunct); 
  anyErrors();
  //s2->setBehaviour(MW_ACTION_CONFIRM,false);
anyErrors();

Serial.print(MW_BTNULL);Serial.print(MW_BTU);Serial.print(MW_BTD);
Serial.print(MW_BTC);Serial.println(MW_BTE);  
  tree.navButtons(UP_BOTTON_PIN,DOWN_BOTTON_PIN,ESCAPE_BOTTON_PIN,CONFIRM_BOTTON_PIN);
}

int i = 0;
void loop(){

  if(i > 1000){
    Serial.print(F("freeRam() reports "));
    Serial.println( tree.freeRam() );
    i = 0;
  }
  else{
    i++;
  } 
  tree.draw(); 
  anyErrors();
}

void readButtons(){  //fired by interrupt 0
  state = !state;
  digitalWrite(pin, state);  // flip pin 13 led state
  /*   511 : none | 614 : up | 768 : down | 1023 : select   */
  lastButtonPressed = buttonPressed;

  buttonValue = analogRead(buttonPin);
  if(buttonValue >= 900) {
    buttonPressed = 3; // select
  }
  else if(buttonValue >= 700){    
    buttonPressed = 2;  // down
  }
  else if(buttonValue >= 600){    
    buttonPressed = 1;  // up
  }
  buttonAct = 1; 
  Serial.print(buttonPressed);
  Serial.print(" XX ");
  Serial.println(buttonValue);
anyErrors();
}

void exitFunct(){
  Serial.println("EXIT Called");
  buttonAct = 1;
  buttonPressed = 4;  //simulate exit button press
//tree.draw();
}

void anyErrors(){
 int err = tree.getErrorMessage(true);
  if(err){
    Serial.println(err);
  }
}

int navMenu(){ //called by menwiz.draw() 
  if(buttonAct){ //true if button press not processed
    buttonAct = 0; // run once per button press
    switch (buttonPressed){
    case 4: //simulated escape
      Serial.println(F("ESCAPE"));
      return MW_BTE;
      break; 
    case 3: //select 
      Serial.println(F("SELECT"));    
      return MW_BTC;
      break;
    case 2: //down
    Serial.println(F("DOWN"));
      return MW_BTD;
      break;     
    case 1: //up
    Serial.println(F("UP"));
      return MW_BTU;
      break;     
    }
  }
  else{
    return MW_BTNULL;
  }

}

ok. Now I can replicate the problem.
I confirm the regression :frowning:

Confirm to run does not actually run the custom function.
-FIXED

If confirm disabled it runs the custom function once selected.
-FIXED

Using the digital escape button goes back a level but line 3 of the lcd isn't cleared (Node4) and can't be selected. Again, it didn't do this in the previous library version, this is a regression in the latest code.
-FIXED

Dannix, can I ask you to test the fixed code before to deliver a new version?
Please find here attached the file to replace the existing ones.

MENWIZ.h (7.13 KB)

MENWIZ.cpp (25.2 KB)

brunialti:
Dannix, can I ask you to test the fixed code before to deliver a new version?
Please find here attached the file to replace the existing ones.

It works as expected both with digital buttons or with the custom button function! This library will save me alot of time in my project, I can start fleshing it out now. Thank you for all your work brunialti this could be the default Arduino IDE menu library

Nice to read the problems are over.
I inserted the new lib v 1.0.1 in git hub at https://github.com/brunialti/MENWIZ/downloads

I think it is unfair for me as author to promote the lib. It is up to the community to signal if there is something new and interesting on the net :slight_smile:

@paco

the problem is over. thnks.
Good luck with your new board!

OK,

Boards are up and running.
Still using the old lib. for the time being.

Just a question.
To reduce wires from a controller to the MEGA I used an analog port with 10 buttons.
4 of those buttons are now supposed to be used for the menu navigation so no more hardcore switched at the digital inputs.
I thougth it was simple to declare 4 variables as boolean and let the line become

menu.navButtons(menuUp,menuDown,menuEscape,menuConfirm);// 4 button navigation
// ++++ Buttons 10 in total ++++++

boolean menuUp;
boolean menuDown;
boolean menuConfirm;
boolean menuEscape;

  buttonValue = analogRead(0);   // read the input pin 0
  // internal 20K ohm pullup is enabled on analog pin 0 (54).
  if(buttonValue >= 920 and buttonValue <= 930 ) 
  {
    menuUp=(HIGH);
    digitalWrite(led7Pin, HIGH);
    delay (200); // 910
  }
    else if(buttonValue >= 820 and buttonValue <= 835)
  {
    menuConfirm=(HIGH);
    digitalWrite(led7Pin, HIGH); 
    delay (200);// 827
  }
  else if(buttonValue >= 725 and buttonValue <= 740)
  {
    menuDown=(HIGH);
    digitalWrite(led7Pin, HIGH); 
    delay (200); // 731
  }
  else if(buttonValue >= 630 and buttonValue <= 645)
  {
    menuEscape=(HIGH);
    digitalWrite(led7Pin, HIGH);
    delay (200); // 637
  }
else
    digitalWrite(led7Pin,LOW);
    menuUp =(LOW);
    menuDown=(LOW);
    menuConfirm=(LOW);
    menuEscape =(LOW);

Instead of the original code for digital buttons I used this:

// DEFINE ARDUINO DIGITAL PINS FOR THE NAVIGATION BUTTONS FOR MENWIZ
#define UP_BUTTON_PIN       menuUp //3
#define DOWN_BUTTON_PIN     menuDown //4
#define CONFIRM_BUTTON_PIN  menuConfirm //2
#define ESCAPE_BUTTON_PIN   menuEscape //5

But that does not work.
Am I working and thinking in the wrong direction?

Yes. that is the wrong direction.
In the previous posts dannix got the true way.

forget navbuttons. That method is used to activate the builtin methods
if you want to use your own device to replace the standards buttons managed by MENWIZ and Buttons libraries (and declared with navButtons functions) you need to write your own function and to declare it to MENWIZ library using addUsrNav method.
The user defined function will replace the following internal one:.
In the manual there is a sample of the method to be replaced by your routine.
The trick is just be able to provide as return code one of the same codes the built in funzion returns.
The codea are the following literals:

// BUTTON CODES
// ----------------------------------------------------------------------
#define MW_BTNULL //null
#define MW_BTU //up
#define MW_BTD // down
#define MW_BTL //RIGTH
#define MW_BTR //LEFT
#define MW_BTE //ESCAPE
#define MW_BTC //CONFIRM

Look at the working code of dannix (your should be a little dfferent) using analog buttnos as you would like to. Be only careful: dannix simulate the forth button ESCAPE (he uses only 3 buttons!) , using a normal MENWIZ menu entry (with an action variable associated) to simulate it. Your code should be even simpler!

Hi Bruno,

Switched to 1.0 lib and needed to change some code first to get rid of error messages.
I looked in the manual ans saw the sample code for custom buttons.
Also looked at DANNIX his sample code.
Still not sure if I fully understand Dannix his code and if using interrupt is really needed.

If Dannix can explain I would be happy.

Paco

The method addUsrNav allows only to replace the 6 buttons model, that is the
actions performed by MENWIZ are those expected by the 6 buttons model.
The next release of addUsrNav release will allows to explicitly set the replaced
model (4 or 6 buttons mode).
With release 1.0.1 if you want to use addUsrNav you can patch with the following
lines in the sketch code

extern byte MW_navbtn;
...
// in setup() just after the addUsrNav call
MW_navbtn=4;  // force 4 buttons mode