So hello everyone. I'm creating simple menu with few sub menus,on my lcd screen. But i have run into small problem, i can't stay in my sub menu. When i press select button, it goes into submenu section and after maybe 1-2 sec it goes back to main menu.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
int upButton = 10;
int downButton = 11;
int selectButton = 12;
int menu = 1;
void setup() {
lcd.begin();
lcd.backlight();
pinMode(upButton, INPUT_PULLUP);
pinMode(downButton, INPUT_PULLUP);
pinMode(selectButton, INPUT_PULLUP);
updateMenu();
}
void loop() {
if (!digitalRead(downButton)){
menu++;
updateMenu();
delay(100);
while (!digitalRead(downButton));
}
if (!digitalRead(upButton)){
menu--;
updateMenu();
delay(100);
while(!digitalRead(upButton));
}
if (!digitalRead(selectButton)){
executeAction();
updateMenu();
delay(100);
while (!digitalRead(selectButton));
}
}
void updateMenu() {
switch (menu) {
case 0:
menu = 1;
break;
case 1:
lcd.clear();
lcd.print(">MenuItem1");
lcd.setCursor(0, 1);
lcd.print(" MenuItem2");
break;
case 2:
lcd.clear();
lcd.print(" MenuItem1");
lcd.setCursor(0, 1);
lcd.print(">MenuItem2");
break;
case 3:
lcd.clear();
lcd.print(">MenuItem3");
lcd.setCursor(0, 1);
lcd.print(" MenuItem4");
break;
case 4:
lcd.clear();
lcd.print(" MenuItem3");
lcd.setCursor(0, 1);
lcd.print(">MenuItem4");
break;
case 5:
menu = 4;
break;
}
}
void executeAction() {
switch (menu) {
case 1:
action1();
break;
case 2:
action2();
break;
case 3:
action3();
break;
case 4:
action4();
break;
}
}
void action1() {
lcd.clear();
lcd.print(">Executing #1");
delay(1500);
}
void action2() {
lcd.clear();
lcd.print(">Executing #2");
delay(1500);
}
void action3() {
lcd.clear();
lcd.print(">Executing #3");
delay(1500);
}
void action4() {
lcd.clear();
lcd.print(">Executing #4");
delay(1500);
}
Hello,
your sketch do what you have coded
You may better structure your sketch wrt IPO model.
Input: Buttons, including debouncing
Processing: FSM for LCD handling
Output: your desired functions
And avoid the usage of the delay() function. A homebrewed timer function will be the better way always.
Hello
I modified your sketch and I think that was what you had in mind to achieve your design goal.
Please review it and don't hesitate if you have more questions.
A microLogic analyzer to see what happens inside I have not deleted.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
enum {Up, Down, Select};
const int UpButton {A0};
const int DownButton = {A1};
const int SelectButton = {A2};
int menu = 1;
struct BUTTON {
byte pin;
bool state;
int click;
unsigned long timeStamp;
unsigned long duration;
} button[] {
{UpButton, 1, 1, 0, 20},
{DownButton, 1, 1, 0, 20},
{SelectButton, 1, 1, 0, 20},
};
void debounce() {
for (auto &make : button) {
if (millis() - make.timeStamp >= make.duration) {
make.timeStamp = millis();
make.state = digitalRead(make.pin);
}
}
}
enum {Pressed, Released, Nothing};
int click (BUTTON &button) {
if (button.click == button.state) return Nothing;
button.click = button.state;
return button.click;
}
void setup() {
lcd.init(); // initialize the lcd
lcd.backlight();
lcd.setContrast(255);
for (auto make : button) pinMode(make.pin, INPUT_PULLUP);
updateMenu();
Serial.begin(9600);
Serial.println("und los geht´s");
}
void loop() {
debounce();
int buttonRead = click(button[Down]);
if (buttonRead == Pressed) Serial.println("DownButton pressed");
if (buttonRead == Pressed) {
menu++;
updateMenu();
}
buttonRead = click(button[Up]);
if (buttonRead == Pressed) Serial.println("UpButton pressed");
if (buttonRead == Pressed) {
menu--;
updateMenu();
}
buttonRead = click(button[Select]);
if (buttonRead == Pressed) Serial.println("SelectButton pressed");
if (buttonRead == Pressed) {
static bool selectAction;
if (!selectAction) executeAction();
if (selectAction) updateMenu();
selectAction = !selectAction;
}
}
void updateMenu() {
switch (menu) {
case 0:
menu = 1;
break;
case 1:
lcd.clear();
lcd.print(">MenuItem1");
lcd.setCursor(0, 1);
lcd.print(" MenuItem2");
break;
case 2:
lcd.clear();
lcd.print(" MenuItem1");
lcd.setCursor(0, 1);
lcd.print(">MenuItem2");
break;
case 3:
lcd.clear();
lcd.print(">MenuItem3");
lcd.setCursor(0, 1);
lcd.print(" MenuItem4");
break;
case 4:
lcd.clear();
lcd.print(" MenuItem3");
lcd.setCursor(0, 1);
lcd.print(">MenuItem4");
break;
case 5:
menu = 4;
break;
}
}
void executeAction() {
switch (menu) {
case 1:
action1();
break;
case 2:
action2();
break;
case 3:
action3();
break;
case 4:
action4();
break;
}
}
void action1() {
lcd.clear();
lcd.print(">Executing #1");
}
void action2() {
lcd.clear();
lcd.print(">Executing #2");
}
void action3() {
lcd.clear();
lcd.print(">Executing #3");
}
void action4() {
lcd.clear();
lcd.print(">Executing #4");
}
Ok, i have a lot of questions
First, why are you using enum with buttons?
Second, why const int UpButton {A0}; is in {} brackets? I changed to 2,3,4 digital bin btw.
And last, button massive (button[] {
{UpButton, 1, 1, 0, 20},
{DownButton, 1, 1, 0, 20},
{SelectButton, 1, 1, 0, 20}) , why is used massive, in such i guess "simple task"?
Hello
Here ny answers: First, why are you using enum with buttons?
The enum´s are used give the name a number.
Up=0
Down=1
Select =2 Second, why const int UpButton {A0}; is in {} brackets?
This one way of many ways to inialize variables with values And last, button massive (button {
The complete instruction is: