MENWIZ: yet another character lcd menu wizard library

OK, so lets assume is the same configuration as the DFRobot (I mean, these guys don't tend to invent much), so this is what the schematic looks like:
http://www.dfrobot.com/image/data/DFR0175/I2C%20LCD%20Backpack%20schematic.pdf

Assuming this is the connection you would have to instantiate the LCD object as follows:

#include <LCD.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd ( 0x27, 2, 1, 0, 4, 5, 6, 7); // Addr, En, Rw, Rs, d4, d5, d6, d7

void setup ( )
{
    lcd.begin ( 16, 2 );  // Size of the LCD
}

On that LCD, I wouldn't use the backlight as is without modifying the board. Effectively you are going to short the P3 output to ground through the trasnsistor base-emitter (not nice).

Hope it works.

It seems to work (hello world). But without backlight the LCD is more or less unusefull.
I use the following inizialization sequence:

lcd->init();
lcd->setBacklight(HIGH);
lcd->setCursor(0, 0);
lcd->noCursor();
lcd->createChar(0,c0);

The init method is not supported in New LiquidCrystal.The other methods does'nt backlight the screen.
The code in my actual LiquidCrystal_I2C involved in backlighting (that is working without any problem) is the following:

#define LCD_BACKLIGHT 0x08

void LiquidCrystal_I2C::backlight(void) {
	_backlightval=LCD_BACKLIGHT;
	expanderWrite(0);
}
void LiquidCrystal_I2C::expanderWrite(uint8_t _data){                                        
	Wire.beginTransmission(_Addr);
	printIIC((int)(_data) | _backlightval);
	Wire.endTransmission();   
}

void LiquidCrystal_I2C::setBacklight(uint8_t new_val){
	if(new_val){
		backlight();		// turn backlight on
	}else{
		noBacklight();		// turn backlight off
	}
}

What do you suggest?

If you look at the schematic that I posted on the LCD backpack, has a nasty bug. If you look at the transistor, it is connected to P3. When P3 goes high (which is what you do), it will make the transistor to conduct through the base without any resistor to limit the current. If you look at how much that backpack consumes with the backlight on, you will be surprised!

In any case, if you what to use the backlight on that backpack after this comment, the New LiquidCrystal library does have support for backlight. Take a look at the methods in the base abstract class LCD. What you can do is:

#include <LCD.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd ( 0x27, 2, 1, 0, 4, 5, 6, 7, 3, NEGATIVE); // Addr, En, Rw, Rs, d4, d5, d6, d7, backlighpin, polarity

void setup ( )
{
    lcd.begin ( 16, 2 );  // Size of the LCD

   lcd.backlight ( );
   delay (1000);
   lcd.nobacklight ( );
}

There are other methods you can use like setBacklight. All these are available in the base abstract LCD class and are inherited by the particular driver.

You can also call the creation as in the previous post and use the setBacklightPin method in the LCD base class too:

#include <LCD.h>
#include <LiquidCrystal_I2C.h>

#define BACKLIGHT_PIN 3

LiquidCrystal_I2C lcd ( 0x27, 2, 1, 0, 4, 5, 6, 7); // Addr, En, Rw, Rs, d4, d5, d6, d7, backlighpin, polarity

void setup ( )
{
    lcd.begin ( 16, 2 );  // Size of the LCD
    lcd.setBacklightPin ( 3, NEGATIVE );

   lcd.backlight ( );
   delay (1000);
   lcd.nobacklight ( );
}

The cool thing about the library is that if you use a pointer to the base class in you library as I mentioned in previous posts, it will work with a wide range of LCD drivers without a single line of code being changed and being so targeted to a particular module.

The init method is not necessary at all with any LCD. Calling the init method is done directly from the constructor or from the begin method and they are private. No one should use init even with the standard LiquidCrystal Library. With the other I2C library that you are using it is necessary because somehow they needed to initialize the I2C that can't be done during object creation (as the standard), therefore, as opposed to calling it from within begin, they added a new function that needs to be called (that is not very standard).

Hope it helps.

I have a problem. None of the two ways seems to work.
I get a short flash at startup, I can barely see the character on the LCD but no backlight.
I'll try to compare the codes of the two libs.

Rey POSITIVE. Sorry for the mistake.

Great. It works.
Now let my work on menwiz in order to integrate with new liquidcrystal lib.
Thanks a lot!

You are very welcome. Now if you have a polimeter handy measure how much current it draws. You will be surprised.

I inserted in github/brunialti/MENWIZ repository the new version 0.2.0 of MENWIZ using the New LiquidCrystal library. This should allow to support many LCD devices other than the I2C I used for testing.
The lcd object must be created inside the user sketch and only a reference to it must be passed to the MENWIZ library. The MENWIZ library does'nt require any change or conditional compiler directive for the use of an interface other than I2C.
Please use the examples as starting point and be carefull about the lib include sequence (this is a point of the Arduino compiler I'm not stil used ....)

Of course my library is still a beta version and not fully debugged. Use it (it is in the attached file) and let me know.

MENWIZ_0_2_0.rar (508 KB)

MENWIZ_0_2_0.zip (514 KB)

Hi,
I need a volunteer for testing :slight_smile:
Before v 1.0 delivery I need to test some functionalities in different environments.
For now I would like to test the user callback that allow the user to overload the internal function (simple 6 buttons management) in order to use its own input device to navigate inside the menus.
If you have a practical case study and an input device able to give you back 6 states (up, down, left, right, escape, confirm) I can write the full menu code, and you have only to write your funcion managing the device. The callback function overwrite (just calling the function addUsrNav and passing your function as argument) the following simple internal method:

int menwiz::scanNavButtons(){ 
  if(btx.BTU.check()==ON){
    return MW_BTU;}
  else if (btx.BTD.check()==ON){
    return MW_BTD;}
  else if (btx.BTL.check()==ON){
    return MW_BTL;}
  else if (btx.BTR.check()==ON){
    return MW_BTR;}
  else if (btx.BTE.check()==ON){
    return MW_BTE;}
  else if (btx.BTC.check()==ON){
    return MW_BTC;}
  else
    return MW_BTNULL;
  }

The only requirement for you is to use New LiquidCrystal llibrary with the interface (serial, I2C, 4 wire, 8 wire ,...) you like.
More details to the volunteeer :slight_smile:

Used the sample with simpel menu and modfied it to my needs to get the feel of it all.

80% I have coped and understand.
One thing sofar I ran into as a problem is that not more then 5 menu's are shown when scrolling.
after s2=menu.addMenu(MW_VAR,s1,"Min"); //add a terminal node in the menu tree (that is "variable");
nothing shown and we start at the top again if we scroll.
So it looks my number of menus is declared somewhere as a max of 5 but cant find it.

Paco

//MENWIZ ESAMPLE
//
//Needs 6 buttons.
//Navigation tips:
//- to confirm chanhes allways push confirm button

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <buttons.h>
#include <MENWIZ.h>

// DEFINE ARDUINO PINS FOR THE NAVIGATION BUTTONS
#define UP_BOTTON_PIN       9
#define DOWN_BOTTON_PIN     10
#define LEFT_BOTTON_PIN     7 
#define RIGHT_BOTTON_PIN    8
#define CONFIRM_BOTTON_PIN  12
#define ESCAPE_BOTTON_PIN   11

//Create global object LCD and MENU
menwiz menu;
LiquidCrystal_I2C lcd(0x27,20,4);

//instantiate global variables to bind to menu
byte tp=2;
int  gp=26;
boolean wr=0;

void setup(){
  char b[84];
  _menu *r,*s1,*s2;
  _var *v; 
  int  mem;

  Serial.begin(19200);  
  
  // inizialize the menu object (20 colums x 4 rows LCD)
  menu.begin(&lcd,20,4);

  //create the menu tree
  r=menu.addMenu(MW_ROOT,NULL,"MAIN MENU");              //create a root menu at first (required)
    s1=menu.addMenu(MW_SUBMENU,r,"Settings");            //add a submenu node to the root menu
      s2=menu.addMenu(MW_VAR,s1,"SpeedStart");           //add a terminal node in the menu tree (that is "variable");
        s2->addVar(MW_AUTO_INT,&gp,1,125,5);             //Set value
      s2=menu.addMenu(MW_VAR,s1,"SpeedCurve");           //add a terminal node in the menu tree (that is "variable");
        s2->addVar(MW_AUTO_INT,&gp,1,125,5);             //Set value 
      s2=menu.addMenu(MW_VAR,s1,"Brake");                //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,125,5);           //Set value
      s2=menu.addMenu(MW_VAR,s1,"Model");                //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,10,1);            //Set value
      s2=menu.addMenu(MW_VAR,s1,"Min");                  //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,255,1);            //Set value
      s2=menu.addMenu(MW_VAR,s1,"Max");                  //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,255,1);            //Set value
      s2=menu.addMenu(MW_VAR,s1,"Reset");                //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_BOOLEAN,&wr);                    //Set value
      s2=menu.addMenu(MW_VAR,s1,"Calibrate");            //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_BOOLEAN,&wr);

  //(optional) create a splash screen (lap 4 seconds) with some usefull infos 
  //the character # marks end of line
  //(tip): use preallocated internal menu.sbuf buffer to save memory space!
  sprintf(menu.sbuf,"DIGITAL SLOT WIZARD #Version 1.01#",6);
  menu.addSplash((char *) menu.sbuf, 4000);
 
  //declare navigation buttons (required)
  // equivalent shorter call: menu.navButtons(9,10,7,8,11,12);
  menu.navButtons(UP_BOTTON_PIN,DOWN_BOTTON_PIN,LEFT_BOTTON_PIN,RIGHT_BOTTON_PIN,ESCAPE_BOTTON_PIN,CONFIRM_BOTTON_PIN);
  // create a user define screen callback to activate after 5 secs since last button push 
  menu.addUsrScreen(msc,5000);
  //
  }

void loop(){
  // NAVIGATION MANAGEMENT & DRAWING ON LCD. NOT BLOCKING
  menu.draw(); 
  //PUT APPLICATION CODE HERE
  }

// user defined default screen
void msc(){
        lcd.begin(20, 4);
        lcd.setCursor(0,0); 
        lcd.print("SPD ");
        lcd.setCursor(6,0);
        lcd.print("123");
        //lcd.print(speedValue);
        lcd.setCursor(12,0); 
        lcd.print("MDL ");
        lcd.setCursor(17,0);
        lcd.print("1");
        //lcd.print(speedValue);
        lcd.setCursor(0,1); 
        lcd.print("SPDST");
        lcd.setCursor(6,1);
        lcd.print("123");
        //lcd.print(speedstartValue);
        lcd.setCursor(12,1); 
        lcd.print("MIN");
        lcd.setCursor(17,1);
        lcd.print("255");
        //lcd.print(minValue);
        lcd.setCursor(0,2); 
        lcd.print("SPDC");
        lcd.setCursor(6,2);
        lcd.print("123"); 
        //lcd.print(speedcurveValue);
        lcd.setCursor(12,2); 
        lcd.print("MAX");
        lcd.setCursor(17,2);
        lcd.print("0"); 
        //lcd.print(maxValue);
        lcd.setCursor(0,3); 
        lcd.print("BRK");
        lcd.setCursor(6,3);
        lcd.print("255");
        //lcd.print(brakeValue);
        lcd.setCursor(12,3); 
        lcd.print("DBD");
        lcd.setCursor(17,3);
        lcd.print("15");
        //lcd.print(deathbandValue);
  }

That's true. There are two defines you can change in MENWIZ.h:

#define MAX_MENU 15 //maximum number of nodes
#define MAX_OPTXMENU 5 //maximum number of oprions for each node

The first one is the number of nodes you can create with addMenu method.
The second is the max number of options you can add with addItem method for each node.
Keep all of them as low as possible (depending from your application) in order to spare ram space.

I'm working to clean the code, adding comments ad some usefull (I hope) new method.
I can anticipate:

addSplash
the string passed to the function use \n (0x0A) character as line delimiter instead of '#'

drawUsrScreen
It quick draw LCD screen with the contents of the argument string. Each line to be shown in the LCD is terminated by char 0x0A ('\n') inside the argument string. Example: "Test user screen\nline1\nline2\n\n". This method provide the user with the quick way to write an entire LCD screen (the lib will manage space padding, cursor position and string length checking)

addUsrNav
user defined navigation device routine (callback). The user can use any device other than buttons to overwrite the internal routine. The callback must return an int code for any pushed "button" (MW_BTU=UP,MW_BTD=DOWN,MW_BTL=LEFT,MW_BTR=RIGHT,MW_BTE=ESCAPE,MW_BTC=CONFIRM,MW_BTNULL=NO BUTTON).

Hope it helps

--- reading better your last post:

the MW_MAXOPTMENU does not only affects the numeber of options in case of variable of type MW_LIST but also the maximum allowed number of submenus for each nodes.
So you have to change it to 8 in your case. Should you add more variables under the same submenu "Settings" the number could increase.

What I suggest, if it make sense for you, is to cluster variables into different submenu nodes.
If you do in such a way and there are not more than 5 submenus or options in a choice list variable of type MW_LIST the change to the defined max values are not required and you spare some ram space

Step by step... :slight_smile:

So would it be better RAM wise to make them all submenu's with only one var inside?

Paco

No, it is the same or even worse.
If it is usefull for you, it could be as following

MAIN MENU
Settings
SpeedStart
SpeedCurve
Brake
Model
Triggers
Min
Max
Actions
Reset
Calibrate

Thats makes more sense to me.

Will change.

Paco

This is what Imade of it.

When the splash screen is active the display blinks each second?
Is this rewriting of the values?

Paco

//create the menu tree
  r=menu.addMenu(MW_ROOT,NULL,"MAIN MENU");              //create a root menu at first (required)
    s1=menu.addMenu(MW_SUBMENU,r,"Settings 1");          //add a submenu node to the root menu
        s2=menu.addMenu(MW_VAR,s1,"SpeedStart");         //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,125,5);           //Set value
        s2=menu.addMenu(MW_VAR,s1,"SpeedCurve");         //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,125,5);           //Set value
        s2=menu.addMenu(MW_VAR,s1,"Brake");              //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,125,5);           //Set value
        s2=menu.addMenu(MW_VAR,s1,"Model");              //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,10,1);            //Set value
    s1=menu.addMenu(MW_SUBMENU,r,"Settings 2");          //add a submenu node to the root menu
        s2=menu.addMenu(MW_VAR,s1,"Min");                //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,255,1);           //Set value
        s2=menu.addMenu(MW_VAR,s1,"Max");                //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_AUTO_INT,&gp,1,0,1);             //Set value
    s1=menu.addMenu(MW_SUBMENU,r,"Settings 3");          //add a submenu node to the root menu
        s2=menu.addMenu(MW_VAR,s1,"Reset");              //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_BOOLEAN,&wr);                    //Set value
        s2=menu.addMenu(MW_VAR,s1,"Calibrate");          //add a terminal node in the menu tree (that is "variable");
          s2->addVar(MW_BOOLEAN,&wr);

In your code you use some variables that does not make sense in your context (i.e. wr, gp).
You need to create your own global variables (i.e. speed, brake, mymin, mymax, ...) of the same type of the declared variable (in this case int for MW_AUTO_INT, byte for MW_AUTO_BOOLEAN) bindig them (passing by reference as an argument of addVar).
In your sketch if you initialize them with a default value, the same value will be visible in the menu. Any change in the menu will affect your global variable as well.

I know like I said step by step.
Code is now running outside my skecth to be, for set up and debugging.
As soon as all looks OK I paste it in the sketch I already have to keep overview.
Variables are the next step.

Paco

The splash screen should'nt blink at all during splash schreen display.... (splash or default userscreen ? The userscreen is shown afer x seconds (4 in your code) without pushing any button)
Ignore it for now. I'm going to insert the new subversion (0.3.0) of library that will help you a lot to simplify the code you wrote for default screen (and let it be displayed without any blink).
The new lib will require you only to change the character '#' in the splashscreen with more friendly '\n'.

I wait XD

Paco