Relay control: Serial interface menu doesn't work as expected

I made a Serial interface with menu and submenu in order to control some devices using relays. What is not working is, when I choose some of the options of the Main menu and then an option [r] (return from switch in the code), Main menu which is in the loop function is not displayed on serial monitor anymore. Can someone tell me why and what should be changed in the code? Here is my code:

const int relay1 = 2;
const int relay2 = 3;
const int relay3 = 4;
const int relay4 = 5;
const int relay5 = 6;
const int relay6 = 7;
const int relay7 = 8;
const int relay8 = 9;
char input;

void setup() {
  // put your setup code here, to run once:
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode(relay5, OUTPUT);
  pinMode(relay6, OUTPUT);
  pinMode(relay7, OUTPUT);
  pinMode(relay8, OUTPUT);
  digitalWrite(relay1, HIGH);
  digitalWrite(relay2, HIGH);
  digitalWrite(relay3, HIGH);
  digitalWrite(relay4, HIGH);
  digitalWrite(relay5, HIGH);
  digitalWrite(relay6, HIGH);
  digitalWrite(relay7, HIGH);
  digitalWrite(relay8, HIGH);
  Serial.begin(9600);
}

void loop() {

    Serial.println("MAIN MENU");
    Serial.println();
    Serial.println("Choose the device you want to control:");
    Serial.println();
    Serial.println("[1] Device1");
    Serial.println("[2] Device2");
    Serial.println("[3] Device3");
    Serial.println("[4] Device4");
    Serial.println();
    
     for (;;) {
         switch (Serial.read()) {
             case '1': device1(); break;
             case '2': device2(); break;
             case '3': device3(); break;
             case '4': device4(); break;
             case '5': return;
             default: continue;  // includes the case 'no input'
        }
    }
}

void device1() {
  Serial.println();
  Serial.println("Device1menu");
  Serial.println();
  Serial.println("Choose the action and press ENTER:");
  Serial.println("[t] Power on/off the device");
  Serial.println("[s] Control1");
  Serial.println("[b] Control2");
  Serial.println("[r] Return to Main Menu");
  Serial.println();

 for (;;) {
       switch (Serial.read()) {
       case 't': 
          Serial.println("Relay ON");
          digitalWrite(relay1, LOW);
          delay(3000);
          Serial.println("Relay OFF");
          digitalWrite(relay1, HIGH);
          delay(3000);
          Serial.read();
          break;
       case 's': 
          Serial.println("Relay ON");
          digitalWrite(relay2, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay2, HIGH);
          delay(700);
          break;
       case 'b': 
          Serial.println("Relay ON");
          digitalWrite(relay2, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay2, HIGH);
          delay(700);
          Serial.println("Relay ON");
          digitalWrite(relay2, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay2, HIGH);
          delay(700);
          break;
       case 'r': return;
       default: continue;  // includes the case 'no input'
       Serial.read();
      }
    }
}

void device2() {
  Serial.println();
  Serial.println("Device2 menu");
  Serial.println();
  Serial.println("Choose the action and press ENTER:");
  Serial.println("[t] Power on/off the device");
  Serial.println("[s] Control1");
  Serial.println("[b] Control2");
  Serial.println("[r] Return to Main Menu");
  Serial.println();

 for (;;) {
       switch (Serial.read()) {
       case 't': 
          Serial.println("Relay ON");
          digitalWrite(relay3, LOW);
          delay(3000);
          Serial.println("Relay OFF");
          digitalWrite(relay3, HIGH);
          delay(3000);
          break;
       case 's': 
          Serial.println("Relay ON");
          digitalWrite(relay4, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay4, HIGH);
          delay(700);
          break;
       case 'b': 
          Serial.println("Relay ON");
          digitalWrite(relay4, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay4, HIGH);
          delay(700);
          Serial.println("Relay ON");
          digitalWrite(relay4, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay4, HIGH);
          delay(700);
          break;
       case 'r': return;
       default: continue;  // includes the case 'no input'
      }
    }
}

void device3() {
  Serial.println();
  Serial.println("Device3 menu");
  Serial.println();
  Serial.println("Choose the action and press ENTER:");
  Serial.println("[t] Power on/off the device");
  Serial.println("[s] Control1");
  Serial.println("[b] Control2");
  Serial.println("[r] Return to Main Menu");
  Serial.println();

 for (;;) {
       switch (Serial.read()) {
       case 't': 
          Serial.println("Relay ON");
          digitalWrite(relay5, LOW);
          delay(3000);
          Serial.println("Relay OFF");
          digitalWrite(relay5, HIGH);
          delay(3000);
          break;
       case 's': 
          Serial.println("Relay ON");
          digitalWrite(relay6, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay6, HIGH);
          delay(700);
          break;
       case 'b': 
          Serial.println("Relay ON");
          digitalWrite(relay6, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay6, HIGH);
          delay(700);
          Serial.println("Relay ON");
          digitalWrite(relay6, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay6, HIGH);
          delay(700);
          break;
       case 'r': return;
       default: continue;  // includes the case 'no input'
      }
    }
}

void device4() {
  Serial.println();
  Serial.println("Device4 menu");
  Serial.println();
  Serial.println("Choose the action and press ENTER:");
  Serial.println("[t] Power on/off the device");
  Serial.println("[s] Control1");
  Serial.println("[b] Control2");
  Serial.println("[r] Return to Main Menu");
  Serial.println();

 for (;;) {
       switch (Serial.read()) {
       case 't': 
          Serial.println("Relay ON");
          digitalWrite(relay7, LOW);
          delay(3000);
          Serial.println("Relay OFF");
          digitalWrite(relay7, HIGH);
          delay(3000);
          break;
       case 's': 
          Serial.println("Relay ON");
          digitalWrite(relay8, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay8, HIGH);
          delay(700);
          break;
       case 'b': 
          Serial.println("Relay ON");
          digitalWrite(relay8, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay8, HIGH);
          delay(700);
          Serial.println("Relay ON");
          digitalWrite(relay8, LOW);
          delay(700);
          Serial.println("Relay OFF");
          digitalWrite(relay8, HIGH);
          delay(700);
          break;
       case 'r': return;
       default: continue;  // includes the case 'no input'
      }
    }
}

There is nothing in the for loop in the loop function that will ever break out of that for loop. So after you run one of the sub menus, you return to the for loop and look for serial input again. In consequence, the main menu prints never occur again.

That makes sense. But when i press 'r', I can choose options 1-4 from the Main menu and it's working, just without displaying the menu. That confuses me. I had to put infinite loop in order to stop infinite printing of menu options. Maybe getchar can resolve the problem, didn't try that approach yet.

You might try adding a Boolean global variable to tell you whether the main menu has been printed. Set it false to start with. Get rid of the infinite loop. If the Boolean tells you that the menu hasn't been printed, print it and set it true. In each of those device functions, set it false.

Could you please help a little bit more in detail how would you do that? Maybe modify my code for the first function? I deleted infinite loops from the sketch and put those boolean variables like this:

  • declared on the top of the sketch boolean isPrinted = false;
  • in the loop:
if(isPrinted == false) {
    Serial.println("MAIN MENU");
    Serial.println();
    Serial.println("Choose the device you want to control:");
    Serial.println();
    Serial.println("[1] Device1");
    Serial.println("[2] Device2");
    Serial.println("[3] Device3");
    Serial.println("[4] Device4");
    Serial.println();
    isPrinted = true;
  }
  • after that there is a switch which represents the Main menu, just without the infinite loop
  • in the first function I just deleted the infinite loop and the submenu is not printing infinitely but the options for controlling the devices are not working anymore; only [r] is working, but the same way as before.

post the whole thing.

const int relay1 = 2;
const int relay2 = 3;
const int relay3 = 4;
const int relay4 = 5;
const int relay5 = 6;
const int relay6 = 7;
const int relay7 = 8;
const int relay8 = 9;
char input;
boolean isPrinted = false;

void setup() {
 // put your setup code here, to run once:
 pinMode(relay1, OUTPUT);
 pinMode(relay2, OUTPUT);
 pinMode(relay3, OUTPUT);
 pinMode(relay4, OUTPUT);
 pinMode(relay5, OUTPUT);
 pinMode(relay6, OUTPUT);
 pinMode(relay7, OUTPUT);
 pinMode(relay8, OUTPUT);
 digitalWrite(relay1, HIGH);
 digitalWrite(relay2, HIGH);
 digitalWrite(relay3, HIGH);
 digitalWrite(relay4, HIGH);
 digitalWrite(relay5, HIGH);
 digitalWrite(relay6, HIGH);
 digitalWrite(relay7, HIGH);
 digitalWrite(relay8, HIGH);
 Serial.begin(9600);
}

void loop() {
 if (isPrinted == false) {
   Serial.println("MAIN MENU");
   Serial.println();
   Serial.println("Choose the device you want to control:");
   Serial.println();
   Serial.println("[1] Device1");
   Serial.println("[2] Device2");
   Serial.println("[3] Device3");
   Serial.println("[4] Device4");
   Serial.println();
   isPrinted = true;
 }

 while (Serial.available() > 0) {
   switch (Serial.read()) {
     case '1': device1(); break;
     case '2': device2(); break;
     case '3': device3(); break;
     case '4': device4(); break;
     case '5': return;
     default: continue;  // includes the case 'no input'
   }
 }
}

void device1() {
 Serial.println();
 Serial.println("Device1menu");
 Serial.println();
 Serial.println("Choose the action and press ENTER:");
 Serial.println("[t] Power on/off the device");
 Serial.println("[s] Control1");
 Serial.println("[b] Control2");
 Serial.println("[r] Return to Main Menu");
 Serial.println();

 while (Serial.available() > 0) {
   switch (Serial.read()) {
     case 't':
       Serial.println("Relay ON");
       digitalWrite(relay1, LOW);
       delay(3000);
       Serial.println("Relay OFF");
       digitalWrite(relay1, HIGH);
       delay(3000);
       Serial.read();
       break;
     case 's':
       Serial.println("Relay ON");
       digitalWrite(relay2, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay2, HIGH);
       delay(700);
       break;
     case 'b':
       Serial.println("Relay ON");
       digitalWrite(relay2, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay2, HIGH);
       delay(700);
       Serial.println("Relay ON");
       digitalWrite(relay2, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay2, HIGH);
       delay(700);
       break;
     case 'r': return;
     default: continue;  // includes the case 'no input'
   }
 }
}

void device2() {
 Serial.println();
 Serial.println("Device2 menu");
 Serial.println();
 Serial.println("Choose the action and press ENTER:");
 Serial.println("[t] Power on/off the device");
 Serial.println("[s] Control1");
 Serial.println("[b] Control2");
 Serial.println("[r] Return to Main Menu");
 Serial.println();

 while (Serial.available() > 0) {
   switch (Serial.read()) {
     case 't':
       Serial.println("Relay ON");
       digitalWrite(relay3, LOW);
       delay(3000);
       Serial.println("Relay OFF");
       digitalWrite(relay3, HIGH);
       delay(3000);
       break;
     case 's':
       Serial.println("Relay ON");
       digitalWrite(relay4, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay4, HIGH);
       delay(700);
       break;
     case 'b':
       Serial.println("Relay ON");
       digitalWrite(relay4, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay4, HIGH);
       delay(700);
       Serial.println("Relay ON");
       digitalWrite(relay4, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay4, HIGH);
       delay(700);
       break;
     case 'r': return;
     default: continue;  // includes the case 'no input'
   }
 }
}

void device3() {
 Serial.println();
 Serial.println("Device3 menu");
 Serial.println();
 Serial.println("Choose the action and press ENTER:");
 Serial.println("[t] Power on/off the device");
 Serial.println("[s] Control1");
 Serial.println("[b] Control2");
 Serial.println("[r] Return to Main Menu");
 Serial.println();

 while (Serial.available() > 0) {
   switch (Serial.read()) {
     case 't':
       Serial.println("Relay ON");
       digitalWrite(relay5, LOW);
       delay(3000);
       Serial.println("Relay OFF");
       digitalWrite(relay5, HIGH);
       delay(3000);
       break;
     case 's':
       Serial.println("Relay ON");
       digitalWrite(relay6, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay6, HIGH);
       delay(700);
       break;
     case 'b':
       Serial.println("Relay ON");
       digitalWrite(relay6, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay6, HIGH);
       delay(700);
       Serial.println("Relay ON");
       digitalWrite(relay6, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay6, HIGH);
       delay(700);
       break;
     case 'r': return;
     default: continue;  // includes the case 'no input'
   }
 }
}

void device4() {
 Serial.println();
 Serial.println("Device4 menu");
 Serial.println();
 Serial.println("Choose the action and press ENTER:");
 Serial.println("[t] Power on/off the device");
 Serial.println("[s] Control1");
 Serial.println("[b] Control2");
 Serial.println("[r] Return to Main Menu");
 Serial.println();

 while (Serial.available() > 0) {
   switch (Serial.read()) {
     case 't':
       Serial.println("Relay ON");
       digitalWrite(relay7, LOW);
       delay(3000);
       Serial.println("Relay OFF");
       digitalWrite(relay7, HIGH);
       delay(3000);
       break;
     case 's':
       Serial.println("Relay ON");
       digitalWrite(relay8, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay8, HIGH);
       delay(700);
       break;
     case 'b':
       Serial.println("Relay ON");
       digitalWrite(relay8, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay8, HIGH);
       delay(700);
       Serial.println("Relay ON");
       digitalWrite(relay8, LOW);
       delay(700);
       Serial.println("Relay OFF");
       digitalWrite(relay8, HIGH);
       delay(700);
       break;
     case 'r': return;
     default: continue;  // includes the case 'no input'
   }
 }
}

I noticed that the program doesn’t enter switch at all, and I have no idea why.

Two problems:

In each device menu function, you need to set isPrinted to false so that when you return to loop, it prints the main menu again.

Also, you removed the infinite loops from the device functions which broke them.

Now I set that boolean variable to false at the beginning of device functions and instead of while(Serial.available() > 0 i put back for( ;; ), but still the program does not enter switch so no commands are executed except [r]. I put inside device functions Serial.println() in order to check is something printed inside switch, but nothing.

Please post your latest version.

It's working now! :slight_smile: I made a mistake and put back the infinite loop around the switch for Main menu instead of putting it back in device functions. Thank you very much. Here is the working code:

const int relay1 = 2;
const int relay2 = 3;
const int relay3 = 4;
const int relay4 = 5;
const int relay5 = 6;
const int relay6 = 7;
const int relay7 = 8;
const int relay8 = 9;
char input;
boolean isPrinted = false;

void setup() {
  // put your setup code here, to run once:
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
  pinMode(relay4, OUTPUT);
  pinMode(relay5, OUTPUT);
  pinMode(relay6, OUTPUT);
  pinMode(relay7, OUTPUT);
  pinMode(relay8, OUTPUT);
  digitalWrite(relay1, HIGH);
  digitalWrite(relay2, HIGH);
  digitalWrite(relay3, HIGH);
  digitalWrite(relay4, HIGH);
  digitalWrite(relay5, HIGH);
  digitalWrite(relay6, HIGH);
  digitalWrite(relay7, HIGH);
  digitalWrite(relay8, HIGH);
  Serial.begin(9600);
}

void loop() {
  if (isPrinted == false) {
    Serial.println();
    Serial.println("MAIN MENU");
    Serial.println();
    Serial.println("Choose the device you want to control:");
    Serial.println();
    Serial.println("[1] Device 1");
    Serial.println("[2] Device 2");
    Serial.println("[3] Device 3");
    Serial.println("[4] Device 4");
    Serial.println();
    isPrinted = true;
  }

  while (Serial.available() > 0) {
    switch (Serial.read()) {
      case '1': device1(); break;
      case '2': device2(); break;
      case '3': device3(); break;
      case '4': device4(); break;
      case '5': return;
      default: continue;  // includes the case 'no input'
    }
  }
}

void device1() {
  isPrinted = false;
  Serial.println();
  Serial.println("Device 1 menu");
  Serial.println();
  Serial.println("Choose the action and press ENTER:");
  Serial.println("[t] Power on/off the device 1");
  Serial.println("[s] Control 1");
  Serial.println("[b] Control 2");
  Serial.println("[r] Return to Main Menu");
  Serial.println();

  for (;;) {
    switch (Serial.read()) {
      case 't':
        Serial.println("Relay ON");
        digitalWrite(relay1, LOW);
        delay(3000);
        Serial.println("Relay OFF");
        digitalWrite(relay1, HIGH);
        delay(3000);
        Serial.read();
        break;
      case 's':
        Serial.println("Relay ON");
        digitalWrite(relay2, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay2, HIGH);
        delay(700);
        break;
      case 'b':
        Serial.println("Relay ON");
        digitalWrite(relay2, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay2, HIGH);
        delay(700);
        Serial.println("Relay ON");
        digitalWrite(relay2, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay2, HIGH);
        delay(700);
        break;
      case 'r': return;
      default: continue;  // includes the case 'no input'
    }
  }
}

void device2() {
  isPrinted = false;
  Serial.println();
  Serial.println("Device 2 menu");
  Serial.println();
  Serial.println("Choose the action and press ENTER:");
  Serial.println("[t] Power on/off the device 2");
  Serial.println("[s] Control 1");
  Serial.println("[b] Control 2");
  Serial.println("[r] Return to Main Menu");
  Serial.println();

  for (;;) {
    switch (Serial.read()) {
      case 't':
        Serial.println("Relay ON");
        digitalWrite(relay3, LOW);
        delay(3000);
        Serial.println("Relay OFF");
        digitalWrite(relay3, HIGH);
        delay(3000);
        break;
      case 's':
        Serial.println("Relay ON");
        digitalWrite(relay4, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay4, HIGH);
        delay(700);
        break;
      case 'b':
        Serial.println("Relay ON");
        digitalWrite(relay4, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay4, HIGH);
        delay(700);
        Serial.println("Relay ON");
        digitalWrite(relay4, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay4, HIGH);
        delay(700);
        break;
      case 'r': return;
      default: continue;  // includes the case 'no input'
    }
  }
}

void device3() {
  isPrinted = false;
  Serial.println();
  Serial.println("Device 3 menu");
  Serial.println();
  Serial.println("Choose the action and press ENTER:");
  Serial.println("[t] Power on/off the device 3");
  Serial.println("[s] Control 1");
  Serial.println("[b] Control 2");
  Serial.println("[r] Return to Main Menu");
  Serial.println();

  for (;;) {
    switch (Serial.read()) {
      case 't':
        Serial.println("Relay ON");
        digitalWrite(relay5, LOW);
        delay(3000);
        Serial.println("Relay OFF");
        digitalWrite(relay5, HIGH);
        delay(3000);
        break;
      case 's':
        Serial.println("Relay ON");
        digitalWrite(relay6, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay6, HIGH);
        delay(700);
        break;
      case 'b':
        Serial.println("Relay ON");
        digitalWrite(relay6, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay6, HIGH);
        delay(700);
        Serial.println("Relay ON");
        digitalWrite(relay6, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay6, HIGH);
        delay(700);
        break;
      case 'r': return;
      default: continue;  // includes the case 'no input'
    }
  }
}

void device4() {
  isPrinted = false;
  Serial.println();
  Serial.println("Device 4 menu");
  Serial.println();
  Serial.println("Choose the action and press ENTER:");
  Serial.println("[t] Power on/off the device 4");
  Serial.println("[s] Control 1");
  Serial.println("[b] Control 2");
  Serial.println("[r] Return to Main Menu");
  Serial.println();

  for (;;) {
    switch (Serial.read()) {
      case 't':
        Serial.println("Relay ON");
        digitalWrite(relay7, LOW);
        delay(3000);
        Serial.println("Relay OFF");
        digitalWrite(relay7, HIGH);
        delay(3000);
        break;
      case 's':
        Serial.println("Relay ON");
        digitalWrite(relay8, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay8, HIGH);
        delay(700);
        break;
      case 'b':
        Serial.println("Relay ON");
        digitalWrite(relay8, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay8, HIGH);
        delay(700);
        Serial.println("Relay ON");
        digitalWrite(relay8, LOW);
        delay(700);
        Serial.println("Relay OFF");
        digitalWrite(relay8, HIGH);
        delay(700);
        break;
      case 'r': return;
      default: continue;  // includes the case 'no input'
    }
  }
}