[Solved]Adding a Menu, Jump between screens with 3 Push Buttons Monitor

Hi.

Ive been building this battery monitor and has it working and running well. PCB’s printed and a few out for testing.

For now, the code is such that battery capacity, Low volts, High Volts and LCD brightness has to be entered into the code part.
I would like to move it out of the code and into a simple menu.
The menu option should be visible during the first 5 seconds after a reset and entered into if a button is pushed during these 5 seconds.
Secondly, once inside the menu i would like to be able to jump inbetween the parameters with that same menu button which is conected to pin9.
On a specific item of the menu such as “battery capacity” the other 2 buttons which are on A4 and A5 should be able to add 1 or substract one from these values. When Menu is then pressed it should save the value into the EEPROM and move to the next menu item until it reaches the end of the menu.
Once it goes out the setup() routine and completes the menu it should run in the main code.
Currently the main code has the menu button in such a way that if its pushed it jumpes between the different screens on the LCD. All i want to add here is that if the menu button is pushed, it should jump to next screen(as now) and if its held down for longer than about 2s it should reset the value of that screen.

Examples of values to be reset would be Max volts, Max Amps in, Max Amps out, AH in, Ah out.

What would be the most feasable way to go forward provided that I dont want to change hardware and having the code working really well as below attached?

Any help or advice would be appreciated.

#include <LiquidCrystal.h>
 

//______________________________________________________________________________________
float capacity = 7; //This value of 7aH must be changed for the size battery you have.  |
float chargeefficiency =0.85; //This value can be changed based on type of battery/charger|
float lowvolts = 11.3;  //Pick the volts that relay should switch off
float highvolts = 12.2;  //Pick the volts that the relay should switch on
int backlightbrightness = 70;//0 backlight off to 255 Backlight at its brightest
//**************************************************************************************|
*************************************************************************************|

int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

long previousMillis = 0;        
long interval = 500;           // interval at which to update screens
unsigned long currentMillis =0;



//==========================================================================================
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);  //dont worry about this, LCD stuff
void setup() {
    pinMode(menubuttonPin, INPUT);
    pinMode(relaypin, OUTPUT);
    pinMode(lcdbacklightpin,OUTPUT);
    digitalWrite(relaypin,HIGH);
    digitalWrite(lcdbacklightpin,HIGH);
    relaycondition = true;
    lcd.begin(16, 2);             //This is the first bit to be displayed when the unit is 
                                                //this will show which code version is loaded
    tone(buzzer,500,1500);            //compare it with the one on the forum
    delay(5000);
    lcd.clear();
    lcd.setCursor(2,0);
    lcd.print("CHARGE = C ");   //powered up. It shows if you have the size battery 
    lcd.setCursor(2,1);           //correct in the top line of the code.
    lcd.print("LOAD   = L"); 
    delay(5000);    
    lcd.clear();
    
}
//------------------------------------Main Program-------------------------------------
void loop() {
 
  
  analogWrite(lcdbacklightpin,backlightbrightness);
  volts = getvolts(); 
  ampin = getampin(); 
  ampout = getampout();
  percent = getcalcpercentage(volts);
  amphoursleftreading = getamphoursleft(percent);
   
  if(millis()>50000)  //Allow voltage to settle on startup before looking to switch relay off
  {
  dorelay(volts);
  }
  
     if (buttonState != lastButtonState)     // compare the buttonState to its previous state
        { 
          lcd.setCursor(0,1);  
          lcd.print("                ");  
            delay(50);               // if the state has changed, increment the counter
       if (buttonState == HIGH)                                    
         {
           buttonPushCounter++;     // if the current state is HIGH then the button changed
              if(buttonPushCounter==8)
              {
                 buttonPushCounter=0;
              }
         } 
        }
      lastButtonState = buttonState; // save the current state as the last state, 
      
      
      
      currentMillis = millis();
       if(currentMillis - previousMillis > interval) 
       {
           previousMillis = currentMillis; //for next time through the loop  
                                              
           lcd.setCursor(0, 0);                  //Start of the first line of the LCD
           lcd.print(volts,2);
           lcd.print("v");
  
              if(percent<20)        {
                 lcd.print(" LOW            ");   //This is when battery is low
                 tone(buzzer,1500,200);
                  
                 }      
                 else if(percent>100){
                 lcd.print(" CHARGING    ");  //This will tell you if you are charging the battery
                 }
                 else
                  {
                   lcd.print("/");
                   lcd.print(percent,0);
                  lcd.print("%/");
                  }
        
          lcd.print(amphoursleftreading,0);
          lcd.print("Ah      ");             //End of the first line of the LCD
          lcd.print(buttonPushCounter);
  
             lcd.setCursor(0,1);          //Start of the second line of the LCD
               if(buttonPushCounter==0)
               {
                  lcd.print("C ");
                  lcd.print(ampin,1);
                  lcd.print("A ");
                  lcd.print("L ");
                  lcd.print(ampout,1);
                  lcd.print("A ");
               }
                 if(buttonPushCounter==1)
                 {
                 lcd.print("C ");
                 lcd.print(amphoursinreading,1);
                 lcd.print("Ah ");
                 lcd.print("L ");
                 lcd.print(amphoursoutreading,1);
                 lcd.print("Ah  ");
                 }
                  if(buttonPushCounter==2)
                   {
                   lcd.print("Est Hours ");
                   lcd.print(timeah);
                   lcd.print("      ");
                   }
                     if(buttonPushCounter==3)
                     {
                     lcd.print("Max Volts ");
                     lcd.print(maxvolts,2);
                     lcd.print("v  ");
                     }
                       if(buttonPushCounter==4)
                       {
                        lcd.print("Max A in ");
                        lcd.print(maxampin,1);
                        lcd.print("A    ");
                       }
                           if(buttonPushCounter==5)
                           {
                            lcd.print("Max A out ");
                            lcd.print(maxampout,1);
                            lcd.print("A   ");
                           }
                              if(buttonPushCounter==6)
                               {
                                lcd.print("Balance ");
                                  if(amphoursbalance>0)
                                  {
                                  lcd.print("+");
                                  }
                                lcd.print(amphoursbalance,1);
                                lcd.print("Ah  ");
                               }
                                   if(buttonPushCounter == 7)
                                   {  
                                     if(relaycondition ==true)
                                     {
                                       lcd.print("Relay On    ");
                                     }
                                     else
                                     if(relaycondition ==false)
                                     {
                                       lcd.print("Relay Off   ");
                                     }
                                   }
             }
 }
    

//----------------------------New Subroutines---------------------------
//Removed due to forum count

Its a open project ive been doing for us here in SA on the 4x4 Comunity which will be used during camping. I had to cut the code much smaller due to forum count size but here is the link to our local site for some background.

http://www.4x4community.co.za/forum/showthread.php?t=190968

Im really not looking for someone to write the code.

Just a simple explanation on how this type if thing would work.

Ive gone through most of the threads, but a menu driven code wont work as i need to remain within the loop to be able to constantly read and display the values in the LCD.

This video shows how well its already working.

https://m.youtube.com/watch?v=FTs_ATZAL-M

but a menu driven code wont work as i need to remain within the loop to be able to constantly read and display the values in the LCD.

How do you intend to display a menu and "the values" at the same time? When you answer that question, I think the "how to" will become obvious.

Hi Paul.

The intention is not to enter the menu while the code is running.

During the initial startup/reset prior to entering the loop i want to give the option of entering the menu by pressing button 1.
This should be an option during say the first 10sec. If button 1 is not pressed during those 10s the loop should be entered.

Inside the loop, button 1 is then used to jump between the 8 different screens. For now i used buttonstate()

if (buttonState != lastButtonState)     // compare the buttonState to its previous state
  { 
    lcd.setCursor(0,1);  
    lcd.print("                ");  
    delay(50);               // if the state has changed, increment the counter
    if (buttonState == HIGH)                                    
    {
      buttonPushCounter++;     // if the current state is HIGH then the button changed
      if(buttonPushCounter==8)
      {
        buttonPushCounter=0;
      }

What i want to add is that if we are for instance on the screen displaying max volts and hold the button 1 in for a period longer than 2s it should reset max value back to 0. Now the values im fine with, but how do I process that button push counter together with button hold provided that there is about 8 different screens being displayed.

if(buttonPushCounter==3)
    {
      lcd.print("Max Volts ");
      lcd.print(maxvolts,2);
      lcd.print("v  ");
    }

Now going back to startup:

ON startup if button1 is pushed during the first 10s, the menu should be entered.
The menu should allow setting up the battery size so button 2 and button 3 needs to change value up and down and once button 1 is pushed to move to the next item to be set, the value of battery size will then be saved in EEPROM.

So once ive jumped thorugh
battery capacity
low volt alarm value
low volt relay cutoff
screen brightness

it should exit the menu and jump to the loop and start with the execution of the program until either a reset or a power off.

Does this make a bit more sense?

During the initial startup/reset prior to entering the loop i want to give the option of entering the menu by pressing button 1.
This should be an option during say the first 10sec. If button 1 is not pressed during those 10s the loop should be entered.

So, what’s the problem? Spinning in setup() waiting for a switch press (I assume that you are not really pushing buttons, except maybe hot ones) or for 10 seconds to elapse is trivial.

What i want to add is that if we are for instance on the screen displaying max volts and hold the button 1 in for a period longer than 2s it should reset max value back to 0.

So, you record when the switch BECOMES pressed, and periodically, using a while loop, check to see if the switch is still pressed and if 2 or more seconds has elapsed. If the time elapses, do one thing. If the switch is released, do something else.

ON startup if button1 is pushed during the first 10s, the menu should be entered.

You need to understand what a program is capable of. You can not “enter a menu”. You can call a function that displays a menu and deals with switch presses while it is executing.

The menu should allow setting up the battery size so button 2 and button 3 needs to change value up and down and once button 1 is pushed to move to the next item to be set, the value of battery size will then be saved in EEPROM.

“The menu” won’t do that. The code will.

How will you exit the menu function, if it gets called?

Does this make a bit more sense?

To who? To me? I didn’t have a problem before.

Ok,

So thanks for that. I had a totally different line of thought. I wrote something quickly, just to test the concept.
Is this what you had explained for the menu??
Or does the concept of my attempt seem accurate?

void loop() {
  
  do
{
  button1State = digitalRead(button1Pin);
     if (button1State != lastButton1State) 
       {
        if (button1State == HIGH&&stayinmenu!=1) 
        {
        menu();
        }
       lastButton1State = button1State;
       }
} while(millis < 10000||stayinmenu=1)

 if (millis > 10000&&stayinmenu==0)
{
  //insert the rest of code in here to do what it should
}  

}

int menu()
{
  stayinmenu = 1;
  while(stayinmenu==1)
  {
    button1menuState = digitalRead(button1Pin);
      if (button1menuState != lastButton1menuState) 
      {
        if (buttonState == HIGH) {
            button1mMenuCounter++;
                                 } 
      }
       lastButton1menuState = button1menuState;
       
if(button1MenuCounter == 1)
{    
  MenuUpButton = digitalRead(button2Pin);
  MenuDownButton = digitalRead(button3Pin);
      if (MenuUpButtonState != MenuUpLastButtonState) 
      {
        if (MenuUpButtonState == HIGH) {
            volts=volts+0.1;
                                 } 
      }
        MenuUpLastButtonState = MenuUpButtonState;
      
       if (MenuDownButtonState != MenuDownLastButtonState) 
      {
        if (MenuDownButtonState == HIGH) {
            volts=volts-0.1;
                                 } 
      }
       MenuDownLastButtonState = MenuDownButtonState;
  setCursor(0,0);
  println("Volts");
  println(volts,2);
}
  if(button1MenuCounter == 2)
{    
  MenuUpButton = digitalRead(button2Pin);
  MenuDownButton = digitalRead(button3Pin);
      if (MenuUpButtonState != MenuUpLastButtonState) 
      {
        if (MenuUpButtonState == HIGH) {
            LCDBrightness=LCDBrightness+1;
                                 } 
      }
        MenuUpLastButtonState = MenuUpButtonState;
      
       if (MenuDownButtonState != MenuDownLastButtonState) 
      {
        if (MenuDownButtonState == HIGH) {
            LCDBrightness=LCDBrightness-1;
                                 } 
      }
       MenuDownLastButtonState = MenuDownButtonState;
  setCursor(0,0);
  println("LCD");
  println(LCDBrightness);
}
  if(button1MenuCounter == 3
  {
    stayinmenu = 0;
  }
  
  }
}

i
    
   [ /code]

Is this what you had explained for the menu??

Without knowing how stayinmenu is defined or initially valued, and what menu does to it, it’s hard to say for sure. But, yeah, something along those lines.

Except that I would have put that do/while in setup() (and not used a do/while - I hate them; they are rarely the right approach, while while is more often what is needed).

Thanks.

I used your advice and have it running as I want now.
in setup I do the following:

 while(millis() < 10000)
  {
    button1state = digitalRead(menubuttonPin);
    delay(20);
    lcd.setCursor(0,0);
    lcd.print("  Press Button ");
    lcd.setCursor(0,1);
    lcd.print("In ");
    lcd.print(10-(millis()/1000));
    lcd.print("s ");
    lcd.setCursor(7,1);
    lcd.print("to Setup ");
    if(button1state == HIGH)
    {
      delay(200);
      menu();


    } 
  }

And then in Menu the following happens:

 int menu()
{ 
  lcd.clear();

  while(buttonPushCounter<7)
  {
    buttonState = digitalRead(menubuttonPin);
    upbutton = digitalRead(upbuttonpin);
    downbutton = digitalRead(downbuttonpin);
    delay(30);


    if (buttonState != lastButtonState)     // compare the buttonState to its previous state
    { 
      lcd.clear();
      delay(100);               // if the state has changed, increment the counter
      if (buttonState == HIGH)                                    
      {
        delay(100);
        buttonPushCounter++;     // if the current state is HIGH then the button changed

      } 
    }
    lastButtonState = buttonState; // save the current state as the last state, 


    if(buttonPushCounter==0)
    {
      lcd.setCursor(0,0);
      lcd.print("******Menu******");   //powered up. It shows if you have the size battery 
      lcd.setCursor(0,1);           //correct in the top line of the code.
      lcd.print(" Firmware V7.6"); 
    }
    if(buttonPushCounter==1)

So in total it looks like this: