LCD MENU Libraries

Hello

I would like to learn how to build menus to be used with encoders and buttons.
I have an I²C LCD 16×2 which I have been able to setup and use with the LiquidCrystal_I2C library.

I have read various posts about how to create a menu but I think the best way for me would be using libraries and examples and learn from that. I have downloaded a three libraries so far (arduino menu, MENWIZ and Liquidmenu).

the example "I_I2C_menu" in Liquidmenu library is helpful as it gives clear instructions on how to adapt the code for the I2C library:

  • The difference in using an I2C display library instead of the
  • official LiquidCrystal library is that void LiquidMenu::init()
  • method needs to be called in setup() after the I2C display library
  • is initialized. The other difference is that I2C needs to be defined
  • as "true" in the "LiquidMenu_config.h" file.

I have made these changes and this example works fine. However I then tried to adapt other examples using the same methods and they do not compile. For example :

#include <LiquidCrystal_I2C.h>
#include <LiquidMenu.h>
#include <EEPROM.h>
#include "Button.h"

// The I2C LCD object
LiquidCrystal_I2C lcd(0x3F, 16, 2);

// Button objects instantiation
const bool pullup = true;
Button left(5, pullup);
Button right(7, pullup);
Button up(8, pullup);
Button down(9, pullup);
Button enter(10, pullup);

// Pin definitions and variables for their state.
const byte pin6 = 6;
byte pin6_level = 0;

const byte pinA0 = A0;
byte pinA0_value = 0;

const byte pinA1 = A1;
byte pinA1_value = 0;

// The analog reading sample period in seconds.
// It is later overwritten by it's EEPROM value.
unsigned short sample_period = 2;

// Text used for indication for the save lines.
char* input_saved;
char* output_saved;

enum FunctionTypes {
  increase = 1,
  decrease = 2,
};


// A LiquidLine object can be used more that once.
LiquidLine back_line(11, 1, "/BACK");


LiquidLine welcome_line1(1, 0, "LiquidMenu ", LIQUIDMENU_VERSION);
LiquidLine welcome_line2(1, 1, "System example");
LiquidScreen welcome_screen(welcome_line1, welcome_line2);

// These lines direct to other menus.
LiquidLine outputs_line(0, 0, "/Outputs");
LiquidLine inputs_line(0, 1, "/Inputs");
LiquidScreen io_screen(outputs_line, inputs_line);

// This is the first menu.
LiquidMenu main_menu(lcd, welcome_screen, io_screen, 1);


LiquidLine pin6_line(0, 0, "Pin 6: ", pin6_level);
LiquidScreen pin6_screen(pin6_line);

LiquidLine oSave_line(0, 0, "Save", output_saved);
LiquidScreen oSecondary_screen(oSave_line, back_line);

// This is the second menu.
LiquidMenu outputs_menu(lcd, pin6_screen, oSecondary_screen);


LiquidLine pinA0_line(0, 0, "Pin A0: ", pinA0_value);
LiquidLine pinA1_line(0, 1, "Pin A1: ", pinA1_value);
LiquidScreen pinsA0_A1_screen(pinA0_line, pinA1_line);

LiquidLine iSample_line(0, 0, "Sample: ", sample_period, "s");
LiquidLine iSave_line(0, 1, "Save", input_saved);
LiquidScreen iSecondary_screen(iSample_line, iSave_line, back_line);

// And this is the final third menu.
LiquidMenu inputs_menu(lcd, pinsA0_A1_screen, iSecondary_screen);

/*
 * LiquidSystem object combines the LiquidMenu objects to form
 * a menu system. It provides the same functions as LiquidMenu
 * with the addition of add_menu() and change_menu().
 */
LiquidSystem menu_system(main_menu, outputs_menu, inputs_menu);


// Checks all the buttons.
void buttonsCheck() {
  if (right.check() == LOW) {
    menu_system.next_screen();
  }
  if (left.check() == LOW) {
    menu_system.previous_screen();
  }
  if (up.check() == LOW) {
    menu_system.call_function(increase);
  }
  if (down.check() == LOW) {
    menu_system.call_function(decrease);
  }
  if (enter.check() == LOW) {
    menu_system.switch_focus();
  }
}

// Callback function that will be attached to back_line.
void go_back() {
  // This function takes reference to the wanted menu.
  menu_system.change_menu(main_menu);
}

void goto_outputs_menu() {
  menu_system.change_menu(outputs_menu);
}

void goto_inputs_menu() {
  menu_system.change_menu(inputs_menu);
}

void increase_pin6() {
  if (pin6_level < 225) {
    pin6_level += 25;
  } else {
    pin6_level = 250;
  }
  analogWrite(pin6, pin6_level);
  output_saved = (char*)"  ";
}

void decrease_pin6() {
  if (pin6_level > 25) {
    pin6_level -= 25;
  } else {
    pin6_level = 0;
  }
  analogWrite(pin6, pin6_level);
  output_saved = (char*)"  ";
}

void save_input() {
  EEPROM.put(11, sample_period);
  input_saved = (char*)" *";
}

void save_output() {
  EEPROM.put(9, pin6_level);
  output_saved = (char*)" *";
}

void increase_samplePeriod() {
  if (sample_period < 10) {
    sample_period++;
    input_saved = (char*)"  ";
  }
}

void decrease_samplePeriod() {
  if (sample_period > 0) {
    sample_period--;
    input_saved = (char*)"  ";
  }
}

void setup() {
  Serial.begin(250000);

  pinMode(pin6, OUTPUT);

  // Reads the values recorded in the EEPROM
  EEPROM.get(9, pin6_level);
  EEPROM.get(11, sample_period);
  analogWrite(pin6, pin6_level);


  // This is the I2C LCD object initialization.
  lcd.init();
  lcd.backlight();

  // This methid needs to be called when using an I2C display library.
  menu.init();
 
  back_line.set_focusPosition(Position::LEFT);

  back_line.attach_function(1, go_back);
  back_line.attach_function(2, go_back);

  outputs_line.attach_function(1, goto_outputs_menu);
  outputs_line.attach_function(2, goto_outputs_menu);
  inputs_line.attach_function(1, goto_inputs_menu);
  inputs_line.attach_function(2, goto_inputs_menu);

  pin6_line.attach_function(increase, increase_pin6);
  pin6_line.attach_function(decrease, decrease_pin6);

  oSave_line.attach_function(1, save_output);
  oSave_line.attach_function(2, save_output);
  iSave_line.attach_function(1, save_input);
  iSave_line.attach_function(2, save_input);
  iSample_line.attach_function(increase, increase_samplePeriod);
  iSample_line.attach_function(decrease, decrease_samplePeriod);

  input_saved = (char*)" *";
  output_saved = (char*)" *";

  menu_system.update();
}

void loop() {
  buttonsCheck();

  static unsigned long lastMillis_sample = 0;
  if (millis() - lastMillis_sample > (sample_period * 1000)) {
    lastMillis_sample = millis();
    pinA0_value = analogRead(pinA0);
    pinA1_value = analogRead(pinA1);
    menu_system.update();
  }

}

I get this error message:

H_system_menu:226: error: 'menu' was not declared in this scope

menu.init();

^

exit status 1
'menu' was not declared in this scope

the code of the "I_I2C_menu" is pretty much the same and I have used the same libraries but I do not get this message.

Any advice please?

fluxia:
the code of the "I_I2C_menu" is pretty much the same

There is a very important difference. In I_I2C_menu, the LiquidMenu object name is menu:

LiquidMenu menu(lcd);

But in the sketch you posted here, you decided to change the name:

// And this is the final third menu.
LiquidMenu inputs_menu(lcd, pinsA0_A1_screen, iSecondary_screen);

/*
   LiquidSystem object combines the LiquidMenu objects to form
   a menu system. It provides the same functions as LiquidMenu
   with the addition of add_menu() and change_menu().
*/
LiquidSystem menu_system(main_menu, outputs_menu, inputs_menu);

Yet you are still trying to use the old name:

menu.init();

Thank you very much!

I have changed it with:

main_menu.init();

It's working now.

Cheers :slight_smile:

You're very welcome. I'm glad to hear it's working. Enjoy!
Per

Flux can you please post your config.h, I am having problems getting my code to compile for i2c display

keyboardcowboy has a dedicated thread about their issue:
https://forum.arduino.cc/index.php?topic=633464

keyboardcowboy I am on holoday at the moment and will be back home in two weeks. If you have not resolved the issue by then please re-post and I will check my config.h file for you.