Giter VIP home page Giter VIP logo

liquidmenu's Introduction

Logo

Menu creation Arduino library for LCDs, wraps LiquidCrystal.

Download Documentation Documentation GitHub workflow - compile examples MIT license

LiquidMenu wraps Arduino's LiquidCrystal library with the ability to create menus. It simplifies the menu creation process by abstracting the elements of a menu into hierarchically organized classes.

Features

  • fast and easy menu creation
  • selectable menu items
  • callback functions
  • parallel or I2C connection

Resources

Installation

Use Arduino's library manager (recommended) or download it directly from here.

Quick start

Requirements

  • Arduino's LiquidCrystal or similar library
  • LCD supported by LiquidCrystal (with Hitachi HD44780 or a compatible chipset)
  • Arduino board or a compatible microcontroller
  • push-buttons or something similar

Classes organization

The library uses hierarchically structured classes to represent the different elements of a menu.

Basic classes structure diagram:

basic classes structure diagram

The LiquidLine class represents a line of text/numbers on the display.

The LiquidScreen class represents a collection of lines that are shown together on the display (i.e. the current screen).

The LiquidMenu class combines the screens to form a menu. This class is used for controlling the menu (switching between screens, selecting lines, calling functions etc...).

If your structure consists of multiple different menus, for example a "Main menu" that displays sensor information on multiple screens and a "Settings" menu for configuring the sensors, you can combine the different menus inside a LiquidSystem class. That way, you can browse the screens of just the menu you are currently on. LiquidSystem has the same public interface as LiquidMenu. Sub-menus classes structure diagram.

Creating a menu

Menu creation is all about structure. First there are variables/constants that go into the LiquidLine objects. Then the LiquidLine objects go into the LiquidScreen objects. Then LiquidScreen objects go into the LiquidMenu object(s). And optionally the LiquidMenu objects go into the LiquidSystem object. This structure can be established either on object instantiation or later using the classes' methods.

// Takes column and row for the position and 1 to 4 variable references. These variable
// references are what is going to be printed on the display. They can be integers,
// string literals or char[] arrays for dynamic text.
LiquidLine(byte column, byte row, A &variableA...);

// Takes 0 to 4 LiquidLine objects.
LiquidScreen(LiquidLine &liquidLine1...);

// Takes a reference to the LiquidCrystal object, 0 to 4 LiquidScreen objects and
// optionally the number of the screen that will be shown first.
LiquidMenu(LiquidCrystal &liquidCrystal, LiquidScreen &liquidScreen1..., byte startingScreen = 1);

// Takes 0 to 4 LiquidMenu objects and optionally the number of the menu that will be shown first.
LiquidSystem(LiquidMenu &liquidMenu1..., byte startingMenu = 1);

Navigating the menu

The menu is navigated from the LiquidMenu object or if there are multiple menus - the LiquidSystem object. The screens can by cycled forward and backward, it's also possible to jump to a specific screen using its object or consecutive number:

void LiquidMenu::next_screen();
void LiquidMenu::previous_screen();
bool LiquidMenu::change_screen(LiquidScreen &liquidScreen);

Focus and callback functions

The lines of text/numbers shown on the display can be made interactive. Every line can have callback functions attached to it (up to 8 by default). Every attached callback function must be identified by number:

bool LiquidLine::attach_function(byte number, void (*function)(void));

To call a line's attached function, the line needs to be focused (selected). To cycle the focus through the lines shown on the screen use:

void LiquidMenu::switch_focus(bool forward = true);

When the line is selected one of its attached functions can be called with:

void LiquidMenu::call_function(byte number);

The number specifies which one of the attached functions should be called.

Conceptionally similar functions should be attached under the same number to the different lines. For example if we are printing the state of four LEDs. Each LEDis instantiated in a LiquidLine object with their name and their state. The functions used to turn them on can be attached under number 1 and the functions for turning them off - under number 2. Then if we have 3 buttons, one can be used to switch the focus, the second (say 'UP') button can be used to call function 1 and the third (say 'DOWN') button can be used to call function 2.

Basic example

// ...

// First we need to instantiate the LiquidCrystal object.
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);

// a variable that will be updated periodically somewhere in our code
float temperature = 0.0f;

// ----- WELCOME SCREEN -----
// a line of one string literal
LiquidLine lineHello(2, 0, "Hello world!");
LiquidLine lineRight(7, 1, ">");

// create a screen from the above lines
LiquidScreen screenMain(lineHello, lineRight);
// --------------------------

// ----- STATUS SCREEN -----
// a line of two string literals and a float variable
LiquidLine lineTemp(0, 0, "Temp: ", temperature, "C");
LiquidScreen screenStatus(lineTemp);
// -------------------------

// ----- MENU -----
// create a menu from the screens
LiquidMenu menu(lcd, screenMain, screenStatus);
// ----------------

void setup() {
    lcd.begin(16, 2);
    
    // ...
}

void loop() {
    temperature = readTemperature();
    menu.update(); // update the display to show the new information
    
    if (rightButton()) {
        menu.next_screen();
    }
    if (leftButton()) {
        menu.previous_screen();
    }
    
    // ...
}

Community and contributing

❔ If you have a question head over to Discussions - Q&A.

🐛 If you encounter a bug, feel free to open an issue.

💡 To propose a feature go to Discussions - Ideas.

😎 You are also welcome to show off your project in Discussions - Show & Tell.

All other forms of contributions (bug fixes, feature implementations, documentation improvements, typos, etc...) are welcome. To get started see the contributing guide.

Contributors

License

The MIT License (MIT)

Copyright (c) 2016 Vasil Kalchev

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

liquidmenu's People

Contributors

circuitsforfun avatar hezy0426 avatar igorkatkov avatar jmpmscorp avatar njh avatar per1234 avatar robotgrrl avatar thefedone avatar thijstriemstra avatar vasilkalchev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

liquidmenu's Issues

call_function() calls update() for previous menu if change_menu() is in the callback function

Is there an existing issue for this?

  • I have searched the existing issues.

Library version

Yes, from Arduino library manager.

Bug description

In version 1.6.0, menus are not refreshing appropriately when change_menu() is called from a callback function and call_function() has refresh = true. The correct screen is being displayed then overwritten by a screen from the original menu (usually).

Steps to reproduce

Running the H_system_menu example, press the next_screen key to get to io_screen. Press the switch_focus key to focus on Outputs. Press the increase key.

Expected behavior

outputs_menu, pin6_screen appears.

Actual behavior

  • the screen flashes and then the io_menu is re-displayed.
  • Pressing increase again does result in the pin6_screen appearing.
  • Pressing next_screen results in displaying oSecondary_screen, as expected.
  • Moving focus to /BACK then pressing increase results in the io_menu() flashing, the the oSecondary_screen is re-displayed.
  • Pressing increase again does result in io_screen being displayed.

Probable cause

LiquidSystem's call_function calls the current menu's call_function(). If the callback function contains change_menu(), change_menu appropriately calls the new menu's update(), displaying the correct screen. When the callback function exits, control returns to the original menu's call_function(), which calls update() for the original screen, overwriting the correct display.

workaround

Use call_function() with refresh=false, preventing the original menu's call_function() from calling update(). The side-effect is that other callback functions associated with the same function number will have to manually call update().

potential solution

change_menu() should not call update, or should optionally call update.
LiquidSystems's call_function() could always pass refresh=false to the menu's call_function(), then call update() itself based on the refresh value it actually received. I have tested this against the above steps and it works. I have not tested any other conditions.

Compiler output

No response

Unable to get current menu from LiquidSystem

Is there an existing issue for this?

  • I have searched the existing issues.

Library version

Yes, from Arduino library manager.

Bug description

Hi I believe this library has overlooked the addition for a getter for the _currentMenu variable. The user has no way to check what menu they are currently on when using a LiquidSystem. Only the current screen can be checked. This means that reusing screens in menus will cause issues. I would like to be able to reuse my "back" screen, but if I do, I will lose my ability to check what menu I am in.

Compiler output

No response

Screens could link to child menus

Was wondering if this sounded like a good request.
Screens could have attached menus.

Screen.attach_menu(ConfigMenu);
Then, no callbacks would be needed to traverse menus.
The ConfigMenu _parent variable is set to the parent of the screen.

When you select a screen, it would take you to the attached menu.

then if you want to go back
CurrentMenu.go_to_parent(); //if no parent, just returns same menu

Dan

Rotary encoder example

I'm wanting to incorporate LiquidMenu into a project that I'm working on. The only input will be a rotary encoder & button. I'm using the EasyButton library which will do the denounce, plus allow for a short-press & long-press (essentially, 2 buttons in 1).

The idea is to use the encoder for selecting the menu line, or sub-menus, using LiquidSystem. The encoder would also be used to increase or decrease the value of a setting. The "enter" (short-press) button would be used to select the menu line & to make the change active. The "back" (long-press) button would return the main "running" menu screen.

I'm just not sure how to implement this into LiquidMenu.

Edit: I don't HAVE to use a long-press for the return. I can idea new menu line to trigger that.

LiquidMenu objects have to be defined globally

I'm using LiquidMenu for a project with the ESP32 with the LiquidCrystal_I2C library. I'm trying to write solid code with separated functionality over different files. For that i want to define LiquidLine and LiquidMenu objects in the constructor of separate classes. When i do this the program crashes as soon as i try to do an operation on the LiquidMenu object.

Am i overlooking something or is it just not possible with this library?

Code example
This works:

LiquidCrystal_I2C lcd(0x27, 20, 4);
LiquidLine exampleLine(0, 0, "Example");
LiquidScreen exampleScreen(exampleLine);

LiquidMenu menu(lcd);

void setup() {
  lcd.init();
  menu.add_screen(exampleScreen);
  menu.update();
}

But this doesn't:

void setup() {
  LiquidCrystal_I2C lcd(0x27, 20, 4);
  LiquidLine exampleLine(0, 0, "Example");
  LiquidScreen exampleScreen(exampleLine);
  LiquidMenu menu(lcd);

  lcd.init();
  menu.add_screen(exampleScreen);
  menu.update();
}

Version of the library
v1.5.0

Optional screen refresh option when calling a function

Is your feature request related to a problem? Please describe.
i have a 4-key i2c keyboard. Two buttons to change the focus and the other two to enter, exit, increase or decrease a variable.
All manipulation is done through callback functions.

I implemented a functionality on the keyboard that when a key is held down, then the function is called repeatedly in order to change the variable value continuously until reaching the desired value.

the problem is that the LCD display starts to flicker badly

Describe the solution you'd like
It would be interesting if:

  • When attaching a function to a line, we could pass the update type option to that function.

Example:

  • Soft update
    LiquidLine :: attach_function(number, function, false)

  • "Standard" update
    LiquidLine :: attach_function(number, function)

Describe alternatives you've considered
Another option would be to choose when the function call is made:
LiquidMenu :: call_function(number, false)

Additional context
That way it would be easy to keep updating the LCD display without flickering...

Example:
When incrementing a variable, we check inside the function that makes the increment if its resulting value has the remainder of the division by 10 equal to zero!

.
.
.
if (...) {
  int i = i++;
  if ( (i % 10) == 0) {
    menu.update();
  }
. 
.
. 
}

Call a variable amount of function

Hi !

First of all thanks for the great library, it's really useful and clear !

I have a problem with the LiquidLine::call_function method. How it works from what I understand from the source code is that it checks if the amount of function given as parameters is valid. If it is, it calls the function, else it does nothing.

My problems lies in the fact that some line have multiple function in my menu. For example some just have a gotoOtherMenu function but some have a goto to change the screen + an other function.

My buttonsCheck function look like this :

void buttonsCheck(Pushbutton& up, Pushbutton& down, Pushbutton& OK, LiquidMenu& menu)
{
	if(up.getSingleDebouncedPress())
	{
		menu.switch_focus(false); //Go up
	}
	else if(down.getSingleDebouncedPress())
	{
		menu.switch_focus(true); //Go down
	}
	else if(OK.getSingleDebouncedPress())
	{
		menu.call_function(1);//Won't work on line with 2 functions
		//menu.call_function(2);//Won't work on line with 1 functions
	}		
}

The only workaround that I have found is to have all Line object have the same amount of function attached with x amount of empty function attached.

Thanks in advance,

Anthony

Scrollable menu

Is it possible to add scrollable menus? right now it cant be scrolled through if there are more lines in the menu than in the display

Blink cursor over editing position

Hopefully this is not already available and I missed it. If so, I apologize.

Is your feature request related to a problem? Please describe.
While editing, the cursor is always at the end of the line being edited.

Describe the solution you'd like
I would like to be able to control the cursor while editing a field to, for instance, make it blink over the position you're editing. As it currently stands the cursor is always at the end of the current line, so turning on blinking does not solve the problem.

Describe alternatives you've considered
I've tried to manually set blinking and to manually move the cursor to the right position. Blinking alone just makes the cursor blink at the end of the line, and trying to manipulate the cursor position accurately without access to the line positioning (row) and the variable in the current line is not possible.

Additional context
Here's a simple editing demo I made that shows how I would like the blinking to work: https://www.youtube.com/watch?v=M6q4AmBr45s
It also shows the cursor on the left, which is a personal preference of mine as well, so if there is an option to change it that I missed please let me know.

Avoid creating duplicate variables

I may be doing something wrong with the library but I find that at the moment, in order to edit a char[] field I seem to need to create an additional pointer with the sole purpose of passing that pointer to the LiquidLine object. For a couple of fields it's fine, but I have a lot of fields and so I have a lot of additional pointers lying around.

Is there any way to avoid creating such unnecessary variables?

Here's an example of what I'm talking about (the variable I wish to remove is switch_ptr:

char switch_status[4] = "Off";
char* switch_ptr = switch;

LiquidLine line3(0, 1, "Enabled: ", switch_ptr);

void toggleEnabled1() {
  schedules[0].enabled = !(schedules[0].enabled);
  if (schedules[0].enabled) {
    strncpy(switch_status, "On", sizeof(switch_status) - 1);
  } else {
    strncpy(switch_status, "Off", sizeof(switch_status) - 1);
  }
}

line3.attach_function(1, toggleEnabled1);

Cheers!

Quickly losing dynamic memory with strings

LiquidMenu is very convenient, although using strings with it quickly eats up RAM on my Arduino Uno. Would it be possible to use LiquidMenu with strings held in program memory, or is there any other solution to avoid the high memory cost of LiquidMenu's strings on dynamic memory?

Edit: Is set_asProgmem() an option?

Can't compile with Arduino IDE under 1.6.6

Hi guy, I am sorry because maybe is not the place where the question should be.

I have tried to use your lib in an arduino Mega and somehow it is not compiling because it says position, an enum class that is holded in LiquidMenu.h is not a class or a namespace. It gives problems because of being an enum class and cannot access the values in there. Do you know if somebody else has experienced it and how did they solve it?

Thank you very much,

Best regards.

`LiquidLine` pointer not passing

Hi Vasil,

I'm having issues passing a simple string pointer to LiquidMenu. I've found I get odd characters sometimes even in your example files where a string is being passed such as the * in H_system_menu. It's like it's pointing to the wrong memory location.

Here's my simple test. If you pass a variable directly (the commented one that says "this works") it seems to work ok.

It's probably a silly mistake on my part somewhere...

Cheers
Andreas
`// The LCD library
#include <LiquidCrystal.h>
// The menu wrapper library
#include <LiquidMenu.h>

// Pin mapping for the display:
const byte LCD_RS = 8;
const byte LCD_E = 9;
const byte LCD_D4 = 4;
const byte LCD_D5 = 5;
const byte LCD_D6 = 6;
const byte LCD_D7 = 7;
//LCD R/W pin to ground
//10K potentiometer wiper to VO
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);

char str[5] = "MyStr";
char *testPointer = str;

// This works (comment out the above)
// char testPointer[] = "MyStr";

// Here there is not only a text string but also a changing integer variable.
LiquidLine testLine(0, 0, "TestPtr: ", testPointer);
LiquidScreen first_screen(testLine);

LiquidMenu menu(lcd);

void setup() {
Serial.begin(115200);
lcd.begin(16, 2);

// This is the method used to add a screen object to the menu.
menu.add_screen(first_screen);
}

void loop() {
menu.update();
delay(500);
}`

Mixing sub-menus and scrolling lines?

Hello

I'm very confused with the syntax of this library. It seems there is a logic, but I can't figure it out...

Is it possible to have such structure on a 2 lines LCD, for example:

Main Menu:

  • Equipment
  • Parameters
  • Go

Sub menu Equipment:

  • Focal length:...
  • Aperture:...
  • Exposure:...

Sub menu Parameters:

  • LCD Brightn:...
  • LCD Contrast:...
  • Delay:...

Sub menu Go:

  • Confirm
  • Cancel

It is about a day I try and I can't make it work...

Whether I use your example with system menu and I can only do 2 lines submenus, whether I use your example with scrolling menus but I can't do submenus. I really don't undestand how to mix them... Do you have a step by step tutorial (or a link to) ?

All the best

Fred

Wrong `DataType::CHAR_PTR` cast

In class liquidLine incorrect cast for char pointer:
char* variable = *reinterpret_cast<char**>( const_cast<void*>(_variable[number]) );

correct variant is:
char* variable = const_cast<char*>(reinterpret_cast<const char *>(_variable[number]));

API reference is 404

Is there an existing issue for this?

  • I have searched the existing issues.

Library version

Yes, from GitHub's "master" branch.

Bug description

The link to the API reference in main doc is 404.

Compiler output

No response

Can't compile I2C example

IDE 1.8.10
sketch example for I2C gives this error:
no matching function for call to 'LiquidMenu::LiquidMenu(LiquidCrystal_I2C&)'
how can I fix it? library was downloaded from IDE

`change_screen` with `LiquidScreen` pointer

Hi !

I have a global variable
LiquidScreen* previous_screen;

Before changing screen, I save the current one in this variable (I have a back button on my board which I would like to use to go to the previous screen) I proceed like this :
previous_screen = menu.get_currentScreen();

When I press the back button this is called:
menu.change_screen(previous_screen);

but when I try to compile I have this error:
invalid conversion from 'LiquidScreen*' to 'uint8_t {aka unsigned char}' [-fpermissive]

I don't understand why it tries to call the function with an uint8_t as parameter and not this one
bool change_screen(LiquidScreen &p_liquidScreen);

I can't use the previous_screen() function because my menu is quite long and the screens are not really ordered by number. Sorry if this is a newb question !

Have a great day,

Anthony

`switch_focus` iterates from the last focusable line to `_lineCount` instead of the first line

Describe the bug
When iterating through the selected lines on a screen with next_screen() (forward/backward same issue), the last selected line is the line at _lineCount, except this line doesn't actually exist - so the cursor disappears for one line.

To Reproduce
After noticing the bug I tried the J_scrolling_menu-example, with the following changes:

  • 20x4 screen --> screen.set_displayLineCount(4);
  • I2C-library
    Bug occures in the example as well. Simply execute the function and watch the cursor disappear for 1 iteration.

Related code
// to solve the bug, I replaced all occurances of "_lineCount" in the following functions in
//LiquidScreen.cpp. I did NOT check if this breaks something else.
void LiquidScreen::switch_focus(bool forward) {
print_me(reinterpret_cast<uintptr_t>(this));
do {
if (forward) {
if (_focus < _lineCount -1) {
_focus++;
if (_focus == _lineCount-1) {
break;
}
} else {
_focus = 0;
}
} else { //else (forward)
if (_focus == 0) {
_focus = _lineCount-1;
break;
} else {
_focus--;
}
} //else (forward)
} while (_p_liquidLine[_focus]->_focusable == false);
DEBUG(F("Focus switched to ")); DEBUGLN(_focus);
}

Version of the library
V1.5.1

Date string `char*`

Hello,
I want to use date Time string on the welcom screenshots. I get this date/Time from rtc library(DS3231) and convert it to a char*[20] but it doesnt display.
The same datestring is correctly displayed with LCD.print()

Switch focus disappears for one step after an iteration

Describe the bug
Screen having 4 lines on last line switch_focus switches to position 4. On screen focus arrow just disappears. Then, on position 0 it switches back to the first line.

Related code

#include <LiquidCrystal_I2C.h>
#define LIQUIDMENU_LIBRARY LiquidCrystal_I2C_LIBRARY
#define DisplayClass LiquidCrystal_I2C
#include <LiquidMenu.h>

#define FUNC_NUM 1

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

LiquidLine line1(1, 0, "Setting 1");
LiquidLine line2(1, 1, "Setting 2");
LiquidLine line3(1, 2, "Setting 3");
LiquidLine line4(1, 3, "Setting 4");
LiquidScreen settings_screen(
  line1, 
  line2, 
  line3, 
  line4);

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

  lcd.init();
  lcd.backlight();

  settings_screen.set_focusPosition(Position::LEFT);
  line1.attach_function(FUNC_NUM, doNothing);
  line2.attach_function(FUNC_NUM, doNothing);
  line3.attach_function(FUNC_NUM, doNothing);
  line4.attach_function(FUNC_NUM, doNothing);

  menu.init();
  menu.set_focusPosition(Position::LEFT);
  menu.add_screen(settings_screen);
  menu.update();
}

void doNothing() {}

void loop() {
  menu.switch_focus(); 
  Serial.println(menu.get_focusedLine());
  delay(3000);
}

Capture13

Version of the library
1.5.1

Switching focus symbol accross menus

When I try to change the focus symbol of 2 menus, only the last define symbol is used for all menus.

main_m.set_focusSymbol(Position::LEFT, glyphs::rFocus);
main_m.set_focusSymbol(Position::RIGHT, glyphs::lFocus);
...
light_on_m.set_focusSymbol(Position::LEFT, glyphs::udFocus);
light_on_m.set_focusSymbol(Position::RIGHT, glyphs::udFocus);

On the main menu, I see the udFocus glyph.

For convenient glyphs used

	uint8_t lFocus[8]= {
		0b00001,
		0b00011,
		0b00111,
		0b01111,
		0b00111,
		0b00011,
		0b00001,
		0b00000
	};

	uint8_t rFocus[8] = {
		0b10000,
		0b11000,
		0b11100,
		0b11110,
		0b11100,
		0b11000,
		0b10000,
		0b00000
	};

	uint8_t udFocus[8]= {
		0b00100,
		0b01110,
		0b11111,
		0b00000,
		0b00000,
		0b11111,
		0b01110,
		0b00100
	};

And the menu creation

LiquidMenu main_m(lcd, home_s, light_s);
LiquidMenu light_on_m(lcd, light_on_s);
LiquidMenu light_off_m(lcd, light_off_s);

LiquidSystem menu(main_m, light_on_m, light_off_m);

Scrolling on 4 row LCD broken

Describe the bug
Setting up a 4 row LCD with lines in the same way as the J_scrolling_menu example produces odd scrolling behavior

To Reproduce
Add 5 or more LiquidLines, numbered as such
LiquidLine(0, 0);
LiquidLine(0, 1);
LiquidLine(0, 2);
LiquidLine(0, 3);
LiquidLine(0, 3);
LiquidLine(0, 3); etc...

Version of the library
1.5

LiquidCrystal not found even if it's installed

Is there an existing issue for this?

  • I have searched the existing issues.

Library version

Yes, from Arduino library manager.

Bug description

I create a new project using your example HelloWorld.
It does not compile because it complains about the missing of LiquidCrystal library.

As you can see from the output below the LiquidCrystal library is installed, detected and included. Still the compiler does not find it from LiquidMenu.h.

I'm running Visual Studio Code 1.65.2, PlatformIO 5.2.5 under Ubuntu 21.10.

Compiler output

> Executing task in folder USBMidi: platformio run --environment leonardo <

Processing leonardo (platform: atmelavr; board: leonardo; framework: arduino)
------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/atmelavr/leonardo.html
PLATFORM: Atmel AVR (3.4.0) > Arduino Leonardo
HARDWARE: ATMEGA32U4 16MHz, 2.50KB RAM, 28KB Flash
DEBUG: Current (simavr) On-board (simavr)
PACKAGES: 
 - framework-arduino-avr 5.1.0 
 - toolchain-atmelavr 1.70300.191015 (7.3.0)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 9 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <LiquidCrystal> 1.0.7
|-- <LiquidMenu> 1.6.0
|-- <MIDIUSB> 1.0.5
|-- <EncButton> 1.21.0
Building in release mode
Compiling .pio/build/leonardo/src/main.cpp.o
Compiling .pio/build/leonardo/lib675/LiquidMenu/LiquidLine.cpp.o
Compiling .pio/build/leonardo/lib675/LiquidMenu/LiquidMenu.cpp.o
Compiling .pio/build/leonardo/lib675/LiquidMenu/LiquidScreen.cpp.o
In file included from .pio/libdeps/leonardo/LiquidMenu/src/LiquidLine.cpp:30:0:
.pio/libdeps/leonardo/LiquidMenu/src/LiquidMenu.h:53:10: fatal error: LiquidCrystal.h: No such file or directory

***********************************************************************
* Looking for LiquidCrystal.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:LiquidCrystal.h"
* Web  > https://registry.platformio.org/search?q=header:LiquidCrystal.h
*
***********************************************************************

 #include <LiquidCrystal.h>
          ^~~~~~~~~~~~~~~~~
compilation terminated.
In file included from src/main.cpp:5:0:
.pio/libdeps/leonardo/LiquidMenu/src/LiquidMenu.h:52:123: note: #pragma message: LiquidMenu: Selected 'LiquidCrystal' (parallel) library. Edit 'LiquidMenu_config.h' file to change it.
 # pragma message ("LiquidMenu: Selected 'LiquidCrystal' (parallel) library. Edit 'LiquidMenu_config.h' file to change it.")
                                                                                                                           ^
*** [.pio/build/leonardo/lib675/LiquidMenu/LiquidLine.cpp.o] Error 1
In file included from .pio/libdeps/leonardo/LiquidMenu/src/LiquidScreen.cpp:30:0:
.pio/libdeps/leonardo/LiquidMenu/src/LiquidMenu.h:53:10: fatal error: LiquidCrystal.h: No such file or directory

***********************************************************************
* Looking for LiquidCrystal.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:LiquidCrystal.h"
* Web  > https://registry.platformio.org/search?q=header:LiquidCrystal.h
*
***********************************************************************

 #include <LiquidCrystal.h>
          ^~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio/build/leonardo/lib675/LiquidMenu/LiquidScreen.cpp.o] Error 1
In file included from .pio/libdeps/leonardo/LiquidMenu/src/LiquidMenu.cpp:30:0:
.pio/libdeps/leonardo/LiquidMenu/src/LiquidMenu.h:53:10: fatal error: LiquidCrystal.h: No such file or directory

***********************************************************************
* Looking for LiquidCrystal.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:LiquidCrystal.h"
* Web  > https://registry.platformio.org/search?q=header:LiquidCrystal.h
*
***********************************************************************

 #include <LiquidCrystal.h>
          ^~~~~~~~~~~~~~~~~
compilation terminated.
*** [.pio/build/leonardo/lib675/LiquidMenu/LiquidMenu.cpp.o] Error 1
======================================================= [FAILED] Took 0.43 seconds =======================================================
The terminal process "platformio 'run', '--environment', 'leonardo'" terminated with exit code: 1.

Terminal will be reused by tasks, press any key to close it.

'recognizeType(int&)' is ambiguous

Is there an existing issue for this?

  • I have searched the existing issues.

Library version

Yes, from Arduino library manager.

Bug description

Hi, when using your library in arduino is fine, but if I use stm32 appears this error:

\LiquidMenu\src/LiquidMenu.h:428:37: error: call of overloaded 'recognizeType(int&)' is ambiguous

Can you make an C STM32 compatible version?

[env:genericSTM32F103C8] platform = ststm32 board = genericSTM32F103C8 framework = arduino lib_deps = fmalpartida/LiquidCrystal@^1.5.0 vase7u/LiquidMenu@^1.6.0 adafruit/Adafruit Unified Sensor@^1.1.13 bxparks/AceButton@^1.10.1 greiman/SdFat@^2.2.2 toannv17/DHT Sensors Non-Blocking@^1.0.4

Compiler output

In file included from .pio\libdeps\genericSTM32F103C8\LiquidCrystal/LiquidCrystal.h:45,
                 from src\main.cpp:3:
.pio\libdeps\genericSTM32F103C8\LiquidCrystal/FastIO.h:217:106: warning: 'boolean' is deprecated [-Wdeprecated-declarations]
  217 | void fio_shiftOut1(fio_register shift1Register, fio_bit shift1Bit, uint8_t value, boolean noLatch = false);
      |                                                                                                          ^
In file included from C:\Users\Fabio\.platformio\packages\framework-arduinoststm32\cores\arduino/wiring.h:34,
                 from C:\Users\Fabio\.platformio\packages\framework-arduinoststm32\cores\arduino/Arduino.h:36,
                 from src\main.cpp:1:
C:\Users\Fabio\.platformio\packages\framework-arduinoststm32\cores\arduino/wiring_constants.h:110:14: note: declared here
  110 | typedef bool boolean __attribute__((deprecated));
      |              ^~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidCrystal/FastIO.h:225:71: warning: 'boolean' is deprecated [-Wdeprecated-declarations]
  225 | void fio_shiftOut1(uint8_t pin, uint8_t value, boolean noLatch = false);
      |                                                                       ^
C:\Users\Fabio\.platformio\packages\framework-arduinoststm32\cores\arduino/wiring_constants.h:110:14: note: declared here
  110 | typedef bool boolean __attribute__((deprecated));
      |              ^~~~~~~
In file included from src\main.cpp:4:
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h: In instantiation of 'bool LiquidLine::add_variable(T&) [with T = int]':
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:372:17:   required from 'LiquidLine::LiquidLine(uint8_t, uint8_t, A&, B&) [with A = const char [6]; B = int; uint8_t = unsigned char]' 
src\main.cpp:54:51:   required from here
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:428:37: error: call of overloaded 'recognizeType(int&)' is ambiguous
  428 |     DataType varType = recognizeType(variable);
      |                        ~~~~~~~~~~~~~^~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:149:10: note: candidate: 'DataType recognizeType(bool)'
  149 | DataType recognizeType(bool variable);
      |          ^~~~~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:155:10: note: candidate: 'DataType recognizeType(char)'
  155 | DataType recognizeType(char variable);
      |          ^~~~~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:173:10: note: candidate: 'DataType recognizeType(int8_t)'
  173 | DataType recognizeType(int8_t variable);
      |          ^~~~~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:179:10: note: candidate: 'DataType recognizeType(uint8_t)'
  179 | DataType recognizeType(uint8_t variable);
      |          ^~~~~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:185:10: note: candidate: 'DataType recognizeType(int16_t)'
  185 | DataType recognizeType(int16_t variable);
      |          ^~~~~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:191:10: note: candidate: 'DataType recognizeType(uint16_t)'
  191 | DataType recognizeType(uint16_t variable);
      |          ^~~~~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:197:10: note: candidate: 'DataType recognizeType(int32_t)'
  197 | DataType recognizeType(int32_t variable);
      |          ^~~~~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:203:10: note: candidate: 'DataType recognizeType(uint32_t)'
  203 | DataType recognizeType(uint32_t variable);
      |          ^~~~~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:209:10: note: candidate: 'DataType recognizeType(float)'
  209 | DataType recognizeType(float variable);
      |          ^~~~~~~~~~~~~
.pio\libdeps\genericSTM32F103C8\LiquidMenu\src/LiquidMenu.h:215:10: note: candidate: 'DataType recognizeType(double)'
  215 | DataType recognizeType(double variable);
      |          ^~~~~~~~~~~~~
*** [.pio\build\genericSTM32F103C8\src\main.cpp.o] Error 1
=================================================================================== [FAILED] Took 3.83 seconds ===================================================================================

`delete_variable()` and `detach_function()`

Forgive me ... first post on Github. Nice library ...

Given we have the facility to add a variable - might it be nice to delete too ?

Also, given we can attach a function - ability to detach might be nice ?

Cheers,

Glenn.

Can't compile on ESP8266 boards

.../Documents/Arduino/libraries/LiquidMenu/src/LiquidLine.cpp: In member function 'bool LiquidLine::attach_function(uint8_t, void ()())':
.../Documents/Arduino/libraries/LiquidMenu/src/LiquidLine.cpp:37:21: error: cast from 'LiquidLine
' to 'uint16_t {aka short unsigned int}' loses precision [-fpermissive]
print_me((uint16_t)this);
^
.../Documents/Arduino/libraries/LiquidMenu/src/LiquidLine.cpp: In member function 'bool LiquidLine::set_focusPosition(Position, uint8_t, uint8_t)':
.../Documents/Arduino/libraries/LiquidMenu/src/LiquidLine.cpp:51:21: error: cast from 'LiquidLine*' to 'uint16_t {aka short unsigned int}' loses precision [-fpermissive]
print_me((uint16_t)this);

`snprintf` prints jibberish

Describe the bug
Part of me hopes this is a bug and not a lack of my own coding skills :P!
Im trying to preformat a char array so I can have it look the way I want before
printing it to the screen.

Using snprintf I can format my output and display it in Serial. I have used this way of formatting char strings before the use of liquidmenu to print text the way I want it on an LCD. With liquidmenu I get trash characters displayed.

To Reproduce
Try code below

Screenshots
Serial
20190406_055214

Related code

char txtbuffer[17] = "Empty";
LiquidLine test(0, 0, txtbuffer);
LiquidScreen testS(test);

void setup() {
  Serial.begin(9600);
  setupEncoder();
  setupMenu();
  
  Serial.println(txtbuffer);
  snprintf(txtbuffer, 17, "BUFF: %s", "test");
  Serial.println(txtbuffer);
  menu.add_screen(testS);
  menu.update();
  
}

Version of the library
1.4.0

Edit:

This is what I am aiming for, as an "timeout/standby" display, to give a hint of what I'm trying to accomplish
image

Usage of `PROGMEM` strings

hello, due to some problems with a too low amount of RAM I've tried to use the Flash memory to store the text lines present inside the menu.. but I had some problems. I've tried to use the PROGMEM command and the relative methods to recall stored data, but the printet characters on lcd seems to be wrong converted (numbers or simbols). Can you explain the correct way to put flash stored text inside a liquidline object? Please, be detailed... I'm just a mechanical engineer :-)

Show screens that are not part of a `LiquidMenu` object

Is your feature request related to a problem? Please describe.
Screens such as

  • welcome screen
  • Confirmation dialogs
  • "Standby" screens

Do not seem to be possible without including them in the LiquidMenu object?
Or am I supposed to have 2 LiquidMenu options to achieve this?

I have some screens that are displayed using a rotary encoder and wish to have some screens
that are displayed at specific events. With limited RAM on my nano I wish not to create a new LiquidMenu object just for this.

Describe the solution you'd like
Allow
bool LiquidMenu::change_screen ( LiquidScreen & p_liquidScreen )

to take and show screens even if they are not in the menu object!
I cant get it to work right now.

I want to use a welcome screen not part of the standard menu aswell as a timeout screen when no user input has been made for some while. Theese should not be a part of the user interactable menu,

Describe alternatives you've considered
2x LiquidMenu objects.. requires to much code

`switch_focus` return `bool`

Is your feature request related to a problem? Please describe.
I am trying to implement menu control using only 1 rotary encoder with push-button built-in.
I'm struggling with making a menu where there are some MenuLines that do not have a function attached but merely act as a "title" and have the variable below it.
Every MenuScreen is 2 MenuLines only,

  • MenuLine1 is a title

  • MenuLine 2 is a variable to change.

Currently encoders left/right swaps screens, When the focus switches via encoder button press left/right becomes functioncallers untill i push the button to end the focus.

The issue in short as I interpret it:
After some presses on the button, the "title"/MenuLine1 gets focused and has no function and I get blocked from returning to the left/right navigation function.

Describe the solution you'd like
Change:
void LiquidMenu::switch_focus | ( | bool | forward = true | ) |  

to:
bool LiquidMenu::switch_focus | ( | bool | forward = true | ) |  

Make the return true if the previous/next MenuLine focused has 1 or more functions attached, false otherwise.

Describe alternatives you've considered
alternatively skip "switch_focus" of MenuLines that do not have functions attached, there is nothing to focus then?

Additional context
I have not played enough with your library to fully understand it, I wonder if I am taking a wrong approach here, perhaps it's not ment to have "dumb title MenuLines" combined with "function attached MenuLines". If above is not possible, I'd love any suggestion to control LiquidMenu with only 1 rotary encoder.

Current state of my loop

void loop() {
  // Interrupt for encoder
  if (intrSwitch || intrEncod)
  {
    if (intrSwitch) 
    {
      encoderResult=readEncoderButton();
      switch(encoderResult)
      {
        case 1:
          Serial.println("Menu press");
          menu.switch_focus();
          isFocused = !isFocused;
          break;
        case 2:         
          Serial.println("Menu hold");
          //menu.call_function(1);
          break;
      }
      intrSwitch = 0;
    }
    else if (intrEncod)
    {
      encoderResult=readEncoder();
      switch(encoderResult)
      {
        case 1:
          Serial.println("Menu Previous");
          if (isFocused)
            menu.call_function(1);
          else          
            menu--;
          break;
        case 2:
          Serial.println("Menu Next");
          if (isFocused)
            menu.call_function(2);
          else
            menu++;
          break;     
      } 
      intrEncod = 0;
    } 
    lastActionMillis = millis();    
  }
}

End result I wish for I guess.
Changing this

case 1:
          Serial.println("Menu press");
          menu.switch_focus();
          isFocused = !isFocused;
          break;

To this

case 1:
          Serial.println("Menu press");          
          isFocused = menu.switch_focus();
          break;

`LiquidSystem::is_callable` makes an undesired `LiquidMenu::call_function`

Describe the bug
when i use the is_callable function it make also an call_function with the same id

To Reproduce
I was testing out the "H" example in order to use UP/DOWN buttons to navigate through the menu and then, if the focused line had a incremental/decremental function attached (using is_callable function), using the SELECT button i enable the UP/DOWN button in order to modify the value. Instead the SELECT button make another action (ex. back buttons)

Possible solving solution
The problem seems to be on the "LiquidSystem.cpp" where:

bool LiquidSystem::is_callable(uint8_t number) const {
return _p_liquidMenu[_currentMenu]->call_function(number);
}

instead of

bool LiquidSystem::is_callable(uint8_t number) const {
return _p_liquidMenu[_currentMenu]->is_callable(number);
}

Now the code seems to be work

`LiquidCrystal_I2C` extension

Hi.

This repository is really great.
I am using it with other library for the same LCD connected with I2C and LiquidCrystal_I2C library.
It works exactly with the same functions of standard LyquidCrystal library; however with I2C connection is simple and fast to connect.

I have created another library replacing the object and the library calls, and it compiles. But the pointer for "_p_liquidCrystal" attribute of LiquidMenu Class does not work calling outside setup and loop like global variables.

However, inside setup i create a local LiquidMenu object and it works really great and my lcd respond... But i need a global object to manipulate it inside loop and other custom functions.

I´m going crazy, because i don´t really understand what is the main problem. I need some help for this xtension and create a fork.

Greetings.

Configure the amount of digits shown after the decimal point

First off, awesome library.....I've been looking for a menu library for a long time that I can understand with my limit coding skills and yours is very understandable. Now to my issue. I am trying to display a float variable on a 20x4 LCD display with 3 places past the decimal. I was able to do this when I was just using the lcd.print by passing in the decimal place argument after the variable. I am not seeing where I can do this in the LiquidLine setup and everything I try ends up with a compile error. Again my coding skills are very basic and this may be something that can be done outside of the LiquidMenu scope but I'm not sure how. Any help would be greatly appreciated.

How to use `char` array for dynamic text?

I am trying to do what seems like it would be simple. I want to show a string in the ScreenLine that I can change programmatically. I have tried a simple char *[] and copying a string to it and also a multi-dimensional string like char mode[][4] = {"Off", "On "} and using mode[currMode] in the ScreenLine, nothing seems to work. Can I see an example of a dynamically changing string? everything I see in the examples are of ints, floats, etc.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.