I've been working on a menu lib to be able to to traverse through a menu on the LCD.
the structure is similar to the tree structure you have in explorer etc.
My problem is that I cant get it to work perfectly. in the code I've posted here I can step through the menu up and down( submenu) and into menu "item2" but I can't do that with the first item (item1) instead it jumps to item3 and not to the "item1 submenu" what am I missing?
the second thing is that when I enter a submenu( submenu for item 2) I end up at the last submenuitem and not the first one. I can't figure out why it does that I would like it to go to the first subitem ( firstChild).
components:
Arduino Uno
adafruit 16x2 LCD negative with the I2C chip and buttons.
I appreciate all the help
library for the menu:
Code: Select all
/*LcdMenu library*/
#ifndef LcdMenu_h
#define LcdMenu_h
class MenuItem {
public:
MenuItem(const char* itemName, char shortKey='\0' ) : name(itemName), shortkey(shortKey) {
parent = prevSibling = nextSibling = firstChild = 0;
}
inline const char* getName() const { return name; }
inline const char getShortkey() const { return shortkey; }
inline const bool hasShortkey() const { return (shortkey!='\0'); }
inline void setBack(MenuItem *b) { parent = b; }
inline MenuItem* getParent() const { return parent; }
inline MenuItem* getPrevSibling() const { return prevSibling; }
inline MenuItem* getNextSibling() const { return nextSibling; }
inline MenuItem* getFirstChild() const { return firstChild; }
MenuItem *moveBack() { return parent; }
MenuItem *moveUp() {
return prevSibling;
}
MenuItem *moveDown() {
return nextSibling;
}
MenuItem *moveLeft() {
return parent;
}
MenuItem *moveRight() {
return firstChild;
}
//default menu
MenuItem &setParent(MenuItem &mi) {
mi.firstChild = this;
parent = &mi;
return mi;
}
MenuItem &setPrevSibling(MenuItem &mi) {
mi.nextSibling = this;
prevSibling = &mi;
return mi;
}
MenuItem &setNextSibling(MenuItem &mi) {
mi.prevSibling = this;
nextSibling = &mi;
return mi;
}
MenuItem &setFirstChild(MenuItem &mi) {
mi.parent = this;
firstChild = &mi;
return mi;
}
protected:
const char* name;
const char shortkey;
MenuItem *parent;
MenuItem *prevSibling;
MenuItem *nextSibling;
MenuItem *firstChild;
};
//no dependant inclusion of string or cstring
bool menuTestStrings(const char *a, const char *b) {
while (*a) { if (*a != *b) { return false; } b++; a++; }
return true;
}
bool operator==(const MenuItem &lhs, MenuItem &rhs) {
return menuTestStrings(lhs.getName(),rhs.getName());
}
struct MenuChangeEvent {
const MenuItem &from;
const MenuItem &to;
};
struct MenuUseEvent {
const MenuItem &item;
};
typedef void (*cb_change)(MenuChangeEvent);
typedef void (*cb_use)(MenuUseEvent);
class LcdMenu {
public:
LcdMenu(cb_use menuUse, cb_change menuChange = 0) : root("MenuRoot") {
current = &root;
cb_menuChange = menuChange;
cb_menuUse = menuUse;
}
MenuItem &getRoot() {
return root;
}
MenuItem &getCurrent() {
return *current;
}
void moveBack() {
setCurrent(current->getParent());
}
void moveUp() {
setCurrent(current->moveUp());
}
void moveDown() {
setCurrent(current->moveDown());
}
void moveLeft() {
setCurrent(current->moveLeft());
}
void moveRight() {
setCurrent(current->moveRight());
}
void use() {
if (cb_menuUse) {
MenuUseEvent mue = { *current };
cb_menuUse(mue);
}
}
private:
void setCurrent( MenuItem *next ) {
if (next) {
if (cb_menuChange) {
MenuChangeEvent mce = { *current, *next };
(*cb_menuChange)(mce);
}
current = next;
}
}
MenuItem root;
MenuItem *current;
cb_change cb_menuChange;
cb_use cb_menuUse;
};
#endif
Code: Select all
#include <LcdMenu.h> //LcdMenu library
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
// These #defines make it easy to set the backlight color
#define RED 0x1
#define GREEN 0x2
#define YELLOW 0x3
#define BLUE 0x4
#define VIOLET 0x5
#define TEAL 0x6
#define WHITE 0x7
//Menu variables
LcdMenu menu = LcdMenu(menuUsed,menuChanged);
//initialize menuitems
MenuItem menu1Item1 = MenuItem("Item1");
MenuItem menuItem1SubItem1 = MenuItem("Item1SubItem1");
MenuItem menuItem1SubItem2 = MenuItem("Item1SubItem2");
MenuItem menu1Item2 = MenuItem("Item2");
MenuItem menuItem2SubItem1 = MenuItem("Item2SubItem1");
MenuItem menuItem2SubItem2 = MenuItem("Item2SubItem2");
MenuItem menuItem2SubItem3 = MenuItem("Item2SubItem3");
MenuItem menu1Item3 = MenuItem("Item3");
void setup()
{
lcd.begin(16, 2);
menu.getRoot().setNextSibling(menu1Item1);
menu1Item1.setPrevSibling(menu1Item3);
menu1Item1.setNextSibling(menu1Item2);
menu1Item1.setFirstChild(menuItem1SubItem1);
menuItem1SubItem1.setParent(menu1Item1);
menuItem1SubItem1.setNextSibling(menuItem1SubItem2);
menuItem1SubItem1.setPrevSibling(menuItem1SubItem2);
menuItem1SubItem2.setPrevSibling(menuItem1SubItem1);
menuItem1SubItem2.setNextSibling(menuItem1SubItem1);
menuItem1SubItem2.setParent(menu1Item1);
menu1Item2.setPrevSibling(menu1Item1);
menu1Item2.setNextSibling(menu1Item3);
menu1Item2.setFirstChild(menuItem2SubItem1);
menuItem2SubItem1.setParent(menu1Item2);
menuItem2SubItem1.setNextSibling(menuItem2SubItem2);
menuItem2SubItem1.setPrevSibling(menuItem2SubItem3);
menuItem2SubItem2.setPrevSibling(menuItem2SubItem1);
menuItem2SubItem2.setNextSibling(menuItem2SubItem3);
menuItem2SubItem2.setParent(menu1Item2);
menuItem2SubItem3.setNextSibling(menuItem2SubItem1);
menuItem2SubItem3.setParent(menu1Item2);
menu1Item3.setParent(menu1Item1);
menu1Item3.setPrevSibling(menu1Item2);
menu1Item3.setNextSibling(menu1Item1);
//menu1Item1.setPrevSibling(menu1Item3);
//menu1Item3.setNextSibling(menu.getRoot());
//menu1Item3.setNextSibling(menu.getRoot());
//menu.toRoot();
lcd.setCursor(0,0);
lcd.print("Main Menu");
} // setup()...
void loop()
{
readButtons();
} //loop()...
void menuChanged(MenuChangeEvent changed){
MenuItem newMenuItem=changed.to; //get the destination menu
lcd.setCursor(0,1); //set the start position for lcd printing to the second row
if(newMenuItem.getName()==menu.getRoot()){
lcd.print("Main Menu ");
}else if(newMenuItem.getName()=="Item1"){
lcd.print("Item1 ");
}else if(newMenuItem.getName()=="Item1SubItem1"){
lcd.setCursor(0,0);
lcd.print("--- Item1 ---");
lcd.setCursor(0,1);
lcd.print("Item1SubItem1");
}else if(newMenuItem.getName()=="Item1SubItem2"){
lcd.print("Item1SubItem2 ");
}else if(newMenuItem.getName()=="Item2"){
lcd.setCursor(0,0);
lcd.print(" Main Menu ");
lcd.setCursor(0,1);
lcd.print("Item2 ");
}else if(newMenuItem.getName()=="Item2SubItem1"){
lcd.setCursor(0,0);
lcd.print("--- Item2 ---");
lcd.setCursor(0,1);
lcd.print("Item2SubItem1 ");
}else if(newMenuItem.getName()=="Item2SubItem2"){
lcd.setCursor(0,0);
lcd.print("--- Item2 ---");
lcd.setCursor(0,1);
lcd.print("Item2SubItem2 ");
}else if(newMenuItem.getName()=="Item2SubItem3"){
lcd.setCursor(0,0);
lcd.print("--- Item2 ---");
lcd.setCursor(0,1);
lcd.print("Item2SubItem3 ");
}else if(newMenuItem.getName()=="Item3"){
lcd.print("Item3 ");
}
}
void menuUsed(MenuUseEvent used){
lcd.setCursor(0,0);
lcd.print("You used ");
lcd.setCursor(0,1);
lcd.print(used.item.getName());
delay(1000); //delay to allow message reading
//menu.toRoot(); //back to Main
}
void readButtons(){
int reading;
uint8_t buttons = lcd.readButtons();
if (buttons) {
MenuItem currentMenu=menu.getCurrent();
if (buttons & BUTTON_UP) {
menu.moveUp();
delay(500);
lcd.setBacklight(RED);
}
if (buttons & BUTTON_DOWN) {
menu.moveDown();
delay(500);
lcd.setBacklight(YELLOW);
}
if (buttons & BUTTON_LEFT) {
menu.moveLeft();
delay(500);
lcd.setBacklight(GREEN);
}
if (buttons & BUTTON_RIGHT) {
menu.moveRight();
delay(500);
lcd.setBacklight(TEAL);
}
if (buttons & BUTTON_SELECT) {
if(!(currentMenu.moveRight())){ //if the current menu has a child and has been pressed enter then menu navigate to item below
menu.use();
delay(500);
}else{ //otherwise, if menu has no child and has been pressed enter the current menu is used
menu.moveRight();
delay(500);
}
lcd.setBacklight(VIOLET);
}
}
}