I have three buttons hooked up to a breadboard and I am using pins 6 through 8 as inputs. Also I have a SparkFun SerLCD V2 connected to the TX pin on the Arduino.
What I am trying to do is make a menu. When button 1 is pressed, text is displayed. When button 2 is pressed a menu item is displayed. When button 2 is pressed again another menu item gets displayed. When its pressed a third time another menu item gets displayed.
Also, I have a third button that I want to program to select one of the menu items and display to the LCD "menu item 1 selected" or "menu item 2 selected" or "menu item three selected"
It was easy to code the first two buttons, but I am not sure how to use another if statement after the first for:
if (val1 == LOW)
Does anyone know how to implement this? Here is my code so far:
#define buttononePin 6 // first button pin
#define buttontwoPin 7 // second button pin
#define buttonthreePin 8 // third button pin
int val = 0; // declares variable for button input values
int val1 = 0; // declares variable for button input values
int val2 = 0; // declares variable for button input values
void setup() {
pinMode(buttononePin, INPUT); // declare first button as input
pinMode(buttontwoPin, INPUT); // declare second button as input
pinMode(buttonthreePin, INPUT); // declare third button as input
Serial.begin(9600); // sets baudrate to 9600 for LCD
}
void loop()
{
val = digitalRead(buttononePin); // read first button input value and stores variable
val1 = digitalRead(buttontwoPin); // read second button input value and stores variable
val2 = digitalRead(buttonthreePin); // read third button input value and stores variable
if (val == LOW) // if first button pressed
{
Serial.print("First Button Pressed"); // print text to LCD
Serial.print(0xFE, BYTE); // command flag
Serial.print(0x01, BYTE); // clears display
Serial.print("First button Pressed"); // needed to keep display from having text printed over and over again
}
if (val1 == LOW) //if second button pressed
{
Serial.print("First Menu Item"); // print text to LCD
Serial.print(0xFE, BYTE); // command flag
Serial.print(0x01, BYTE); // clears display
Serial.print("First Menu Item"); // needed to keep display from having text printed over and over again
}
if (val1 == LOW) //if second button pressed again... NOT SURE ABOUT THIS PART
{
Serial.print("Second Menu Item"); // print text to LCD
Serial.print(0xFE, BYTE); // command flag
Serial.print(0x01, BYTE); // clears display
Serial.print("Second Menu Item"); // prints second label to LCD
}
}
The comments are there just to aid others when reading the program. They will eventually be removed before upload once everything is figured out.
First, you have val1 == LOW twice, where I think you meant val2 instead.
Second, don't remove comments if you find them helpful. They take no room on the Arduino at all. The sketch source code is for humans to read.
Third, you're inconsistent-- your question says you want to do three menu items, but your code shows text and two menu items. Your variables are named buttonone, buttontwo, buttonthree, but then val, val1, val2. Very confusing when someone is not consistent about such things.
I am going to post the main loop of a sketch I wrote. It pretty much illustrates what you are trying to do:
void mainMenu() {
byte offset=0, loopcnt, whichkey,point=0;
do {
ClearLCD();
PrintLCD_P(6); //" Main Menu "
PrintLCDAt_P(offset, 0, 2);
whichkey = PollKey();
switch(whichkey) {
case KeyUp:
if (offset > 0) offset--;
break;
case KeyDown:
if (offset < 5) offset++;
break;
case KeySel:
switch (offset) {
case 0: //Enroll
enrollUser();
break;
case 1: //Edit users
editUser();
break;
case 2: //Delete Users
deleteUser(false);
break;
case 3: //Delete all users
deleteUser(true);
break;
case 4: //List Users
listUsers();
break;
case 5: //Set options
optionsMenu();
break;
}
break;
}
} while (1 == 1);
}
You'll notice that it's all in an infinite loop (though you could do this a little differently if you wanted) and that it polls for a key and then uses switch/case to decide what to do. It handles multiple different keys. It might be a little confusing as all text is displayed out of progmem strings and I wrote a bunch of custom routines for the LCD and the display of strings out of progmem. But you should get the general idea.
halley,
I think as per mkub's post, he is trying to use input 2 as like a next item button to step down through options. So if button 2 is pressed, it goes through item a, then item b on the next press, c, d, e...
But, yes, mkub, the naming is confusing. variables are yours for the naming, name it consistent to the control. But, that said, perhaps if this is how you are comfortable... Then have at it.
This is something I have been trying to do myself (same LCD too). But because I have so many little branches, I am concerned about using up a ton of space in strings. I am thinking about creating a set of words that live as constants and then create sentences or phrases via variables. This way I am not repeating words over and over from menu to menu. But I am also concerned about RAM usage going this route.
I was thinking about doing each menu result as a separate sub routine and using cases as AdderD was saying. You can accomplish more with less code and not have to fight all the {} associated with the if, then, elses. (I always seem to miss somewhere.)
BTW...if you haven't already, map out the whole menu first on something like a white board (if you have one). It makes a visual for you to think through which can be easily changed. Then you can just focus on the code when it is coding time and use less brain power as you are not having to envision the menu structure at the same time.
If you are worried about memory usage then use PROGMEM strings (like what I did in my example). In fact, in my sketch the strings take up something like 1.5K all by themselves so without progmem I'd be dead in the water. Basically, what you want to do is store all the strings in flash and copy one string at a time into a character array so that you can print it out. In my example code you'll see PrintLCD_P. That function takes as a parameter the index into an array of progmem strings. Internally it copies the progmem string to an in RAM string and then prints it out on the LCD.
Eek, you are cutting it close (1.5k). Was looking into the mega328 because of the size constraints of the 168(or external memory which seems to be giving many folks a headache). But fortunately, I am not yet at that point.
AdderD, do you have the whole code posted somewhere so I can follow the dots? Or, could you post the other bits that pertain to this sketch? I am re reading about progmem as of this writing(thank you btw, this is what I was after). I would like to see it in this context. I am assuming that the ClearLCD from the LCD library, yes? How are you handling moving backwards (a back key for sub menus)? I was thinking about having button 1 on my panel be a universal esc. key. This would take you back to the previous menu for less frustrating navigation, and or escape endlessly looping subs.
mkub, I promise this is not a thread hijack. These questions seemed relevant to your original question though. Cool?
The 168 has 1K of memory (it's the 328 that has 2K) so 1.5K of strings is 150% of the RAM size of the whole chip. Needless to say I had a little trouble with the sketch until I switched to PROGMEM strings.
I'll tell you what, I don't want to post a whole bunch of my sketch because it's related to prototyping a potentially commercial venture, but I'll either post enough to make things clear or create a new sketch which has all of the stuff you'd need and post that. I should be back to post this within a few hours. (I'm at work now and can't really put it together at the moment).
#include <string.h>
#include <SoftwareSerial.h>
#include <avr/pgmspace.h>
//Flash based string table. Necessary because otherwise the strings will take up all of our ram.
//Use either PrintLCDAt_P or PrintLCD_P to print these strings to the LCD.
prog_char string_0[] PROGMEM = " 1. ENROLL USER "; //0
prog_char string_1[] PROGMEM = " 2. EDIT USERS "; //1
prog_char string_2[] PROGMEM = " 3. DELETE USERS "; //2
prog_char string_3[] PROGMEM = " 4. DEL ALL USERS "; //3
prog_char string_4[] PROGMEM = " 5. LIST USERS "; //4
prog_char string_5[] PROGMEM = " 6. SET OPTIONS "; //5
prog_char string_6[] PROGMEM = " MAIN MENU "; //6
PGM_P PROGMEM StringTable[] = {
string_0, string_1, string_2, string_3, string_4, string_5, string_6
};
//Global Variables for the Application
#define LCDrxpin 5//not used
#define LCDtxpin 6//we only transmit
#define KeyPin 1 //which analog pin is used for the 5 key pad
#define KeyLeft 10
#define KeyRight 20
#define KeyUp 30
#define KeyDown 40
#define KeySel 50
#define KeyInv 60 //invalid key value
SoftwareSerial LCDSerial = SoftwareSerial(LCDrxpin, LCDtxpin);
void setup() // run once, when the sketch starts
{
byte Status;
Status = LCDSetup(); //get the screen up and running
if (Status == true) {
delay(2000);
mainMenu();
}
}
void loop() // run over and over again
{
}
void mainMenu() {
byte offset=0, loopcnt, whichkey,point=0;
do {
ClearLCD();
PrintLCD_P(6); //" Main Menu "
PrintLCDAt_P(offset, 0, 2);
whichkey = PollKey();
switch(whichkey) {
case KeyUp:
if (offset > 0) offset--;
break;
case KeyDown:
if (offset < 5) offset++;
break;
case KeySel:
switch (offset) {
case 0: //Enroll
// enrollUser();
break;
case 1: //Edit users
//editUser();
break;
case 2: //Delete Users
//deleteUser(false);
break;
case 3: //Delete all users
//deleteUser(true);
break;
case 4: //List Users
//listUsers();
break;
case 5: //Set options
//optionsMenu();
break;
}
break;
}
} while (1 == 1);
}
byte LCDSetup() {
pinMode(LCDrxpin, INPUT);
pinMode(LCDtxpin, OUTPUT);
LCDSerial.begin(9600);
delay(1000);
InitLCD();
return true;
}
void InitLCD() {
//make sure the initial parameters are right
LCDSerial.print("?S0?f?G420");
delay(120);
}
//Print a string at the specified location
//Does not try to clear the line or anything
void PrintLCDAt(char *inStr, char x, char y) {
LCDSerial.print("?y");
LCDSerial.print(y, DEC);
delay(20);
LCDSerial.print("?x");
if (x < 10) LCDSerial.print("0");
LCDSerial.print(x, DEC);
delay(20);
LCDSerial.print(inStr);
delay(40);
}
//print a string found in program memory to the LCD at a certain location
//Basically this just sets the location and then calls PrintLCD_P to do the work
void PrintLCDAt_P(int which, char x, char y) {
LCDSerial.print("?y");
LCDSerial.print(y, DEC);
delay(20);
LCDSerial.print("?x");
if (x < 10) LCDSerial.print("0");
LCDSerial.print(x, DEC);
delay(20);
PrintLCD_P(which);
}
//this is the gatekeeper function. The only one to really touch the strings in program memory. This
// keeps the need
//for a buffer string to only one. So, basically we're only using 21 bytes of memory total to store
// strings.
void PrintLCD_P(int which) {
char buffer[21];
strcpy_P(buffer, (char*)pgm_read_word(&(StringTable[which])));
LCDSerial.print(buffer);
delay(40);
}
//Clear the entire LCD
void ClearLCD() {
LCDSerial.print("?f");
delay(20);
}
//Scan the key pin and determine which key was pressed. Then return a code specifying each key
//There is a fuzzy range for each key. This is a central space for all key presses. That way it's
//easy to tweak these values if need be.
char KeyScan() {
int which, which2, diff,retVal;
which = analogRead(KeyPin);
delay(20);
which2 = analogRead(KeyPin);
retVal = KeyInv;
diff = abs(which - which2);
if (diff < 12) {
if (which > 2 && which < 36) retVal = KeyLeft;
if (which > 58 && which < 100) retVal = KeySel;
if (which > 241 && which < 283) retVal = KeyDown;
if (which > 409 && which < 451) retVal = KeyRight;
if (which > 577 && which < 619) retVal = KeyUp;
}
return retVal;
}
//repeatedly calls KeyScan until a valid key press occurs
char PollKey() {
char Whichkey;
do {
Whichkey = KeyScan();
delay(30);
} while (Whichkey==KeyInv);
delay(80);
return Whichkey;
}
I basically wrote my own LCD library. But, it's very simple as I am using an add-on board that inputs commands over serial and outputs the proper control codes to the LCD. This allowed me to use only two wires (tx and gnd) instead of the many wires you'd normally need. If you don't use a serial interface (or a different one) then you'll need to modify that code to make it work for you. I'm using software serial for the LCD as this came out of a sketch which used the hardware serial to talk to a biometric device.
Also, note that this sketch implements switch debouncing. You should definitely do this. There are various delays in the switch reading code. This is mostly to slow down the switch reading. Otherwise at 16Mhz the sketch will run so fast that you'll blow through menu items like a speed demon.