Maker Pro
Maker Pro

Menu code/buttons problems

Hi.
Got a music player project where I am using bitmaps to make an icon menu.
I'm using a button library to handle button stuff, it seems to work if I make a simple code with just button inputs.
I'm sure I'm missing something with the loops, something I haven't considered.

The buttons works and it does reach the end of the if loop and switch case statements, but it is sporadic behaviour. Can't get it to go both left and right, sometimes freezes. I can't understand what it wrong. I'm using Seeeduino Xiao, pins are connected through button to ground. I'm posting part of the code that doesn't work, it handles only the main menu. This is to avoid overwhelming readers, I hope this will be enough, but if I missed including something let me know.

Code:
void loop() { //frame Coords: 0,0  ,  32,0   ,   64,0   ,   96,0    32 widht 21 length


  //add menuState 0 (music player default)

    //menuState 1, main menu
    if (menuState == 1) { //main menu, does not handle any commands to DFplayer
        u8g2.clearBuffer(); //this clears the buffer entirely, add the main menu bitmap, draws a frame around the first item then pushes it to screen.
        u8g2.drawXBMP(0, 0, 128, 32, mainMenu);
        //u8g2.drawFrame(0, 0, 32, 21);
        u8g2.sendBuffer();
        Serial.println("draw menu"); //debug
        //cursorPos = 0;
        Serial.println(cursorPos); //debug
       
       switch (cursorPos) { //each case instance handles removal and insertion of applicable frame
        case 0: //cursor is folders
            menuFrameClear(); //makes all frames transparent in buffer
            u8g2.drawFrame(0, 0, 32, 21); //add correct frame, push to screen
            u8g2.sendBuffer();
            Serial.println("frame drawn"); //debug
            while (!btnMenu.pressed() && !btnLeft.pressed() && !btnRight.pressed()){ //required to make code pause until input is received
             
            if (btnMenu.pressed()) { //if menu button is pressed, enter folder menu
                menuState = 2; //this changes the menu state. Program should then exit the parent if loop and proceed to next if loop (next menu). This rarely happens.
                cursorPos = 0; //menuState 2 handles removal of previous menu. Cursor remains in 0 position.
                Serial.println("menu case 0"); //debug
               
            }
            if (btnRight.pressed()) {        
                cursorPos = 1; //move cursor to right
                Serial.println("right case 0"); //debug
               
            }
            if (btnLeft.pressed()) {
           
                cursorPos = 3; //loop around to the left
                Serial.println("left case 0"); //debug
               
            }
            }
        case 1: //cursor is at play mode
            menuFrameClear(); //remove all frames, add new one
            u8g2.drawFrame(32, 0, 32, 21);
            u8g2.sendBuffer();
            while (!btnMenu.pressed() && !btnLeft.pressed() && !btnRight.pressed()){
             
            if (btnMenu.pressed()) {
             
                menuState = 3; //set menu to play mode, state 3 menu handles delete of previous screen
                cursorPos = 0;
                Serial.println("menu case 1");
               
            }
            if (btnRight.pressed()) {
           
                cursorPos = 2; //send cursor to the right
                Serial.println("right case 1");
               
            }
            if (btnLeft.pressed()) {
           
                cursorPos = 0; //send cursor to the Left
                Serial.println("left case 1");
               
            }
            }
        case 2:
            menuFrameClear(); //remove all frames add new one
            u8g2.drawFrame(64, 0, 32, 21);
            u8g2.sendBuffer();
            while (!btnMenu.pressed() && !btnLeft.pressed() && !btnRight.pressed()){
         
            if (btnMenu.pressed()) {
                menuState = 4; //set menu to EQ mode, state 4 menu handles delete of previous screen
                cursorPos = 0;
                Serial.println("menu case 2");
               
            }
            if (btnRight.pressed()) {
             
                cursorPos = 3; //send cursor to the right
                Serial.println("right case 2");
               
            }
            if (btnLeft.pressed()) {
           
                cursorPos = 1; //send cursor to the Left
                Serial.println("left case 2");
               
            }
            }
        case 3:
            menuFrameClear(); //remove all frames
            u8g2.drawFrame(96, 0, 32, 21);
            u8g2.sendBuffer();
            while (!btnMenu.pressed() && !btnLeft.pressed() && !btnRight.pressed()){
     
            if (btnMenu.pressed()) {
           
                menuState = 5; //set menu to music mode, state 5 menu handles delete of previous screen
                cursorPos = 0;
                Serial.println("menu case 3");
               
            }
            if (btnRight.pressed()) {
           
                cursorPos = 0; //send cursor to the right
                Serial.println("right case 3");
               
            }
            if (btnLeft.pressed()) {
             
                cursorPos = 2; //send cursor to the Left
                Serial.println("left case 3");
               
            }
            }

        }
 
    }
 
best if you post the full code and a diagram. You can be sure there are those here who will not be "overwhelmed" .
 

Harald Kapp

Moderator
Moderator
I think you're experiencing an issue with incomplete switch - case statements. Your code in short is like this:
Code:
switch (cursorPos) { //each case instance handles removal and insertion of applicable frame
        case 0: //cursor is folders
            {...
            }
        case 1: //cursor is at play mode
            {...
            }
        case 2:
            {...
            }
        case 3:
            {...
            }

        }
This means that:
  • For cursorPos == 0 first the statements for case 0 are executed, then the statements for case 1, then case 2 and lastly case 3.
  • For cursorPos == 1 the statements for case 1 are executed, then the statements for case 2 and lastly case 3.
  • For cursorPos == 2 the statements for case 2 are executed, then the statements for case 3.
  • For cursorPos == 3 only the statements for case 3 are executed.
In addition to this, you modify the state of cursorPos within the case statements, so when the code leaves one case section, it enters the next case section with a modified value of cursorPos.

What you (usually) want to happen is that after executing the code for any one case the switch statement is terminated. To do this, you need to incorporate a "break;" statement:
Code:
switch (cursorPos) { //each case instance handles removal and insertion of applicable frame
        case 0: //cursor is folders
            {...
            break;
            }
        case 1: //cursor is at play mode
            {...
            break;
            }
        case 2:
            {...
            break;
            }
        case 3:
            {...
            break;
            }

        }
See e.g. here how to use a "switch" statement in C.
 
I think you're experiencing an issue with incomplete switch - case statements.

Hi, thanks, this solved the logic. I did this very quickly, uploading the new code with break in it. Still some issues I will elaborate on.

I note the request for full code and diagram, I'll do my best to draw one and fix the code ready for uploading.

So the issues now are with the button behaviour and my theories on why it fails.

* Button does not register - floating pin problem? But you'd need full code and diagram for this I suspect. Code problem? The way the code runs prevents event like registering the button from happening?

* Button registers, but does not move the cursor. (The cursor redraws itself) This is probably because of the while statment in each case. The while statement ends because of button input, but the case runs through without registering a key press. Maybe this can be solved using pressed in the while statement and released in the if statement. Maybe it would be better to code something that behaves differently. As it stands, no option is a possible conclusion of a case, but it should always be right, left or menu. How can I do this?

* Button left is clicked with no response. Button right is clicked with button left response. Something to do with the code, the left button is pressed and registered as pressed, while the right button ends the while statement. When this happens the if statement for left runs.

All in all it seems I might want to modify the logic so that I don't require the while statement, then look for button presses. And also have some code where it can't exit the loop unless either left, right or menu is executed. How can I do something like that?
 

Harald Kapp

Moderator
Moderator
Button does not register
A specific button or all buttons? Always or only in some cases?
Button registers, but does not move the cursor.
In each case statement or only in some?
Button left is clicked with no response. Button right is clicked with button left response.
Which response? The serial print or the cursor movement? Or both?
All in all it seems I might want to modify the logic so that I don't require the while statement, then look for button presses. And also have some code where it can't exit the loop unless either left, right or menu is executed. How can I do something like that?
Maybe start by telling us what you want to do? Analyzing your code to find out what's going on is a bit tedious.
Also show us the schematic as requested. Without knowing how things are connected, it is only guesswork on our side to try to find out what's going on.

Tip: Use enums instead of plain numbers for the menu states. That makes the code much better legible.
 
A specific button or all buttons? Always or only in some cases?

In each case statement or only in some?

Which response? The serial print or the cursor movement? Or both?
1. All buttons, "always" but they work as expected sometimes. when they do work, the logic (explained detailed below) works. (That means, if I press the button enough times I get the cursor to move to the right)

2. Every case statement, but sometimes the button does not register, sometimes it does but does not move. I notice because the frame (explained below) redraws itself in the same position.

3. Cursor movement, I haven't checked the serial (it is a bit tedious with this controller and I only had a few mins) I will do more troubleshooting after this post, where I'll also fix the code up for posting.

Diagram attached, hope it is sufficient.

What I have already posted is the main menu code. the initial setup of the code sets the menuState to 1 and cursorPos to 0, the first icon. 1st. It clears the buffer, draws a 4 icon bitmap. 2nd. Each case handles one of the 4 icon positions. Based on button input there are only three options in each case, move the cursor right, move the cursor left or enter the next sub menu. The cursor is a frame around the icon
 

Attachments

  • diagram.pdf
    341.2 KB · Views: 1
Solved!
Before I asked for help here I had break statements in the if statements, but like HaraldKapp said, I didn't also put them to close the case loops. I re-added the break statements properly both to the if and case statements and the code now works as expected. I also added an if/else statement to the bottom of the parent Menu if statement to stop it exiting the parent if loop entirely, speeding up the cursor change and preventing it from redrawing the bitmap needlessly.

Also, the while statement needs to end on the basis of pressed button, while the changing of the cursor/menu needs to be on the basis of released button.

I still have problems with buttons occationally not registering. They don't register in any way, including on the serial. Not entirely sure why that might be.

Working code:
Code:
 if (menuState == 1) { //main menu, does not handle any commands to DFplayer
        u8g2.clearBuffer(); //this clears the buffer entirely, add the main menu bitmap, draws a frame around the first item then pushes it to screen.
        u8g2.drawXBMP(0, 0, 128, 32, mainMenu);
        //u8g2.drawFrame(0, 0, 32, 21);
        u8g2.sendBuffer();
        Serial.println("draw menu"); //debug
        //cursorPos = 0;
        Serial.println(cursorPos); //debug

        cursorChange:
       switch (cursorPos) { //each case instance handles removal and insertion of applicable frame
        case 0: //cursor is folders
            menuFrameClear(); //makes all frames transparent in buffer
            u8g2.drawFrame(0, 0, 32, 21); //add correct frame, push to screen
            u8g2.sendBuffer();
            Serial.println("frame drawn"); //debug
            while (!btnMenu.pressed() && !btnLeft.pressed() && !btnRight.pressed()){ //required to make code pause until input is received
              
            if (btnMenu.released()) { //if menu button is pressed, enter folder menu
                menuState = 2; //this changes the menu state. Program should then exit the parent if loop and proceed to next if loop (next menu). This rarely happens.
                cursorPos = 0; //menuState 2 handles removal of previous menu. Cursor remains in 0 position.
                Serial.println("menu case 0"); //debug
                break;
            }
            if (btnRight.released()) {         
                cursorPos = 1; //move cursor to right
                Serial.println("right case 0"); //debug
                break;
            }
            if (btnLeft.released()) {
            
                cursorPos = 3; //loop around to the left
                Serial.println("left case 0"); //debug
                break;
            }
            }
            break;
        case 1: //cursor is at play mode
            menuFrameClear(); //remove all frames, add new one
            u8g2.drawFrame(32, 0, 32, 21);
            u8g2.sendBuffer();
            while (!btnMenu.pressed() && !btnLeft.pressed() && !btnRight.pressed()){
              
            if (btnMenu.released()) {
              
                menuState = 3; //set menu to play mode, state 3 menu handles delete of previous screen
                cursorPos = 0;
                Serial.println("menu case 1");
                break;
            }
            if (btnRight.released()) {
            
                cursorPos = 2; //send cursor to the right
                Serial.println("right case 1");
                break;
            }
            if (btnLeft.released()) {
            
                cursorPos = 0; //send cursor to the Left
                Serial.println("left case 1");
                break;
            }
            }
            break;
        case 2:
            menuFrameClear(); //remove all frames add new one
            u8g2.drawFrame(64, 0, 32, 21);
            u8g2.sendBuffer();
            while (!btnMenu.pressed() && !btnLeft.pressed() && !btnRight.pressed()){
          
            if (btnMenu.released()) {
                menuState = 4; //set menu to EQ mode, state 4 menu handles delete of previous screen
                cursorPos = 0;
                Serial.println("menu case 2");
                break;
            }
            if (btnRight.released()) {
              
                cursorPos = 3; //send cursor to the right
                Serial.println("right case 2");
                break;
            }
            if (btnLeft.released()) {
            
                cursorPos = 1; //send cursor to the Left
                Serial.println("left case 2");
                break;
            }
            }
            break;
        case 3:
            menuFrameClear(); //remove all frames
            u8g2.drawFrame(96, 0, 32, 21);
            u8g2.sendBuffer();
            while (!btnMenu.pressed() && !btnLeft.pressed() && !btnRight.pressed()){
      
            if (btnMenu.released()) {
            
                menuState = 5; //set menu to music mode, state 5 menu handles delete of previous screen
                cursorPos = 0;
                Serial.println("menu case 3");
                break;
            }
            if (btnRight.released()) {
            
                cursorPos = 0; //send cursor to the right
                Serial.println("right case 3");
                break;
            }
            if (btnLeft.released()) {
              
                cursorPos = 2; //send cursor to the Left
                Serial.println("left case 3");
                break;
            }
            }
            break;
        }
      if (menuState != 1){
        
      }
      else {
        goto cursorChange;
      }
    }

Now, the change is still a bit slow. Any way I can do this better?
 
Top