Problem with switch/case menu

Hi everyone,

So i have found a simple menu on switch/case which uses 3 buttons: up, down, select. Ok menu it's working i can move up/down/select it works, but my problem looks like... I've made a program to setting control temperature. It is:

przycisk = digitalRead(12);
przycisk1 = digitalRead(13);
  

if(przycisk != last) {
  delay(30);
  if(przycisk == LOW){
    numer = numer+1;
    lcd.setCursor(7, 0);
    lcd.print(numer);
    lcd.println(" 'C ");
    delay(100);
  }
  last = przycisk ;
}

if(przycisk1 != last) {
  delay(30);
  if(przycisk1 == LOW){
    numer = numer-1;
    lcd.setCursor(7, 0);
    lcd.print(numer);
    lcd.println(" 'C ");
    delay(100);
  }
  last = przycisk1 ;
}

And now i want to include it into case in my menu. Problem is there it doesn't works.Im opening my menu at arudino im setting 1st case which is temperature setting and i see only " lcd.print(numer);" which value is = 0 and it doesn't react for any button. My question is it's possible to do this type of meni with 3 buttons, how should I include code here?

#include <OneWire.h>
#include <DS18B20.h>
#include <LiquidCrystal.h>

#define ONEWIRE_PIN 8

byte address[8] = {0x28, 0x5F, 0x68, 0xC3, 0x4, 0x0, 0x0, 0x14
};

OneWire onewire(ONEWIRE_PIN);
DS18B20 sensors(&onewire);
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);


int przycisk = 0;
int last = 0;
int przycisk1 = 0;







int numer = 0;
int timer = 0;
byte totalRows = 2;                // rzedy
byte totalCols = 16;               // kolumny
int returndata = 0;                // Used for return of button presses
unsigned long timeoutTime = 0;     // Czas od ostatniej czynnosci
const int menuTimeout = 20000;     // Czas od ostatniej czynnosci w menu.
unsigned long lastButtonPressed;   // Ostatni przycisniety przycisk
const int debounceTime = 150;      // this is the debounce and hold delay. Otherwise, you will FLY through the menu by touching the button. 
const int buttonUp = 12;            // Set pin for UP Button
const int buttonDown = 13;          // Set pin for DOWN Button
const int buttonSelect = 10;             // Set pin for SLELECT Button


int buttonStateUp = 0;             // Initalise ButtonStates
int buttonStateDown = 0;
int buttonState = 0;
int buttonStateSelect = 0;






#define MOVECURSOR 1 

#define MOVELIST 2  





void autor()
{

lcd.clear();
        lcd.print("abcd");
        lcd.setCursor(0,1);
        lcd.print("abcvd  v1.0");
        
}








// Poczatek kodu

void setup()


{
    lcd.begin(totalCols, totalRows);  
    Serial.begin(9600);
        
    //Ustawianie przycisków
    pinMode(buttonUp, INPUT_PULLUP);
    pinMode(buttonDown, INPUT_PULLUP);
    pinMode(buttonSelect, INPUT_PULLUP);

    //Czujnik

     sensors.begin();
     sensors.request(address);
}



void loop()
{

  {

// Deklaracje
  
  
    
  
  

    // Read the Button States
    
    buttonStateUp = digitalRead(buttonUp);
    buttonStateDown = digitalRead(buttonDown);
    buttonStateSelect = digitalRead(buttonSelect);
  
  
//Poczatek setupu

  if (buttonStateUp == LOW && buttonStateDown == LOW) { 
  
  
  byte topItemDisplayed = 0;  
  byte cursorPosition = 0;   

//Dodatkowe zmienne
  byte redraw = MOVELIST;  
  byte i=0; 
  byte totalMenuItems = 0;  

//Tworzenie menu
  char* menuItems[]={
    "1.Temperature Set", 
    "2.Histereza", 
    "3.Puste",
    "4.Autor ", 
    "",
  };

// Podliczanie rzeczy

  while (menuItems[totalMenuItems] != ""){
    totalMenuItems++;  
  }
  
  //subtract 1 so we know total items in array.
  totalMenuItems--;  
  

  lcd.clear();  // clear the screen so we can paint the menu.

  boolean stillSelecting = true;  // set because user is still selecting.

  timeoutTime = millis() + menuTimeout; // set initial timeout limit. 

  do   // Run a loop while waiting for user to select menu option.
  {

// Call any other setup actions required and/or process anything else required whilst in setup mode as opposed to things setup regardless of setup mode


    
// Call read buttons routine which analyzes buttons and gets a response. Default response is 0.  
switch(read_buttons())
    {  

   
      // Case responses depending on what is returned from read buttons routine
      
      case 1:  // 'UP' BUTTON PUSHED

      timeoutTime = millis()+menuTimeout;  // reset timeout timer
      //  if cursor is at top and menu is NOT at top
      //  move menu up one.
      if(cursorPosition == 0 && topItemDisplayed > 0)  //  Cursor is at top of LCD, and there are higher menu items still to be displayed.
      {
        topItemDisplayed--;  // move top menu item displayed up one. 
        redraw = MOVELIST;  // redraw the entire menu
      }

      // if cursor not at top, move it up one.
      if(cursorPosition>0)
      {
        cursorPosition--;  // move cursor up one.
        redraw = MOVECURSOR;  // redraw just cursor.
      }
      break;

   
   
   
      case 2:    // 'DOWN' BUTTON PUSHED

      timeoutTime = millis()+menuTimeout;  // reset timeout timer
      // this sees if there are menu items below the bottom of the LCD screen & sees if cursor is at bottom of LCD 
      if((topItemDisplayed + (totalRows-1)) < totalMenuItems && cursorPosition == (totalRows-1))
      {
        topItemDisplayed++;  // move menu down one
        redraw = MOVELIST;  // redraw entire menu
      }
      if(cursorPosition<(totalRows-1))  // cursor is not at bottom of LCD, so move it down one.
      {
        cursorPosition++;  // move cursor down one
        redraw = MOVECURSOR;  // redraw just cursor.
      }
      break;

      
      
      
      
      
      case 4:  // SELECT BUTTON PUSHED

      timeoutTime = millis()+menuTimeout;  // reset timeout timer
      switch(topItemDisplayed + cursorPosition) // adding these values together = where on menuItems cursor is.
      {
      case 0:  // menu item 1 selected          
        lcd.clear();
        lcd.print("Ustawianie Set ");
        lcd.setCursor(0,1);
        lcd.print(digitalRead(buttonSelect));


        
        Serial.print("Menu item ");
        Serial.print(topItemDisplayed + cursorPosition);
        Serial.print(" selected - ");
        Serial.println(menuItems[topItemDisplayed + cursorPosition]);
        delay(10000);
        stillSelecting = false;
        
        
       break;

      case 1:  // menu item 2 selected
        lcd.clear();
        lcd.print("Run Item2 code");
        lcd.setCursor(0,1);
        lcd.print("from here");
        Serial.print("Menu item ");
        Serial.print(topItemDisplayed + cursorPosition);
        Serial.print(" selected - ");
        Serial.println(menuItems[topItemDisplayed + cursorPosition]);
        break;

      case 2:  // menu item 3 selected
       autor();
       delay(10000);
        break;

      case 3:  // menu item 4 selected
        lcd.clear();
        lcd.print("abcd");
        lcd.setCursor(0,1);
        lcd.print("gdgf  v1.0");
        break;
case 4:  // menu item 5 selected
        lcd.clear();
        lcd.print("abcd");
        lcd.setCursor(0,1);
        lcd.print("gfdg  v1.0");Serial.print("Menu item ");        Serial.print("Menu item ");
        Serial.print(topItemDisplayed + cursorPosition);
        Serial.print(" selected - ");
        Serial.println(menuItems[topItemDisplayed + cursorPosition]);
        break;

      case 5:  // menu item 6 selected
        lcd.clear();
        lcd.print("abcd");
        lcd.setCursor(0,1);
        lcd.print("fdsf  v1.0");Serial.print("Menu item ");        Serial.print("Menu item ");
        Serial.print(topItemDisplayed + cursorPosition);
        Serial.print(" selected - ");
        Serial.println(menuItems[topItemDisplayed + cursorPosition]);
        break;
       
      }
      break;
      
    
    
    
    
    
    
    //case 8:  //  CANCEL BUTTON PUSHED - Not currently used
    //  stillSelecting = false;
    //  Serial.println("Button held for a long time");
    //  break;



}

    switch(redraw){  //  checks if menu should be redrawn at all.
    case MOVECURSOR:  // Only the cursor needs to be moved.
      redraw = false;  // reset flag.
      if (cursorPosition > totalMenuItems) // keeps cursor from moving beyond menu items.
        cursorPosition = totalMenuItems;
      for(i = 0; i < (totalRows); i++){  // loop through all of the lines on the LCD
        lcd.setCursor(0,i);
        lcd.print(" ");                      // and erase the previously displayed cursor
        lcd.setCursor((totalCols-1), i);
        lcd.print(" ");
      }
      lcd.setCursor(0,cursorPosition);      // go to LCD line where new cursor should be & display it.
      lcd.print(">");
      lcd.setCursor((totalCols-1), cursorPosition);
      lcd.print("<");
      break;  // MOVECURSOR break.

    case MOVELIST:  // the entire menu needs to be redrawn
      redraw=MOVECURSOR;  // redraw cursor after clearing LCD and printing menu.
      lcd.clear(); // clear screen so it can be repainted.
      if(totalMenuItems>((totalRows-1))){  // if there are more menu items than LCD rows, then cycle through menu items.
        for (i = 0; i < (totalRows); i++){
          lcd.setCursor(1,i);
          lcd.print(menuItems[topItemDisplayed + i]);
        }
      }
      else{  // if menu has less items than LCD rows, display all available menu items.
        for (i = 0; i < totalMenuItems+1; i++){
          lcd.setCursor(1,i);
          lcd.print(menuItems[topItemDisplayed + i]);
        }
      }
   
      break;  // MOVELIST break
      
    }

    if (timeoutTime<millis()){  // user hasn't done anything in awhile
      stillSelecting = false;  // tell loop to bail out.
     
    }
  } 


  while (stillSelecting == true);  //
  
//End of Start Setup mode if
}
  
  
//End of Void Setup() 


// Clear LCD on exit from setup routine
lcd.clear();
}  

  lcd.setCursor(0,0); //Ustawienie kursora
    lcd.print("Temp ");
    
    lcd.println(F(" 'C "));
    lcd.setCursor(0,1);
    lcd.print("Set:");
    lcd.print(numer);
    lcd.print(" H: ");
 
  if (sensors.available())
  {
    float temperature = sensors.readTemperature(address);
    lcd.setCursor(6,0);
    lcd.print(temperature);
    lcd.print(" 'C ");
    delay(500);
    

    sensors.request(address);
  }
  if (sensors.readTemperature(address)>numer) {
  digitalWrite(11,HIGH);
  
  }
 else
 digitalWrite(11,LOW);


   if (buttonStateUp == HIGH && buttonStateDown == HIGH) { 
  
  
  byte topItemDisplayed = 0;  // stores menu item displayed at top of LCD screen
  byte cursorPosition = 0;  // where cursor is on screen, from 0 --> totalRows. 


  byte redraw = MOVELIST;  // triggers whether menu is redrawn after cursor move.
  byte i=0; // temp variable for loops.
  byte totalMenuItems = 0;  //a while loop below will set this to the # of menu items.
   }

}




// This routine reads the buttons, applys the debounce and returns the result to the calling routine

int read_buttons(){  // you may need to swap "void" with "int" or "byte"
  
  int returndata = 0;
   

  if ((lastButtonPressed + debounceTime) < millis()){  // see if it's time to check the buttons again
    
    // read Up button
    buttonState = digitalRead(buttonUp);
   
    if (buttonState == LOW){
      returndata = returndata + 1;
      lastButtonPressed = millis();
    }

    // read Down button
    buttonState = digitalRead(buttonDown);
    
    if (buttonState == LOW){
      returndata = returndata + 2;
      lastButtonPressed = millis();
    }

    // read Select button
    buttonState = digitalRead(buttonSelect);
    if (buttonState == LOW){
      returndata = returndata + 4; 
      lastButtonPressed = millis();
    }

    // read Cancel button - Not used at present
    //buttonState = digitalRead(buttonCancel);
    //if (buttonState == LOW){
    //  returndata = returndata + 8;
    //  lastButtonPressed = millis();
    //}
  }
  
  return returndata; // this spits back to the function that calls it the variable returndata.
}

Yes, it can be done.

How? No idea as you did not show the full code that you tried.

One thing strikes me as odd and that is that, in the shown code, you use 'last' for both buttons; usually one keeps a 'last' variable for each button.

PS Thanks for using code tags in your first post.

Well, that was my first code still it works, but now i want more functions so i have to include it into case. How should i take control of up/down buttons to set temperature in case.

przycisk = digitalRead(12);
przycisk1 = digitalRead(13);

I really hate to see related variables where only one of them is numbered. You don't count that way do you? Uh-huh, one, two...

I think you started with the wrong type of sketch for what you are doing.

You need a sketch that can handle the up and down buttons being used in more than one way

I have cut some code from a sketch I am using see if you can understand how its working

const byte buttonEdit = 5;
const byte buttonPlus = 4;
const byte buttonMinus = 7;
byte menu = 0;
byte editMode = 0;
float tempSetPoint = 35.5;


void setup() {
  // put your setup code here, to run once:
  pinMode (buttonEdit, INPUT_PULLUP);
  pinMode (buttonPlus, INPUT_PULLUP);
  pinMode (buttonMinus, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  menu = buttonCheck(menu, 1);//name then scale
  menu = constrain(menu, 0, 5);
  switch (menu) {
    case 0://home screen
      //show something on lcd
      Serial.println(" home ");
      editMode = 0;
      break;
    case 1:
      //show temp on lcd
      Serial.println(" page 1 ");
      editMode = 0;
      break;
    case 2:
      //set temp lcd display
      Serial.println(" temp display ");

      if (editMode == 1) {
        //lcd display "use buttons to set"
        Serial.println("enter to set");

        while (editMode == 1) {
          tempSetPoint =  buttonCheck(tempSetPoint, 0.1);
          //show temp on lcd
          Serial.print(" tempSetPoint ");
          Serial.println(tempSetPoint);
        }
        break;
      case 3:
        Serial.println(" page 3 ");
        editMode = 0;
        break;
      case 4:
        Serial.println(" page 4 ");
        editMode = 0;
        break;
      case 5:// return
        Serial.println(" page 5 ");
        editMode = 0;
        break;
      default:
        break;
      }
  }

}

float buttonCheck(float viarable, float scale) {
  static byte preveditButton;
  static byte prevplusButton;
  static byte prevMinusButton;
  byte plusButton = digitalRead(buttonPlus);
  byte minusButton = digitalRead(buttonMinus);
  byte editButton = digitalRead(buttonEdit);

  if (plusButton != prevplusButton) {
    if (plusButton == LOW) {
      //lcd.clear();
      viarable = viarable + scale;
    }
    prevplusButton = plusButton;
  }

  if (minusButton != prevMinusButton) {
    if (minusButton == LOW) {
      //lcd.clear();
      viarable = viarable - scale;
      if (viarable < 0) {
        viarable = 0;
      }
    }
    prevMinusButton = minusButton;
  }

  if (editButton != preveditButton) {
    if (editButton == LOW) {
      //lcd.clear();
      editMode = ! editMode;
    }
    preveditButton = editButton;
  }
  //add millis timer so escape=0, menu = 0 after 1 minute to kick back to home screen

  return viarable;
}

ive made it so it works in serial print so you only have to add your pin numbers then use the serial monitor to understand what its doing.

try setting the temp on the serial using the up/down button then enter to exit the menu selection

Thanks gpop1,that's something what i wanted :) I though that may be problem with the sketch but i wasn't sure. Already i have done my code and it finally works. But i don't know how to do the last thing [u]//add millis timer so escape=0, menu = 0 after 1 minute to kick back to home screen[/u] would u help me with this?

//add millis timer so escape=0, menu = 0 after 1 minute to kick back to home screen

It would help to stop thinking of millis() as a timer. The millis() function is a clock, not a timer.

If you record that (use a boolean variable) and when (use millis()) an event occurs, then you can, on every pass through loop(), see how long it has been since the event occurred, if it occurred. If the event occurred, and it occurred long enough ago, make some other event occur.

I don't know what "kick back to home" means, because you don't really have a menu. You have different text that you display under different conditions. You can "kick back to home" by making the conditions that cause the "home" text to be displayed true.

PaulS: You can "kick back to home" by making the conditions that cause the "home" text to be displayed true.

That's it, If there won't be any button press for piece of time go to home(home screen)

think this will work.

const byte buttonEdit = 5;
const byte buttonPlus = 4;
const byte buttonMinus = 7;
byte menu = 0;
byte editMode = 0;
float tempSetPoint = 35.5;
unsigned long prevMillis = 0;
unsigned long currentMillis = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode (buttonEdit, INPUT_PULLUP);
  pinMode (buttonPlus, INPUT_PULLUP);
  pinMode (buttonMinus, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  checkTimer();
  menu = buttonCheck(menu, 1);//name then scale
  menu = constrain(menu, 0, 5);
  switch (menu) {
    case 0://home screen
      //show something on lcd
      Serial.println(" home ");
      editMode = 0;
      break;
    case 1:
      //show temp on lcd
      Serial.println(" page 1 ");
      editMode = 0;
      break;
    case 2:
      //set temp lcd display
      Serial.println(" temp display ");

      if (editMode == 1) {
        //lcd display "use buttons to set"
        Serial.println("enter to set");

        while (editMode == 1) {
          tempSetPoint =  buttonCheck(tempSetPoint, 0.1);
          //show temp on lcd
          Serial.print(" tempSetPoint ");
          Serial.println(tempSetPoint);
        }
        break;
      case 3:
        Serial.println(" page 3 ");
        editMode = 0;
        break;
      case 4:
        Serial.println(" page 4 ");
        editMode = 0;
        break;
      case 5:// return
        Serial.println(" page 5 ");
        editMode = 0;
        break;
      default:
        break;
      }
  }

}

float buttonCheck(float viarable, float scale) {
  static byte preveditButton;
  static byte prevplusButton;
  static byte prevMinusButton;
  byte plusButton = digitalRead(buttonPlus);
  byte minusButton = digitalRead(buttonMinus);
  byte editButton = digitalRead(buttonEdit);


  if (plusButton != prevplusButton) {
    if (plusButton == LOW) {
      //lcd.clear();
      prevMillis = currentMillis;
      viarable = viarable + scale;
    }
    prevplusButton = plusButton;
  }

  if (minusButton != prevMinusButton) {
    if (minusButton == LOW) {
      //lcd.clear();
      prevMillis = currentMillis;
      viarable = viarable - scale;
      if (viarable < 0) {
        viarable = 0;
      }
    }
    prevMinusButton = minusButton;
  }

  if (editButton != preveditButton) {
    if (editButton == LOW) {
      //lcd.clear();
      prevMillis = currentMillis;
      editMode = ! editMode;
    }
    preveditButton = editButton;
  }
   checkTimer();
   
  return viarable;
 
}

void checkTimer() {
  currentMillis = millis();
  if (currentMillis - prevMillis >= 60000L) {//60 second timer
    editMode = 0;
    menu = 0;
  }
}

Well it works fine but maybe u know what kind of problem may be that after “kick back” i can’t re-enter to menu again…

You need to condition when to call checkTimer. Once you have stored prevMillis and one minutes has past the condition will always be true and therefore forcing you to or keeping you on the home screen.

You missed a step from PaulS’ post. You have when the event occurred (prevMillis) but you are missing the record that the event occurred (a boolean). Each time you store prevMillis, buttonPressed needs to be set to true. When the 60 second timer expires, set buttonPressed false. Then only call checkTimer on buttonPressed == true.

const byte buttonEdit = 5;
const byte buttonPlus = 4;
const byte buttonMinus = 7;
byte menu = 0;
byte editMode = 0;
float tempSetPoint = 35.5;
boolean buttonPressed = 0; // <===============
unsigned long prevMillis = 0;
unsigned long currentMillis = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode (buttonEdit, INPUT_PULLUP);
  pinMode (buttonPlus, INPUT_PULLUP);
  pinMode (buttonMinus, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  checkTimer();
  menu = buttonCheck(menu, 1);//name then scale
  menu = constrain(menu, 0, 5);
  switch (menu) {
    case 0://home screen
      //show something on lcd
      Serial.println(" home ");
      editMode = 0;
      break;
    case 1:
      //show temp on lcd
      Serial.println(" page 1 ");
      editMode = 0;
      break;
    case 2:
      //set temp lcd display
      Serial.println(" temp display ");

      if (editMode == 1) {
        //lcd display "use buttons to set"
        Serial.println("enter to set");

        while (editMode == 1) {
          tempSetPoint =  buttonCheck(tempSetPoint, 0.1);
          //show temp on lcd
          Serial.print(" tempSetPoint ");
          Serial.println(tempSetPoint);
        }
        break;
      case 3:
        Serial.println(" page 3 ");
        editMode = 0;
        break;
      case 4:
        Serial.println(" page 4 ");
        editMode = 0;
        break;
      case 5:// return
        Serial.println(" page 5 ");
        editMode = 0;
        break;
      default:
        break;
      }
  }

}

float buttonCheck(float viarable, float scale) {
  static byte preveditButton;
  static byte prevplusButton;
  static byte prevMinusButton;
  byte plusButton = digitalRead(buttonPlus);
  byte minusButton = digitalRead(buttonMinus);
  byte editButton = digitalRead(buttonEdit);


  if (plusButton != prevplusButton) {
    if (plusButton == LOW) {
      //lcd.clear();
      prevMillis = currentMillis;
      buttonPressed = 1; // <===============
      viarable = viarable + scale;
    }
    prevplusButton = plusButton;
  }

  if (minusButton != prevMinusButton) {
    if (minusButton == LOW) {
      //lcd.clear();
      prevMillis = currentMillis;
      buttonPressed = 1; // <===============
      viarable = viarable - scale;
      if (viarable < 0) {
        viarable = 0;
      }
    }
    prevMinusButton = minusButton;
  }

  if (editButton != preveditButton) {
    if (editButton == LOW) {
      //lcd.clear();
      prevMillis = currentMillis;
      buttonPressed = 1; // <===============
      editMode = ! editMode;
    }
    preveditButton = editButton;
  }
 if (buttonPressed==1) // <===============
  {                              // <===============
   checkTimer();
  }                              // <===============
  return viarable;
 
}

void checkTimer() {
  currentMillis = millis();
  if (currentMillis - prevMillis >= 60000L) {//60 second timer
    editMode = 0;
    menu = 0;
    buttonPressed = 0; // <===============
  }
}

NewbieUser: Well it works fine but maybe u know what kind of problem may be that after "kick back" i can't re-enter to menu again...

I tested the sketch I posted and I am not finding a problem with entering the menu after being kicked out by the timer. To be honest the only button that should work is the up button as down would run the menu backwards and there's nothing for the enter button to do. (cut and pasted the sketch then ran on a mega using 3 buttons)

If you have modified the program then post your code so I can see if something is missing.

p.s prevMillis = currentMillis; is in the button function so it keeps updating the 60 seconds every time a button is pressed. This may not have been a idea way to code as it requires certain lines of the program to be global and in the correct place ( e.g return must be the last line in the button function etc)