Serial Menu/Submenu Issues

The following is my attempt to try and understand how to structure menus used in the Serial Monitor. Once I have this figured out, I want to try to use switch/case for it. The problem right now is, when you type 'a' for the main menu, it goes to the submenu, then type 1 and it goes to the item in the submenu, BUT... try to change from a 1 (in the submenu) to a 2. You have to press 2 twice. Any subsequent selection requires pushing the number twice. Any ideas how to make that stop?

char menu;
char subMenu;
bool runOnce = true;

void setup() {
  Serial.begin(115200);
}

void topMenu() {
  if (runOnce == true) {
    Serial.println ("                     Main Menu                          ");
    Serial.println (" A) Menu = A!  B) Menu = B!  C) Menu = C!  D) Menu = D! ");  
    runOnce = false;
    
  }      
}

void midMenu() {
  Serial.println ("                     Middle Menu                               ");
  Serial.println("1) Choice One  2) Choice Two  3) Choice Three  4) Choice Four  5) Quit to Main");
}

void loop() {  
   topMenu();
   while (Serial.available() == 0) {} 
   char menu = Serial.read(); 
    if (menu == 'a') {       
    Serial.println ("Menu = A!");
    midMenu();
    }
    else if (menu == 'b') {       
    Serial.println ("Menu = B!");
    midMenu();
    }
    else if (menu == 'c') {       
    Serial.println ("Menu = C!");
    midMenu();
    }
    else if (menu == 'd') {       
    Serial.println ("Menu = D!");
    midMenu();
    }
    else if (menu >= 'e')
    {
      Serial.println ("Choose Options A through D");
    }      
    
    while (Serial.available() == 0) {} 
    subMenu = Serial.read();
    if (subMenu == '1'){
      Serial.println("SubMenu = 1");
    }
    else if (subMenu == '2'){
      Serial.println("SubMenu = 2");
    }    
    else if (subMenu == '3'){
      Serial.println("SubMenu = 3");
    }
    else if (subMenu == '4'){
      Serial.println("SubMenu = 4");
    }
    else if (subMenu == '5'){
      Serial.println("SubMenu = 5");
      menu = 'q';
      runOnce = true;
      topMenu();
    }  
} // end void loop

What have you set as end line marker in the Serial Monitor?

Are you sending \r\n ?

If so try to understand what it means to your code if you type a and send that
Into your code (which will receive ‘a’ ‘\r’ and ‘\n’ in a row, knowing that ‘\n’ is not > ‘e’)

I have it set to No line ending. No matter what I use in that window, I still get a gap where you need to hit the number twice. Very odd and I wonder what I am doing wrong. What do you suggest?

What do you suggest?

your whole construct depends on runOnce not displaying to the menu but this does not prevent entering in the first waiting while() in case of an issue and so you don’t see all what’s happening.

If I had to debug this I would:

1] Just to be sure to capture all cases I would change

else if (menu >= 'e')
    {
      Serial.println ("Choose Options A through D");
    }

into

else
    {
      Serial.println ("Choose Options A through D");
    }

numbers being before letters in ASCII you would see if you end up in the top of the loop expecting a number

2] I would also add serial.print to show where you are in the code

If I had to write this I would:

3] actually rewrite the whole thing differently, as a small state machine: you should not have two active waiting while() blocking your code. the loop should just spin always, listening for Serial and you could have a variable memorizing at which menu level you are and the state machine deciding what is the next menu based on current menu and character entry

Yes. Just the clues I needed once again! Thank you! I am trying to understand how all this works so next I will try this in a switch/case configuration. Should be fun! Here is the code that works…

char menu;
char subMenu;
bool runOnce = true;
bool menuState = true;
void setup() {
  Serial.begin(115200);
}

void topMenu() {
   
  if (runOnce == true) {
    Serial.println ("                     Main Menu                          ");
    Serial.println (" A) Menu = A!  B) Menu = B!  C) Menu = C!  D) Menu = D! ");  
    runOnce = false;
    menuState = true;
  }      
}

void sub1Menu() {
  menuState = false; 
  Serial.println ("                      Sub Menu                               ");
  Serial.println("1) Choice One  2) Choice Two  3) Choice Three  4) Choice Four  5) Quit to Main");
}

void loop() {   
   topMenu();   
   while (Serial.available() == 0) {}    
   if (menuState == true) {
    char menu = Serial.read(); 
    if (menu == 'a') {       
      Serial.println ("Menu = A!");
      sub1Menu();
    }
    else if (menu == 'b') {       
      Serial.println ("Menu = B!");
      sub1Menu();
    }
    else if (menu == 'c') {       
      Serial.println ("Menu = C!");
      sub1Menu();
    }
    else if (menu == 'd') {       
      Serial.println ("Menu = D!");
      sub1Menu();
    }
    else  {
      Serial.println ("Choose Options A through D");
    }      
  }
  if (menuState == false) {
    subMenu = Serial.read();
    if (subMenu == '1'){
      Serial.println("SubMenu = 1");
    }
    else if (subMenu == '2'){
      Serial.println("SubMenu = 2");
    }    
    else if (subMenu == '3'){
      Serial.println("SubMenu = 3");
    }
    else if (subMenu == '4'){
      Serial.println("SubMenu = 4");
    }
    else if (subMenu == '5'){
      Serial.println("SubMenu = 5");
      menu = 'q';
      runOnce = true;
      topMenu();
    }
  } 
}

Here is an unexpected problem...

When asking the program to return what is on Analog 0 of the Mega, like this:

Serial.println(potPin1); and potPin1 = A0;

it reports back the number 54. Since I am using a Mega, I have 15 Analog ports and I want them all to report back their proper numbers (A0,A1, etc).

That would make this segment of the code kind of cumbersome. Is there a better way to do this?

else if (menu == 'b' || menu == 'B') {       
      Serial.println ("PotControl Gives You Analog Potentiometer Control using");
      Serial.print ("the Potentiometer connected to ");
      if (potPin1 == 54){
        Serial.print ("A0 (ServoPan on Digital Pin ");
        Serial.print (servoPin1);
        
      }
      if (potPin1 == 55){
        Serial.print ("A1");
      }
      if (potPin1 == 56){
        Serial.print ("A2");
      }
      Serial.print (" or ");
      if (potPin2 == 55){
        Serial.print ("A1");
      }
      
      Serial.println (" on the Arduino.");

so far there are only two servos. Also, is there a way to concatenate the Serial.println to include a string + a variable? Thanks!