Go Down

Topic: Enter a menu mode with a long press (Read 2065 times) previous topic - next topic

spleen

Hi all,

Im trying to write some code that will allow me to enter a menu mode by using a long press with a button.

What i'd like to happen is, when i hold a button down for, say, 3 seconds, a menu mode will be entered that then allows you to use short button presses on the same button to scroll through the pages. Another 3 second button push will then take it back to normal mode.

Code: [Select]
/*
*  Press & Hold program
*/

// Parameters
int HOLD_DELAY = 3000;    // Sets the hold delay of switch for LED state change

int ledPin     = 13;      // LED is connected to pin x
int switchPin  = 6;      // Switch is connected to pin x

// In-Program Variables
int menuState = 0;
int lastmenuState = 0;
unsigned long start_hold;
int sw_state;
int sw_laststate;

// Setup
void setup() {
pinMode(switchPin, INPUT);    // Set the switch pin as input
digitalWrite(switchPin, HIGH);
Serial.begin(9600);
}

// Loop
void loop(){
sw_state = digitalRead(switchPin);             // read input value

if (sw_state == LOW && sw_laststate == HIGH){  // for button pressing
start_hold = millis();                       // mark the time
}

if ((sw_state == LOW && sw_laststate == LOW) && ((millis() - start_hold) >= HOLD_DELAY)){
menuState ++;
}
lastmenuState = menuState;
if(menuState > 1){
  menuState = 0;
}
if(menuState == 1){
  Serial.println("Menu Mode");
  delay(200);}
 
  else
 
  Serial.println("Clock Mode");
  delay(200);
}


This is what i have so far. I haven't yet included the short button presses for scrolling, at this stage i'm just trying to enter menu mode.

At the moment i think it needs some debouncing somewhere because its quite erratic and also doesnt always wait the full 3 seconds before changing modes.

I am still very new to programming but i'm slowly learning.

Any pointers much appreciated!


funkyguy4000

Oh wow, this would be really nice to know.

I'm bumping this.
Accelerate to 88 miles per hour.

spleen

Thanks Emarref,

They are all very close to what i want it to do but still not quite right.

This is the code i'm using at the moment. Still pinched off Mr Jeff Saltzman but slightly modified

Code: [Select]
/* Click and Press+Hold Test Sketch
By Jeff Saltzman
To keep input interfaces simple, I want to use a single button to:
1) click (fast press and release) for regular button use, and
2) press and hold to enter a configuration mode.
*/

#define buttonPin 6 // analog input pin to use as a digital input
#define ledPin1 12 // digital output pin for LED 1 indicator
#define ledPin2 13 // digital output pin for LED 2 indicator

#define debounce 20 // ms debounce period to prevent flickering when pressing or releasing the button
#define holdTime 1000 // ms hold period: how long to wait for press+hold event

// Button variables
int buttonVal = 0; // value read from button
int buttonLast = 0; // buffered value of the button's previous state
long btnDnTime; // time the button was pressed down
long btnUpTime; // time the button was released
boolean ignoreUp = false; // whether to ignore the button release because the click+hold was triggered
boolean menuMode = false;

// LED variables
boolean ledVal2 = false; // state of LED 2

//=================================================


void setup()
{

// Set button input pin
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH );

// Set LED output pins
pinMode(ledPin1, OUTPUT);
digitalWrite(ledPin1, LOW);
pinMode(ledPin2, OUTPUT);
digitalWrite(ledPin2, ledVal2);


}


//=================================================


void loop()
{

// Read the state of the button
buttonVal = digitalRead(buttonPin);
delay(10);

// Test for button pressed and store the down time
if (buttonVal == LOW && buttonLast == HIGH && (millis() - btnUpTime) > long(debounce))
{
btnDnTime = millis();
}

// Test for button release and store the up time
if (buttonVal == HIGH && buttonLast == LOW && (millis() - btnDnTime) > long(debounce))
{
if (ignoreUp == false) event1();
else ignoreUp = false;
btnUpTime = millis();
}

// Test for button held down for longer than the hold time
if (buttonVal == LOW && (millis() - btnDnTime) > long(holdTime))
{
event2();
ignoreUp = true;
btnDnTime = millis();
}

buttonLast = buttonVal;

}


//=================================================
// Events to trigger by click and press+hold

void event1()
{
digitalWrite(ledPin1, HIGH);
delay(50);
digitalWrite(ledPin1, LOW);
}

void event2()
{
ledVal2 = !ledVal2;
digitalWrite(ledPin2, ledVal2);
}


I modified it slightly so that event 1 simply flashes the led on briefly instead of cycling between on and off. More just for prettiness than functionality. I also added a debounce delay after the button is pressed at the start of the loop because the button was a bit erratic.

What i really want is for the same button that flashes the led momentarily to flash a different led while event 2 is active. i.e. pressing the button briefly will have one function but after pressing and holding the same button to activate a menu mode, a brief press of the same button will now have a different function.

I added a "boolean menuMode = false;" statement during the intial declarations with the intention of having it go "true" after the long press. Should i be making a 3rd event that states that when menuMode = true, the button has a different function to when it is false? If so, I just have no idea how to go about doing that.

Can anyone point me in the right direction?

PaulS

The code you first posted only responds when there is a transition from not pressed to pressed. What you need to do is record when the switch is pressed (the transition is from not pressed to pressed) and when it is released (the transition is from pressed to not pressed).

Then, the difference in times will defined whether it was a short press or a long press. It is easy to determine what to do then, based on the pressed time.

Putting each { on its own line, so it stands out, and using Tools + auto format, would make your code less difficult to read.

Variable names like currState and prevState imply a lot more than buttonVal and buttonLast, too.

spleen

Thank you Paul

Ill Have another look in the morning

spleen

#6
May 06, 2012, 10:55 pm Last Edit: May 06, 2012, 11:53 pm by spleen Reason: 1
So i've had some mild success this morning. The code now works more or less how i want it to.

A single button now has one function in normal mode or you can hold the button down for 1 second and it will enter menu mode. While in menu mode the button now has a completely different function!

SUCCESS!

Just a few minor niggles. If i hold the button down continuously, it just cycles between normal mode and menu mode every second. How do i stop this?

Also, when entering menu mode, it gives one press of button 2 as i release the button from the long hold but not as i enter back into normal mode???? Why is this and how do i stop it? The idea is that once in menu mode, the same button will be used to cycle through different pages in the menu. Obviously if button 2 is pressed every time i enter menu mode then it will immediately cycle to page 2.

Code: [Select]

#define buttonPin 6                           // analog input pin to use as a digital input
#define ledPin1 12                            // digital output pin for LED 1 indicator
#define ledPin2 13                            // digital output pin for LED 2 indicator

#define debounce 20                           // ms debounce period to prevent flickering when pressing or releasing the button
#define holdTime 1000                         // ms hold period: how long to wait for press+hold event

// Button variables
int buttonVal = 0;                            // value read from button
int buttonLast = 0;                           // buffered value of the button's previous state
long btnDnTime;                               // time the button was pressed down
long btnUpTime;                               // time the button was released
boolean ignoreUp = false;                     // whether to ignore the button release because the click+hold was triggered
boolean menuMode = false;                     // whether menu mode has been activated or not


//=================================================


void setup()
{

 // Set button input pin
 pinMode(buttonPin, INPUT);
 digitalWrite(buttonPin, HIGH );

 // Set LED output pins
 pinMode(ledPin1, OUTPUT);
 digitalWrite(ledPin1, LOW);
 pinMode(ledPin2, OUTPUT);
 digitalWrite(ledPin2, LOW);
 Serial.begin(9600);

}


//=================================================


void loop()
{

 // Read the state of the button
 buttonVal = digitalRead(buttonPin);
 delay(10);

 // Test for button pressed and store the down time
 if (buttonVal == LOW && buttonLast == HIGH && (millis() - btnUpTime) > debounce)
 {
   btnDnTime = millis();
 }

 // Test for button release and store the up time
 if (buttonVal == HIGH && buttonLast == LOW && (millis() - btnDnTime) > debounce)
 {
   if (ignoreUp == false && menuMode == false) event1();
   else ignoreUp = false;
   btnUpTime = millis();
 }

 // Test for button held down for longer than the hold time
 if (buttonVal == LOW && menuMode == false && (millis() - btnDnTime) > long(holdTime))
 {
   Serial.println("Menu Mode");
   ignoreUp = true;
   btnDnTime = millis();
   menuMode = true;
   digitalWrite(ledPin2, HIGH);
 }
 if (buttonVal == HIGH && buttonLast == LOW && (millis() - btnDnTime) > debounce)
 {
   if (ignoreUp == false && menuMode == true) event2();
   else ignoreUp = false;
   btnUpTime = millis();
 }
 if (buttonVal == LOW && menuMode == true && (millis() - btnDnTime) > long(holdTime))
 {
   Serial.println("Normal Mode");
   ignoreUp = true;
   btnDnTime = millis();
   menuMode = false;
   digitalWrite(ledPin2, LOW);
 }


 buttonLast = buttonVal;

}


//=================================================
// Events to trigger by click and press+hold

void event1()
{
 digitalWrite(ledPin1, HIGH);
 Serial.println("button 1");
 delay(50);
 digitalWrite(ledPin1, LOW);
}

void event2()
{
 digitalWrite(ledPin1, HIGH);
 Serial.println("button 2");
 delay(50);
 digitalWrite(ledPin1, LOW);
}



funkyguy4000

Accelerate to 88 miles per hour.

spleen

For the most part yes. It is a work in progress. It still has bugs as per my previous entry.

Still looking for help with those bugs!

funkyguy4000

Accelerate to 88 miles per hour.

spleen

Quote
Just a few minor niggles. If i hold the button down continuously, it just cycles between normal mode and menu mode every second. How do i stop this?

Also, when entering menu mode, it gives one press of button 2 as i release the button from the long hold but not as i enter back into normal mode???? Why is this and how do i stop it? The idea is that once in menu mode, the same button will be used to cycle through different pages in the menu. Obviously if button 2 is pressed every time i enter menu mode then it will immediately cycle to page 2.


i just realised i had written the bugs wrong in my previous entry so i've edited them and included them above.

Cheers

spleen

Ive also now got it counting up through the pages while in menu mode.

Still can't solve the problem of the automatic button press immediately after entering menu mode though.

=(

Go Up