M2TKLIB - A User-Interface-Toolkit for the Arduino Hardware

Hi

M2_STRLIST: Yes, M2_STRLIST is more like a toplevel element and is difficult to use in between other elements.
I will add a more flexible M2_COMBO with dynamic number of elements.

In COMBO would be interesting to know if the value of combo has changed by a msg in cb function.

There is no value change callback. Instead you can observe the value, which is modified by M2_COMBO

uint8_t var_for_m2_combo;  // adr of this var is given to M2_COMBO
uint8_t prev_value;             // this is a backup value to detect changes by M2_COMBO
...
void loop(void)
{
   ...
   if ( prev_value != var_for_m2_combo )
  {
     // value has changed, do some action
    ...
    prev_value = var_for_m2_combo;
  }
}

From a menu item. I want to execute a function

With M2_2LMENU you can only call other menus. Use M2_STRLIST, which allows you to call other menues or execute procedures.

BTW: You should free the allocated memory in your callback proc.

Oliver

Hi

I have added the requested functionality: M2_COMBOPTR and M2_SPACECB

M2_COMBOPTR is identical to M2_COMBO except that the cnt (len) parameter now is a pointer to a 8-bit variable:

uint8_t comboptr_val = 0;
uint8_t comboptr_cnt = 3;
const char *comboptr_idx_to_color(uint8_t idx)
{
  if ( idx == 0 )
    return "red";
  else if (idx == 1 )
    return "green";
  return "blue";
}
M2_COMBOPTR(el_comboptr, NULL, &comboptr_val, &comboptr_cnt, comboptr_idx_to_color);
M2_ALIGN(top_el_comboptr_menu, "W64H64", &el_comboptr);

M2_SPACECB is identical to M2_SPACE except that a callback procedure will be called as soon as a new menu is rendered on the screen. This makes M2_SPACECB a suitable element that implements a single action for the 2LMENU. In the following example, the 2LMenu does not refer to other menues, but instead calls M2_SPACECB and its action procedures. These action procedures modify a 8 bit variable in the following example:

M2_EXTERN_ALIGN(top_el_spacecb_menu);
uint8_t el_space_u8 = 0;
void fn_space_cb_zero(m2_el_fnarg_p fnarg) {
  el_space_u8 = 0;
  m2_SetRootExtended(&top_el_spacecb_menu, 0, 0);
}
void fn_space_cb_inc(m2_el_fnarg_p fnarg) {
  puts("inc");
  el_space_u8++;
  m2_SetRootExtended(&top_el_spacecb_menu, 1, 0);
}
void fn_space_cb_dec(m2_el_fnarg_p fnarg) {
  el_space_u8--;
  m2_SetRootExtended(&top_el_spacecb_menu, 2, 0);
}
M2_SPACECB(el_space_cb_zero, NULL, fn_space_cb_zero);
M2_SPACECB(el_space_cb_inc, NULL, fn_space_cb_inc);
M2_SPACECB(el_space_cb_dec, NULL, fn_space_cb_dec);
m2_menu_entry space_cb_menu_data[] = {
  { "Zero", &el_space_cb_zero },
  { "Inc", &el_space_cb_inc },
  { "Dec", &el_space_cb_dec },
  { NULL, NULL },
};
uint8_t el_2lspace_first = 0;
uint8_t el_2lspace_cnt = 3;
M2_U8NUM(el_2lspace_u8, "r1c3", 0, 255, &el_space_u8);
M2_2LMENU(el_2lspace_strlist, "l3e15F3W47", &el_2lspace_first, &el_2lspace_cnt, space_cb_menu_data, 65, 102, '\0');
M2_LIST(list_2lspace_strlist) = { &el_2lspace_u8, &el_2lspace_strlist };
M2_VLIST(el_2lspace_hlist, NULL, list_2lspace_strlist);
M2_ALIGN(top_el_spacecb_menu, "-1|1W64H64", &el_2lspace_hlist);

M2tklib for GLCD attached. Let me know if other variants are needed.

Oliver

m2tklib_arduino_glcd_1.10pre1.zip (198 KB)

tested and perfect, this is what I needed.
I have trouble express my thoughts in english, very bad. :blush:

Thanks you

Afternoon, is it possible for a user to input a negative number in the M2_U8NUM or U32NUM elements?

e.g.

M2_U8NUM(el_u1, "c2", -20, 80, &n1);

I'm just playing around with the Rotary Encoder example at the moment to get to grips with this.

I'm trying to get a user to select a target temperature for a heater/cooler combination that can range from -20c to 80c.

Hi

Thanks for the request. I added issue 96 to the project for the implementation of M2_S8NUM.
It is almost finished:

M2_S8NUM(el_num_2, "+1c3", -128, 127, &s8num);

The new format option "+" controls visibibility of the "+" sign:
"+1": "+" is visible
"+0": "+" is not displayed

Let me know which m2tklib variant you need. I will add a prerelease here.

Oliver

Ooh, thank you.
I'm using the Liquid Crystal varient.

I spent a few hours today just playing around with menu's. i must say i'm impressed, this is perfect for my project.

Now, only if i could get my damn weird bouncy rotary encoder working well! Accidentally bought a ABC switch type one.

The attached 1.10pre2 includes the "M2_S8NUM" element. It is identical to the M2_U8NUM object except for the additional "+" format option (see above).

Let me know if you have any further questions.

Oliver

m2tklib_arduino_lc_1.10pre2.zip (196 KB)

I think the archive may be corrupted, or prehaps my computors playing up.

Oliver,
I use both character and graphic lcds in different projects, both the expanded libraries are named m2tklib.
How can I install both, or do I have to uninstall one to use the other, then switch when I change displays?
Thanks

@Veco
I have uploaded the prerelease here: Google Code Archive - Long-term storage for Google Code Project Hosting..

@Tumbleweed
You could merge files of two variants of m2tklib (must be the same release number). But in your sketch (.ino file) you must include both, LiquidChrystal and the GLCD header. In fact you must include both, even if you use only one output device.
If you do not include both headers, the Arduino IDE will fail to compile M2tklib: If you have only included LiquidCrystal, Arduino IDE will fail to compile GLCD specific files of M2tklib.
An other option is to install the Arduino IDE twice. Setup one instance for your character displays and the other for graphics displays.

Oliver

Cheers olikraus,

Is there a signed flavour of uint8_t?

Same goal as last time, trying to use a range of -20 to 80 within M2_S8NUM with my rotary encoder.

Finally got a standard rotary encoder, it works very smoothly with your implimentation.
Just begining to put together my menu now i've got my prototyping rig set up since this weekend.

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

LiquidCrystal lcd(12, 11, 7, 6, 5, 4);

//=================================================
// Forward declaration of the toplevel element
M2_EXTERN_ALIGN(top_menu);

//=================================================
// Simple dialog: Input two values n1 and n2

sint8_t n1 = 0;
uint8_t n2 = 0;

M2_LABEL(el_l1, NULL, "Temperature:");
M2_S8NUM(el_u1, "c2", -20, 80, &n1);
M2_LABEL(el_l2, NULL, "Humidity:");
M2_U8NUM(el_u2, "c2", 0, 100, &n2);

M2_LIST(list) = { &el_l1, &el_u1, &el_l2, &el_u2 };
M2_GRIDLIST(el_gridlist, "c2", list);
M2_ALIGN(top_menu, "-1|1W64H64", &el_gridlist);

// m2 object and constructor
// Note: Use the "m2_eh_4bd" handler, which fits better to the "m2_es_arduino_rotary_encoder" 
M2tk m2(&top_menu, m2_es_arduino_rotary_encoder, m2_eh_4bd, m2_gh_lc);

//=================================================
// Arduino Setup & Loop

void setup() {
  m2_SetLiquidCrystal(&lcd, 20, 4);
  
  // define button for the select message
  m2.setPin(M2_KEY_SELECT, 8); 
  
  // The incremental rotary encoder is conected to these two pins
  m2.setPin(M2_KEY_ROT_ENC_A, 2);
  m2.setPin(M2_KEY_ROT_ENC_B, 3);
}

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

Also, what is the purpose of calling "m2.checkKey();" multiple times within the main loop?

I'm also confused about how i pass a value back through the callback procedure. I was looking at the example code and i'm unsure how i would use that. Say for example i wanted to do a Serial.print(); of the current S8NUM value for el_u1.

uint8_t global_value = 0;
 
uint8_t u8_cb(m2_rom_void_p element, uint8_t msg, uint8_t val)
{
  if ( msg == M2_U8_MSG_SET_VALUE )
    global_value = val;
  return global_value;
}

M2_U8NUMFN(el_u8_cb, NULL, 0, 10, u8_cb);

Thank you for putting up with me! I'm a determined Newb i'm afraid.

Hi

Is there a signed flavour of uint8_t?

This should be "int8_t".

Also, what is the purpose of calling "m2.checkKey();" multiple times within the main loop?

A call to "checkKey()" polls the values of the rotary encoder and the other keys. There is no interrupt based check of the rotary encoder, instead M2tklib requires as much calls to "checkKey()" as possible.

I'm also confused about how i pass a value back through the callback procedure. I was looking at the example code and i'm unsure how i would use that. Say for example i wanted to do a Serial.print(); of the current S8NUM value for el_u1.

You need to use global variables to pass values to a callback procedure (as you did correctly in the second code example).

This will do the print for each changed value:

uint8_t global_value = 0;
 
uint8_t u8_cb(m2_rom_void_p element, uint8_t msg, uint8_t val)
{
  if ( msg == M2_U8_MSG_SET_VALUE )
  { 
    global_value = val;
    Serial.print(global_value);
  }
  return global_value;
}

Oliver

Hi,

For a project I use an old loran C navigator box with intergrated keypad. It has 20 keys in a 5x8 matrix, yes very inefficient.

How should I modify the library to be able to use a matrix keypad?
So if I look deeper into the code I should change m2_arduino_get_key in m2esarduino.c to return the messages on result of a keypress on my matrix keypad. Is that the only place where I should change the code?
Would it be possible to make extra messages for the numbers 0...9?

Cheers,

Marijn

Hi

  1. Extend the event source to generate the new events (M2_EP_MSG_KEY_1, M2_EP_MSG_KEY_2, ...)

  2. Introduce a new (corresponding) element msg, e.g. M2_EL_MSG_KEY_1
    Extend one of the event handlers to process the new key event, for example extend

uint8_t m2_eh_4bs(m2_p ep, uint8_t msg, uint8_t arg1, uint8_t arg2)

Then add this code to the event handler (for each event):

  case M2_EP_MSG_KEY_1:
    m2_nav_prepare_fn_arg_parent_element(nav);
    m2_fn_arg_call(M2_EL_MSG_KEY_1);			// assign the char 
    return m2_nav_user_next(nav);				// go to next position
  1. Next step is to make the elements aware about the new element messages.
    For example M2_TEXT would make sense here (m2eltext.c):

Extend the char processing function: M2_EL_FN_DEF(m2_el_char_fn)

  case M2_EL_MSG_KEY_1:
      cp = m2_get_char_ptr(fn_arg->nav);
      *cp = '1';
      return 1;

Of course: not tested...

Oliver

Thanks Oliver,

I will try to extend the lib that way.
The matrix keypad question was a RTFM problem :blush:
I used m2.SetKey to store the result of the matrix into the que.
I'm still amazed about this lib...
Cheers,

Jelbert

Yes, it should be possible to use setKey() if you do not want to rewrite the event source.

Oliver

Hi Jelbert

I have added the mentioned code for the keypad support. See Google Code Archive - Long-term storage for Google Code Project Hosting..
Instead of extending the existing handlers, i have introduced m2_eh_4bks and m2_eh_6bks (and extended m2_eh_4bsts and m2_eh_6bsts) to support the keypad values.

Just for reference, the new code in the event handler was wrong. Correct code is:

  case M2_EP_MSG_KEY_1:
    m2_nav_prepare_fn_arg_current_element(nav);
    m2_fn_arg_call(M2_EL_MSG_KEY_1);			// assign the char 
    return m2_nav_user_next(nav);				// go to next position

I have introduced the following constants (which can be used with setKey or as return value of the event source):
#define M2_KEY_HASH 35
#define M2_KEY_STAR 42
#define M2_KEY_0 48
#define M2_KEY_1 49
#define M2_KEY_2 50
#define M2_KEY_3 51
#define M2_KEY_4 52
#define M2_KEY_5 53
#define M2_KEY_6 54
#define M2_KEY_7 55
#define M2_KEY_8 56
#define M2_KEY_9 57

Additionally you can use any other ASCII code like 'a', 'b', 'c', etc.., so m2.setKey('B') will send the ASCII B to the element. At the moment only M2_TEXT will be able to handle these new codes.

Let me know your variant of m2tklib, then i will upload a prerelease of m2tklib including keypad support.

Oliver

Hi Oliver and others,

Based on the 2L example I went on with my code. And made some modifications in oa the used font to create a space on the top of the display for a status line.
At frst I had troubles with the display suddenly blanking but the arduino kept on running.
That was fixed with a buffer that was too long in the drawStatus routine and moving the fontsetting to setup.
After that everything ran fine for a long while.

But now, when just waiting in the main menu, the display blanks again after 8 hours or so.
Is there a way to reset the display system (software part), without resetting the arduino?

Besides the drawstatus routine I included most of the rest for completeness sake.

here is the drawStatus routine

    void drawStatus(void)
{

  static long bmpTime=0;
  static char StatBuf[26];
  static float temprature;
  static float pressure;
  static int hi, lo;

     hi=int(RATemp.getAverage());
    lo=(RATemp.getAverage()-float(hi))*100;

  String line =  String(rtc.getTimeStr());// + bmp.readPressure() + " " + bmp.readTemperature()+" C");
  line += " ";
  line +=  (int(RADruk.getAverage())); //
  if(statMsgStat==1 && m2.getRoot() == &m2_null_element)
  {
    switch (Step)
    {
    case 1:
      line += " 12u  ";
      break;
    case 2:
      line += " 1d   ";
      break; 
    case 3:
      line += " 1.5d ";
      break; 
    case 4:
      line += " 2d   ";
      break; 
    case 5:
      line += " 2.5d ";
      break; 
    case 6:
      line += " 3d   ";
      break; 
    case 7:
      line += " 3.5d ";
      break;
    case 8:
      line += " 4d   ";
      break;
    default:
      line += "      ";
      break;      
    }
  }
  else
    line += "      ";
  line.concat(hi);
  line += '.';
  line.concat(lo);
  if(lo<10)
    line +='0';
  line += "C";
  line.toCharArray(StatBuf,26);
  u8g.drawStr(0,7, StatBuf);

}

The menu side. The stuff from the example has been cut out because the post exeded the maximum length.

U8GLIB_KS0108_128 u8g(27, 28, 29, 30, 31, 32, 33, 34, 26, 35, 36, 24, 25,37); 		// 8Bit Com: D0..D7: 8,9,10,11,4,5,6,7 en=18, cs1=14, cs2=15,di=17,rw=16

//=================================================
// Forward declaration of the toplevel element
M2_EXTERN_ALIGN(top_el_expandable_menu);


/*Create empty screen for graph barrograaf*/
M2_ROOT(el_barrograaf_goto_top, NULL, "t ",&top_el_expandable_menu);

//here very much code has been deleted to fit forum post
//these were modifications of the demo routines to do my menu things

menus call the toplevel element again
m2_menu_entry m2_2lmenu_data[] = 
{
//  { "Menu",  NULL },
  { "Show Graph", &m2_null_element },  //disable menu system
//  { "Show Graph", &list_element },  //disable menu system
  { "Navtex", &top_el_expandable_menu },
  { ". Allerts", &top_el_expandable_menu },
  { ". All msgs", &top_el_expandable_menu },
  { "Navtex Setup", &top_el_expandable_menu },
  { ". Set Area", &top_el_muse },
  { ". Set MSG", &top_el_muse1 },
  { "WIFI Setup", &top_el_expandable_menu },
  { ". Radio", &el_rb_grid },
  { ". Combo", &el_top_combo },  
  { "Serial setup", &el_setspeed_top_combo},
  { "Serial Monitor", NULL },
  { ". COM1", NULL },
  { ". COM2", NULL },
  { ". COM3", NULL },
  { ". COM4", NULL },
  { "Set Date", &el_top_date_menu },
  { "Set Time", &el_top_time_menu },
  { "Numbers ", &el_top_num_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(el_2lmenu,"l5F1e0W43",&m2_2lmenu_first,&m2_2lmenu_cnt, m2_2lmenu_data,65,102,'\0');
M2_SPACE(el_space, "W1h1");
M2_VSB(el_vsb, "l5W2r1", &m2_2lmenu_first, &m2_2lmenu_cnt);
M2_LIST(list_2lmenu) = { &el_2lmenu, &el_space, &el_vsb };
M2_HLIST(el_hlist, NULL, list_2lmenu);
M2_ALIGN(top_el_expandable_menu, "-1|0W64H64", &el_hlist);

// m2 object and constructor
M2tk m2(&top_el_expandable_menu, m2_es_arduino, m2_eh_4bs, m2_gh_u8g_ffs);

//key matrix

byte key_rows[] = {
  40,38,42,43,44,45,46,47};
byte key_cols[] = {
  10,11,12,41,39};


SPISRAM myRAM(7); // CS pin
char buffer[128];



long Pressure;


void setup() {

   //enable 5V dc/dc converter  
  pinMode(DC5V, OUTPUT);
  digitalWrite(DC5V,LOW);
  pinMode(CSRAM, OUTPUT);
  pinMode(Backlight, OUTPUT);
  analogWrite(Backlight,128);

  //setup keypad
  for(int i=0;i<8;i++)
  {
    //    Serial.println(key_rows[i]);
    pinMode(key_rows[i],OUTPUT);
    digitalWrite(i,HIGH);
  }
  for(int i=0;i<5;i++)
  {
    //    Serial.println(key_cols[i]);
    pinMode(key_cols[i],INPUT);
    digitalWrite(key_cols[i],HIGH);
  }
  
    if ( u8g.getMode() == U8G_MODE_R3G3B2 ) 
    u8g.setColorIndex(255);     // white
  else if ( u8g.getMode() == U8G_MODE_GRAY2BIT )
    u8g.setColorIndex(3);         // max intensity
  else if ( u8g.getMode() == U8G_MODE_BW )
    u8g.setColorIndex(1);         // pixel on


  // Connect u8glib with m2tklib
  m2_SetU8g(u8g.getU8g(), m2_u8g_box_icon);

  // Assign u8g font to index 0
  m2.setFont(0, u8g_font_5x8r);

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

  Wire.begin();
  Serial.begin(115200);


  if (!bmp.begin()) 
  {
    Serial.println("Could not find a valid BMP085 sensor, check wiring!");
    while (1) {
    }
  }
 //Setup SPI for SRAM
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  SPI.setDataMode(SPI_MODE0);
 
 //some test on the RTC for rome unresolved error
 Serial.println(rtc.getTimeStr());  //deze tijd is onjuist. Meestal 00:00:00
 Serial.println(rtc.getDateStr());
 delay(1000);
 Serial.println(rtc.getTimeStr()); //deze tijd is juist
 Serial.println(rtc.getDateStr());
 


 //init Flagspace
  getFlagSpace();

  if(flagSpace.regs.dataPointer<START_DATA||flagSpace.regs.dataPointer>END_DATA)
    flagSpace.regs.dataPointer=START_DATA;
//fat starts on 0x0000 so no check needed for msgPointer



//  Timeadjust(DateTime(__DATE__, __TIME__));

}

// Draw procedure, Arduino Setup & Loop


void draw(void) {
  drawStatus();
  drawGraphics();
  m2.draw();
}


void loop(){
  long r,timer=0;
 // float temp;
  while(1)
  {
    scheduler();

    KeyMatrix();

    m2.checkKey();

    if ( m2.handleKey() != 0 || millis()>timer ) 
    {
      timer=millis()+1000;
      u8g.firstPage();  
      do 
      {
        KeyMatrix();
        m2.checkKey();
          draw();
   
      } 
      while( u8g.nextPage() );
    }
  }
 }

And for completeness here is the head of my code.

#include <Wire.h>
#include <SPI.h>
#include "U8glib.h"
#include <Adafruit_BMP085.h>
#include <SPISRAM.h>
#include <stdlib.h>		// for itoa
#include "M2tk.h"
#include "utility/m2ghu8g.h"
#include <DS1302.h>
#include <DateTime.h>
#include "RunningAverage.h"

#define END_FAT         0x00BBF
#define START_DATA      0x00BC0
#define END_DATA        0x02BBF
#define MSG_BUF_START   0x02BC0
#define MSG_BUF_END     0x03CAF
#define MSG_START       0x03CB0
#define MSG_LAST        0x1EFFF
#define MSG_STOP        0x1EFFF
#define FLAGSPACE       0xFFF0

#define DC5V 22
#define CSRAM 7
#define Backlight 8

#define M2_KEY_0 20
#define M2_KEY_1 21
#define M2_KEY_2 22
#define M2_KEY_3 23
#define M2_KEY_4 24
#define M2_KEY_5 25
#define M2_KEY_6 26
#define M2_KEY_7 27
#define M2_KEY_8 28
#define M2_KEY_9 29

#define BARROGRAAF 1
#define THERMOGRAAF 2
#define PT_GRAPH 3
long currMsg=MSG_START;
long nu;
int sampleTime=5;
byte drawFunction=0;
byte Step=8; //Geeft de stap grootte aan voor het tekenen van het grafiek max END_DATA-START_DATA/128=65
char tMaxBuf[8];
char tMinBuf[8];
char dMaxBuf[8];
char dMinBuf[8];
char statMsgStat=1;

  float maxTempDraw;
  float minTempDraw;
  float maxDrukDraw;
  float minDrukDraw;

  long minMaxTimeout=0;

#define FLAGSPACE_UNION_SIZE 16

union bla{
  struct blie{
  unsigned int dataPointer; //2bytes
  unsigned int msgFATPointer;  //2bytes
  byte maxTemp;  //
  byte minTemp;
  byte test3;
  byte test4;
  int maxDruk; 
  int minDruk;
  byte test5;
  byte test6;
 
    } 
  regs;
  byte flagMap[FLAGSPACE_UNION_SIZE-1];
} 
flagSpace;  //regU

#define REG_UNION_SIZE 8

union regUnion {
  struct regStruct{
    float temp;
    float druk;
    
  } 
  regs;
  byte registerMap[REG_UNION_SIZE-1];
} 
dataUnion;  //regU

#define EEPROM_UNION_SIZE 8

union{
  struct{
   unsigned int com1Baud; 
   unsigned int com2Baud; 
   unsigned int com3Baud; 
   unsigned int com4Baud; 
  }
  regs;
  byte PROM[EEPROM_UNION_SIZE-1];
}
EE;

Is there a way to reset the display system (software part), without resetting the arduino?

There is the procedure "begin" (Google Code Archive - Long-term storage for Google Code Project Hosting.) which does a reset of the display for newer versions of U8glib.

Oliver

Today, I have released version 1.11 of M2tklib.

M2tklib is a menu/GUI library for graphics and character displays.
Projekt page: Google Code Archive - Long-term storage for Google Code Project Hosting.

The new version is available for download:
@Google: Google Code Archive - Long-term storage for Google Code Project Hosting.
@Bintray: Service End for Bintray, JCenter, GoCenter, and ChartCenter | JFrog

From the ChangeLog:

  • Keypad Support for M2_TEXT and M2_U32NUM (Issue 108)
  • Fixed XBM code (Issues 110, 111)
  • Touch screen support for M2_STRLIST and M2_VSB (Issue 112)
  • New color graphics handler for U8glib (Issue 113)
  • Quick and Home keys (Issues 114 and 115)
  • Changed default behavioir for M2_ALGIN (Issue 121)
  • New element: M2_COMBOFN (Issue 122)
  • New tutorial on event sources (Issues 117 and 123)

Oliver