LCD menu's

hey guys,

I am trying to create a menu on an LCD in which you can scroll up and down the menu and select an option. I am using a shift in register to control 7 switches. I have no idea where to start with interfacing the buttons with the menu :(.

once the shift register sends its data to the arduino, how do i then remove this data from the arduino storage?

for example my down button sends 32 (0000100). the problem i have is in the loop the if statements sequence down automatically when the down button is pressed and i want to use this button to scroll manually. do i need some sort of break or return statement.

between if statements i have tried setting the value to 0 but didnt work.

i have tried an array but cannot get to grips with it

could i use switch case statements?

i have tried all sorts but if proving too complicated for me, can someone please help me with an generic piece of code that i can adapt or shed some light on how i could do this

thanks again guys

Do you have any code that you’ve been working on?

how about a schematic ? Can you post a photo of a schematic ?

#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;
#include <TimeAlarms.h>
#include <Time.h>
#include <LiquidCrystal.h>
#include <SPI.h>

LiquidCrystal lcd(12,11,5,4,3,2);
int backLight = 10;
const int latchPin = 8;
const int dataPin = 9;
const int clockPin = 7;

byte switchVar1 = 0;

const int numRows = 4;
const int numCols = 20;
const byte Symbol_1 = B10110000;
const byte Symbol_2 = B01111110;

/*Switch Binary Code Reference
2 == Back
4 == Options 
8 == Ok
16 == Left
32 == Down
64 == Up 
129 == Right
*/

void setup()
{
  pinMode (backLight, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, INPUT);
  
  //RTC boot up
  Wire.begin();
  RTC.begin();
  
//lcd boot up screen
  digitalWrite(backLight, HIGH);
  lcd.begin(numCols,numRows);
  lcd.setCursor(6,0);
  lcd.print("WELCOME");
  lcd.setCursor(1,1);
  lcd.print("Dosing Pump Module");
  lcd.setCursor(5,2);
  lcd.print("By JUNBLUD");
  lcd.setCursor(4,3);
  lcd.print("Loading:");
  for (int i = 0; i <= 100; i++)
  {
    lcd.setCursor(12,3);
    if (i<100) lcd.print(" "); 
    if (i<10) lcd.print(" ");
    lcd.print(i);
    lcd.print("%");
    delay(100); 
  }
   delay(2000);
   lcd.clear();
{
  lcd.setCursor(0,0);
  lcd.print("Main Menu");
     lcd.setCursor(0,1);
     lcd.write(Symbol_1);
     lcd.setCursor(2,1);
     lcd.print("Pump 1");
       lcd.setCursor(0,2);
       lcd.write(Symbol_1);
       lcd.setCursor(2,2);
       lcd.print("Pump 2");
         lcd.setCursor(0,3);
         lcd.write(Symbol_1);
         lcd.setCursor(2,3);
         lcd.print("Set Schedule");
           lcd.setCursor(10,1);
           lcd.write(Symbol_1);
           lcd.setCursor(12,1);
           lcd.print("Pump 3");
    {
      DateTime now = RTC.now();
      lcd.setCursor(12,0);
      lcd.print(now.hour(), DEC);
      lcd.print(':');
      lcd.print(now.minute(), DEC);
      lcd.print(':');
      lcd.print(now.second(), DEC);
      delay(1000); 
    }
  } 
}

void pumpStatus(const char *pumpStatusNum)
{
  lcd.setCursor(0,0);
  lcd.print(pumpStatusNum);
   lcd.setCursor(0,1);
   lcd.write(Symbol_1);
   lcd.setCursor(2,1);
   lcd.print("First Dosage");
     lcd.setCursor(0,2);
     lcd.write(Symbol_1);
     lcd.setCursor(2,2);
     lcd.print("Second Dosage");
}

void setScheduleMenu()
{
   lcd.setCursor(0,0);
   lcd.print("Set Schedule");
     lcd.setCursor(0,1);
     lcd.write(Symbol_1);
     lcd.setCursor(2,1);
     lcd.print("Adjust Pump 1");
       lcd.setCursor(0,2);
       lcd.write(Symbol_1);
       lcd.setCursor(2,2);
       lcd.print("Adjust Pump 2");
         lcd.setCursor(0,3);
         lcd.write(Symbol_1);
         lcd.setCursor(2,3);
         lcd.print("Adjust Pump 3");
}

void adjustPump(const char *adjustPumpNum)
{
   lcd.setCursor(0,0);
   lcd.print(adjustPumpNum);
     lcd.setCursor(0,1);
     lcd.write(Symbol_1);
     lcd.setCursor(2,1);
     lcd.print("Everyday at");
       lcd.setCursor(0,2);
       lcd.write(Symbol_1);
       lcd.setCursor(2,2);
       lcd.print("everyday at");
         lcd.setCursor(0,3);
         lcd.write(Symbol_1);
         lcd.setCursor(2,3);
         lcd.print("Amount 000ml");
}

void options()
{
  lcd.setCursor(0,0);
  lcd.print("Options");
     lcd.setCursor(0,1);
     lcd.write(Symbol_1);
     lcd.setCursor(2,1);
     lcd.print("Pump Flush");
       lcd.setCursor(0,2);
       lcd.write(Symbol_1);
       lcd.setCursor(2,2);
       lcd.print("Schedule Reminder");
}

void pumpFlush()
{
   lcd.setCursor(0,0);
   lcd.print("Pump Flush");
     lcd.setCursor(0,1);
     lcd.write(Symbol_1);
     lcd.setCursor(2,1);
     lcd.print("Flush Pump 1");
       lcd.setCursor(0,2);
       lcd.write(Symbol_1);
       lcd.setCursor(2,2);
       lcd.print("Flush Pump 2");
         lcd.setCursor(0,3);
         lcd.write(Symbol_1);
         lcd.setCursor(2,3);
         lcd.print("Flush Pump 3");
}

void scheduleReminder()
{
  lcd.setCursor(0,0);
   lcd.print("fist line goes here");
     lcd.setCursor(0,1);
     lcd.write(Symbol_1);
     lcd.setCursor(2,1);
     lcd.print("second line goes here");
       lcd.setCursor(0,2);
       lcd.write(Symbol_1);
       lcd.setCursor(2,2);
       lcd.print("third line goes here");
         lcd.setCursor(0,3);
         lcd.write(Symbol_1);
         lcd.setCursor(2,3);
         lcd.print("forth line goes here");
}

void shiftRegister()
{
  digitalWrite(backLight, HIGH);
  
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(20);
  digitalWrite(latchPin, LOW);

  switchVar1 = shiftIn(dataPin, clockPin, MSBFIRST);
}

void loop()
{
  shiftRegister();
  {
    if (switchVar1 == 32)
    {
      lcd.setCursor(0,1);
      lcd.write(Symbol_2);
    }
  }
}

the problem is, is that i want the shift register input values to do more than one thing

cheers, guys

the problem is, is that i want the shift register input values to do more than one thing

So call the shiftRegister function where you need it.

For the main menu, you can use case statements, and when you need to switch into the sub-menus, you call the shiftRegister function again.

the shift register function is just to read the input value of the shift register

do you mean something like this:

void loop()
{
  shiftRegister();
  {
    if (switchVar1 == 32)
    {
      lcd.setCursor(0,1);
      lcd.write(Symbol_2);
    }
  }
  //do i need something here that sets the shift register value to 0,
  //if so how do i do that?
  // ive tried switchvar1 = 0;
  shiftRegister();
  {
    if (switchVar1 == 32)
    {
      lcd.setCursor(0,1);
      lcd.write(Symbol_1);
      lcd.setCursor(0,2);
      lcd.write(Symbol_2);
    }
  }
}
byte shiftRegister()
{
  digitalWrite(backLight, HIGH);
  
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(20);
  digitalWrite(latchPin, LOW);

  return shiftIn(dataPin, clockPin, MSBFIRST);
}

void loop()
{
  switchVar1 = shiftRegister(); // this should return 0 if nothing is pressed, if it constantly returning a value then something is wired wrong.

  if (switchVar1 == 32)
  {
    lcd.setCursor(0,1);
    lcd.write(Symbol_2);
  }
}

thankyou ever so much for that!

it works, but the problem now is Symbol_2's location printed on the lcd is dependent of where in the loop the code is

Can you post a picture?

picture of circuit or lcd?

as the down button (shift register 32) needs to print Symbol_2 to the lcd in various locations it get confused in the loop where Symbol_2 needs to be printed . I am just a noob but i think switch case may be a good way to write this. but switch case will only accept different values per case.

maybe a for loop, although that is way over me just yet.

byte shiftRegister()
{
  digitalWrite(backLight, HIGH);
  
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(20);
  digitalWrite(latchPin, LOW);

  return shiftIn(dataPin, clockPin, MSBFIRST);
}

void loop()
{
  switchVar1 = shiftRegister();
  
  if (switchVar1 == 32)
  {
    lcd.setCursor(0,1);
    lcd.write(Symbol_2);
  }
  switchVar1 = shiftRegister();
  
    if (switchVar1 == 32)
  {
    lcd.setCursor(0,1);
    lcd.write(Symbol_1);
    lcd.setCursor(0,2);
    lcd.write(Symbol_2);
  }
    switchVar1 = shiftRegister();
  
    if (switchVar1 == 32)
  {
    lcd.setCursor(0,2);
    lcd.write(Symbol_1);
    lcd.setCursor(0,3);
    lcd.write(Symbol_2);
  }
}

Use switchVar1 in a IF statement to increment a counter that cycles through the cases.

Hello, yes you can use switch, somewhat like this :

byte shiftRegister()
{
  digitalWrite(backLight, HIGH);
  
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(20);
  digitalWrite(latchPin, LOW);

  return shiftIn(dataPin, clockPin, MSBFIRST);
}

void loop(){
///// the rest 

switch(shiftRegister()) {

case 32:
    lcd.setCursor(0,1);
    lcd.write(Symbol_2);
    break;

///// repeat for other buttons
}

}

Yea, but the way you showed, looked like you wanted the button to do different things when pressed multiple times.

i did indeed want the button to do different things. for example:

  1. down button is pressed and the lcd displays an arrow at 0,1
  2. down button is pressed again and the lcd displays an arrow at 0,2
  3. down button is pressed again and the lcd displays an arrow at 0,3

I cannot use switch case at the arduino gets confused as to where the arrow needs displaying/ compiler will not allow me to call more than one case the same, i.e. 32

if i used a series of if statements the position of the arrow that is printed, depends of where in the loop the program is. also it is very slow to register when the button is pressed.

could you help with any other options i have please?

very much appreciated!!!

is it possible to record how many times the button has been pressed, for exmaple: if the shift register input = 32 once, do x if the shift register input = 32 twice, do y etc.

cheers again

Look at reply #10 again, what you need is a counter.
if(switchVar1 == 32) counter++;
You can then use this counter to cycle through the cases:
switch(counter)
{
case 0: //do x
break;

case 1: //do y
break;

case 2: // do z
break;
}

However if you want to have it as click and double click, then it’s a little different because it would require timers.

Ok so how do i make a counter?

a for loop?

cheers

byte counter = 0;
byte maxMenus = 3; // 0, 1, 2

// In loop with rest of code
if(switchVar1 == 32) // you might want make it so the counter is only incremented if the button is pressed & released and not held.
{
   counter+1 < maxMenus?  counter++ : counter = 0; 
}

switch(counter)
{
  case 0:
     // something
     break;
  
  case 1:
     // something
     break;

  case 2:
     // something
     break;
}

Hi. I’m trying to do menus with Arduino too. Right now I’ve the folowing code:

#include <LiquidCrystal.h>

// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

#define MAXOUT 8
const int output[MAXOUT] = {31, 33, 35, 37, 39, 41, 43, 45};

#define MAXMEM 5
#define MAXELE 4
struct {
   char name[10];
   boolean state;
   int hr_start;
   int min_strat;
   int min_on;
   int relay_output;
} outMemory[MAXMEM] = {   
               { "Memory01", 0, 0, 0, 15, 1 }, 
               { "Memory02", 0, 0, 0, 15, 1 },
               { "Memory03", 0, 0, 0, 15, 1 },
               { "Memory04", 0, 0, 0, 15, 1 },
               { "Memory05", 0, 0, 0, 15, 1 } 
            };

int opt1;
int opt2;

// read the buttons
int read_LCD_buttons()
{
 int adc_key_in;
 
 adc_key_in = analogRead(0);

 if (adc_key_in > 1000) return btnNONE; 
 
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 195)  return btnUP; 
 if (adc_key_in < 380)  return btnDOWN; 
 if (adc_key_in < 555)  return btnLEFT; 
 if (adc_key_in < 790)  return btnSELECT;
  
 return btnNONE;  // when all others fail, return this...
}

int menu1 () {
int opt = 0;
int current_key;
int last_key=btnSELECT;
int exit = 0;

   do {
      lcd.setCursor(0,1);
      lcd.print(outMemory[opt].name);
    
      current_key = read_LCD_buttons();
      if (current_key != last_key) {
         last_key = current_key;
         switch (current_key) {
            case btnRIGHT:
            break;
            case btnUP:
               if (--opt < 0) {
                  opt = 0;
               }
               break;
            case btnDOWN:
               if (++opt >= MAXMEM) {
                  opt = MAXMEM-1;
               }
            break;
            case btnLEFT:
            break;
            case btnSELECT:
            exit = 1;
            break;
            case btnNONE:
            break;
         }
      }
   }while (!exit);
   
   return opt;
}

int menu2 (int menu1_opt) {
int opt = 0;
int current_key;
int last_key=btnSELECT;
int exit = 0;

   do {
      lcd.setCursor(0,0);
      lcd.print(outMemory[menu1_opt].name);
      lcd.setCursor(0,1);
      switch (opt) {
         case 0:
            lcd.print("State:          ");         
         break;
         case 1:
            lcd.print("Hour:           ");         
            break;
         case 2:
            lcd.print("Time ON:        ");         
            break;
         case 3:
            lcd.print("Output:         ");         
         break;
      }

      current_key = read_LCD_buttons();
      if (current_key != last_key) {
         last_key = current_key;
         switch (current_key) {
            case btnRIGHT:
            break;
            case btnUP:
               if (--opt < 0) {
                  opt = 0;
               }
               break;
            case btnDOWN:
               if (++opt >= MAXELE) {
                  opt = MAXELE-1;
               }
            break;
            case btnLEFT:
            break;
            case btnSELECT:
            exit = 1;
            break;
            case btnNONE:
            break;
         }
      }
   }while (!exit);
   
   return opt;
}

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

void loop() {
  lcd.clear();   
  opt1 = menu1();
  opt2 = menu2(opt1);

}

I don’t know if this can help or not, but here it is. There are still mush work to do, but this part (1 menu + 1 sub-menu) is working.

thanks for all the help!!

just a few final things.

When i press the button it seems to enter each case randomly, either the counter is not working correctly or the button is bouncing. please help

byte counter = 0;
byte maxMenus = 5;

void loop()
{
  shiftRegister();
  if(switchVar == 32)
  {
    counter+1 < maxMenus? counter++ : counter = 0;
  }
    switch(counter)
    {
      case 0:
        lcd.setCursor(0,1);
        lcd.write(Symbol_1);
        lcd.setCursor(0,2);
        lcd.write(Symbol_1);
        lcd.setCursor(0,3);
        lcd.write(Symbol_1);
        lcd.setCursor(10,1);
        lcd.write(Symbol_1);
        break;
        
      case 1:
        lcd.setCursor(0,1);
        lcd.write(Symbol_2);
        break;

      case 2:
        lcd.setCursor(0,1);
        lcd.write(Symbol_1);
        lcd.setCursor(0,2);
        lcd.write(Symbol_2);
        break;
      
      case 3:
        lcd.setCursor(0,2);
        lcd.write(Symbol_1);
        lcd.setCursor(0,3);
        lcd.write(Symbol_2);
        break;
    
      case 4:
        lcd.setCursor(0,3);
        lcd.write(Symbol_1);
        lcd.setCursor(10,1);
        lcd.write(Symbol_2);
        break;
    }
}

.

also for some reason i cant get the lcd to display the main menu function and carry out the “if (switchVar ==32)” statement at the same time:

#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;
#include <TimeAlarms.h>
#include <Time.h>
#include <LiquidCrystal.h>
#include <SPI.h>

LiquidCrystal lcd(12,11,5,4,3,2);
int backLight = 10;
const int latchPin = 8;
const int dataPin = 9;
const int clockPin = 7;

byte switchVar = B00000000;
byte counter = 0;
byte maxMenus = 5;

const int numRows = 4;
const int numCols = 20;
const byte Symbol_1 = B10110000;
const byte Symbol_2 = B01111110;

/*Switch Binary Code Reference
2 == Back
4 == Options 
8 == Ok
16 == Left
32 == Down
64 == Up 
129 == Right
*/

void setup()
{
  pinMode (backLight, OUTPUT);
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, INPUT);
  
  //RTC boot up
  Wire.begin();
  RTC.begin(); 
}

void mainMenu()
{
  lcd.setCursor(0,0);
  lcd.print("Main Menu");
     lcd.setCursor(0,1);
     lcd.write(Symbol_1);
     lcd.setCursor(2,1);
     lcd.print("Pump 1");
       lcd.setCursor(0,2);
       lcd.write(Symbol_1);
       lcd.setCursor(2,2);
       lcd.print("Pump 2");
         lcd.setCursor(0,3);
         lcd.write(Symbol_1);
         lcd.setCursor(2,3);
         lcd.print("Set Schedule");
           lcd.setCursor(10,1);
           lcd.write(Symbol_1);
           lcd.setCursor(12,1);
           lcd.print("Pump 3");
  {
    DateTime now = RTC.now();
    lcd.setCursor(12,0);
    lcd.print(now.hour(), DEC);
    lcd.print(':');
    lcd.print(now.minute(), DEC);
    lcd.print(':');
    lcd.print(now.second(), DEC);
    delay(1000); 
  }
}

void shiftRegister()
{
  digitalWrite(backLight, HIGH);
  
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(20);
  digitalWrite(latchPin, LOW);

  switchVar = shiftIn(dataPin, clockPin, MSBFIRST);
}

void loop()
{
  shiftRegister();
  if(switchVar == 32)
  {
    counter+1 < maxMenus? counter++ : counter = 0;
     }
    switch(counter)
    {
      case 0:
        mainMenu();
        break;
        
      case 1:
        lcd.setCursor(0,1);
        lcd.write(Symbol_2);
        break;

      case 2:
        lcd.setCursor(0,1);
        lcd.write(Symbol_1);
        lcd.setCursor(0,2);
        lcd.write(Symbol_2);
        break;
      
      case 3:
        lcd.setCursor(0,2);
        lcd.write(Symbol_1);
        lcd.setCursor(0,3);
        lcd.write(Symbol_2);
        break;
    
      case 4:
        lcd.setCursor(0,3);
        lcd.write(Symbol_1);
        lcd.setCursor(10,1);
        lcd.write(Symbol_2);
        break;
    }
}

with this code the main menu function is displayed but the switch doesn’t display Symbol_2 in any position when the button is pressed.

please help me, thankyou

ive fixed the main menu and debouncing problem. more by luck than anything else i think lol