Back, Enter, and Cancel Buttons using Arduino Mega

hi all,

I am trying to create a simple menu interface for a project that I am working on. The user will go through a series of questions to enter information. Once they have entered numbers from a keypad they will press the enter button to move on to the next menu screen. The problem I am facing is how to implement cancel and back buttons in my code. I have tried the attachInterrupt function but that will not restart the void loop, it just continues from where it was interrupted. I have not looked into coding for the back button yet but I'm assuming it will be very similar to the cancel button code. I would like to avoid writing if statements for each line of code inside the void loop as I have hundreds of lines of code inside of it. Thanks!

Perhaps if you show what you have so far? If it is nice and modular you should be able to cancel without too many problems.

Since the code is way too long I just pasted the void loop below which should suffice:

void loop()
{
  welcome();  // System Start Up
  first_menu();  // First Menu Selection
        
/************************************
       Sunlight Data Entry
*************************************/
  sunlight_brightness_range_select();  // Gets and stores sunlight brightness range
  sunlight_manual_range();  // Gets and stores sunlight manual range
  sunlight_preselect_range();  // Gets and stores sunlight pre-select range

/************************************
       Temperature Data Entry
*************************************/
  temperature_manual_range();  // Gets and stores temperature manual range
  temperature_preselect_range();  // Gets and stores temperature pre-select range

/***********************************
       Humidity Data Entry
************************************/ 
  humidity_manual_range();  // Gets and stores humidity manual range
  humidity_preselect_range();  // Gets and stores humidity pre-select range

/***********************************
       Soil Saturation Range
************************************/
  soil_moisture_saturation_range();  // Gets and stores soil saturation range
    
/***********************************************

        Begin Monitoring Process
        
***********************************************/
  lcd.clear();
  lcd.print("Starting Monitoring ");
  lcd.setCursor(0,1);
  lcd.print("Process...");
  
  /*TEST*/ Serial.print("\n\nStarting Monitoring Process...\n\n");
  
  delay(2000);
while(1)
{
  /* Read Sensors */
  read_soil_moisture_sensor();
  read_sunlight_sensor();
  read_temperature_sensor();
  //read_humidity_sensor();
  
  /* Check conditions */
  soil_moisure_condition_check();
  temperature_sensor_condition_check();
  sunlight_brightness_check();
  
  // Send Condition Status to Local Network
  pachube_in_out();
  
  Serial.print("\nSoil Moisture is ");
  Serial.print(soil_moisture_network);
  
  delay(1000);
}    
          

}  // void loop bracket

The code pretty much calls a bunch of functions then enters the while loop to continuously monitor the sensors.

Moderator edit: [code] ... [/code] tags added. (Nick Gammon)

I suppose it's an LCD we're talking about...

For LCD interfacing with menus I think that the best option is to create a state machine. That way the Cancel button would leave you in the same state as you were but without any action pending and Back would make you move to a previous state.

Post the code and we'll look into it.

Yes I am working with a Basic 20x4 LCD from Sparkfun.

That's fantastic that it is already this modular. So fixing your problem is now easy. Instead of making it linear like that you need some sort of "state machine" as bubulindo said.

Something along these lines:

int state = 0;

void loop ()
  {
    
  switch (state)
    {
    case 0:
      sunlight_brightness_range_select();  // Gets and stores sunlight brightness range
      break;

    case 1:
      sunlight_manual_range();  // Gets and stores sunlight manual range
      break;

    case 2:
      sunlight_preselect_range();  // Gets and stores sunlight pre-select range
      break;
      
    // and so on ...
    
    } // end of switch
    
  } // end of loop

So the first time through loop we end up in sunlight_brightness_range_select, same as before.

Inside each function (like sunlight_brightness_range_select) we make a decision:

  • Advance to next question (normal behaviour): state++;
  • Go back a question (back button): state--;
  • Cancel button: state = 0;

So next time through loop we show the correct question.

Meanwhile, in loop, once state hits the maximum (all questions answered) then you can start taking readings.

Ok great! I will try that out and let you know how it goes.

Ok so I know I am close. I'm assuming that my code works but there may be a problem with my board. Every time I run the code it works fine until I go to grab the pushbutton, in which the program starts to spaz out. I modified my code to focus on getting the button to work in general:

#include <LiquidCrystal.h>  // LCD Library

LiquidCrystal lcd(32,30,28,26,24,22);// RS, E, D4-D7  initialize the library with the numbers of the interface pins


int enter_button;
int state = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(37,INPUT);
  lcd.begin(20,4);
}

void loop()
{
  confirmation_screen();
}

void confirmation_screen()
{
 lcd.clear();
 lcd.print("Press enter to");
 lcd.setCursor(0,1);
 lcd.print("confirm choice.");
 
 while(1)
 { 
   Serial.print("\n1");
   
   enter_button = digitalRead(37);

   if(enter_button == HIGH)
   {
     state++;
     Serial.print("\nState = ");
     Serial.print(state);
     delay(1000);
   }
 }
}

This code basically waits for the button to be pressed and then increments the state variable once pressed. There is also some debugging code in there to print to the serial port. I also tried replacing the button with a single wire connected to pin 37 and nothing else. As soon as I touch the wire or move it slightly the code thinks the button has been pressed and increments the state variable even though the wire is not even connected to anything but pin 37.

Moderator edit: [code] ... [/code] tags added. Again. (Nick Gammon)

bubulindo:
I suppose it's an LCD we're talking about...

For LCD interfacing with menus I think that the best option is to create a state machine. That way the Cancel button would leave you in the same state as you were but without any action pending and Back would make you move to a previous state.

Post the code and we'll look into it.

Completely agree. You will also be able to do fancier things with states like jump over 3 questions (say only girls answer them and the user is a boy).

Completely wrong logic with buttons. You should not look for HIGH state but instead look for status change from LOW to HIGH, if that is understandable. Try some sample code you can find. Maybe just skip the part what you process buttons yourself and give in to a nicely written button library.

I followed the arduino tutorial on a pushbutton from this link http://arduino.cc/en/Tutorial/ButtonStateChange. I am still having problems with it increasing the state whenever i simply just touch any part of the pushbutton circuitry. Even when I actually push the button it doesn't seem to read that the button has been pressed. Here is my code below:

#include <LiquidCrystal.h> // LCD Library

LiquidCrystal lcd(32,30,28,26,24,22);

int enter_button;
int state = 0;

void setup()
{
Serial.begin(9600);
pinMode(37,INPUT);
}

void loop()
{
confirmation_screen();
}

void confirmation_screen()
{
lcd.clear();
lcd.print("Press enter to");
lcd.setCursor(0,1);
lcd.print("confirm choice.");

Serial.print("Ready");

while(1)
{
//Serial.print("\n1");
enter_button = digitalRead(37);

if(enter_button == HIGH)
{
state++;
Serial.print("\nState = ");
Serial.print(state);
delay(1000);
}
}
}

Please edit your post, select the code, and put it between [code] ... [/code] tags.

You can do that by hitting the # button above the posting area.

It's generally far easier to wire up a switch using the internal pullup resistor. Supply power to one side of the switch. Connect the other side to a digital pin. Turn on the pullup resistor, using digitalWrite(somePin, HIGH); in setup(), right after setting the pin mode to input.

Then, the switch will read HIGH when not pressed, and LOW when pressed.

It sounds like your switch is not wired quite right, and is causing a floating pin condition.

Read reply #9. Do you have any pull-up or pull-down resistors?

(edit) ninja'd by 16 seconds ...

PaulS,

I tried to do the digitalWrite suggestion but that still did not fix the problem.

I tried to do the digitalWrite suggestion but that still did not fix the problem.

Along with rewiring the switches? They are not independent actions.

I'm using the arduino mega and pins 37 and 36. The pushbutton is connected between pins 37 and 36 with 37 being the input and 36 being the output (using the digitalWrite function).

You don't need two pins for one button ...

(oops) hit Modify rather than Quote. Sorry. (Nick Gammon)

What about reply #9?

void confirmation_screen()
{

...

 while(1)
 {
   enter_button = digitalRead(37);

   if(enter_button == HIGH)
   {
     state++;
...
     delay(1000);
   }
 }
}

When are you expecting control to leave confirmation_screen?

I am trying to simply test if the button is working because right now it just increments the state variable without me pressing the button rather incrementing the state variable when i touch the wire or pick up the button. When I let the button rest on the table it is fine.

kdwalton:
I'm using the arduino mega and pins 37 and 36. The pushbutton is connected between pins 37 and 36 with 37 being the input and 36 being the output (using the digitalWrite function).

You don't need two pins for one button. Connect the button between one pin (eg. 37) and Gnd. Do this:

pinMode (37, INPUT);
digitalWrite (37, HIGH);

Now the pin is "weakly" pulled high. When you press it it goes low. So you test for LOW in your code.