Nested switch...case

I am trying to write a menu system to integrate with my home automation system but I have run across a problem with nested switch...case switches used to perform the menu routines. The menu is driven off one keypad and needs to be multi-level.

This is the menu code so far:

void loop() {
  char key = keypad.getKey();
  switch (key) {
    case '*':
      menuActive = true; //Allows access to the menu
      GLCD.ClearArea();
      GLCD.println("1. LED On");
      GLCD.println("2. LED Off");
      GLCD.println("3. LED Blink");
      GLCD.println("4. Master config");
      GLCD.println("5. MenuItem5");
      GLCD.println("6. MenuItem6");
      GLCD.println("7. MenuItem7");
      GLCD.println("# To exit menu");
    break;
    
    case '#': //Disables menu access
      menuActive = false;
      HomeScreen(); //Basic home screen presenting user with some basic options
    break;
    
    case '1':
      if (menuActive == true) {
        digitalWrite(ledPin, HIGH); //For testing only
        ActionCompleted();
      }
    break;
    
    case '2':
      if (menuActive == true) {
        digitalWrite(ledPin, LOW);
        ActionCompleted(); //Generic screen to show user action performed sucessfully
      }
    break;
    
    case '3':
      if (menuActive == true) {
        ActionCompleted();
        digitalWrite(ledPin, HIGH);
        delay(100);
        digitalWrite(ledPin, LOW);
        delay(100);
        digitalWrite(ledPin, HIGH);
        delay(100);
        digitalWrite(ledPin, LOW);
      }
    break;
    
    case '4':
      if (menuActive == true) {
        GLCD.ClearArea();
        GLCD.println("Master Configuration");
        GLCD.println("# To exit menu");
        GLCD.println("1. Device ID");
          case '1': //Error is given on this line because the number 1 is used twice...
            GLCD.ClearArea();
            GLCD.println("Device ID menu");
          break;
      }
    break;
  }
}

The error says:
VOSHA_UCP.ino: In function 'void loop()':
VOSHA_UCP:116: error: duplicate case value
VOSHA_UCP:66: error: previously used here

But I need that to work to provide me with a multi level menu. Is there any workarounds to this problem?

uber

use if elseif else

Just tried throwing this in on the end:

case '4':
      if (menuActive == true) {
        GLCD.ClearArea();
        GLCD.println("Master Configuration");
        GLCD.println("# To exit menu");
        GLCD.println("1. Device ID");
          if(key == '1') { //This toggles the function to enable LED instead of entering menu
            GLCD.ClearArea();
            GLCD.println("Device ID menu");
          }
      }

And it just enabled the other function with the same key that it was assigned to... I'm thinking about toggling the main menu lock once I get into a lower menu...

uber

OK, I give up, where is the second switch hiding ?
Presumably it should be in your case '4' somewhere

I did try that earlier before I posted and it didn't seem to like nested switches, I'll try again now...

uber

Just tried this. Again, to no avail...

case '4':
      if (menuActive == true) {
        menuActive = false;
        GLCD.ClearArea();
        GLCD.println("Master Configuration");
        GLCD.println("# To exit menu");
        GLCD.println("1. Device ID");
          switch (key) {
            case '1':
            //if(key == '1')
            GLCD.ClearArea();
            GLCD.println("Device ID menu");
            break;
      }
    break;

I tried disabling access to the menu system but that seems to lock me out entirely and if I remove the menuActive = false line I'm back to the problem of it turning the LED on again... :S

uber

If I understand correctly you want a new switch...case after displaying a sub menu in your case '4'. Is that right ?

If so then you will need to read the keyboard again in case '4' and assign the input to a different variable. You currently have only one keypad.getKey in your code.

Just tried this now as you suggested:

    case '4':
      if(menuActive == true){
        GLCD.ClearArea();
        GLCD.println("Master Configuration");
        GLCD.println("# To exit menu");
        GLCD.println("1. Device ID");
        char key2 = keypad.getKey(); //Used for this sub menu only
          switch (key2) {
            case '1':
            GLCD.ClearArea();
            GLCD.println("Device ID menu");
            break;
      }

And it still toggles the LED, this is infuriating...

uber

Is this menu supposed to be blocking (the code does not leave the switch case until the user has left the nested menu)?

In your original code it looks as if you were going to introduce a nested switch statement, but you omitted the switch statement itself - you only included the case statements. Hence it was considered as a (mangled) part of the outer switch, causing the error.

In a later copy of your code you added the switch statement but did not include the code to read the user input used to navigate within the menu or anything to cause the sketch to wait until that input arrived or cope if it did not arrive.

Yes, the menu system is supposed to be blocking as the actions are performed in the root menu itself. I am trying to get the user input to this one sub menu but it keeps triggering the second function (To turn on the LED) in the menu above the main configuration menu which isn't what I want and the only way I could see this possible is to put the menu in a routine and then call it and just pass it user input from the loop :confused:

uber

That is a lot of stuff crammed into your loop() function, just put the case statements in there with calls to the stuff inside your case statements.

Case 0:
Function1();
Break;

Case 1:
Function2();
Break;

Etc...

It is easier to isolate the problems.

I'll have to try that tomorrow, I've switched my laptop off for the night now

uber

But the problems with you as this compiles

int i;

void setup(){
}

void loop(){
  switch (i){
    case 1:
    break;
    case 2:
     switch (i){
      case 1:break;
      case 2:break;
    }
    break;
  }
}

Mark

PS try auto formatting your code then post it ALL with the current error message.

M

I tried adding a function and I couldn't give it user input properly, I could only back out of the menu... Here is my entire code at the moment:

#include <Keypad.h>

#include <glcd.h>
#include <glcd_Buildinfo.h>
#include <glcd_Config.h>

#include "fonts/allFonts.h"

int ledPin = 13;

boolean menuActive = false; //

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {
    '1','2','3'  }
  ,
  {
    '4','5','6'  }
  ,
  {
    '7','8','9'  }
  ,
  {
    '*','0','#'  }
};
byte rowPins[ROWS] = {
  40, 41, 42, 43}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {
  44, 45, 46}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
  pinMode(ledPin, OUTPUT);
  GLCD.Init();
  GLCD.SelectFont(System5x7);
  GLCD.println("VOSHA UCP");
  GLCD.println("Press * for menu");
  GLCD.println("1. Lamp toggle");
  GLCD.println("2. Alarm silence");
  GLCD.println("3. Alarm sound");
  GLCD.println("4. LED value set");
}

void loop() {
  char key = keypad.getKey();
  switch (key) {
  case '*':
    menuActive = true;
    GLCD.ClearArea();
    GLCD.println("1. LED On");
    GLCD.println("2. LED Off");
    GLCD.println("3. LED Blink");
    GLCD.println("4. Master Config");
    GLCD.println("5. MenuItem5");
    GLCD.println("6. MenuItem6");
    GLCD.println("7. MenuItem7");
    GLCD.println("# To exit menu");
    break;

  case '#':
    menuActive = false;
    HomeScreen();
    break;

  case '1':
    if (menuActive == true) {
      digitalWrite(ledPin, HIGH);
      ActionCompleted();
    }
    break;

  case '2':
    if (menuActive == true) {
      digitalWrite(ledPin, LOW);
      ActionCompleted();
    }
    break;

  case '3':
    if (menuActive == true) {
      ActionCompleted();
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
      digitalWrite(ledPin, HIGH);
      delay(100);
      digitalWrite(ledPin, LOW);
      delay(100);
    }
    break;

  case '4':
    if (menuActive == true) {
      MasterConfig();
      break;

    default:
      break;
    }
  }
}

void HomeScreen() { //Routine to display home screen and provide updates
  GLCD.ClearArea();
  GLCD.println("VOSHA UCP");
  GLCD.println("Press * for menu");
  GLCD.println("1. Lamp toggle");
  GLCD.println("2. Alarm silence");
  GLCD.println("3. Alarm sound");
  GLCD.println("4. LED value set");
}

void ActionCompleted() {
  GLCD.ClearArea();
  GLCD.CursorTo(0,0);
  GLCD.print("Action completed!");
  delay(500);
  menuActive = false;
  HomeScreen();
}

void MasterConfig() {
  menuActive = false;
  GLCD.ClearArea();
  GLCD.println("Master Configuration");
  GLCD.println("# To exit menu");
  GLCD.println("1. Device ID");
  char key2 = keypad.getKey();
  switch (key2) {
  case '1':
    GLCD.ClearArea();
    GLCD.println("Device ID menu");
    break;
  default:
    break;
  }
}

MasterConfig is where I have moved that menu to try out Hazards suggestion and I can't give it input...

uber

You need to add code so that the inner switch..case is not exited until the user releases the key. Otherwise it will be picked up as a keypress and interpreted by the outer switch..case.

At the moment, when the user triggers the sub menu the MasterConfig menu appears. They then press 1 and see the message "Device ID menu". The inner switch..case then exits and we are back in the outer switch but the user still has their finger on the 1 key, hence the LED lights.