Projekt: LCDMenuLib / LCDMenuLib2 ( LCDML ) - Menü mit mehreren Ebenen

Hi Jomelo, thank you very much for your work with the library. For years I have been working with a project without being able to finish it due to the menu that does not overflow the memory of my Mega.
A few months ago I found your library and step by step I am understanding how it work.
The only problem that I could not solve is with the screensaver function that reactivates "itselfe" occasionally in an inexplicable way, possibly something I have done wrong.
I am working with simple functions that I use in various menu positions helped with the getID to return the menu positions. What I can't find is a way to get a function to return the "name" (lang_char_array) of the id . I belive to have seen it in one of the 106 pages, but my German doesn't help me. If you have a clue it will be welcome.
Thanks again.

Osvaldo
From Argentina

Good morning,

in this post i will answer the last three questions from orlin278, rspecht, osvaldoll on german / english ,-)

@ orlin278
I have understead what you mean. This way is at the moment not implemented but it is a good idea an i will add this in the next version. To track this idea, please look here: Implement a way to use dynamic menu elements with sub menus. · Issue #72 · Jomelo/LCDMenuLib2 (github.com)

@ respecht
Die Encoder Funktion wurde mit den letzten Versionen auf einen neuen Stand gebracht. Teste mal bitte den folgenden Code ob er bei dir besser läuft.
LCDMenuLib2/LCDML_control.ino at master · Jomelo/LCDMenuLib2 (github.com)

@ osvaldoll
Hello, this function are implemented as macros. and used for example on this line:
LCDMenuLib2/LCDML_display_menu.ino at master · Jomelo/LCDMenuLib2 (github.com)
LCDML_getContent(var, id)
This macro have no retun value. You give the variable as reference to this function and after call this line, the searched content is available on the "var" variable you have declared.

Best Regards

Jomelo:
..........................

@ osvaldoll
Hello, this function are implemented as macros. and used for example on this line:
LCDMenuLib2/LCDML_display_menu.ino at master · Jomelo/LCDMenuLib2 (github.com)
LCDML_getContent(var, id)
This macro have no retun value. You give the variable as reference to this function and after call this line, the searched content is available on the "var" variable you have declared.

Best Regards

Hi Jomelo, thank you for your quick reply and the solution I was looking for.
I will try including your information in the function i'm working on.

Best Regards
Osvaldoll

@ orlin276

I have implement this feature in the latest master branch on github, (V2.2.7 beta)

You can enable the submenus for dynamic function when you are using this line in the arduino setup function.

 // Enable special for dynamic menu elements submenus
    LCDML.MENU_enUseDynElementsWithSubElements();

When you are using it, the "enter" button have no effekt in dynamic functions.
Jomelo/LCDMenuLib2: Create a tree menu. Use it with different lcd types / console output / ssh console. (github.com)

Hallo
Ich bin kein programmier Experte, aber habe es durch viel herum probieren, lesen, testen und noch mehr lesen hinbekommen das Menü zum laufen zu bekommen und zu verstehen, wie ich darin meinen eigenen Code ausführen kann. Kurz zum Verständnis was ich vor habe:
Ich will mit einem WS2811 Led Strip Röhren Leuchten bauen (für Video Produktionen usw.). Die Röhren sollen verschiedene Farben (komplett, in Segmenten), sowie Effekte darstellen können. Um all diese Funktionen übersichtlich steuern zu können hilft mir das Menü enorm.

Wenn ich es richtig verstanden habe, kann ich aber nicht die "dynParam" als Menüpunkt verwenden wenn Zeitgleich meine Backend Funktion laufen soll. Deshalb habe ich erstmal probiert ohne Display (Oled) alle LEDs beim auswählen des Menupunkts zu starten, im Loop laufen zu lassen und beim verlassen wieder auszuschalten (das hat ewig gedauert, da ich erstemal nicht gefunden hab wie der Loop als Loop ohne Eingabe läuft). Das läuft jetzt aber soweit. Da ich aber während dessen auch Parameter verändern möchte (zb. Brightness, Temperature) habe ich erstmal probiert die Variable Brightness mit dem Encoder zu verändern. Dies klappt soweit gut und Flüssig, allerdings:
Sobald ich mir den Wert auf dem Display auch ausgeben lassen möchte, wird alles sehr langsam und der Wert zieht lange nach (wenn ich schnell drehe).
Gibt es die Möglichkeit dies zu beschleunigen, oder ist mein kompletter Ansatz falsch?

Das ganze Menü besteht am Ende in erster Linie aus dem Hauptmenü und dahinter aus mehreren Variablen welche mit dem Encoder verstellt werden sollen. Submenü Punkte gibt es eigentlich fast gar nicht.
Oder lässt sich das doch über "dynParam" realisieren? Da ist ja eigentlich schon alles drin, was ich benötige.

Habe herausgefunden warum die Funktion so langsam lief. Die Display Höhe und Breite war nur für LCD gesetzt und nicht für mein Oled Display. Jetzt läuft es flüssig.
Jetzt hätte ich trotzdem noch die Frage, ob ich nicht doch mit "dynParam" arbeiten kann um mehrere Werte anzupassen?
Beispiel:

-Solid White (Root Menu Punkt)
-Temperature 2700 - 6000 (Variable Einstellbar mit Encoder)
-Brightness 0 - 100 (Variable Einstellbar mit Encoder)
-Solid RGB
-Hue 0 - 360 (Variable Einstellbar mit Encoder)
-Brightness 0 - 100 (Variable Einstellbar mit Encoder)
-Saturation 1 - 100 (Variable Einstellbar mit Encoder)
-Presets

Sobald "Solid White" aufgerufen wird, soll die Led angehen und dann "Live" die Variablen verändert werden können.

Moin,

klar geht das. Einfach pro dynParam Wert eine eigene globale Variabel anlegen und im Hintergrund in der Haupt-Loop-Funktion passend drauf reagieren.

Jomelo:
@ orlin276

I have implement this feature in the latest master branch on github, (V2.2.7 beta)

You can enable the submenus for dynamic function when you are using this line in the arduino setup function.

 // Enable special for dynamic menu elements submenus

LCDML.MENU_enUseDynElementsWithSubElements();




When you are using it, the "enter" button have no effekt in dynamic functions. 
[Jomelo/LCDMenuLib2: Create a tree menu. Use it with different lcd types / console output / ssh console. (github.com)](https://github.com/Jomelo/LCDMenuLib2)

Thanks @Jomelo for your work, I just had time to look into it.

Edit:
Now I found a way to implement it, thanks. I post my example below and explain it here:
The idea is to enable the SubElements part in the function where you want to have NO subelements. Like this it is active all the time.
If no the line number is the same as the cursor-position, you disable the dynamic subelement with the function
LCDML.MENU_disUseDynElementsWithSubElements();
Like this the enter button can be used again fo entering the function (I'm calling a function t3 for testing here, this can be used for pretty much everything).
It has to be in this order, otherwise it is not working!

Now I just have to figure out how I can modify the display_menu function in a way I want it :smiley:

LCDML_addAdvanced (22 , LCDML_0         , 7  , NULL,   ""       , t1,                0,   _LCDML_TYPE_dynParam);                     // NULL = no menu function
LCDML_addAdvanced (23 , LCDML_0_7       , 1  , NULL,   ""       , t2,                0,   _LCDML_TYPE_dynParam);                     // NULL = no menu function
LCDML_addAdvanced (24 , LCDML_0_7       , 2  , NULL,  "test3"   , mFunc_screensaver, 0,   _LCDML_TYPE_default);                     // NULL = no menu function
 
void t2(uint8_t line) {
  LCDML.MENU_enUseDynElementsWithSubElements();
  if (line == LCDML.MENU_getCursorPos()) {
    LCDML.MENU_disUseDynElementsWithSubElements();
    if(LCDML.BT_checkAny()) {
      if(LCDML.BT_checkEnter()) {
        t3();
      }
      if (LCDML.BT_checkLeft()) {
        counterTest--;
      }
      if (LCDML.BT_checkRight()) {
        counterTest++;
      }
  }
  }
  Serial.print("d:");
  Serial.print(counterTest);
  Serial.print(", ");
  Serial.print(timeMilli); 
}

This was part of the old message. I just wanted to keeep it because maybe somebody will face the same problems.

Unfortunately it is not working for me like this.
The reason ist following:
I need some dynamic menu entries which are calling a function and some entries which are entries with submenus.
Therefore like this it is only possible to have one of these, as I cannot call the function when i disable the enter button (I want to use the enter-button (which I know is a part of the problem)).
Maybe you thought of a workaround for this, I didn't find any (but it's your library, you know it way better than I do ;)).
If maybe I am allowed to propose a idea to you:
I see, that there could be problems when removing the constant tag for the menu entries, but I would still prefer this as then one wouldn't has to distinguish between dynamic and default parameter any more.
Another idea would be to add a third menu type, so that you have default, dynParam and dynParam_menuEntry. I think this would be the easiest (but I'm still impressed about this library and am pretty happy to understand roughly how to work with it).
Third idea (I think this is more an idea for Version 3 of the library) is to add an additional function for the naming of the menu entries. It is a little bit like idea 1, but in a more ordered way.

Wie bekomme ich es denn hin, dass meine Funktion im Hintergrund läuft? Sobald ich dynParam einbinde, wird die Funktion (z.B. Solid White) nicht mehr gestartet.
Sorry wenn das eigentlich offensichtilich ist, aber ich bekomme es irgendwie nicht heraus.

Englisch (german below)
As my last program (see quote at the end of this post) had some errors which is why it only worked in specific cases, I made an update to it.

Important is that you have to decide if you want a dynamic menu with submenus or one which should call an other function.

Always start the function with checking if you are in the correct line (like in the examples from Jomelo). Now the next call is either enable or disable the use of submenus, depending if this entry is one with or without submenus (see example code below.
Like this every time you are changing your cursor position you get the correct status for enter-button (enabled or disabled for dynamic entries).

German
Hallo, mein letztes Beispielprogram hatte leider einen Fehler, so dass es nur in bestimmten Situationen korrekt funktionierte. Daher hier ein Update.

Wichtig ist die Unterscheidung, ob der Menüeintrag Untermenüs hat oder nicht, je nachdem muss die Funktion leicht abgeändert werden.

Nach Prüfung, ob der Cursor auf der jeweiligen Linie des Menüs steht sollte man direkt bei einem Menü mit Untermenüs die Funktion "LCDML.MENU_enUseDynElementsWithSubElements();" ausgeführt werden, wenn das Menü keine Untermenüs hat muss "LCDML.MENU_disUseDynElementsWithSubElements();" ausgeführt werden. Danach ganz normal die Methode schreiben (siehe Beispiele unten).

LCDML_addAdvanced (0 , LCDML_0         , 1  , NULL,          ""       , t0,                0,   _LCDML_TYPE_dynParam);
LCDML_addAdvanced (1 , LCDML_0         , 2  , NULL,          ""       , t1,                0,   _LCDML_TYPE_dynParam);
LCDML_addAdvanced (2 , LCDML_0_2       , 1  , NULL,          ""       , t2,                0,   _LCDML_TYPE_dynParam);
LCDML_addAdvanced (3 , LCDML_0_2       , 2  , NULL,          "test3"  , mFunc_screensaver, 0,   _LCDML_TYPE_default);
LCDML_addAdvanced (4 , LCDML_0_2       , 3  , NULL,          ""       , t3,                0,   _LCDML_TYPE_dynParam);
LCDML_addAdvanced (5 , LCDML_0_2_3     , 1  , NULL,          "test3"  , mFunc_screensave   0,   _LCDML_TYPE_default);
LCDML_addAdvanced (6 , LCDML_0_2_3     , 2  , NULL,          "test5"  , mFunc_screensaver, 0,   _LCDML_TYPE_default);

#define _LCDML_DISP_cnt  6

LCDLM_createMenu(_LCDML_DISP_cnt)

//[...]
// this should call a function (not any submenus)
void t1(uint8_t line) {
  if (line == LCDML.MENU_getCursorPosition()) {
    LCDML.MENU_disUseDynElementsWithSubElements();
    if (LCDML.BT_checkAny()) {
      if (LCDML.BT_checkEnter()) {
        //do whatever you want
      }
    }
  }
  // write what will be shown on screen
}

// this is a menu with submenus (enter should call submenus)
void t2(uint8_t line) {
  if (line == LCDML.MENU_getCursorPosition()) {
    LCDML.MENU_enUseDynElementsWithSubElements();
    if (LCDML.BT_checkAny()) {
      if (LCDML.BT_checkEnter()) {
        // not active, cannot do anything
      }
    }
  }
  // write what will be shown on screen
}

Edit:
Now I found a way to implement it, thanks. I post my example below and explain it here:
The idea is to enable the SubElements part in the function where you want to have NO subelements. Like this it is active all the time.
If no the line number is the same as the cursor-position, you disable the dynamic subelement with the function
LCDML.MENU_disUseDynElementsWithSubElements();
Like this the enter button can be used again fo entering the function (I'm calling a function t3 for testing here, this can be used for pretty much everything).
It has to be in this order, otherwise it is not working!

Now I just have to figure out how I can modify the display_menu function in a way I want it :D

Code: [Select]

LCDML_addAdvanced (22 , LCDML_0         , 7  , NULL,   ""       , t1,                0,   _LCDML_TYPE_dynParam);                     // NULL = no menu function

LCDML_addAdvanced (23 , LCDML_0_7       , 1  , NULL,   ""       , t2,                0,   _LCDML_TYPE_dynParam);                     // NULL = no menu function
LCDML_addAdvanced (24 , LCDML_0_7       , 2  , NULL,  "test3"   , mFunc_screensaver, 0,   _LCDML_TYPE_default);                     // NULL = no menu function
 
void t2(uint8_t line) {
  LCDML.MENU_enUseDynElementsWithSubElements();
  if (line == LCDML.MENU_getCursorPos()) {
    LCDML.MENU_disUseDynElementsWithSubElements();
    if(LCDML.BT_checkAny()) {
      if(LCDML.BT_checkEnter()) {
        t3();
      }
      if (LCDML.BT_checkLeft()) {
        counterTest--;
      }
      if (LCDML.BT_checkRight()) {
        counterTest++;
      }
  }
  }
  Serial.print("d:");
  Serial.print(counterTest);
  Serial.print(", ");
  Serial.print(timeMilli);
}

Hello
sorry if that has been already answered:
I need to use this library with 20x2 LCD with single analogue input, which only has 5 button values (select,left,right,up, down) and 'right' button generates value of 0 (zero) which per library docs means 'don't use'.
How I could use this library in this case?

And another one:
how to switch to some menu entry on 'Select' button?
Example: select menu item using direction buttons.
When 'Select' pressed, say, I want to switch to simple menu 'Start' and 'Cancel', 'Cancel' should return to the previous item, 'Start' will call the same process from any menu item.

Hi I'm trying to use the shield LCD 1602 Keypad and it just didn't work, as this menu is pnsat for LCD 2004, 
In LCDML_Control, I change the data to CONTROL OVER ONE ANALOG PIN and the LCD keyboard doesn't work for me. 
and when I try to change the lines shown from 4 to 2, the screen goes crazy. You can guide / help them.

[color=#202124][pre]Forgive my English, I used google translator.

[/color]

[/pre]

Hey,
has someone allready adapted the LCDMenuLib to a LillyGo TTGO-T-Display?
It's a ESP32 based board with a 240x135 color TFT driven by a ST7789 driver.
I have tried to modify the LCDML_adafruit_gfx_st7735 example, but the display stays black (backlight is on)
Following the modified code:

// for example when your chip is an ESP or a STM or SAM or something else

#define _LCDML_cfg_use_ram

// include libs
#include <LCDMenuLib2.h>

#include <SPI.h>
//  #include <Wire.h>
#include <Adafruit_GFX.h>
//  #include <Adafruit_ST7735.h> // Hardware-specific library
#include <Adafruit_ST7789.h> // Hardware-specific library

// #define _LCDML_ADAFRUIT_TEXT_COLOR       ST7735_WHITE
#define _LCDML_ADAFRUIT_TEXT_COLOR       ST77XX_WHITE
//  #define _LCDML_ADAFRUIT_BACKGROUND_COLOR ST7735_BLACK
#define _LCDML_ADAFRUIT_BACKGROUND_COLOR ST77XX_BLACK

#define _LCDML_ADAFRUIT_FONT_SIZE   3
#define _LCDML_ADAFRUIT_FONT_W      (6*_LCDML_ADAFRUIT_FONT_SIZE)             // font width 
#define _LCDML_ADAFRUIT_FONT_H      (8*_LCDML_ADAFRUIT_FONT_SIZE)             // font heigt 

// settings for u8g lib and lcd
//  #define _LCDML_ADAFRUIT_lcd_w       128            // lcd width
//  #define _LCDML_ADAFRUIT_lcd_h       160             // lcd height
#define _LCDML_ADAFRUIT_lcd_w       135            // lcd width
#define _LCDML_ADAFRUIT_lcd_h       240             // lcd height
#define TFT_BL 4



// TFT display and SD card will share the hardware SPI interface.
// Hardware SPI pins are specific to the Arduino board type and
// cannot be remapped to alternate pins.  For Arduino Uno,
// Duemilanove, etc., pin 11 = MOSI, pin 12 = MISO, pin 13 = SCK.
#define TFT_CS  5  // Chip select line for TFT display
#define TFT_RST  23  // Reset line for TFT (or see below...)
#define TFT_DC   16  // Data/command line for TFT
#define TFT_MOSI 19  // Data out
#define TFT_SCLK 18  // Clock out

//  #define SD_CS    4  // Chip select line for SD card
//  Adafruit_ST7735 display = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
Adafruit_ST7789 display = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

// nothing change here
#define _LCDML_ADAFRUIT_cols_max    (_LCDML_ADAFRUIT_lcd_w/_LCDML_ADAFRUIT_FONT_W)
#define _LCDML_ADAFRUIT_rows_max    (_LCDML_ADAFRUIT_lcd_h/_LCDML_ADAFRUIT_FONT_H)

// rows and cols
// when you use more rows or cols as allowed change in LCDMenuLib.h the define "_LCDML_DISP_cfg_max_rows" and "_LCDML_DISP_cfg_max_string_length"
// the program needs more ram with this changes
#define _LCDML_ADAFRUIT_cols        20                   // max cols
#define _LCDML_ADAFRUIT_rows        _LCDML_ADAFRUIT_rows_max  // max rows 


// scrollbar width
#define _LCDML_ADAFRUIT_scrollbar_w 6  // scrollbar width  


// old defines with new content
#define _LCDML_DISP_cols      _LCDML_ADAFRUIT_cols
#define _LCDML_DISP_rows      _LCDML_ADAFRUIT_rows


// *********************************************************************
// Prototypes
// *********************************************************************
void lcdml_menu_display();
void lcdml_menu_clear();
void lcdml_menu_control();

// unchanged menu definition removed

// *********************************************************************
// SETUP
// *********************************************************************
void setup()
{
    // serial init; only be needed if serial control is used
    Serial.begin(115200);                // start serial
    Serial.println(F(_LCDML_VERSION)); // only for examples
    /* INIT DISPLAY */
    SPI.begin();
    // Our supplier changed the 1.8" display slightly after Jan 10, 2012
    // so that the alignment of the TFT had to be shifted by a few pixels
    // this just means the init code is slightly different. Check the
    // color of the tab to see which init code to try. If the display is
    // cut off or has extra 'random' pixels on the top & left, try the
    // other option!
    // If you are seeing red and green color inversion, use Black Tab
    // If your TFT's plastic wrap has a Black Tab, use the following:
    //    display.initR(INITR_BLACKTAB);   // initialize a ST7735S chip, black tab
    // If your TFT's plastic wrap has a Red Tab, use the following:
    //tft.initR(INITR_REDTAB);   // initialize a ST7735R chip, red tab
    // If your TFT's plastic wrap has a Green Tab, use the following:
    //tft.initR(INITR_GREENTAB); // initialize a ST7735R chip, green tab
    display.init(135, 240);           // Init ST7789 240x135
    pinMode(TFT_BL, OUTPUT);
    digitalWrite(TFT_BL, HIGH);
    // clear lcd
    display.fillScreen(_LCDML_ADAFRUIT_BACKGROUND_COLOR);
    // set text color / Textfarbe setzen
    display.setTextColor(_LCDML_ADAFRUIT_TEXT_COLOR);

I hope someone can tell me, what i'm doing wrong.
Thanks a lot
Lena

The strange thing i don't understand is the fact that the modified Adafruit ST7789 grafictest example works as expected.
Where is the difference?

Greetings Lena

graphicstest_ST7789.ino (10.3 KB)

Hello Lena,

i have order such a display. It needs 3 days until it is here + some further days because the snow chaos here. (+35 cm in 2 days are to much for this region) :wink:

I think on the next weekend i have time to answer your and the other questions here.

Hey Jomelo,
I know, 35 cm snow in febuary is very suprising!
In good old days we called it simpy winter :smiley:
Great that you want to try it by yourself - these TTGO-T-Display are really nice parts.
Good luck - i'll stay tuned.

Greetings Lena

Evtl interessant für einige:

Hat ein ST7796 Controller drauf und wird vom WROOVER gesteuert.

Wie kann man das ganze mit Platformio nutzen?

platformio.ini

[env:promini]
platform = atmelavr
framework = arduino
board = atmega328p
board_build.mcu = atmega328p
board_build.f_cpu = 16000000L
upload_protocol = usbasp
upload_flags =
  -e
  ;-V
lib_deps =
     Wire
     jomelo/LCDMenuLib2 @ 2.2.6
     arkhipenko/TaskScheduler @ 3.2.2
     bitbucket-fmalpartida/LiquidCrystal @ 1.5.0

main.h

#include <Arduino.h>
#include <LCDMenuLib2.h>
#include <TaskScheduler.h>

//TaskScheduler
void Task_Serial_Blink_Example();
void Task_input_check();
void Task_LCDMenuLib();

//TaskScheduler
void lcdml_menu_control(void);

//TaskScheduler
void mDyn_para(uint8_t);

//TaskScheduler
void lcdml_menu_clear();
void lcdml_menu_display();

//TaskScheduler
void mFunc_thread_start(uint8_t);
void mFunc_thread_stop(uint8_t);
void mFunc_information(uint8_t);
void mFunc_timer_info(uint8_t);
void mFunc_p2(uint8_t);
void mFunc_back(uint8_t);
void mFunc_screensaver(uint8_t);
void mFunc_goToRootMenu(uint8_t);
void mFunc_jumpTo_timer_info(uint8_t);
void mFunc_para(uint8_t);

main.cpp

#include "main.h"

+ der ganze .ino Kram

die anderen .cpp haben #include "main.h"

src.zip (13.5 KB)

Hey,

ich bin gerade von LCDMenuLib auf LCDMenuLib2 umgestiegen. Dabei ist mir aufgefallen, dass in der neuen Lib der Display (i2c 20x4) bei jeder Bewegung des Cursors (Encoder) komplett neu geschrieben wird.

In der alten Lib wurde der Display nur beim Überschreiten des Cursors des aktiven Menüfensters komplett überschrieben, nicht jedoch beim Bewegen des Cursors innerhalb des aktiven Menüausschnittes oder beim Erreichen und Scrollen über die Menügrenzen (bei _LCDML_DISP_cfg_scrollbar = 0). Ansonsten wurde lediglich der Cursor selbst geupdated.
Insgesamt war der Menüfluss damit sehr flüssig.

In der lib2 wird der Display jedoch permanent mit LCDML_lcd_menu_clear überschrieben, was den ganzen Menüablauf sehr unruhig macht. Gibt es die Möglichkeit die alte Funktionsweise auf die neue lib zu übertragen oder wird dieses Menüverhalten für neue Funktionen der lib2 benötigt?

Ich schätze mal die Funktion wird bei der LCDMenuLib in LCDML_DISP_update_content und bei der LCDMenuLib2 in LCDML.DISP_checkMenuUpdate codiert, hab es bis jetzt aber noch nicht geschafft die ältere Funktion zu integrieren.

Grüße
Marvin

so als .ino kompilierts.

[platformio]
default_envs = promini

[env:promini]
platform = atmelavr
framework = arduino
board = atmega328p
board_build.mcu = atmega328p
board_build.f_cpu = 16000000L
upload_protocol = usbasp
upload_flags =
  -e
  -V
lib_deps =
     LiquidCrystal
     Wire
     https://github.com/Jomelo/LCDMenuLib2.git
     ;arkhipenko/TaskScheduler @ 3.2.2
     ;bitbucket-fmalpartida/LiquidCrystal @ 1.5.0