Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to exit from main menu? #13

Closed
graziano71 opened this issue Aug 30, 2015 · 24 comments
Closed

How to exit from main menu? #13

graziano71 opened this issue Aug 30, 2015 · 24 comments

Comments

@graziano71
Copy link

Hello! Really great library, I tested with arduino nano+serial 1604 display and encoder with button and it works very well!
More than the issue already reported few days ago by lfc7 on menu refresh, which I haven't solved as well, I am wondering if there is any "embedded mechanism" to exit from main menu, and in such a case, how to re-enter.
Currently I have simply included an "Exit" menu option calling an exit function which prevent menu to be called in void loop().
Thanks!

@neu-rah
Copy link
Owner

neu-rah commented Aug 30, 2015

  Hi,
  Just don't call the menu function on your loop and clear the
  screen


  Às 19:08 de 30-08-2015, graziano71 escreveu:


  Hello! Really great library, I tested with arduino nano+serial
    1604 display and encoder with button and it works very well! 
    More than the issue already reported few days ago by lfc7 on
    menu refresh, which I haven't solved as well, I am wondering if
    there is any "embedded mechanism" to exit from main menu, and in
    such a case, how to re-enter.
    Currently I have simply included an "Exit" menu option calling
    an exit function which prevent menu to be called in void loop().
    Thanks!
  —
    Reply to this email directly or view
      it on GitHub.

@graziano71
Copy link
Author

thanks for quick feedback! the problem in clearing the screen is that when the menu is called again in the loop, it doesn't show up until the encoder is moved again and the menu is redrawed. any suggestion to make it appear again without moving within menu options? thanks!!

@neu-rah
Copy link
Owner

neu-rah commented Sep 2, 2015

hi!

  try this:
  «lcd».drawn=NULL;

  «lcd» being your menuOut object


  Às 21:28 de 01-09-2015, graziano71 escreveu:


  thanks for quick feedback! the problem in clearing the screen
    is that when the menu is called again in the loop, it doesn't
    show up until the encoder is moved again and the menu is
    redrawed. any suggestion to make it appear again without moving
    within menu options? thanks!!
  —
    Reply to this email directly or view
      it on GitHub.

@graziano71
Copy link
Author

It works! Thanks so much!
One last question: When I recall the Menu, it keeps the last position (Exit Option). Any suggestion on how to bring the cursor on top position when I recall the menu? Thanks once again.

@benovitch
Copy link

Hi Graziano,

I have the same problem as you. Is it possible to get your code that you used to solve this.

Ben

@nicodonte
Copy link

Hi, have the same problem, when I recall the menu it keeps in the last position. How I put the cursor on the first item again? Thanks!!
Nico.

@benovitch
Copy link

Nico,

Together with a friend we came up with a solution to exit the menu and see other things on the LCD. and then go back to the menu when needed. I will try to post my code this evening.

@benovitch
Copy link

Here is the code I wrote for my CoG project.

#include <HardwareSerial.h>
#include <LiquidCrystal.h>
#include <menu.h>//menu macros and objects
#include <menuFields.h>
#include <pcint.h>//this is incompatible with software serial (arduino needs an handler!)
#include <quadEncoder.h>//quadrature encoder driver and fake stream
#include <keyStream.h>//keyboard driver and fake stream (for the encoder button)
#include <chainStream.h>// concatenate multiple input streams (this allows adding a button to the encoder)
#include <menuLCD.h>

LiquidCrystal lcd1(7, 6, 5, 4, 3, 2);

////////////////////////////////////////////
// ENCODER (aka rotary switch) PINS
// rotary
#define encA 8
#define encB 9
//this encoder has a button here
#define encBtn A0
#define sensorPin_LA A1 // gewichtssensoren
#define sensorPin_RA A2
#define sensorPin_Nose A3

int opdracht = 0;
//functions to wire as menu actions

//aux function
void nothing() {}

void Afzonderlijke_Gewichten(){opdracht = 1; lcd1.clear();} // Hier zijn alle afzonderlijke gewichten te zien
void calibLA(){opdracht = 2; lcd1.clear();} // voorlopig om te testen
void calibRA(){opdracht = 3; lcd1.clear();}
void calibNose(){opdracht = 4; lcd1.clear();}
void Totaal_Gewicht(){opdracht = 5; lcd1.clear();} // totaal gewicht toestel
void CGmeten(){opdracht = 6; lcd1.clear();} // Hier wordt het toe of af te trekken gewicht weergegeven op het corectiepunt CP

float Zwaartekracht =9.81;
int MainToNose =0;
int MainToCG=0;
int CoGToCP=0;

/////////////////////////////////////////////////////////////////////////
// MENU DEFINITION
// here we define the menu structure and wire actions functions to it
// empty options are just for scroll testing

MENU(Gewichten,"Gewichten",
OP("Gewichten",Afzonderlijke_Gewichten),
OP("Totaal Gewicht",Totaal_Gewicht)
);

MENU(Calibratie,"Calibratie",
OP("Calibratie LA",calibLA),
OP("Calibratie RA",calibRA),
OP("Calibratie Nose",calibNose)
);

MENU(Instellingen,"Instellingen", // Hier zijn de afstanden aan te passen van de onderlinge punten
FIELD(MainToNose,"Main to Nose","mm",-2000,2000,10,1,nothing),
FIELD(MainToCG,"Main to CG","mm",-2000,2000,10,1,nothing),
FIELD(CoGToCP,"CoG to CP","mm",-2000,2000,10,1,nothing)
);

MENU(Metingen,"Metingen",
OP("CG Meten",CGmeten),
SUBMENU(Gewichten)
);

MENU(Meten,"Metingen ",
SUBMENU(Metingen),
SUBMENU(Instellingen)
);

MENU(mainMenu,"Main menu",
SUBMENU(Meten),
SUBMENU(Calibratie)
);

//the quadEncoder
quadEncoder quadEncoder(encA,encB);//simple quad encoder driver
quadEncoderStream enc(quadEncoder,5);// simple quad encoder fake Stream

//a keyboard with only one key :D, this is the encoder button
keyMap encBtn_map[]={{-encBtn,menu::enterCode}};//negative pin numbers means we have a pull-up, this is on when low
keyLook<1> encButton(encBtn_map);

//multiple inputs allow conjugation of the quadEncoder with a single key keyboard that is the quadEncoder button
Stream* in[]={&enc,&encButton};
chainStream<2> quadEncoder_button(in);

//alternative to previous but now we can input from Serial too...
Stream* in3[]={&enc,&encButton,&Serial};
chainStream<3> allIn(in3);

//describing a menu output, alternatives so far are Serial or LiquidCrystal LCD
menuLCD lcd(lcd1,20,4);

/////////////////////////////////////////////////////////////////////////
void setup() {
quadEncoder.begin();
Serial.begin(9600);

lcd1.begin(20,4);
lcd1.clear();
lcd1.setCursor(3,1);
lcd1.print("CoG Calculator");
lcd1.setCursor(4,3);
lcd1.print("By Benovitch");
delay(3000);

pinMode(encBtn, INPUT);
digitalWrite(encBtn,1);

pinMode(sensorPin_LA, INPUT);
pinMode(sensorPin_RA, INPUT);
pinMode(sensorPin_Nose, INPUT);
}

///////////////////////////////////////////////////////////////////////////////
// testing the menu system
static unsigned long NextTrigger = 0;
int plotDelay = 200;
float Sens_LA = 0;
float Sens_RA = 0;
float Sens_Nose = 0;

void loop() {

float Gewicht_LA = 0;
float Gewicht_RA = 0;
float Gewicht_Nose = 0;
float Gewicht_MainGear = (Gewicht_LA + Gewicht_RA);
float Afstand_CGtoNose = (MainToNose - MainToCG);
float Totaal_Gewicht = (Gewicht_LA + Gewicht_RA + Gewicht_Nose);
float Moment_MGtoCG = ((Gewicht_MainGear * Zwaartekracht) * MainToCG);
float Moment_NGtoCG = ((Gewicht_Nose * Zwaartekracht) * Afstand_CGtoNose);
int Gewicht_CP = (((Moment_MGtoCG - Moment_NGtoCG)/(CoGToCP * Zwaartekracht))_1000) ; // Hiermee wordt het eiegnlijke gewicht op het CP berekend
//float Gewicht_CP = ((5000_9.81_4.5)-(1500_9.81_12))/(12_9.81);

switch (opdracht){
case 1: //Gewichten Meten
lcd1.setCursor(0,0);
lcd1.print("Gewichten");
lcd1.setCursor(0,1);
lcd1.print("LA: ");
lcd1.print(Gewicht_LA,3);
lcd1.print(" ");
lcd1.setCursor(0,2);
lcd1.print("RA: ");
lcd1.print(Gewicht_RA,3);
lcd1.print(" ");
lcd1.setCursor(0,3);
lcd1.print("Neus: ");
lcd1.print(Gewicht_Nose,3);
lcd1.print(" ");
break;

case 2:    //Calibreren
 lcd1.setCursor(0,0);
 lcd1.print("Calibreren LA");
 if (millis() > NextTrigger){
  Serial.println(Sens_LA);
  lcd1.setCursor(0,1);
  lcd1.print("Linksachter: ");
  lcd1.print(Sens_LA);
  lcd1.print("  "); 
 } 
 break;

 case 3:
 lcd1.setCursor(0,0);
 lcd1.print("Calibreren RA");
 if (millis() > NextTrigger){
  Serial.println(Sens_RA);
  lcd1.setCursor(0,1);
  lcd1.print("Rechtsachter: ");
  lcd1.print(Sens_RA);
  lcd1.print("  ");
  }
  break;

 case 4:
 lcd1.setCursor(0,0);
 lcd1.print("Calibreren Nose");
 if (millis() > NextTrigger){
  Serial.println(Sens_Nose);
  lcd1.setCursor(0,1);
  lcd1.print("Nose: ");
  lcd1.print(Sens_Nose);
  lcd1.print("  ");
  }
  break;

 case 5:
 /*lcd1.setCursor(0,0);
 lcd1.print("Gewicht");
 if (millis() > NextTrigger)*/{
  Serial.println(Totaal_Gewicht);
  lcd1.setCursor(0,0);
  lcd1.print("Totaal Gewicht: ");
  lcd1.setCursor(0,1);
  lcd1.print(Totaal_Gewicht,3);
  lcd1.print("  ");
  }
  break;

  case 6:
 lcd1.setCursor(0,0);
 lcd1.print("CG meten");
 if (millis() > NextTrigger){
  Serial.println(Gewicht_Nose,3);
  lcd1.setCursor(0,1);
  lcd1.print("Gewicht CP:");
  lcd1.print(Gewicht_CP);
  lcd1.print(" Gr");
  lcd1.print("    ");


  }
  break;

default:
 mainMenu.poll(lcd,allIn);
 break;
}

if (opdracht != 0) { // om vanuit de berekeningen terug naar het menu te gaan
int stateButton = digitalRead(encBtn);
if(stateButton == 0) {
opdracht = 0;
lcd1.clear();
lcd.drawn=NULL;
delay(500);
}
}

if (millis() > NextTrigger){
NextTrigger = (long)(millis() + plotDelay);
}
}

@nicodonte
Copy link

Hi @benovitch, thanks for sharing your code, I have figured out how to refresh the menu when I go to a function, my question now is how I return to the top menu instead of the last item after that I have cleaned the screen. It seems always return to the last item selected when .drawn=NULL; is invoked.

@neu-rah
Copy link
Owner

neu-rah commented Sep 24, 2015

well, the philosophy was that your functions should return only when they are done, and you can have a loop on your functions... but if for some reason you wish to redraw or change focus then I've just added some new functions "redraw" and "focus" for that.

https://github.com/neu-rah/ArduinoMenu/wiki/Customizing-menus

@nicodonte
Copy link

Thanks @neu-rah!!! I will test these new code tonight.

@nicodonte
Copy link

hi @neu-rah, I can't make .focus work in my project. .redraw(); works perfect, I don't need lcd.clear(); menu_lcd.drawn=NULL; anymore. But with .focus(); allways shows me a compile error. I don't understand whats happends.

These are part of my code, tell me if a miss something or I'm doing something wrong:

#include <menu.h>//menu macros and objects
#include <menuLCDs.h>//F. Malpartida LCD's
#include <pcint.h>//this is incompatible with software serial (arduino needs an handler!)
#include <menuFields.h>
#include <quadEncoder.h>//quadrature encoder driver and fake stream
#include <keyStream.h>//keyboard driver and fake stream (for the encoder button)
#include <chainStream.h>// concatenate multiple input streams (this allows adding a button to the encoder)
#include <controls/common/menuTextFields.h>

menuLCD menu_lcd(lcd,20,3);//menu output device



void bklMenu(){
        menuSeleccion = 1;
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print("AJUSTAR  ILUMINACION");
        lcd.setCursor(0,3);
        lcd.print("(+/-)NIVEL (S)SALIR");

        do{
        if (bPlus.isPressed()) {bl+=5; bl = min (bl,255); analogWrite(6,bl); delay(50);}
        if (bMinus.isPressed()) {bl-=5; bl = max (bl,0); analogWrite(6,bl); delay (50);}
        if (bSelect.uniquePress()) menuSeleccion = 0;
        lbg.drawValue( bl, 255); 
        delay (100);
        } while (menuSeleccion != 0);
        //lcd.clear();
        //menu_lcd.drawn=NULL;
        menu_lcd.focus(1);
       menu_lcd.redraw();
}


MENU(configMenu, "Configuracion",
SUBMENU (relojMenu),
OP("Intensidad Display", bklMenu)
);

MENU(mainMenu,"Main",
SUBMENU(configMenu),
OP("Prueba A",clockMenu),
OP("Prueba B",clockMenu),
OP("Salir",exitMenu)
);



                 mainMenu.poll (menu_lcd, allIn , true);

This is the error in compiler windows:

Reloj_Nico_v2.ino: In function 'void bklMenu()':
Reloj_Nico_v2:537: error: 'class menuLCD' has no member named 'focus'
'class menuLCD' has no member named 'focus'

Thanks for your help!!
Regards,
Nico.

@neu-rah
Copy link
Owner

neu-rah commented Sep 25, 2015

focus is a member of menu, not from menuOut

it should be called from a menu
ex:

mainMenu.focus(2)

or

configMenu.focus(0)

@nicodonte
Copy link

With:

        if (bPlus.isPressed()) {bl+=5; bl = min (bl,255); analogWrite(6,bl); delay(50);}
        if (bMinus.isPressed()) {bl-=5; bl = max (bl,0); analogWrite(6,bl); delay (50);}
        if (bSelect.uniquePress()) menuSeleccion = 0;
        lbg.drawValue( bl, 255); 
        delay (100);
        } while (menuSeleccion != 0);
        mainMenu.focus(1);
       menu_lcd.redraw();

Not working, now its show:

Reloj_Nico_v2.ino: In function 'void bklMenu()':
Reloj_Nico_v2:537: error: 'mainMenu' was not declared in this scope
'mainMenu' was not declared in this scope

@neu-rah
Copy link
Owner

neu-rah commented Sep 25, 2015

well you get that variable by declaring

MENU(mainMenu,"Main menu", ...

and it should be a global variable, no?
I mean the menu definition is NOT inside a function, right?

@nicodonte
Copy link

I found my error, I declare MENU before void setup() but after my bklMenu() routine.

I move all the MENU declaration to the top of the project.

Now it works flawless!!! Thank you so much for your help!!
Regards,
Nico.

@neu-rah
Copy link
Owner

neu-rah commented Sep 25, 2015

great!
sory it took so long...

@graziano71
Copy link
Author

Hi! Thanks for the precious update, the new functions works perfectly!

Here is my code for the "Button" function, meant to let the Menu appear when keeping it pressed for more than 2 seconds:

void Button(){

Serial.println(Counter-Timer1);
current = digitalRead(encBtn); // Read Encoder Button Status
if (current == LOW && previous == HIGH){ // Check Encoder Button changes status
previous = LOW;
Timer1 = millis(); //Takes note of the times of status change
}

if (current == LOW) { //Check Encoder button being kept pressed
Counter = millis(); //Counts ms of button kept pressed
} else if (current == HIGH){ // Reset Counters if button is released in the meantime
Counter=Timer1;
previous=HIGH;
}
if ((Counter - Timer1)>=2000) { //CHeck if button is pressed for more than 2sec
MenuActive=1; // Variable used in Loop to validate the menu call
GoOut=0; // Reset the variable used to exit menu
lcd.drawn=NULL; // Redraw the menu
mainMenu.focus(0); //Bring the menu focus to first position
}

if (current == HIGH){ // reset the counter if the button is not pressed
Counter = Timer1;
}

}

@neu-rah
Copy link
Owner

neu-rah commented Sep 29, 2015

thanks for sharing 👍

@nicodonte
Copy link

Hi, could you make an example of:
menuNode::activeMenu;
menuNode::activeMenu->sel;

I can't figure out how to use it.

@graziano71
Copy link
Author

Hello!

I noticed something strange happening when the menu is deactivated. If the encoder is rotated, then when the menu is re-called the encoder stream is executed in the menu. This is happening also if the menu is "focused" on a specific position through your last function. How to avoid that the encoder rotation is buffered by the encoder library? Thanks!

@neu-rah
Copy link
Owner

neu-rah commented Oct 1, 2015

yes, it should be like so as it is interrupt driven...
we can avoid that by calling flush() on the input stream...

enc.flush();//flush encoder position

not sure if on the quad encoder stream or on composed stream.

allIn.flush();//flush all input streams by consuming the input and ignore it

still we should have deactivated (ignored) input while the menu is suspended.
the quad encoder stream is storing only a position, so it can't become full, and the current keyboard stream is a fake stream it does not buffer input, only returns current button status.
But when using the menu over a serial terminal then buffers can get full.

@graziano71
Copy link
Author

Clear, thanks!

I tried with

enc.flush();

and it works! :-) The Encoder stream is now ignored!

Thanks!!

Graziano

@neu-rah
Copy link
Owner

neu-rah commented Oct 1, 2015

menuNode::activeMenu; is a static member of class menuNode (base for all menus and fields)
it means (static) that there is only one definition of it for all menus. This works like a global variable of the class.
So no matter from what menu or field you read/write it, the same value is affected, in fact for being a static member you can access it with a menu or field (menuNode::activeMenu)
The value contained in it is a pointer to the currect active menu (the one that will receive the actions, and do output) or NULL if no menu is focused, on the last case the invoked menu will receive the input (input processing always start with a menu/field, normally mainMenu)

menuNode::activeMenu->sel; refers tho the current hilited element on the current focused menu/field

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants