menubackend moveToLevel(lvl) resets uno

Hi all,

I’ve tried posting this in Programming forum but I’ve not had any luck.As a last resort before I assume MenuBackend is broken I thought I’d try here.

basically I have 3 buttons and and a 20x4 LCD to form part of a wider project. 3 Buttons are a resistor array to A0 which when either pressed an interrupt is triggered on D0/int0/pin2 All this is working fine besides some bounce which will be hardware resolved later.

I’m trying to build a menu using menuBackend as below but when moveToLevel is called to exit the menu and move it’s position to the beginning it resets the UNO. If I comment out moveToLevel it works fine but of course the next time you enter the menu you are where you last exit.

I’ve also had a problem using any of the getX commands, none compile. I should point out moveRelativeLevel doesn’t work either.

I’d really appreciate some pointers on other techniques I can research should menuBackend not be possible for what ever reason.

EXIT(0)
CONFIGURE(0)
EXIT(1)
CONFIG HEATER(1)
EXIT (2)
HEATER ON (2)
HEATER OFF (2)
HEATER DISABLE (2)

//MENU SETUP
#include <MenuBackend.h>

//initialize menu
MenuBackend menu = MenuBackend(menuUseEvent,menuChangeEvent);
//initialize menuitems
//MenuItem configure = SubMenu(menuChanged);

MenuItem configure = MenuItem(menu,"CONFIGURE",0);
MenuItem exit1 = MenuItem(menu,"EXIT",0);
MenuItem config_heater = MenuItem(menu,"CONFIG HEATER",1); 
MenuItem exit2 = MenuItem(menu,"EXIT",1);
MenuItem heater_on = MenuItem(menu, "HEATER ON",2);
MenuItem heater_off = MenuItem(menu, "HEATER OFF",2);
MenuItem heater_disable = MenuItem(menu,"HEATER DISABLE",2);
MenuItem exit3 = MenuItem(menu,"EXIT",2);

unsigned long menuTimer = millis();              
byte timerEnable = 0;
//END MENU  

/*LCD*/
#include <LCD.h>
#include <LiquidCrystal_SR.h>
LiquidCrystal_SR iLCD(10, 11, 12); //Data,Clk,Enable Assuming that the header is connected to the same digital IO pin.
//END LCD
int pin = 13;
int buttonPin = A0;
volatile byte buttonAct = 0;
volatile int buttonValue = 0;
volatile int buttonPressed = 0;
volatile int lastButtonPressed = 0;
volatile int state = LOW;
volatile int lastState = HIGH;
void setup()
{
  //MENU SETUP
  //configure menu
  menu.getRoot().add(configure).addAfter(exit1).addAfter(configure); 
                     configure.addRight(exit2).addAfter(config_heater).addAfter(exit2);
                                     config_heater.addRight(exit3).addAfter(heater_on).addAfter(heater_off).addAfter(heater_disable).addAfter(exit3);



  //END MENU SETUP
  Serial.begin(9600);
  Serial.println("STARTED");
  pinMode(pin, OUTPUT);
  attachInterrupt(0, readButtons, RISING);

  iLCD.begin(20,4);               // initialize the lcd
  delay(400); // wait to start lcd writing
  iLCD.home ();                   // go home
  iLCD.print("Button Interrupt");

}
void buttonStat(){ 
  Serial.print(buttonPressed); 
  Serial.print(" ");
  Serial.println(lastButtonPressed);
}
void loop(){
  if(buttonAct == 1){ //buttonAct eq 1 when interrupt received
    buttonAct = 0;
    navMenu();
  }
  if(timerEnable == 1 && millis() - menuTimer >= 10000){
    Serial.println("Timer Clear.........");
    menu.moveToLevel(0);
  //  menu.moveRelativeLevels(-1);
    timerEnable = 0;
    iLCD.clear();                   // go home
    iLCD.print("Button Interrupt");
    delay(60);
    buttonAct = 0;
  }
  //lastButtonPressed = 0;
  delay(100);

}



void readButtons(){
  state = !state;
  digitalWrite(pin, state);
  /*   511 : none | 614 : up | 768 : down | 1023 : select   */
  lastButtonPressed = buttonPressed;

  buttonValue = analogRead(buttonPin);
  if(buttonValue >= 900) {
    buttonPressed = 3;
  }
  else if(buttonValue >= 700){    
    buttonPressed = 2;
  }
  else if(buttonValue >= 600){    
    buttonPressed = 1;
  }
  buttonAct = 1;
  Serial.print(buttonPressed);
  Serial.print(" ");
  Serial.println(buttonValue);

}

void navMenu(){
  //MenuItem currentMenu=menu.getCurrent();

  switch (buttonPressed){
  case 3: //select     

    if (menu.getCurrent().getRight() != 0)  //The current item has an element right, it's a sub menu so nav right.
    {
      menu.moveRight();
      Serial.print(menu.getCurrent().getName());
      Serial.println("has menu right");
    }
    else{  //otherwise, menu has no child and has been pressed. enter the current menu
      menu.use();
    }
    break;
  case 2: //down
    menu.moveDown();
    break;     
  case 1: //up
    menu.moveUp();
    break;     
  }
}

/*This is where you define a behaviour for a menu item
 */
void menuUseEvent(MenuUseEvent used)
{
  Serial.print("Menu use: ");
  Serial.println(used.item.getName());
  iLCD.clear();                   // go home
  iLCD.print("USED: "); 
  iLCD.print(used.item.getName());
  
  if (used.item.getName() == "EXIT"){
   Serial.println("exit call");
   menu.moveLeft();
 }
}

/*
  This is an important function
 Here we get a notification whenever the user changes the menu
 That is, when the menu is navigated
 */
void menuChangeEvent(MenuChangeEvent changed)
{
  menuTimer = millis(); 
  timerEnable = 1;
  //MenuItem currentMenu=menu.getCurrent();
  iLCD.clear();                   // go home
  iLCD.setCursor ( 0, 0 );        // go to the next line
  iLCD.print("1)");
  iLCD.print(changed.from.getName());
  iLCD.setCursor ( 0, 1 );        // go to the next line
  iLCD.print("2)");    
  iLCD.print(changed.to.getName());

  Serial.print("Menu change FROM:TO ");
  Serial.print(changed.from.getName());
  Serial.print(":");
  Serial.println(changed.to.getName());
}

I still haven’t worked out why moveToLevel or moveRelativeLevel resets the Arduino but I need to move on so instead wrote a while loop to automate the exit to root instead.

//MENU SETUP
#include <MenuBackend.h>

//initialize menu
MenuBackend menu = MenuBackend(menuUseEvent,menuChangeEvent);
//initialize menuitems
MenuItem exit1 = MenuItem(menu,"EXIT",1);
MenuItem configure = MenuItem(menu,"CONFIGURE",1);
  MenuItem config_heater = MenuItem(menu,"CONF HEAT",2); 
   MenuItem heater_on = MenuItem(menu, "HEAT ON TEMP",3);
   MenuItem heater_off = MenuItem(menu, "HEAT OFF TEMP",3);
   MenuItem heater_disable = MenuItem(menu,"HEAT DISABLE",3);
   MenuItem exit3 = MenuItem(menu,"EXIT",3);
  MenuItem config_fan = MenuItem(menu,"CONF FANS",2); 
   MenuItem fan_on = MenuItem(menu, "FAN ON TEMP",3);
   MenuItem fan_off = MenuItem(menu, "FAN OFF TEMP",3);
   MenuItem fan_on_rh = MenuItem(menu, "FAN ON RH",3);
   MenuItem fan_off_rh = MenuItem(menu, "FAN OFF RH",3);   
   MenuItem fan_disable = MenuItem(menu,"FAN DISABLE",3);
   MenuItem exit4 = MenuItem(menu,"EXIT",3);
  MenuItem config_mist = MenuItem(menu,"CONF MIST",2); 
   MenuItem mist_on = MenuItem(menu, "MIST ON RH",3);
   MenuItem mist_off = MenuItem(menu, "MIST OFF RH",2);
   MenuItem mist_disable = MenuItem(menu,"MIST DISABLE",3);
   MenuItem exit5 = MenuItem(menu,"EXIT",3);   
  MenuItem exit2 = MenuItem(menu,"EXIT",2);

unsigned long menuTimer = millis();              
byte timerEnable = 0;
//END MENU  

/*LCD*/
#include <LCD.h>
#include <LiquidCrystal_SR.h>
LiquidCrystal_SR iLCD(10, 11, 12); //Data,Clk,Enable Assuming that the header is connected to the same digital IO pin.
//END LCD

//INTERRUPT BUTTONS
int pin = 13;
int buttonPin = A0;
volatile byte buttonAct = 0;
volatile int buttonValue = 0;
volatile int buttonPressed = 0;
volatile int lastButtonPressed = 0;
volatile int state = LOW;
volatile int lastState = HIGH;

//DEBUG
#include <MemoryFree.h> 
int tempMemCount = 0;
//



void setup()
{
  //MENU SETUP
  //configure menu
  menu.getRoot().add(configure).addAfter(exit1).addAfter(configure); 
  configure.addRight(exit2).addAfter(config_heater).addAfter(config_fan).addAfter(config_mist).addAfter(exit2);
  config_heater.addRight(exit3).addAfter(heater_on).addAfter(heater_off).addAfter(heater_disable).addAfter(exit3);
config_fan.addRight(exit4).addAfter(fan_on).addAfter(fan_off).addAfter(fan_on_rh).addAfter(fan_off_rh).addAfter(fan_disable).addAfter(exit4);
config_mist.addRight(exit5).addAfter(mist_on).addAfter(mist_off).addAfter(mist_disable).addAfter(exit5);


  //END MENU SETUP
  Serial.begin(9600);
  Serial.println("STARTED");
  pinMode(pin, OUTPUT);
  attachInterrupt(0, readButtons, RISING);

  iLCD.begin(20,4);               // initialize the lcd
  delay(400); // wait to start lcd writing
  iLCD.home ();                   // go home
  iLCD.print("Button Interrupt");

}
void buttonStat(){ 
  Serial.print(buttonPressed); 
  Serial.print(" ");
  Serial.println(lastButtonPressed);
}
void loop(){

    
  if(timerEnable == 1 && millis() - menuTimer >=4000){
       Serial.print("freeMemory() reports ");
    Serial.println( freeMemory() );
    Serial.println("Timer Clear.........");
    delay(300);
  [color=yellow] // menu.moveToLevel(0);
    
    while(menu.getCurrent().getLevel() > 1){
       if(menu.getCurrent().getLeft() != 0){
         menu.moveLeft();
         Serial.println("  L  ");
       }else{
         menu.moveUp();
         Serial.println("  U  ");
       }  
    }[/color]
    
    //  menu.moveRelativeLevels(-1);
    timerEnable = 0;
    iLCD.clear();                   // go home
    iLCD.print("Button Interrupt");
    delay(60);
    buttonAct = 0;
  }

  if(buttonAct == 1){ //buttonAct eq 1 when interrupt received
    buttonAct = 0;
    navMenu();
  }
  //lastButtonPressed = 0;
  delay(100);
  tempMemCount++;
  if(tempMemCount > 9){ //once a sec will do
    Serial.print("freeMemory() reports ");
    Serial.println( freeMemory() );
    tempMemCount = 0;  
  }
}



void readButtons(){
  state = !state;
  digitalWrite(pin, state);
  /*   511 : none | 614 : up | 768 : down | 1023 : select   */
  lastButtonPressed = buttonPressed;

  buttonValue = analogRead(buttonPin);
  if(buttonValue >= 900) {
    buttonPressed = 3;
  }
  else if(buttonValue >= 700){    
    buttonPressed = 2;
  }
  else if(buttonValue >= 600){    
    buttonPressed = 1;
  }
  buttonAct = 1;
  Serial.print(buttonPressed);
  Serial.print(" ");
  Serial.println(buttonValue);

}

void navMenu(){
  //MenuItem currentMenu=menu.getCurrent();

  switch (buttonPressed){
  case 3: //select     

    if (menu.getCurrent().getRight() != 0){  //The current item has an element right, it's a sub menu so nav right.

      menu.moveRight();
      Serial.print(menu.getCurrent().getName());
      Serial.println("has menu right");
    }
    else{  //otherwise, menu has no child and has been pressed. enter the current menu
      menu.use();
    }
    break;
  case 2: //down
    menu.moveDown();
    break;     
  case 1: //up
    menu.moveUp();
    break;     
  }
}

/*This is where you define a behaviour for a menu item
 */
void menuUseEvent(MenuUseEvent used){
  Serial.print("Menu use: ");
  Serial.println(used.item.getName());
  iLCD.clear();                   // go home
  iLCD.print("USED: "); 
  iLCD.print(used.item.getName());

  if (used.item.getName() == "EXIT"){
    Serial.println("exit call");
         menu.moveLeft();  
  }
}

/*
  This is an important function
 Here we get a notification whenever the user changes the menu
 That is, when the menu is navigated
 */
void menuChangeEvent(MenuChangeEvent changed){
  menuTimer = millis(); 
  timerEnable = 1;


  iLCD.clear();                   // go home
  iLCD.setCursor ( 0, 0 );        // go to the next line

  iLCD.print("1)");
  // iLCD.print(menu.getCurrent().getName());
  iLCD.print(changed.from.getName());
  iLCD.setCursor ( 0, 1 );        // go to the next line
  iLCD.print("2)");    
  iLCD.print(changed.to.getName());

  Serial.print("Menu change FROM:TO ");
  Serial.print(changed.from.getName());
  Serial.print(":");
  Serial.println(changed.to.getName());
}

I still haven't worked out why moveToLevel or moveRelativeLevel resets the Arduino ...

If MenuBackend deals with the serial port then there is a good chance that auto-reset 'feature' that is part of the bootloading procedure is the source of your problem. You might want to look into some of the threads dealing with disabling that 'feature'.

Don

floresta:

I still haven’t worked out why moveToLevel or moveRelativeLevel resets the Arduino …

that auto-reset ‘feature’

I’ll look into that, thanks.

I tried adding a 10uF and a 100uF cap across RESET and GND after powerup but it hasn't made any difference. I believe that's how to defeat the auto reset.