The Machines are here!!! :D

Hey Guys!
I programmed my arduino to create a menu system on an LCD display(character). It has a single button interface and the button is debounced using the Bounce2 library. It seems that whenever the main menu is being displayed the button signals are interpreted perfectly but when the “Play” option is selected, it is supposed to display two other options whereas it executes the first one automatically. I tried to debug it and found out that the “select” function was getting wrong output from the check function. It tells the select function that the button is pressed whereas it isn’t so it automatically executes the option the marker is on.
I know this sounds very confusing with all the functions and other stuff so here is a brief concept of the code:

  1. All the functions are controlled by the control function.
    2.There are two basic screens(menus) which are two be displayed:
    One is the main menu for which the variable screen has a value of 0.
    The other is the sub menu which is displayed when the Play option is selected.
  2. The dataRead function just takes the values from a potentiometer and returns a mapped value.
  3. This mapped value is passed onto the updateDisplay function which displays (if screen=0) the main menu or
    (ifscreen=1) the sub menu.
  4. Then the same value is used for displayMarker function which just displays a little cursor to point out which option is
    being pointed at by the user.
  5. Finally a select function checks if a button is pressed and if yes then on which option of which screen to execute the
    respective functions.

I think the problem is in the select function. Somehow it has decided to become self-aware and make me bang my head
against the wall! :smiley: Please tell me if you guys find a bug.

P.S Sorry for the verryyyyyyyyyyyyy long post but it had to be done in order for you guys to understand the code:

#include <Bounce2.h>

#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

#define BUTTON_PIN 10
Bounce debouncer = Bounce(); 


byte armsCenter[8] = {
        0b00100,
	0b01010,
	0b00100,
	0b00100,
	0b11111,
	0b00100,
	0b00100,
	0b01010
};

byte rightUp[8] = {
        0b00100,
	0b01010,
	0b00100,
	0b00110,
	0b11101,
	0b00100,
	0b00100,
	0b01010
};

byte leftUp[8] = {
        0b00100,
	0b01010,
	0b00100,
	0b01100,
	0b10111,
	0b00100,
	0b00100,
	0b01010
};

byte block[8] = {
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111
};


byte data;
char *menu[] = {
  "1)Play","2)Instructions","3)About","4)Exit" };
  
char *submenu[] = {
  "1)Ocean Motion", "2)Loading Bar"};

int potPin = A0;
int lastVal = 0;
int lastVal2 = 0;

int screen=0;

void setup(){

  lcd.begin(16, 2);
  delay(100);
  lcd.createChar(0, block);
  lcd.createChar(5, armsCenter);
  lcd.createChar(6, rightUp);
  lcd.createChar(7, leftUp);
  Serial.begin(9600);
  pinMode(potPin, INPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(13, OUTPUT);

  debouncer.attach(BUTTON_PIN);
  debouncer.interval(50);

}

void loop()
{
   control();
}

void control() {
if(screen==0) {
int val = dataRead();
int data = updateDisplay(val);
displayMarker(data);
select(val);
}
else if(screen==1) {
  //Sub-Menu Initialization...
 
  int data = updateDisplay(0);
 
  int val = dataRead();
 
  if(val==1 || val==3) {
    val = 1;
    }
    else if(val==2 || val==4) {
    val = 2;
    }
    else {
    
    }

  displayMarker(val);
  Serial.println(check());
  select(val);
  Serial.println(check());
} 
else {
lcd.clear();
lcd.print("Critical Error!");
}
}

int dataRead() {
  int val = analogRead(potPin);
  val = map(val,0,1023,0,100);

  if(val>0 && val<20) {
    return 1;
  }

  else if(val>20 && val<40) {
    return 2;
  }

  else if(val>40 && val<60) {
   return 3;
  } 

  else if(val>60 && val<80) {
   return 4;
  } 
  else {

  }
}

int updateDisplay(int number) {
  if(screen==0) {
  
  if(number==1 && number != lastVal) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(menu[0]);
    lcd.setCursor(0,1);
    lcd.print(menu[1]);
    lastVal=number;
    return 1;
  }

  else if(number==2 && number != lastVal) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(menu[0]);
    lcd.setCursor(0,1);
    lcd.print(menu[1]);
    lastVal=number;
    return 2;
  }

  else if(number==3 && number != lastVal) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(menu[2]);
    lcd.setCursor(0,1);
    lcd.print(menu[3]);
    lastVal=number;
    return 1;
  }

  else if(number==4 && number != lastVal) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(menu[2]);
    lcd.setCursor(0,1);
    lcd.print(menu[3]);
    lastVal=number;
    return 2;
  }
}
  else if(screen==1 && number != lastVal2){
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(submenu[0]);
    lcd.setCursor(0,1);
    lcd.print(submenu[1]);
    lastVal2 = number;
    }
 }

void displayMarker(int number) {
if(screen==0) {
    if(number==1) {
    lcd.setCursor(15,0);
    lcd.print("<");
    
  }

  else if(number==2) {
    lcd.setCursor(15,1);
    lcd.print("<");
    
  }
  else {

  }
}

else {
 if(number==1) {
    lcd.setCursor(15,0);
    lcd.print("<");
    
  }

  else if(number==2) {
    lcd.setCursor(15,1);
    lcd.print("<");
    
  }
}
} 


int check() {
  
  debouncer.update();
  int val = debouncer.read();
  if(val==HIGH) {
    return 0;
  }

  else if(val==LOW) {
    return 1;
  }

  else {

  }
}

void select(int number) {
 if(screen==0) { 
  if(check()==1) {
    if(number==1) {
      screen = 1;
    }
    else if(number==2) {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Animation Centre");
      lcd.setCursor(0,1);
      lcd.print("Enjoy!");
    }
    else if(number==3) {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Made By:");
      lcd.setCursor(0,1);
      lcd.print("Owais Bin Asad");
    }
    else if(number==4) {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Bye Bye :D");
    }
  }
 else {

  }
 }
 if(screen==1){
    if(check()==1) {
      
   if(number==1) {
     motion();
    }
   else if(number=2) {

     load();
    }
   else {
   
    } 
  }
  else {
  
  }
  
 screen=0;
 }
}

void center(int col, int row, int del) {
  lcd.setCursor(col, row);
  lcd.write((byte)5);
  delay(del);
}

void right(int col, int row, int del) {
  lcd.setCursor(col, row);
  lcd.write((byte)6);
  delay(del);
}

void left(int col, int row, int del) {
  lcd.setCursor(col, row);
  lcd.write((byte)7);
  delay(del);
}

void motion() {
lcd.clear();
center(0,1,0);
center(1,1,0);
center(2,1,0);
center(3,1,0);
center(4,1,0);
center(5,1,0);
center(6,1,0);
center(7,1,0);
center(8,1,0);
center(9,1,0);
center(10,1,0);
center(11,1,0);
center(12,1,0);
center(13,1,0);
center(14,1,0);
center(15,1,0);

//first man
left(0,1,200);
right(0,1,200);
center(0,1,0);

//second man
left(1,1,200);
right(1,1,200);
center(1,1,0);

//third man
left(2,1,200);
right(2,1,200);
center(2,1,0);

//fourth man
left(3,1,200);
right(3,1,200);
center(3,1,0);

//fifth man
left(4,1,200);
right(4,1,200);
center(4,1,0);

//sixth man
left(5,1,200);
right(5,1,200);
center(5,1,0);

//7
left(6,1,200);
right(6,1,200);
center(6,1,0);

//8
left(7,1,200);
right(7,1,200);
center(7,1,0);

//9
left(8,1,200);
right(8,1,200);
center(8,1,0);

//10
left(9,1,200);
right(9,1,200);
center(9,1,0);

//11
left(10,1,200);
right(10,1,200);
center(10,1,0);

//12
left(11,1,200);
right(11,1,200);
center(11,1,0);

//13
left(12,1,200);
right(12,1,200);
center(12,1,0);

//14
left(13,1,200);
right(13,1,200);
center(13,1,0);

//15
left(14,1,200);
right(14,1,200);
center(14,1,0);

//16
left(15,1,200);
right(15,1,200);
center(15,1,0);
}

void load() {
lcd.clear();
for(int x=0; x<=15; x++) {
lcd.setCursor(x,0);
lcd.write((byte)0);
delay(500);
}
}

Sorry, but I'm too lazy to work throug all that code.

It sounds like you have a problem with interpreting a button in different phases of the program. Perhaps you could write a short sketch with no LCD stuff that demostrates the problem?

...R

Sure, Why Not!

The code basically works like this:
1> The position of the pot is determined by the readData function and then passed onto the updateDisplay function.

int dataRead() {
  int val = analogRead(potPin);
  val = map(val,0,1023,0,100);

  if(val>0 && val<20) {
    return 1;
  }

  else if(val>20 && val<40) {
    return 2;
  }

  else if(val>40 && val<60) {
   return 3;
  } 

  else if(val>60 && val<80) {
   return 4;
  } 
  else {

  }
}

2> updateDisplay does what its name suggests, it prints out the appropriate text on the LCD.

int updateDisplay(int number) {
  if(screen==0) {
  
  if(number==1 && number != lastVal) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(menu[0]);
    lcd.setCursor(0,1);
    lcd.print(menu[1]);
    lastVal=number;
    return 1;
  }

  else if(number==2 && number != lastVal) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(menu[0]);
    lcd.setCursor(0,1);
    lcd.print(menu[1]);
    lastVal=number;
    return 2;
  }

  else if(number==3 && number != lastVal) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(menu[2]);
    lcd.setCursor(0,1);
    lcd.print(menu[3]);
    lastVal=number;
    return 1;
  }

  else if(number==4 && number != lastVal) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(menu[2]);
    lcd.setCursor(0,1);
    lcd.print(menu[3]);
    lastVal=number;
    return 2;
  }
}
  else if(screen==1 && number != lastVal2){
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(submenu[0]);
    lcd.setCursor(0,1);
    lcd.print(submenu[1]);
    lastVal2 = number;
    }
 }

3> Then a displayMarker function just comes in and displays a little cursor to indicate the position of the user.

void displayMarker(int number) {
if(screen==0) {
    if(number==1) {
    lcd.setCursor(15,0);
    lcd.print("<");
    
  }

  else if(number==2) {
    lcd.setCursor(15,1);
    lcd.print("<");
    
  }
  else {

  }
}

else {
 if(number==1) {
    lcd.setCursor(15,0);
    lcd.print("<");
    
  }

  else if(number==2) {
    lcd.setCursor(15,1);
    lcd.print("<");
    
  }
}
}

4> finally the select function checks(through the check function) whether the button has been pressed or not. If yes then o
which screen and on which options.

void select(int number) {
 if(screen==0) { 
  if(check()==1) {
    if(number==1) {
      screen = 1;
    }
    else if(number==2) {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Animation Centre");
      lcd.setCursor(0,1);
      lcd.print("Enjoy!");
    }
    else if(number==3) {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Made By:");
      lcd.setCursor(0,1);
      lcd.print("Owais Bin Asad");
    }
    else if(number==4) {
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("Bye Bye :D");
    }
  }
 else {

  }
 }
 if(screen==1){
    if(check()==1) {
   if(number==1) {
     motion();
    }
   else if(number=2) {
     load();
    }
   else {
   
    } 
  }
  else {
  
  }
  
 screen=0;
 }
}

NOTE: All the functions are aware of the screen variable that is they know which screen to work on, the main menu or the sub menu.

The main menu is displayed as the default screen but when the PLAY option is selected then it goes over to the sub menu which only has two options as compared to the main menu which had four and had to scroll.

Now for the technical problem:
What happens is that when the PLAY option is selected on the main menu it just changes the variable screen to 1:

if(screen==0) { 
  if(check()==1) {
    if(number==1) {
      screen = 1;
    }

And yeah, all the functions(there order of execution) is controlled by the control function:

void control() {
if(screen==0) {
int val = dataRead();
int data = updateDisplay(val);
displayMarker(data);
select(val);
}
else if(screen==1) {
  //Sub-Menu Initialization...
 
  int data = updateDisplay(0);
 
  int val = dataRead();
 
  if(val==1 || val==3) {
    val = 1;
    }
    else if(val==2 || val==4) {
    val = 2;
    }
    else {
    
    }

  displayMarker(val);
} 
else {
lcd.clear();
lcd.print("Critical Error!");
}
}

So the program is to loop over and over with the correct parameters so once the screen is changed to 1, the program loops, all the functions are re-executed and they now see that its time for the sub menu(screen==1) and they start doing the same stuff all over but the thing that is happening is that at the end when the select function has to come up it first checks if the screen is 1, in this case it is then it checks for a button press and if yes then on which position. Definitely i can not change the cursor position in microseconds(im a human by the way :D) so it takes the position and the check function returns a 1 that is it tells the select function that, “Hey! I got a press!!! Do your stuff now”. The select function innocently does what he says.
so the main problem turns out to be:
The check function is returning a 1 when the sub menu is first displayed whereas it should return a 0

I thought that may be when i press the button to select the PLAY option it is quick enough to display all the stuff and check for a press even before i could get my finger off of that button so i added a huge delay. 1 second to be precise(don’t laugh thats a 1000 milliseconds :wink: ) it didn’t work. HELP!

Yet again sorry for the huge post but somethings had to be understood. You can skip all the code if you want, i have explained it as briefly as possible.

Sorry, I'm still lazy. That code doesn't seem to be any shorter than the first version. I do appreciate that you have taken the trouble to write an explanation.

I was hoping you might be able to write a complete 20 or 30 line sketch that illustrates the problem.

...R

OK I’ll try but it would be very hard for me to explain it in such a short amount of words.

CODE:

#include <Bounce2.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

#define BUTTON_PIN 10
Bounce debouncer = Bounce();
int screen=1;

void setup(){

  lcd.begin(16, 2);
  delay(100);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  debouncer.attach(BUTTON_PIN);
  debouncer.interval(50);

}

void loop() {
select();
}

//Select Function----------------START-----------
void select(int number) {
 if(screen==1){
    if(check()==1) { //Right now this should be false that is check() should return a 0
   if(number==1) {
     //Option 1
    }
   else if(number=2) {
     //Option 2
    }
   else {
   
    } 
  }
  else {
  
  }
  
 screen=0;
 }
}
//SELECT function--------------END---------------
//Check Function---------------START-------------
int check() {
  delay(100);
  debouncer.update();
  int val = debouncer.read();
  if(val==HIGH) {
    return 0;
  }

  else if(val==LOW) {
    return 1;
  }

  else {

  }
}
//Check Function-------------END----------------

The problem: When screen==1, and number==1, check should be equal to 0 whereas it is not, its a 1(false triggering )

There were several things I see wrong in your simplified code. I have not checked the more complex sketches, and maybe some of the problems are due to your attempt at pruning it down.

  1. The function select() requires an input parameter "number".

  2. The return state from check() was not being passed into select() correctly.

3). check() is dependent upon the button read value, and is independent of screen==1 and number==1.

I have put together some simplified code to demonstrate these points. I have played around with number = 1 and 2, and with the button value being high and low. I can certainly get check() and select() talking to each other.

int screen=1;

void setup(){

  Serial.begin(9600);

}

void loop() {
  select(1);
}

//Select Function----------------START-----------
void select(int number) {
  if(screen==1){
    byte state=check();
    Serial.println("State returned from check   ");
    Serial.println(state);
    //if(check()==1) { //Right now this should be false that is check() should return a 0
    if (state==1){
      if(number==1) {
        Serial.println("Option 1");
      }
      if(number==2) {
        Serial.println("Option 2");
      }
    }

    screen=0;
  }
}
//SELECT function--------------END---------------

//Check Function---------------START-------------
int check() {
  delay(100);
  int val = 1;//debouncer.read(); I have set it 1 or 0
  if(val==HIGH) {
    return 0;
  }

  else if(val==LOW) {
    return 1;
  }

}
//Check Function-------------END----------------

Well here are the answers:
1> The select function is given a correct parameter in the actual code. I just forgot to type it in there.

2> Well you took the value as a byte whereas i designed the check function to return an integer. So returning an integer, then interpreting it as a byte and then using it then this will cause some problems(maybe).

3> The variable number basically is the position of the pot and the screen variable determines which menu( main or sub menu) to display so the check function has nothing to do with them. All it has to do is to check for a button press irrespective of the screen mode or the position of the pot.

By position of pot I basically mean the position of the cursor determined by the physical rotation of a potentiometer.

arduino2399:
OK I'll try but it would be very hard for me to explain it in such a short amount of words.

I am not asking you to explain it in a few words. I am asking you to write a short sketch that demonstrates the incorrect behaviour. That is unlikely to be impossible, or even difficult. My guess is that is writing such a sketch you will find the solution to your problem.

This comment

The problem: When screen==1, and number==1, check should be equal to 0 whereas it is not, its a 1(false triggering )

is not consistent with this code

 if(screen==1){
    if(check()==1) { //Right now this should be false that is check() should return a 0
   if(number==1) {

because the order of the tests is different.

Try to structure your code to avoid nested tests - it can be very difficult to figure out all the possible combinations that can occur and to be sure you respond to the correct combination and only to that combination.

And it doesn't make sense to say if(check() ==1 should return a 0. If it did you wouldn't need the test.

Maybe the order of your tests needs to change. Perhaps the if(check() == 1 should come first. You should probably do the test that eliminates the most options first.

"check" is a very meaningless name for a function.

...R

...R

Ok i'll give you the code and yes i know check is a very stupid name.. but i couldnt think of anything else at that time.. :wink: