Help with LCD keypad menu

Hi everyone,

I'm just playing around with a lcd keypad shield, but I'm stuck when making my menu work correctly.

The code is probably very simple to most people and the code could probably be shortened a lot! but since I'm just learning to code i thought this would be the easy way for now and ill get people to help me shorten it later. I think it will also help new people like me understand how menus work.

Anyways the problem I'm facing at the moment is trying to select items in the menu to bring up a new screen with a new menu and new choices. I'm trying to do this first with the back light.

#include <LiquidCrystal.h>
#include "LCDKeypad.h"
LCDKeypad lcd;

#define LCD_BLIGHT 10

boolean backlight=true;

unsigned long lastInput = 0;
int mainControl = 0;
int mainList = 0;
int backlightList = 0;

void setup() {
   
  lcd.begin(16, 2);               // start the library
  pinMode(LCD_BLIGHT, OUTPUT);
  digitalWrite(LCD_BLIGHT, HIGH);
}

void loop() {
  //main menu
int button = lcd.buttonBlocking();

  if (mainList < 0 || mainList > 7) {
   mainList = 0;}
     
    switch (button) {

    // Right button was pushed
    case KEYPAD_UP:
      mainList--;
      lcd.clear();
      break;

    // Left button was pushed
    case KEYPAD_DOWN:
      mainList++;
      lcd.clear();
      break;

    case KEYPAD_SELECT:
      mainControl = mainList;
      break;

    case KEYPAD_RIGHT:
      mainList=0;
      lcd.clear();
      break;
      
    case KEYPAD_NONE:
      break;
    }

    switch (mainList){
    case 0:
      //idle screen, repeatedly printing pH and CF
      lcd.home();
      lcd.print("Home Screen     ");
      break;

    case 1:
      lcd.home();
      lcd.print("Calibration");
      break;

    case 2:
      lcd.home();
      lcd.print("Set temperature");
      break;

    case 3:
      lcd.home();
      lcd.print("Set alarm");
      break;

    case 4:
      lcd.home();
      lcd.print("Titration curve");
      break;

    case 5:
      lcd.home();
      lcd.print("Measure");
      lcd.setCursor(0, 1);
      lcd.print("over time");
      break;

    case 6:
      lcd.home();
      lcd.print("Download data");
      break;

    case 7:
      lcd.home();
      lcd.print("Backlight");
      break;
      }
        
    //if (button != 0){
    //lastInput = millis();
    //}
    
    //if ((millis() - lastInput) > 3000){  // return to Home screen after 3 seconds idle
    //mainList = 0;
    //return;
    //}
    
    if (mainControl==7) {
    Backlight();
  }
 
 }
 
 void Backlight(){
   
   int x=0;
   int button = lcd.buttonBlocking();
   
  if (backlightList < 0 || backlightList > 5) {
   backlightList = 0;}
     
    switch (button) {

    // Right button was pushed
    case KEYPAD_UP:
      backlightList--;
      lcd.clear();
      break;

    // Left button was pushed
    case KEYPAD_DOWN:
      backlightList++;
      lcd.clear();
      break;

    case KEYPAD_RIGHT:
      mainControl=0;
      lcd.clear();
      break;
      
    case KEYPAD_NONE:
      break;
    }
    
    switch (backlightList){
    case 0:
      lcd.home();
      lcd.print("Backlight Off");
      break;

    case 1:
      lcd.home();
      lcd.print("Backlight 25");
      break;

    case 2:
      lcd.home();
      lcd.print("Backlight 50");
      break;

    case 3:
      lcd.home();
      lcd.print("Backlight 75");
      break;
    
    case 4:
      lcd.home();
      lcd.print("Backlight 100");
      break;
      
    }
 }

So as you see in my code im having trouble with

if (mainControl==7) {
    Backlight();
  }

How do i make it so when i make mainControl==7 it goes to a new screen independent to the main menu?

Hope this all makes sense to someone.

Thanks very much

Use an array to store your "screens", and use mainList as an index into that array. You can add a screen type to the entries, indicating special cases like the idle screen. Then use switch to show only the fixed text of the entry, or whatever else should be shown.

DrDiettrich:
Use an array to store your "screens", and use mainList as an index into that array. You can add a screen type to the entries, indicating special cases like the idle screen. Then use switch to show only the fixed text of the entry, or whatever else should be shown.

Hi DrDiettrich I have tried making an array and it has worked but i cant get my buttons to work, When i press any button it cycles through the menu instead of just the down key anyone got any ideas?

I used some code from a sous vide the link is below.

[url=https://learn.adafruit.com/sous-vide-powered-by-arduino-the-sous-viduino/the-whole-enchilada[/url]

#include <LiquidCrystal.h>
#include "LCDKeypad.h"
LCDKeypad lcd;

#define LCD_BLIGHT 10

boolean backlight=true;

enum operatingState { HOME = 0, CALI, TEMP, ALARM, TITR, MEAS, BLIGHT};
operatingState opState = HOME;

unsigned long lastInput = 0;
int mainList = 0;

void setup() {
   
  lcd.begin(16, 2);               // start the library
  pinMode(LCD_BLIGHT, OUTPUT);
  digitalWrite(LCD_BLIGHT, HIGH);
}

void loop() {
  //main menu
while(ReadButtons() != 0) {}
 
      lcd.clear();

   switch (opState)
   {
   case HOME:
      Homescreen();
      break;
   case CALI:
      Calibration();
      break;
    case TEMP:
      Tempature();
      break;
   case ALARM:
      Alarm();
      break;
   case TITR:
      Titration();
      break;
   case BLIGHT:
      Backlight();
      break;
   }
 }
 
 void Homescreen()
 {
   lcd.home();
   lcd.print("Home Screen");
 
   uint8_t buttons = 0;
   while(true)
   {
      buttons = ReadButtons();
 
      if (KEYPAD_DOWN)
      {
        opState = CALI;
        return;
      }
   }
 }
 
  void Calibration()
 {
   lcd.home();
   lcd.print("Calibration");
   
   uint8_t buttons = 0;
   while(true)
   {
      buttons = ReadButtons();
 
      if (KEYPAD_DOWN)
      {
        opState = TEMP;
        return;
      }
   }
 }
 
   void Tempature()
 {
   lcd.home();
   lcd.print("Tempature");
 
   uint8_t buttons = 0;
   while(true)
   {
      buttons = ReadButtons();
 
      if (KEYPAD_DOWN)
      {
        opState = ALARM;
        return;
      }
   }
 }
 
    void Alarm()
 {
   lcd.home();
   lcd.print("Alarm");
 
   uint8_t buttons = 0;
   while(true)
   {
      buttons = ReadButtons();
 
      if (KEYPAD_DOWN)
      {
        opState = TITR;
        return;
      }
   }
 }
 
    void Titration()
 {
   lcd.home();
   lcd.print("Titration");
 
   uint8_t buttons = 0;
   while(true)
   {
      buttons = ReadButtons();
 
      if (KEYPAD_DOWN)
      {
        opState = BLIGHT;
        return;
      }
   }
 }

The state change detection example (in the IDE, File, Examples, Digital) might help here. With state change detection you can take action only when the button goes from HIGH to LOW (detect transition, not level) or LOW to HIGH.

groundfungus:
The state change detection example (in the IDE, File, Examples, Digital) might help here. With state change detection you can take action only when the button goes from HIGH to LOW (detect transition, not level) or LOW to HIGH.

Would that also work for the LCD shield where it uses the analog pin instead of digital ones?

This code from an example at lcd keyad example uses the same concept of looking for a change from one iteration of loop() to another using analog keypad.

adc_key_in = analogRead(0);    // read the value from the sensor 
   key = get_key(adc_key_in);  // convert into key press
   if (key != oldkey)   // if keypress is detected
   {
     delay(50);  // wait for debounce time
     adc_key_in = analogRead(0);    // read the value from the sensor 
     key = get_key(adc_key_in);    // convert into key press
     if (key != oldkey)    
     {   
       lcd.setCursor(0, 1);
       oldkey = key;                    // record old key value
       if (key >=0)
       {
           lcd.print(msgs[key]);              
       }
     }
   }

I was just looking over my code that i posted aand it looks like i missed a bit at the end. the missing code is...

 uint8_t ReadButtons()
{
  uint8_t buttons = lcd.buttonBlocking();
  if (buttons != 0)
  {
    lastInput = millis();
  }
  return buttons;
}

It doesn't have any code for the debouncing because the LCD library does it for me.

I don't know why this isn't working properly, it seems no matter what button i press it changes to the next item in the menu except when i press left, then it doesn't do anything.

Also the screen cant be seen very clearly because of the lcd.clear();

I pretty much have it the same as this code here:
sous vide powered by arduino

If anyone can help a noob out that would be great. and really appreciated. Thanks

#include <LiquidCrystal.h>
#include "LCDKeypad.h"
LCDKeypad lcd;

#define LCD_BLIGHT 10

enum operatingState { HOME = 0, CALI, TEMP, ALARM, TITR, MEAS, BLIGHT};
operatingState opState = HOME;

unsigned long lastInput = 0;

void setup() {
   
  lcd.begin(16, 2);               // start the library
}

void loop() {
  //main menu
while(ReadButtons() != 0) {}

   lcd.clear();
   
   switch (opState)
   {
   case HOME:
      Homescreen();
      break;
   case CALI:
      Calibration();
      break;
    case TEMP:
      Tempature();
      break;
   case ALARM:
      Alarm();
      break;
   case TITR:
      Titration();
      break;
   case BLIGHT:
      Backlight();
      break;
   }
 }
 
 void Homescreen()
 {
   lcd.home();
   lcd.print("Home Screen");

   int button = ReadButtons();
   {
 
      if (button & KEYPAD_DOWN)
      {
        opState = CALI;
        return;
      }
   }
 }
 
  void Calibration()
 {
   lcd.home();
   lcd.print("Calibration");  
 
   int button = ReadButtons();
   {
 
      if (button & KEYPAD_DOWN)
      {
        opState = TEMP;
        return;
      }
   }
 }
 
   void Tempature()
 {
   lcd.home();
   lcd.print("Tempature");
   
   int button = ReadButtons();
 
   {
      if (button & KEYPAD_DOWN)
      {
        opState = ALARM;
        return;
      }
   }
 }
 
    void Alarm()
 {
   lcd.home();
   lcd.print("Alarm");
   
    int button = ReadButtons();
   {
      if (button & KEYPAD_DOWN)
      {
        opState = TITR;
        return;
      }
   }
 }
 
    void Titration()
 {
   lcd.home();
   lcd.print("Titration");

   int button = ReadButtons();
   { 
      if (button & KEYPAD_DOWN)
      {
        opState = BLIGHT;
        return;
      }
   }
 }
 
    void Backlight()
 {
   lcd.home();
   lcd.print("Backlight");
   
   int button = ReadButtons();

      if (button & KEYPAD_DOWN)
      {
        opState = HOME;
        return;
      }
      
      if (button & KEYPAD_UP)
      {
         opState = TITR;
         return;
      }

   }

 
 uint8_t ReadButtons()
{
  uint8_t button = lcd.buttonBlocking();
  if (button != 0)
  {
    lastInput = millis();
  }
  return button;
}

It were helpful if you'd start trying to understand the basics, instead of copying arbitrary code until you find one that happens to do what you want :-]

Try to base your actions on changes, as already suggested in #5.

The display needs a refresh only when the state changes.
The state will only change when a button is pressed, not while a button is pressed.