Go Down

Topic: [SOLVED] Menu Structure - returning to main loop from submenu (Read 421 times) previous topic - next topic

anishkgt

I am trying to get a menu structure and after a several hours, i've managed to get the below working but just one problem tho. How do i return to configMode() from menu1 (). calling it after the save part works but i also need to get back to configMode() without updating as well.

Code: [Select]
void configMode()        // End configuration mode.
{
  lcd.setCursor(1, 1);
  lcd.print("Start");
  lcd.setCursor(8, 1);
  lcd.print("End");
  lcd.setCursor(13, 1);
  lcd.print("Test");
  lcd.setCursor(1, 2);
  lcd.print("W1");
  while ((digitalRead(buttonState) == HIGH) && menuFlag == true)
  {
    noInterrupts();
    int select = map(rotaryEncoder(), 0, 4, 1, 4);
    interrupts();

    switch (select)
    {
      case 1:
        lcd.setCursor(0, 1);
        lcd.print(">");
        lcd.setCursor(7, 1);
        lcd.print(" ");
        if ((digitalRead(ENC_SW) == LOW) && select == 1)
        {
          lcd.clear();
          menu1();
        }
        break;

      case 2:
        {
          lcd.setCursor(0, 1);
          lcd.print(" ");
          lcd.setCursor(7, 1);
          lcd.print(">");
          break;
        }
    }
  }
}

int menu1 ()
{
  while (digitalRead(buttonState) == HIGH)
  {
    noInterrupts();
    int startSrv = map(rotaryEncoder(), 0, 160, 24, 160);
    interrupts();
    if  (startSrv < 25)
    {
      startSrv = 0;
    }
    if  (startSrv >= 160)
    {
      startSrv = 160;
    }
    
    lcd.setCursor(0, 0);
    lcd.print("Set Start Point   ");
    lcd.setCursor(2, 2);
    lcd.print("Set start:");
    lcd.print(startSrv);
    lcd.print(char(223));
    lcd.print("    ");
    lcd.setCursor(0, 3);
    lcd.print("Stored     :");
    lcd.print(SrvPotMap2);
    lcd.print(char(223));
    lcd.print("    ");
    myServo.attach(servoPin);
    myServo.write(startSrv);
    delay(10);
    int ENC_SWstate = digitalRead(ENC_SW);
    if ((ENC_SWstate == LOW) && SrvPotMap2 != startSrv)     // Display message only if current value is different from the stored value
    {
      SrvPotMap2 = startSrv;
      //EEPROM.update(startSrvAddr, startSrv); lcd.setCursor(0, 2);
      lcd.clear();
      lcd.setCursor(2, 2);
      lcd.print("SAVE SUCCESSFULL");
      lcd.setCursor(0, 3);
      lcd.print("value changed: ");
      lcd.print(startSrv);
      lcd.print(char(223));
      digitalWrite(bzr, HIGH);
      delay(100);
      digitalWrite(bzr, LOW);
      delay(100);
      digitalWrite(bzr, LOW);
      delay(2500);
      menuFlag = true;
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("CONFIGURATION MODE");
      configMode();
    }
  }
}

rw950431

What is the condition that you are trying to detect for the "without updating" part?

From what I can see the only other way that menu1() will exit is when the while loop fails

Code: [Select]
while (digitalRead(buttonState) == HIGH)


But this will likely also cause the configMode() while loop to exit as well

Code: [Select]
while ((digitalRead(buttonState) == HIGH) && menuFlag == true)


And since you havent posted complete code we dont know what happens after that..

Its probably better to have each function return a status code then have a selector in the main loop call the appropriate "next menu" eg

Code: [Select]

int whichMenu=0;

void loop() {

   switch (whichMenu)
   {
       case 0:      
          whichMenu=configMode();
          break;

        case 1:
          whichMenu=menu1();
          break;
    };

}


Then each function can return a value which tells the main loop where to go next.






anishkgt

#2
Jul 17, 2017, 07:21 am Last Edit: Jul 17, 2017, 07:23 am by anishkgt
Quote
But this will likely also cause the configMode() while loop to exit as well
not really it breaks the first loop and pressing again breaks the main loop

so i was trying your suggestion and got stuck, how do i call a switch statement when a long press is detected. Would it be correct to add the whole switch inside another function ?

Code: [Select]

switch (checkButton())
  {
    case shortPress:
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Mode: Manual Weld    ");
      lcd.setCursor(0, 2);
      lcd.print("W1:");
      lcd.print(pre);
      //lcd.print(char(0xE4));
      lcd.print("ms");
      lcd.setCursor(9, 2);
      lcd.print("P:");
      lcd.print(pause);
      lcd.print("ms");
      lcd.setCursor(0, 3);
      lcd.print("W2:");
      lcd.print(WeldTime);
      lcd.print("ms ");
      lcd.backlight();
      Weld();
      break;

    case longPress:
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Mode: Automatic Weld");
      continousWeld();
      break;

    case longerPress:
      menuFlag = true;
      lcd.clear();
      lcd.setCursor(1, 0);
      lcd.print("CONFIGURATION MODE");
      menuSelect;
      break;
  }


depending on the press of a button it moves to specific functions. Here i am trying the case longPress: and am not sure how to call the switch (menuSelect() show below

Code: [Select]
switch (menuSelect)
{
case 0:
  menuSelect = configMode();
  void configMenu()        // End configuration mode.
  {
    lcd.setCursor(1, 1);
    lcd.print("Start");
    lcd.setCursor(8, 1);
    lcd.print("End");
    lcd.setCursor(13, 1);
    lcd.print("Test");
    lcd.setCursor(1, 2);
    lcd.print("W1");
    while (menuFlag == true)
    {
      noInterrupts();
      int select = map(rotaryEncoder(), 0, 4, 1, 4);
      interrupts();

      switch (select)
      {
        case 1:
          lcd.setCursor(0, 1);
          lcd.print(">");
          lcd.setCursor(7, 1);
          lcd.print(" ");
          if ((digitalRead(ENC_SW) == LOW) && select == 1)
          {
            lcd.clear();
            menu1();
          }
          break;

        case 2:
          {
            lcd.setCursor(0, 1);
            lcd.print(" ");
            lcd.setCursor(7, 1);
            lcd.print(">");
            break;
          }
      }
    }
    break;
  case 2:
    menuSelect = menu1();
    int menu1 ()
    {
      if (Counter1 == 1)
      {
        noInterrupts();
        int startSrv = map(rotaryEncoder(), 0, 160, 24, 160);
        interrupts();
        if  (startSrv < 25)
        {
          startSrv = 0;
        }
        if  (startSrv >= 160)
        {
          startSrv = 160;
        }

        lcd.setCursor(0, 0);
        lcd.print("Set Start Point   ");
        lcd.setCursor(2, 2);
        lcd.print("Set start:");
        lcd.print(startSrv);
        lcd.print(char(223));
        lcd.print("    ");
        lcd.setCursor(0, 3);
        lcd.print("Stored     :");
        lcd.print(SrvPotMap2);
        lcd.print(char(223));
        lcd.print("    ");
        myServo.attach(servoPin);
        if ((digitalRead(ENC_SW) == LOW) && SrvPotMap2 != startSrv)     // Display message only if current value is different from the stored value
        {
          SrvPotMap2 = startSrv;
          //EEPROM.update(startSrvAddr, startSrv); lcd.setCursor(0, 2);
          lcd.clear();
          lcd.setCursor(2, 2);
          lcd.print("SAVE SUCCESSFULL");
          lcd.setCursor(0, 3);
          lcd.print("value changed: ");
          lcd.print(startSrv);
          lcd.print(char(223));
          digitalWrite(bzr, HIGH);
          delay(100);
          digitalWrite(bzr, LOW);
          delay(100);
          digitalWrite(bzr, LOW);
          delay(2500);
          Counter1 = 0;
          menuFlag = true;
          lcd.clear();
          lcd.setCursor(1, 0);
          lcd.print("CONFIGURATION MODE");
          configMode();
        }
      }
    }
  }
}

Robin2

How do i return to configMode() from menu1 ().
Rather than calling menu1() from configMode() I suspect you should call both from loop() with the appropriate one chosen by a variable (perhaps named menuSelect) that contains (say) 'C' for configMode() and 'M' for menu1()

Then to change menus your code just needs to change the value of menuSelect.


Having said all that it would be much better to treat a menu as a set of data that needs to be displayed (which could be in an array) and a common piece of code to detect the user input. There should be no need for two (or more) sets of lcd.print() statements. Indeed you could probably display the whole menu (of any length) with a few lines of code that iterates over an array.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

anishkgt

Ok so i've been trying a few ad this is what i've come up with. Maybe not the ideal one but i guess am getting there, in a way.

I tried to compile the below
Code: [Select]
#include <PinChangeInterrupt.h>
#include <Wire.h>

const int buttonPin  = 9;  // Weld button
const int ENC_SW     = 11; // Push button on Encoder
const int ENC_PinB   = 12; // PIN B of Encoder
const int ENC_PinA   = 13; // PIN A of Encoder
const int noEvent = 0;
const int shortPress = 1;
const int longPress = 2;
const int longerPress = 3;
const int shortTime = 500; //if equal longTime there is no unrecorded press
const int longTime = 500;
const int longerTime = 2000;

int InMenu = 0;
int ENC_PinAState = LOW;
int ENC_PinALastState = LOW;
int Counter1 = 0;


unsigned long buttonPressStartTimeStamp;
unsigned long buttonPressDuration;

void setup()
{
  pinMode (ENC_PinA, INPUT_PULLUP);
  pinMode (ENC_PinB, INPUT_PULLUP);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ENC_SW, INPUT_PULLUP);
  attachPCINT(digitalPinToPCINT(13), rotaryEncoder, CHANGE);
}

int rotaryEncoder()
{
  ENC_PinAState = digitalRead(ENC_PinA);
  if (ENC_PinAState != ENC_PinALastState)
  {
    if (digitalRead(ENC_PinB) != ENC_PinAState) {
      Counter1++;
    } else {
      Counter1--;
    }
    Serial.println(Counter1 >> 1);
  }
  ENC_PinALastState = ENC_PinAState;
  if (Counter1 <= 0)
  {
    Counter1 = 0;
  }
  if (Counter1 >= 500)
  {
    Counter1 = 500;
  }
  return (Counter1 >> 1);
}

void configMenu()        // End configuration mode.
{
  int menuFlag;
  Serial.println("Start");
  Serial.println("End");
  Serial.println("Test");
  Serial.println("W1");
  while (menuFlag == true)
  {
    noInterrupts();
    int select = map(rotaryEncoder(), 0, 4, 1, 4);
    interrupts();

    switch (select)
    {
      case 1:
        Serial.println("> Start");
        if ((digitalRead(ENC_SW) == LOW) && select == 1)
        {
          Serial.println("> Start_Menu");
          InMenu = 1;
        }
        break;

      case 2:
        {
          Serial.println("> End");
          if ((digitalRead(ENC_SW) == LOW) && select == 1)
          {
            Serial.println("> End_Menu");
            InMenu = 2;
          }
          break;
        }
    }
  }
  switch (InMenu)
  {
    case 1:
      {
        if (Counter1 == 1)
        {
          Serial.print("Start_Menu: 2");
        }
        if (digitalRead(ENC_SW) == LOW)
        {
          Serial.println("Start_Menu: 1-Saved");
        }
      }
    case 2:
      {
        Serial.print("End_Menu: 2");
      }
      if (digitalRead(ENC_SW) == LOW)
      {
        Serial.println("End_Menu: 1-Saved");
      }
  }

  void loop()
  {
    switch (checkButton())
    {
      case shortPress:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Mode: Manual Weld    ");
        lcd.setCursor(0, 2);
        lcd.print("W1:");
        lcd.print(pre);
        //lcd.print(char(0xE4));
        lcd.print("ms");
        lcd.setCursor(9, 2);
        lcd.print("P:");
        lcd.print(pause);
        lcd.print("ms");
        lcd.setCursor(0, 3);
        lcd.print("W2:");
        lcd.print(WeldTime);
        lcd.print("ms ");
        lcd.backlight();
        Weld();
        break;

      case longPress:
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Mode: Automatic Weld");
        continousWeld();
        break;

      case longerPress:
        menuFlag = true;
        lcd.clear();
        lcd.setCursor(1, 0);
        lcd.print("CONFIGURATION MODE");
        break;
    }
    zeroCrossingFlag = false;
    rotaryEncoder();
  }
 
byte checkButton()
  {
    byte event = noEvent;
    buttonState = digitalRead(buttonPin);

    //button pressed
    if (buttonState == LOW && previousButtonState == HIGH)
    {
      delay(20);   //blocking debounce routine
      buttonState = digitalRead(buttonPin);//read button again
      if (buttonState == LOW && previousButtonState == HIGH)
      {
        buttonPressStartTimeStamp = millis();
        startTimeout = true;
      }
    }

    //button released
    if (buttonState == HIGH && previousButtonState == LOW)
    {
      delay(20);//blocking debounce routine
      buttonState = digitalRead(buttonPin);//read button again
      if (buttonState == HIGH && previousButtonState == LOW)
      {
        buttonPressDuration = (millis() - buttonPressStartTimeStamp);
        startTimeout = false;//duration determined no timeout required
      }
    }

    if (buttonPressDuration > 0 && buttonPressDuration <= shortTime)
    {
      event = shortPress;
      buttonPressDuration = 0;
    }

    if (buttonPressDuration > longTime && buttonPressDuration <= longerTime)
    {
      event = longPress;
      buttonPressDuration = 0;
    }

    //button not released and still timing
    if (buttonState == LOW && startTimeout == true && (millis() - buttonPressStartTimeStamp) > longerTime)
    {
      event = longerPress;
      startTimeout = false;
    }

    buttonPressDuration = 0;
    previousButtonState = buttonState;
    return event;
  }


The error
Quote
TestMenu:118: error: a function-definition is not allowed here before '{' token

   {

   ^

TestMenu:212: error: expected '}' at end of input

   }

   ^

exit status 1
a function-definition is not allowed here before '{' token
Can' really unedrstand it ?

anishkgt

ok figured it out it was the function above the loop. did not notice the error message.

anishkgt

uploading this code just gave

Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
Mode: Automatic Weld
 and stopped after a while.

Robin2

uploading this code just gave
You have not posted the latest version of your program.

And why do YOU think it is doing what it is? The first step in debugging is thinking.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

anishkgt

Ooops sorry here it is.

Code: [Select]
#include <PinChangeInterrupt.h>
#include <Wire.h>

const int buttonPin  = 9;  // Weld button
const int ENC_SW     = 11; // Push button on Encoder
const int ENC_PinB   = 12; // PIN B of Encoder
const int ENC_PinA   = 13; // PIN A of Encoder
const int noEvent = 0;
const int shortPress = 1;
const int longPress = 2;
const int longerPress = 3;
const int shortTime = 500; //if equal longTime there is no unrecorded press
const int longTime = 500;
const int longerTime = 2000;

boolean startTimeout = false;
int InMenu = 0;
int ENC_PinAState = LOW;
int ENC_PinALastState = LOW;
int Counter1 = 0;


unsigned long buttonPressStartTimeStamp;
unsigned long buttonPressDuration;

void setup()
{
  pinMode (ENC_PinA, INPUT_PULLUP);
  pinMode (ENC_PinB, INPUT_PULLUP);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ENC_SW, INPUT_PULLUP);
  attachPCINT(digitalPinToPCINT(13), rotaryEncoder, CHANGE);
  Serial.begin(9600);
}

int rotaryEncoder()
{
  ENC_PinAState = digitalRead(ENC_PinA);
  if (ENC_PinAState != ENC_PinALastState)
  {
    if (digitalRead(ENC_PinB) != ENC_PinAState) {
      Counter1++;
    } else {
      Counter1--;
    }
    Serial.println(Counter1 >> 1);
  }
  ENC_PinALastState = ENC_PinAState;
  if (Counter1 <= 0)
  {
    Counter1 = 0;
  }
  if (Counter1 >= 500)
  {
    Counter1 = 500;
  }
  return (Counter1 >> 1);
}

void configMenu()        // End configuration mode.
{
  int menuFlag;
  Serial.println("Start");
  Serial.println("End");
  Serial.println("Test");
  Serial.println("W1");
  while (menuFlag == true)
  {
    noInterrupts();
    int select = map(rotaryEncoder(), 0, 4, 1, 4);
    interrupts();

    switch (select)
    {
      case 1:
        Serial.println("> Start");
        if ((digitalRead(ENC_SW) == LOW) && select == 1)
        {
          Serial.println("> Start_Menu");
          InMenu = 1;
        }
        break;

      case 2:
        {
          Serial.println("> End");
          if ((digitalRead(ENC_SW) == LOW) && select == 1)
          {
            Serial.println("> End_Menu");
            InMenu = 2;
          }
          break;
        }
    }
  }
  switch (InMenu)
  {
    case 1:
      {
        if (Counter1 == 1)
        {
          Serial.print("Start_Menu: 2");
        }
        if (digitalRead(ENC_SW) == LOW)
        {
          Serial.println("Start_Menu: 1-Saved");
        }
      }
    case 2:
      {
        Serial.print("End_Menu: 2");
      }
      if (digitalRead(ENC_SW) == LOW)
      {
        Serial.println("End_Menu: 1-Saved");
      }
  }
}

  void loop()
  {
    switch (checkButton())
    {
      case shortPress:
      Serial.println("Mode: Manual Weld");
        break;

      case longPress:
        Serial.println("Mode: Automatic Weld");
        break;

      case longerPress:
      Serial.println("CONFIGURATION MODE");
        break;
    }
    rotaryEncoder();
  }
 
byte checkButton()
  {
    int buttonState;
    int previousButtonState;
    byte event = noEvent;
    buttonState = digitalRead(buttonPin);

    //button pressed
    if (buttonState == LOW && previousButtonState == HIGH)
    {
      delay(20);   //blocking debounce routine
      buttonState = digitalRead(buttonPin);//read button again
      if (buttonState == LOW && previousButtonState == HIGH)
      {
        buttonPressStartTimeStamp = millis();
        startTimeout = true;
      }
    }

    //button released
    if (buttonState == HIGH && previousButtonState == LOW)
    {
      delay(20);//blocking debounce routine
      buttonState = digitalRead(buttonPin);//read button again
      if (buttonState == HIGH && previousButtonState == LOW)
      {
        buttonPressDuration = (millis() - buttonPressStartTimeStamp);
        startTimeout = false;//duration determined no timeout required
      }
    }

    if (buttonPressDuration > 0 && buttonPressDuration <= shortTime)
    {
      event = shortPress;
      buttonPressDuration = 0;
    }

    if (buttonPressDuration > longTime && buttonPressDuration <= longerTime)
    {
      event = longPress;
      buttonPressDuration = 0;
    }

    //button not released and still timing
    if (buttonState == LOW && startTimeout == true && (millis() - buttonPressStartTimeStamp) > longerTime)
    {
      event = longerPress;
      startTimeout = false;
    }

    buttonPressDuration = 0;
    previousButtonState = buttonState;
    return event;
  }


Quote
And why do YOU think it is doing what it is? The first step in debugging is thinking.
and yes i did think, actually the error was in the log itself and i did not scroll up to see it.

Robin2

and yes i did think, actually the error was in the log itself and i did not scroll up to see it.
That sounds like you now have a solution but you have not told us.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

anishkgt

Quote
That sounds like you now have a solution but you have not told us.
to what ? still trying to get the menu worked out. if i do come up with it i would post it here.

Robin2

to what ? still trying to get the menu worked out. if i do come up with it i would post it here.
You posted the code in Reply #8 and in response to my question about why YOU thought the code was repeating one line as you showed in Reply #6 you said "actually the error was in the log itself and i did not scroll up to see it."

You did not say what error was, or what action you have taken in response to it?

Does the code in Reply #8 include whatever action you have taken to deal with the error that you found? If not, make the corrections and post the latest code. I don't plan to spend time on yesterday's problems.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

anishkgt

The error is in Post#4 and not 8. Post#8 had the error eliminated. The error was that i did not close the function before the Loop.


after the compile neither button works not the encoder just outs what i've posted in Post#6.

cattledog

#13
Jul 17, 2017, 09:43 pm Last Edit: Jul 17, 2017, 09:45 pm by cattledog
You need to make these two variables in checkButton() static and to explicitly initialize them to avoid buttonState logic which looks like a button press when there has not been one.

Code: [Select]
byte checkButton()
{
  static int buttonState = HIGH;
  static int previousButtonState = HIGH;



Also remove the call to the ISR rotaryEncoder() from loop. It is called by the interrupt.

anishkgt

You need to make these two variables in checkButton() static and to explicitly initialize them to avoid buttonState logic which looks like a button press when there has not been one.

Code: [Select]
byte checkButton()
{
  static int buttonState = HIGH;
  static int previousButtonState = HIGH;



Also remove the call to the ISR rotaryEncoder() from loop. It is called by the interrupt.

Thanks that worked. So back to the built.

Go Up