Go Down

Topic: Simple menu m2tklib with example. (Read 3 times) previous topic - next topic


Hi Oliver,

When I digg through the manual and code I start to feel very dumb. There are some things I just don't get.

Code: [Select]

// show screen with my A0 pin value
M2_LABEL(el_a0_label, NULL, "A0");
M2_U32NUM(el_a0_u32, "c4r1", &value_from_A0);
M2_ROOT(el_a0_ok, NULL, " ok ", &top_el_menu);
M2_LIST(list_a0) = { &el_a0_label, &el_a0_u32, &el_a0_ok };
M2_VLIST(el_a0_vlist, NULL, list_a0);
M2_ALIGN(top_el_a0, "-1|1W64H64", &el_a0_vlist);

in M2_LABEL(el_a0_Label...
what is this el_a0_Label? It has not been declared. Maybe it is one of those c++ things I did not yet figure out.
Then my application does receive navtex messages and does measure air pressure and temprature. The menustructure is to navigate setup pages and to display the measurement data. So I was figuring that the menu would at some point execute a loop to keep filling the display wit the mesurement data. Exept for the callback function in the MenuX2L example I have not found a way to do that yet. Is the callback the best way or is there a better way to execute code that is not part of the menu?
Then I would like a status bar on top of the display. It wil show waht tle last received navtex message type is and should be visible in all menus. Is there a way to do that?




Apr 11, 2013, 12:07 am Last Edit: Apr 11, 2013, 12:14 am by olikraus Reason: 1

All good questions. Let me try to explain some of these m2tklib things.

I decided to let users define a static menu structure, which is located in flash-rom area at startup. Basically the menues are just global variables. To hide the complexity of these data structures, I invented macros (usually called elements in m2tklib).
Such a global variable is:

Code: [Select]
M2_LABEL(el_a0_label, NULL, "A0");

This macro gets expanded and will look like this:

Code: [Select]
label_strcture el_a0_label = { magic_argument, NULL, "A0" } PROGMEM_BLABLA;

So, el_a0_label is a variable name (in fact it is a structure). It will represent an element within m2tklib, which just displays a text ("A0" in this example). Usually you will need the address of this "element". In fact all other procedures or elements just expect the address:
Code: [Select]
M2_LIST(list_a0) = { &el_a0_label, &el_a0_u32, &el_a0_ok };
This defines a group of three elements (the label is the first element of that group) and calls this group "list_a0". So list_a0 is another variable name.

Conclusion: The first argument to any of the M2_xxx macros is a variable name.
For elements like M2_LABEL, use &<variablename>, for M2_LIST variables just use the name without "&".

Please also have look here: http://code.google.com/p/m2tklib/wiki/elref#Use_of_Elements

So I was figuring that the menu would at some point execute a loop to keep filling the display wit the mesurement data.

You can let m2tklib display the content of a global variable. Whenever you refresh the menu (m2.draw()), then m2tklib will take the value from that global variable. It is really like connecting your menu with some outside data: Whenever the value somewhere in the memory changes, m2tlib will display the current value (once updated with m2.draw())

Let us have a look at your example:
Code: [Select]
M2_U32NUM(el_a0_u32, "c4r1", &value_from_A0);
The menu will display the value of variable "value_from_A0". "value_from_A0" is a global variable. Whenever you put data into it, it will get displayed. For example like this:
Code: [Select]

void loop(void)
  value_from_A0 = get_adc_value_from_a0();

Whatever is measured via ADC gets shown in the menu.

Exept for the callback function in the MenuX2L example I have not found a way to do that yet. Is the callback the best way or is there a better way to execute code that is not part of the menu?

Another good question and i do not know the answer. It depends on your personal preference. There are two ways to display measured data:
- global variables (as discussed above)
- callback procedures

MenuX2L is not the only element which provides callback procedures. For example also all elements ending with M2_...FN() get their values from callback procedures. But also other elements like M2_COMBO expect a callback procedure. Let us solve the same problem from above with a callback procedure. We need to use M2_U32NUMFN (http://code.google.com/p/m2tklib/wiki/elref#U32NUMFN). It expects a callback procedure:

Code: [Select]

uint32_t u32numcb(m2_rom_void_p element, uint8_t msg, uint32_t val) {
  if ( msg == M2_U32_MSG_GET_VALUE )
   return get_adc_value_from_a0();
  return 0;
M2_U32NUM(el_a0_u32, "c4r1", u32numcb);

And the main loop will look like this:
Code: [Select]

void loop(void)

Might look simpler, but there is a drawback with the callback procedure: They might get called more than once during one call to "m2.draw()". For that reason i would suggest to start with the "global variable" approach.

Conclusion: There are two ways to display data in a menu:
- connected global variables (prefered method)
- values returned from global variables (optional, if you accept the drawbacks)

Then I would like a status bar on top of the display. It wil show waht tle last received navtex message type is and should be visible in all menus. Is there a way to do that?

No, there is not only one way but probably a many ways to do this  ;)

1. Method: M2_COMBO
M2_COMBO will display a string, given by an index number from a list of strings (here: navtex messages types). Mark this element as read-only ("r1" format option)
Place this element at the beginning of each menu. Please note that you can reuse elements. Define it once , use it oftern.
Also note, that you usuallly will need a "container" element like XYLIST. XYLIST will allow you to place M2_COMBO at specific xy position on the screen.

2. Method: M2_LABELPTR
Similar to M2_COMBO, but you have to copy the type name into a string buffer. M2_LABELPTR is already read-only, so "r1" is not required.

3. Method: GLCD low level graphics commands
You can always draw a string directly to the screen. Just ensure that menu and your low level output do not overlap. In this case you would use the GLCD string draw procedure to output the navtex message type. A tutorial on this topic is here: http://code.google.com/p/m2tklib/wiki/t06glcd

4. Method: M2_X2LMENU
Thinkable, if you intend to build all menus just with X2LMENU (but this will restrict you a lot). In this case, just make the toplevel menu line of X2LMENU a callback procedure which returns a string with the name of the message type.

So, i will finish for now and I hope that i was able to clarify things a little bit.



Hi Oliver,
I am making progress. I have got a menu on my display and can navigate with my buttons OK.
I am struggling how to figure out how to use the menu to display some buttons to perform some functions.
I want 1 button to increase a value and one to decrease it.
I have tried the following code...
Code: [Select]

#include "U8glib.h"
#include "M2tk.h"
#include "utility/m2ghu8g.h"

U8GLIB_PCD8544 u8g(7, 6, 4, 5, 3);  // SPI Com: SCK = 7, MOSI = 6, CS = 4, A0 = 5, Reset = 3

int bl = 9, blValue = 31;

uint8_t uiKeySelectPin = A4;
uint8_t uiKeyDownPin = A1;
uint8_t uiKeyUpPin = A2;
uint8_t uiKeyExitPin = A0;

// Forward declaration of the toplevel element

// m2 object and constructor
M2tk m2(&top_el_menu, m2_es_arduino, m2_eh_4bs, m2_gh_u8g_bf);
//M2tk m2(&top_el_expandable_menu, m2_es_arduino_rotary_encoder, m2_eh_4bs, m2_gh_u8g_ffs);
//M2tk m2(&top_el_expandable_menu, m2_es_arduino, m2_eh_4bs, m2_gh_arduino_serial);

// Left entry: Menu name. Submenus must have a '.' at the beginning
// Right entry: Reference to the target dialog box (In this example all menus call the toplevel element again
m2_menu_entry m2_2lmenu_data[] =
  { "Show IP", NULL },
  { ". Address etc", &top_el_menu },
  { ". Internet", &top_el_menu },
  { "Ping", &top_el_menu },
  { ". x.x.x.1", &top_el_menu },
  { ". x.x.x.254", &top_el_menu },
  { ". Enter IP", &top_el_menu },
  { "Set IP", NULL },
  { ". DHCP", &top_el_menu },
  { ". Manual", &top_el_menu },
  { "Display", NULL },
  { ". Backlight", &el_backlight_menu },
  { NULL, NULL },

// The first visible line and the total number of visible lines.
// Both values are written by M2_2LMENU and read by M2_VSB
uint8_t m2_2lmenu_first;
uint8_t m2_2lmenu_cnt;

// M2_2LMENU definition
// Option l4 = four visible lines
// Option e15 = first column has a width of 15 pixel
// Option W43 = second column has a width of 43/64 of the display width

M2_2LMENU(el_2lmenu,"l6F3e14W56",&m2_2lmenu_first,&m2_2lmenu_cnt, m2_2lmenu_data,65,102,'\0');
M2_SPACE(el_space, "W1h1");
//M2_VSB(el_vsb, "l6W2r1", &m2_2lmenu_first, &m2_2lmenu_cnt);
M2_LIST(list_2lmenu) = { &el_2lmenu, &el_space };
M2_HLIST(el_hlist, NULL, list_2lmenu);
M2_ALIGN(top_el_menu, "-1|2W64H64", &el_hlist);

void fn_bl_up(m2_el_fnarg_p fnarg) {
  blValue = blValue + 32;
  analogWrite(9, blValue);
void fn_bl_down(m2_el_fnarg_p fnarg) {
  blValue = blValue - 32;
  analogWrite(9, blValue);

void fn_goto_main_menu(m2_el_fnarg_p fnarg)

void fn_goto_pwm_menu(m2_el_fnarg_p fnarg)

M2_BUTTON(el_btn_bl_up, "x1y1", "+", fn_bl_up);
M2_BUTTON(el_btn_bl_down, "x15y1", "-", fn_bl_down);
M2_BUTTON(el_btn_ok, "x30y1", "OK", fn_goto_main_menu);
M2_LIST(list) = { &el_btn_bl_up, &el_btn_bl_down, &el_btn_ok };
M2_XYLIST(list_element, NULL, list);
M2_ALIGN(el_backlight_menu, "-1|2W64H64", &list_element);
//M2tk m2(&list_element, m2_es_arduino, m2_eh_2bs, m2_gh_u8g_bf);

// Draw procedure, Arduino Setup & Loop

void draw(void) {

void setup(void) {
  // Connect u8glib with m2tklib
  pinMode(bl, OUTPUT);
  analogWrite(bl, blValue); //turns on backlight   
  m2_SetU8g(u8g.getU8g(), m2_u8g_box_icon);

  // Assign u8g font to index 0
  m2.setFont(0, u8g_font_5x7r);
  //m2.setFont(0, u8g_font_04b_03r);
  // Assign icon font to index 3
  m2.setFont(3, u8g_font_m2icon_7);

  // Setup keys
  m2.setPin(M2_KEY_SELECT, uiKeySelectPin);
  m2.setPin(M2_KEY_PREV, uiKeyUpPin);
  m2.setPin(M2_KEY_NEXT, uiKeyDownPin);
  m2.setPin(M2_KEY_EXIT, uiKeyExitPin);   
  m2.setPin(M2_KEY_SELECT, 7);
  m2.setPin(M2_KEY_ROT_ENC_A, 3);
  m2.setPin(M2_KEY_ROT_ENC_B, 4);

void loop() {
  // menu management
  if ( m2.handleKey() != 0 ) {
    do {
    } while( u8g.nextPage() );

When I compile I get an error :
m2tk_menu_list:41: error: 'el_backlight_menu' was not declared in this scope
Could you please point out where I have gone wrong?

There are 10 types of people. Those who understand binary, and those who don't.


'el_backlight_menu' is used before it is defined. This is a common problem in C and C++. Use a forward declaration and place the command
next to

See also: http://code.google.com/p/m2tklib/wiki/elref#Forward_Declaration



Hi Oliver,

Yes you clarified alot  :)

The data that I want to display on the screen is not just a global but it is a graph of measurement data. I could make this graph in a bitmap and then display it within the menu structure or, as what I was planning to do, draw it on the display with lowlevel commands.
As what I read about the menu this would maybe not work.
What do you think is the best aproach?




I would suggest to use low level graphics commands to display the measurement date.
Use M2_XYLIST() go get full controll of the placement of your buttons and other M2tklib elements.
Tutorial on low level graphics: http://code.google.com/p/m2tklib/wiki/t06u8g



Hi Oliver,
Thanks for your help so far. I have a much better understanding now.
My next issue is the size of the compiled code.
Is there any way to reduce the size of the compiled code as I have run out of memory.
I am using your m2tk and u8g libraries but I also need SPI, Ethernet and ICMPPing libraries. Once I have included those and written a few functions to use them, the compiled code is larger the the 32kB of my Arduino board.
Is it possible to cut from the library the functions I don't use, or is the compiler doing that by only compiling the functions that I call into my code?
I am not familiar with the workings of the compiler in this area.
Would it be simpler to use an Arduino based on a chip with with more memory?

Don =)
There are 10 types of people. Those who understand binary, and those who don't.



If you are working with the Arduino IDE, then unused procedures and data will be removed.
Both, u8glib and m2tklib, are already optimized for size. Here are some general ideas for the users of these libs:
- Avoid large fonts in u8glib. Use fonts with end in "r". The size of the font in bytes is always given in the overview picture.
- Only use a small number of fonts or only one font.
- Do not use large functions like drawLine or drawCircle
- Restrict yourself on a subset of elements in m2tklib: Maybe only use the XYLIST container.
- Reuse the format option string
- Reuse complete elements: For example a cancel button can be defined once and placed on all of the sub-menues.


Go Up