Help with basic LCD menu

I've searched and searched and can't seem to find exactly what I'm looking for. I want to create a basic menu (if you can even call it that) for my LCD display with just a single button to change the menus, and up/down buttons for fan speeds (that'll be controlled only when that fan's menu is displayed). Here's how I want it to go:

Welcome screen (on power-up) | | Display 1 (coolant temp/flow rate) | | Display 2 (Intake fan speed/voltage) | | Display 3 (Exhaust fan 1 speed/voltage) | | Display 5 (Exhaust fan 2 speed/voltage) | | Back to display 1 and so on

There will also be a button-controlled servo to open and close a door but that will be independent of the menu system and not displayed.

Obviously I need 5 interrupts (flow meter, fan 1, fan 2, fan 3, servo button) and only have 2, which is why I'm going to use a ChipKIT MAX32 which has 5 interrupts and plenty of I/O for controlling fan speeds, etc. so that problem it solved.

I've got each system (except fan speed control) working independently with separate coding and each works fine (temp/flow works fine, fan speed display works fine). I just can't figure out how to merge them all together into a single working unit. I've seen several different menu libraries but they seem way more complex than what I need. I was thinking button state change or something but can't figure out how to get it all working properly.

What's the best way to go about that?

You just need a user interface to adjust parameters. For simplicity, such as maybe 3 lines per parameter adjustment, you can get the phi-panel:

http://www.inmojo.com/store/liudr-arduino-and-physics-gadgets/item/serial-lcd-back-pack---phi-panel/

Then the menu will be like

case ‘U’:
if (speed<max_speed) speed+=speed_increment;
//update serial lcd
break;

case ‘D’:
if (speed>minspeed) speed-=speed_increment;
//update serial lcd
break;

Look this thread and especially the end of pages and you will find no menu easier than it.This is called MENWIZ by brunialti… Its brilliant, easy and cover almost all the things in a very easy way. Dont forget to read the pdf guide.:slight_smile:
http://arduino.cc/forum/index.php/topic,99693.0.html

Khalid: Look this thread and especially the end of pages and you will find no menu easier than it.This is called MENWIZ by brunialti... Its brilliant, easy and cover almost all the things in a very easy way. Dont forget to read the pdf guide.:) http://arduino.cc/forum/index.php/topic,99693.0.html

Yes it is good but what you view as easy maybe hard to those beginners. If you have found my phi-panels, you will take back the "find no menu easier than it" statement.

liudr, I downloaded your phi library but it was difficult to understand, its not your fault, but i have less brain power.. I really like the videos you posted about your library but its difficult to find simple examples using Analoge button and 3wire lcds... Is their any, pls point out.

Khalid:
liudr,
I downloaded your phi library but it was difficult to understand, its not your fault, but i have less brain power… I really like the videos you posted about your library but its difficult to find simple examples using Analoge button and 3wire lcds…
Is their any, pls point out.

Khalid, there are two things I provide that does menus:

  1. phi_prompt library and its support library. They run with most types of inputs and HD44780 LCD. The setup part is a bit hard but my new version will provide simple setup functions.
  2. phi-panel serial lcd panels or backpacks. These are hardware I designed and are already set up to run some of the phi_prompt menu functions. You simply send out a plain text to these panels through serial port with a command. The panels render the menu and interacts with users then return to you what the user selects.

I was referring to solution 2 for the OP.

both the options require custom hardware which i have to purchase with the high shipment. ^_^

What's the best way to go about that?

It is fairly simple:

  button=read_button(button_pin); //read button on button_pin
  switch (button) {
  case 'U': //up button is pressed
     app_index = (app_index)?(app_index-1):MAX_APP; //update app_index
     break;
  case 'D': //down button is pressed
     app_index = (app_index==MAX_APP)?0:(app_index+1_; //update app_index
     break;
  default: do_something(); //illegal key press. do something / nothing
  }

  switch (app_index) {
  case 0: app0(); //run 1st application
             break;
  case 1: app1(); //run 2nd application
             break;
  ...
  }

You can start to fill the various boxes / functions. For example, if you use analog buttons, you just need to implement it in read_button(); and you can put your coolant / flowrate display in app0(); your fan speed/voltage in app1(); ...

It cannot be simpler than that, once you cut it down to logical blocks and implement those blocks one at a time.

Khalid: both the options require custom hardware which i have to purchase with the high shipment. ^_^

Option one is free. just get an hd44780 display and a few buttons. If you think $6 is expensive for international shipping, FYI shipping to Australia or Europe is almost $6 plus bubble wrap and time.

dhenry:

What's the best way to go about that?

It is fairly simple:

  button=read_button(button_pin); //read button on button_pin
  switch (button) {
  case 'U': //up button is pressed
     app_index = (app_index)?(app_index-1):MAX_APP; //update app_index
     break;
  case 'D': //down button is pressed
     app_index = (app_index==MAX_APP)?0:(app_index+1_; //update app_index
     break;
  default: do_something(); //illegal key press. do something / nothing
  }

  switch (app_index) {   case 0: app0(); //run 1st application              break;   case 1: app1(); //run 2nd application              break;   ...   }




You can start to fill the various boxes / functions. For example, if you use analog buttons, you just need to implement it in read_button(); and you can put your coolant / flowrate display in app0(); your fan speed/voltage in app1(); ...

It cannot be simpler than that, once you cut it down to logical blocks and implement those blocks one at a time.

ok so where exactly do I put my functions? do I put it in app0(xxxx);?

That code is a snippet, i.e. not complete. You complete it. Yes that was what dhenry meant.

liudr: That code is a snippet, i.e. not complete. You complete it. Yes that was what dhenry meant.

I know it's just a snippet I'm just lost as to where to put my actual analogRead etc etc in the snippet

Here’s what I’ve got so far. It works as is with the commented-out lines (kinda - I can’t get the % to display if it’s showing 100). Am I on the right track?

#include <LiquidCrystal.h>
LiquidCrystal lcd(9, 8, 7, 6, 5, 4);

const int buttonPin = 11;
const int potPin = A0;
const int tempPin = A1;
const int fan = 10;
const int buttonMin = 0;
const int buttonMax = 1;
int buttonState;
int lastButtonState = LOW;
long lastDebounceTime = 0;
long debounceDelay = 50;

void setup(){
  pinMode(buttonPin, INPUT);
  pinMode(potPin, INPUT);
  pinMode(tempPin, INPUT);
  pinMode(fan, OUTPUT);
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print(" Project:    ");
  lcd.setCursor(0, 1);
  lcd.print("    Circuit Box ");
  delay(5000);
  lcd.clear();
}

void loop(){
  int reading = digitalRead(buttonPin);
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    buttonState = reading;
  }
  //int range = map(reading, buttonMin, buttonMax, 0, 1); //Map the range to 2 options
//  switch (range) {
  //  case '0': // Manual Mode
      lcd.setCursor(0, 0);
      lcd.print("  Manual Mode   ");
      int fanSpeed = analogRead(potPin);
      fanSpeed = map(fanSpeed, 0, 1023, 120, 255);
      int  fanpct = map(fanSpeed, 120, 254, 40, 100);
      lcd.setCursor(0, 1);
      lcd.print("Fan Speed:");
      lcd.setCursor(11, 1);
      lcd.print(fanpct);
      if (fanpct < 100) lcd.print ('%');
  //    lcd.setCursor(14, 1);
//      lcd.print("%");
      analogWrite(fan, fanSpeed);
/*      break;
    case '1': // Automatic Mode
      lcd.setCursor(0, 0);
      lcd.print(" Automatic Mode ");
      int 
      break;
    }
    delay(1);
}

  lcd.setCursor(0, 0);
  lcd.print("  Manual Mode   ");
  int fanSpeed = analogRead(potPin);
  fanSpeed = map(fanSpeed, 0, 1023, 120, 255);
  int  fanpct = map(fanSpeed, 120, 255, 40, 100);
  lcd.setCursor(0, 1);
  lcd.print("Fan Speed:");
  lcd.setCursor(14, 1);
  lcd.print("%");
  analogWrite(fan, fanSpeed); */
}

I’m confused by dhenry’s answer.

Your original question was how to show 5 screens by pushing one button?

This shows the general idea. It outputs to serial so you can test by just grounding pin 8 (to act as your switch).

const byte mySwitch = 8;  

// these "states" are what screen is currently being displayed.
typedef enum 
  {
  POWER_ON, COOLANT, INTAKE_SPEED, EXHAUST_FAN1, EXHAUST_FAN2,
  // add more here ...
  
  LAST_STATE  // have this last
  } states;

byte state = POWER_ON;

byte oldSwitch = HIGH;

void powerOn ()
  {
  Serial.println ("Welcome!");
  }
 
void showCoolant ()
  {
  Serial.println ("Coolant level = x");
  }
  
void showIntakeSpeed ()
  {
  Serial.println ("Intake speed = x");
  }
  
void showExhaustFan1 ()
  {
  Serial.println ("Exhaust Fan 1 = x");
  }
  
void showExhaustFan2 ()
  {
  Serial.println ("Exhaust Fan 2 = x");
  }
  
void setup ()
  {
  pinMode (mySwitch, INPUT_PULLUP);
  Serial.begin (115200);
  powerOn ();
  }  // end of setup
  
void loop ()
  {
  byte switchValue = digitalRead (mySwitch);
  
  // detect switch presses
  if (switchValue != oldSwitch)
    {
    delay (100);   // debounce

    // was it pressed?
    if (switchValue == LOW)
      {
      state++;      // next state
      if (state >= LAST_STATE)
        state = COOLANT;  
      
      switch (state)
        {
        case POWER_ON:     powerOn ();         break;
        case COOLANT:      showCoolant ();     break;
        case INTAKE_SPEED: showIntakeSpeed (); break;
        case EXHAUST_FAN1: showExhaustFan1 (); break;
        case EXHAUST_FAN2: showExhaustFan2 (); break;
        }  // end of switch
      }  // end of switch being pressed
      
    oldSwitch = switchValue;  
    } // end of switch changing state
    
  }  // end of loop

[quote author=Nick Gammon link=topic=136652.msg1101669#msg1101669 date=1359856574] I'm confused by dhenry's answer.

Your original question was how to show 5 screens by pushing one button?

This shows the general idea. It outputs to serial so you can test by just grounding pin 8 (to act as your switch).

const byte mySwitch = 8;  

// these "states" are what screen is currently being displayed.
typedef enum 
  {
  POWER_ON, COOLANT, INTAKE_SPEED, EXHAUST_FAN1, EXHAUST_FAN2,
  // add more here ...
  
  LAST_STATE  // have this last
  } states;

byte state = POWER_ON;

byte oldSwitch = HIGH;

void powerOn ()
  {
  Serial.println ("Welcome!");
  }

void showCoolant ()
  {
  Serial.println ("Coolant level = x");
  }
  
void showIntakeSpeed ()
  {
  Serial.println ("Intake speed = x");
  }
  
void showExhaustFan1 ()
  {
  Serial.println ("Exhaust Fan 1 = x");
  }
  
void showExhaustFan2 ()
  {
  Serial.println ("Exhaust Fan 2 = x");
  }
  
void setup ()
  {
  pinMode (mySwitch, INPUT_PULLUP);
  Serial.begin (115200);
  powerOn ();
  }  // end of setup
  
void loop ()
  {
  byte switchValue = digitalRead (mySwitch);
  
  // detect switch presses
  if (switchValue != oldSwitch)
    {
    delay (100);   // debounce

    // was it pressed?
    if (switchValue == LOW)
      {
      state++;      // next state
      if (state >= LAST_STATE)
        state = COOLANT;  
      
      switch (state)
        {
        case POWER_ON:     powerOn ();         break;
        case COOLANT:      showCoolant ();     break;
        case INTAKE_SPEED: showIntakeSpeed (); break;
        case EXHAUST_FAN1: showExhaustFan1 (); break;
        case EXHAUST_FAN2: showExhaustFan2 (); break;
        }  // end of switch
      }  // end of switch being pressed
      
    oldSwitch = switchValue;  
    } // end of switch changing state
    
  }  // end of loop

[/quote]

OK that makes much more sense now that I see it laid out. I'm going much simpler than originally planned with only 2 options instead of 5 - automatic mode (temp-sensor controlled PWM fan speed) and manual mode (potentiometer-controlled PWM fan speed). I'll also be upgrading to a 20x4 LCD (when it arrives). The top 2 lines will display coolant temp and flow rate all the time with the bottom 2 lines switching between manual and auto modes with fan speed shown (which is what I'm trying to get working right now on the 16x2 I'm currently using)