MENWIZ: yet another character lcd menu wizard library

brunialti:
Do you real want test such a young baby as menwiz against such a mature and wise counterparts ? :slight_smile:
My lib goals are: near asynchronous, simple coding, user defined screens (splash and default) and "overwrite callbacks" (input device scan, actions/triggers ...).
I have more work to do. If somebody is interested I am ready to go on.
I would be pleased if some could test the lib with standard LiquidCrytal.
I have noy 4/8 wires LCD available at the moment.

Not to challenge you, just comparison as I suggested. So you are going into the dynamic and callbacks direction. You could get one of my shields to test out the library :wink:

I recently failed to support a user. It turned out that M2tklib is still too complicated (will i win the price for the most complicated lib?).

I would like to make M2tklib more simple. So I try to understand other approaches. And I like to see fresh libs like yours. For me a comparison about the programming interfaces would be useful (not about features). What would be the most easy approach for a beginner? What do others think about these different approches regading the programming API?

If we find something common which can be implemented by each of the libs, then i think i could get a better understanding of each programming model. And maybe other users can rate the programming effort for each API.

My idea was something about this: On a 16x4 (or 20x4) display 4 selectable lines. The user selects one line and the display shows the line number (0-3). No scrolling, just a cursor which is used to select one of four lines. Is this possible with MENWIZ?

Oliver

Looking at the direction you are going with the MENWIZ library, I think it would be worth while looking at the New LiquidCrystal library. Since you are using a pointer to the LCD, you would benefit from it by using a pointer to its base class LCD (a pure abstract LCD class). This would give you support for a wide range of drivers controlling Hitachi based LCDs.

Not only is it 3.5 times faster than the original LIquidCrystal library, it also gives you the flexibility of being compatible with a wide range of LCDs out there without changing a single line of code.

Just FYI, I am thinking about a slightly different approach compared with a base class approach. For input devices, I totally did base class approach and built my library from ground up to support all possible input devices. For displays, I'm thinking about using ANSI escape codes to control everything. I will need to write support for every new type of display so all my UI command to the displays will be entirely string commands. :slight_smile:

At this time MENWIZ needs only few methods of the LiquidCrystal class. It should work with any lib providing such methods. I cannot test it immediately as I have not a standard 4/8 wires lcd in my hand, but I'll do it after easter time.
I include the two different libraries depending on the #define I2C (first line in MENWIZ.h file). Another way could be difefrentiate MENWIZ.h in MENWIZ_I2C.h and MENWIZ.h.

The code needed to implememt the example Olikraus suggested is as following. Most part of the code is just to implement the screen showing selection choice.
An other usefull comparison could be done on a real request arose in this forum (see the GOLF code example here above).

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

menwiz menu;
LiquidCrystal_I2C lcd(0x27,20,4);

int choice=0;

void setup(){
  _menu *r;

  menu.begin(&lcd,20,4);
  menu.navButtons(9,10,7,8,12,11);

  r=menu.addMenu(MW_ROOT,NULL,"TEST MENU");           
    r->addVar(MW_LIST,&choice);                    
    r->addItem(MW_LIST,"Line 1");             
    r->addItem(MW_LIST,"Line 2");           
    r->addItem(MW_LIST,"Line 3");     
    r->addItem(MW_LIST,"Line 4");     
          
  menu.addUsrScreen(msc,3000);
  }

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

//USER SCREEN SHOWED AFTER 3 SECS SINCE LAST BUTTON PUSH
void msc(){
  Serial.println(choice);
  lcd.setCursor(0, 0);
  lcd.print("Total uptime ");lcd.print(millis()/1000);
  lcd.setCursor(0, 1);
  lcd.print("Choice is ");lcd.print((int) choice);
  lcd.setCursor(0, 2);
  lcd.print("                    ");
  lcd.setCursor(0, 3);
  lcd.print("                    ");
  }

That's the nice thing about the "New LiquidCrystal" library, it already supports a wide range of LCD controllers:

  • I2C base on the PCF8574 or compatible
  • 4bit and 8bit parallel
  • ShiftRegister: 3wire, 2wire, 1wire (beta)

Your code would look something like this:

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C myLCD (0x27);  // Base address of IO expander

void setup ()
{
   menu.begin ( &myLCD, 20, 4 );
   ...
}

Assuming that your menu class makes the following modification:

#include <LCD.h>

void menu::begin ( LCD *iLCD, uint8_t cols, uint8_t rows )

With this LCD library you will get almost 32 fps as opposed to the one you are currently using that barely does 10fps.

Now if you want to use for example a 4bit parallel interface, the only thing that would change in you code is the creation of the LCD object:

#include <LiquidCrystal_I2C.h>

LiquidCrystal myLCD ( 12, 11, 5, 4, 3, 2 ); // Rs, E, D4, D5, D6, D7

void setup ()
{
   menu.begin ( &myLCD, 20, 4 );
   ...
}

Not a lot to do in your code really.

It seems really easy. I'll try. It would be very nice to have a broad range of compatible devices.

I cant find the MENWIZ.h file on the web :roll_eyes:
Did I missed something?
Github does not bring it either as was mentioned in oen of the previous posts.

Paco

Try this . I can see it on my mobile

The code needed to implememt the example Olikraus suggested is as following.

So, here is the M2tklib version. You will see, that the menu is defined outside any procedure. It is placed directly in the data area (PROGMEM) of the controller. The other difference is the "choice" variable. In M2tklib, buttons only call a sub-procedure. The result must be assigned manually in this case. In the MENWIZ example, it seems that choice is automatically connected to the line number.

Regarding the discussion about LiquidCrystal variants: M2tklib uses a message based interface to an "output handler". This is " m2_gh_lc" for LiquidCrystal (see below). Replacing this with "m2_gh_glcd", will render the output for GLCD v3. By writing such a handler, any character or graphical output device could be used.

Oliver

//  Menu.pde
// http://arduino.cc/forum/index.php/topic,99693.15.html

#include <LiquidCrystal.h>
#include "M2tk.h"
#include "utility/m2ghlc.h"

//=========================================================
// fotward declaration
extern M2tk m2;

//=========================================================
// keys 
uint8_t uiKeySelectPin = 10;
uint8_t uiKeyNextPin = 9;

//=========================================================
// global variable: selected line
uint8_t choice = 0;

//=========================================================
// display result
M2_U8NUM(result_menu, NULL, 0, 255, &choice);

//=========================================================
// line selection

// callbacks for the buttons
void fn_line1(m2_el_fnarg_p fnarg) { choice = 0;  m2.setRoot(&result_menu); }
void fn_line2(m2_el_fnarg_p fnarg) { choice = 1;  m2.setRoot(&result_menu); }
void fn_line3(m2_el_fnarg_p fnarg) { choice = 2;  m2.setRoot(&result_menu); }
void fn_line4(m2_el_fnarg_p fnarg) { choice = 3;  m2.setRoot(&result_menu); }

// layout of the menu
M2_BUTTON(el_line1, NULL, "Line 1", fn_line1);
M2_BUTTON(el_line2, NULL, "Line 2", fn_line2);
M2_BUTTON(el_line3, NULL, "Line 3", fn_line3);
M2_BUTTON(el_line4, NULL, "Line 4", fn_line4);
M2_LIST(list_lines) = { &el_line1, &el_line2, &el_line3, &el_line4 };
M2_VLIST(line_menu, NULL, list_lines);

//=========================================================
// lcd setup
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

//=========================================================
// M2tklib setup
// m2_es_arduino: Arduino event source
// m2_eh_2bs: Two button handler
// m2_gh_lc: Character subsystem
M2tk m2(&line_menu, m2_es_arduino, m2_eh_2bs, m2_gh_lc);


//=========================================================
// Arduino setup()
void setup() {
  // Connect M2tklib to LCD 
  m2_SetLiquidCrystal(&lcd, 16, 4);
  
  // Apply keys
  m2.setPin(M2_KEY_SELECT, uiKeySelectPin);
  m2.setPin(M2_KEY_NEXT, uiKeyNextPin);
}

//=========================================================
// Arduino loop()
void loop() {
  m2.checkKey();
  if ( m2.handleKey() )
    m2.draw();
  m2.checkKey();
}

New LiquidCrystal: I cannot let it work with my LCD. I, downloaded, expanded and renamed LiquidCrystal_I2C and dropped it in libraries dir of the Arduino environment. The Hello world example does not compile and i get "LiquidCrystal_I2C: does not name a name." at the following line

LiquidCrystal_I2C lcd(0x27); // Set the LCD I2C address

I inserted "#include LCD.h" before "#include <LiquidCrystal_I2C.h>" It compiles but the screen flash randomically.

The LCD has a YwRobot Arduino LCM1602 IIC V1 backpack.
Do I make something wrong?

I can't find the datasheet or schematic for the backpack. What chip does it use? Also, you need to map its pins in the library. Do you have an schematic for it? If not, you can always look at the code of the library you were using and see the pin mapping, then call the appropriate constructor to match that pin map.

Also, if you instal the library, you will have to delete the old one. That is why you are getting the compilation errors.

The chips are hd44780 and PCA8574
I can give you the link of the producer, that is chinese ...
http://item.taobao.com/item.htm?spm=11024chU.1-2gqGK.s-3HMdjl&id=13749999858
There is also this reference page.
http://www.dfrobot.com/wiki/index.php?title=I2C_TWI_LCD2004_(SKU:DFR0154)

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.