Using 1 button for 4 functions.

Hello Guys,

I'm trying to write a code which should enable to use 1 button for 4 functions.

  1. If button was hold for 3 seconds. Turn off 7 segment indicator.
    2)If button was hold for 3 seconds and 7 segement indicator is off then turn on 7 segment indicator.
  2. If short press was detected Start countdown
    4)If short press was detected Stop countdown

How you would implement this kind of fuction?

I would use a state machine to determine when the button becomes pressed, save the current value of millis() and again when the button becomes released.

Subtract to determine how long the button was pressed.

There are really only two functions: long press toggles the indicator, short press toggles start/stop.

The state change detection example shows how to detect transitions like jremington mentions.

See this thread for instruction on using the state change detection with a active low switch and hints on debounce.

Welcome to the forum

Please read the "How to post". It is at the beginning of each sub forum. People here prefer to help you develop things yourself.

Looks like you have some clear idea what you want. Start converting it into a sketch. Post your code (use code tags) and then ask some specific questions. You will see you get plenty of suggestions of how to improve your code in no time. :slight_smile:

The Arduino IDE comes with a couple of examples that are a good source of inspiration for common tasks like buttons and timing.

File -> Examples -> 02.Digital

See One button for multiple functions

Thank you very much for providing answer to my question. It feels nice when you can get advices from more expirienced.

I will try to explain my problem more detailed.

I'm using MPR121 capacitive touch module, which in my opinion doesn't have much differnce talking about state detection.

It can have 2 states, either pressed or released. With 2 states it's quite hard for me to think the way how to make it work for 2 long presses (touches in this case) with the same lenght of hold of the button(3 seconds).

When I press and hold (touch) START/ STOP button after code is uploaded to arduino, 7 segment indicator is turned off, as it should.

When I press and hold the button for a second time (first time after 7 segment indicator was turned off) 7segment indicator is turned on. But If I press and and hold the button for a third time it will not turn off 7 segment indicator. I can press and hold as many times as I want, but 7 segment indicator won't be turned off. But if is press the button shortly and then again press and hold the button it will turn off 7 segment indictor.

And this is not even close to use a 1 button for ON/OFF PLAY/STOP.

Please ignore some declared variables as I have changed the code for many many times so there could be left one or two not variables which are not used in the main code. :confused:

//======include library's==========


#include "Arduino.h"
#include "SoftwareSerial.h"


#include <Wire.h>
#include "Adafruit_MPR121.h"
#include <ShiftRegister74HC595.h>


//======define pins for 7 segment display=========
#define SDI 5
#define SCLK 7
#define LOAD 6
#define DIGITS 2




// create shift register object (number of shift registers, data pin, clock pin, latch pin)
ShiftRegister74HC595 sr (DIGITS, SDI, SCLK, LOAD);
int value, digit1, digit2;
uint8_t  digits[] = {
  B11000000, //0
  B11111001, //1
  B10100100, //2
  B10110000, //3
  B10011001, //4
  B10010010, //5
  B10000010, //6
  B11111000, //7
  B10000000, //8
  B10010000 //9
};




//===== Stop button ==========


int lastTouchStateOFF = 0 ;
int  lastTouchStateSTOP = 0;
//const unsigned long pressedTime = 3000UL;  // milliseconds
unsigned long buttonStopTime;  //
int offButtonState = 0;
int flag1 = 1;
int flag2 = 0;
byte tapCounter;
byte longPressCounter;


//======for the countdown timer==========
const unsigned long debounceTime = 100;  // milliseconds
unsigned long currentTime;
int setTime = 15;
//unsigned long remainingTime;


//unsigned long firstTime;
unsigned long lastDebounceTime = 0;
//unsigned long firstTimeOn;


//long prev_secs_screenControl;
//unsigned long previousTime = 0;
//int counterRunning = 0;
//int plusTimer = 15;
int screenControl = 1;


unsigned long pressedTime = 0;
unsigned long releasedTime = 0;




int timediff = 0;
//======seven segment declaration==========
SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
Adafruit_MPR121 cap = Adafruit_MPR121();
long currtouched;


int currtouched10;


void setup () {


 mySoftwareSerial.begin(9600);
  Serial.begin(115200);
  //======MPR capacitive touch module==========
  while (!Serial) { // needed to keep leonardo/micro from starting too fast!
    delay(10);
  }


  Serial.println("Adafruit MPR121 Capacitive Touch sensor test");


  // Default address is 0x5A
  if (!cap.begin(0x5A)) {
    Serial.println("MPR121 not found, check wiring?");
    while (1);
  }
  Serial.println("MPR121 found!");




}






void loop() {


showNumber(setTime);
off();
}


void off() {
  currtouched10 = cap.touched() & (1 << 10);
  currentTime = millis();


  if (currtouched10 != lastTouchStateOFF) {
    lastDebounceTime = millis(); // save the time when button was pressed
    //Serial.println(currtouched10);
    //Serial.print("Lastdebounce");


  }


  if (currentTime - lastDebounceTime > 50UL) { // if button was pressed longer than 50ms it's a real press.


    if (currtouched10 != offButtonState) { // read the value of the button. If not equal to offButtonState value then write  currtouched10 value to variable offButtonState
      offButtonState = currtouched10;
      //Serial.print (offButtonState);
    }


  }




  //when switch is pressed
  if (offButtonState == 1024 &&  flag2 == 0) {


    flag1 = 0; //flag 1
    flag2 = 1; //flag 2
    pressedTime = millis(); //time from millis fn will be saved to firstTime var
    tapCounter++;
    Serial.println("Button pressed");
  }


  //when sw is released
  if (offButtonState == 0 && flag1 == 0)
  {
    releasedTime = millis(); //time from millis fn will be saved to releasetime var
    flag1 = 1; //flag 1
    flag2 = 0;  //flag 2


    Serial.println("Button realeased");


    timediff = releasedTime - pressedTime; //here we find the time gap between firstTime and releasedTime and stored to timediff var


  }


  if (offButtonState == 1024 && currentTime - pressedTime >= 100 ) 
  {
    if (currentTime - pressedTime >= 2500UL && screenControl == 1  )
    {
      screenControl = 0;


    } else if ( offButtonState == 1024 && timediff > 100 && screenControl == 1  )


    {
      Serial.println("Short press");




    }
  }
  if (offButtonState == 1024 && currentTime - pressedTime >= 90 )


    if (timediff >= 2500UL && currentTime - pressedTime >= 2500UL && screenControl == 0 )
    {


      screenControl = 1;


    }


 // tapCounter = 0;
  Serial.println(timediff);




  lastTouchStateOFF = currtouched10;
  //Serial.println(lastTouchStateOFF);// currtuoched value updated every loop








}


//======seven segment showing numbers ==========
void showNumber(int num)
{
  if (screenControl == 0) {
    sr.setAllHigh();
    


   
  }
  if (screenControl == 1) {
    digit2 = num % 10 ;
    digit1 = (num / 10) % 10 ;
    //Send them to 7 segment displays
    uint8_t numberToPrint[] = {digits[digit2], digits[digit1]};
    sr.setAll(numberToPrint);
    //Serial.println(screenControl);
  }
}

oswa90:
I'm trying to write a code which should enable to use 1 button for 4 functions.

  1. If button was hold for 3 seconds. Turn off 7 segment indicator.
    2)If button was hold for 3 seconds and 7 segement indicator is off then turn on 7 segment indicator.
  2. If short press was detected Start countdown
    4)If short press was detected Stop countdown

There are only two types of button-press here - a long one and a short one.

IMHO 3 seconds is much too long. Also keep in mind that if the user releases the button after 2.9 seconds it will probably be treated a a short press even though the user knows very well that it was not a short press. It would probably be better to time the short press so that anything longer than (say) 200 millisecs constitutes a long press.

The code in the this link illustrates how to do different types of button press.

...R

Robin2:
There are only two types of button-press here - a long one and a short one.

IMHO 3 seconds is much too long. Also keep in mind that if the user releases the button after 2.9 seconds it will probably be treated a a short press even though the user knows very well that it was not a short press. It would probably be better to time the short press so that anything longer than (say) 200 millisecs constitutes a long press.

The code in the this link illustrates how to do different types of button press.

...R

Hi Robin, thank you for your opinion and advice.

As you mentioned "There are only two types of button-press here - a long one and a short one. ".
Is it possible to perform 4 different actions (events) with only 2 states.
Only short press and long press can be used.

Idea is that user have to hold the button until 7segment indictor is off. User shouldn't release button untill 7 segment indictor is off. Time is started to count when button is preesed and it's counted until the event happens. In this case button is pressed for 3 seconds. And this would work without problem if I wouldn't need to make another action with the same button with the same hold time.

oswa90:
Hi Rob ert in

:wink:

fionning_macara:
:wink:

Thanks fionning_macara,
Mistake fixed. :roll_eyes:

oswa90:
Is it possible to perform 4 different actions (events) with only 2 states.

Yes, certainly.

But it might be easier to think about it if you describe it as two actions with one type of button press and two actions with the other type of button press.

And see Reply #2 - whichever type of button press is detected it should result in a change the state of the appropriate system.

...R

Robin2:
Yes, certainly.

But it might be easier to think about it if you describe it as two actions with one type of button press and two actions with the other type of button press.

And see Reply #2 - whichever type of button press is detected it should result in a change the state of the appropriate system.

...R

I have looked at the examples provided in the Reply #2.
Seems like state detection is working ok in my code.
I have another problem:
1)First time I‘m pressing START/STOP button until screen is turned OFF (3 seconds). ALL OK
2) Second time I‘m pressing START/STOP button until screen is turned ON (3 seconds). ALL OK
3)Third time I‘m pressing START/STOP button until screen is turned OFF (3 seconds). ALL OK
4)Fourth time I‘m pressing START/STOP button 7 seg. Indicator is turned on imdediatlly after button was pressed. NOT OK
I can not find the reason why it‘s happening. Not necessarily everything happens in the same way as in the example given. Sometimes I can make 8 on / off cycles properly and sometimes only 5.
For me it looks like something wrong with the line below. Or to be more exact with currentTime - pressedTime>=2500UL.

    if (offButtonState == 1024  && screenControl ==1 && currentTime - pressedTime >= 2500UL && tapCounter2 %2 ==0 ) // detect longpress when button is hold/ check if 7 seg indicator is on.
//======include library's==========

#include "Arduino.h"
#include "SoftwareSerial.h"
#include <Wire.h>
#include "Adafruit_MPR121.h"
#include <ShiftRegister74HC595.h>

//======define pins for 7 segment display=========
#define SDI 5
#define SCLK 7
#define LOAD 6
#define DIGITS 2


// create shift register object (number of shift registers, data pin, clock pin, latch pin)
ShiftRegister74HC595 sr (DIGITS, SDI, SCLK, LOAD);
int value, digit1, digit2;
uint8_t  digits[] = {
  B11000000, //0
  B11111001, //1
  B10100100, //2
  B10110000, //3
  B10011001, //4
  B10010010, //5
  B10000010, //6
  B11111000, //7
  B10000000, //8
  B10010000 //9
};


SoftwareSerial mySoftwareSerial(10, 11); // RX, TX


//===== Start button ==========

int lastTouchStateSTART = 0; // writes the last button state Pressed/ Not pressed
unsigned long buttonPressTime;
int startButtonWasPressed = 0;
int lastTouchStateOn = 0;

//confirm button state

//===== Stop button ==========

int lastTouchStateOFF = 0 ;
int  lastTouchStateSTOP = 0;

unsigned long buttonStopTime;  //
int offButtonState = 0;
int flag1 = 1;
int flag2 = 0;
int tapCounter = 0;
int tapCounter2 = 1;
int longPressCounter = 1;

//======for the countdown timer==========
const unsigned long debounceTime = 100;  // milliseconds

int setTime = 15;

unsigned long buttonHoldTime;  // milliseconds
unsigned long firstTime;
unsigned long currentTime = 0;
unsigned long firstTimeOn;
unsigned long previousTime = 0;
int counterRunning = 0;
int plusTimer = 15;
int screenControl = 1;

unsigned long pressedTime = 0;
unsigned long releasedTime = 0;
unsigned long lastDebounceTime = 0;

int timediff = 0;
//======seven segment declaration==========

Adafruit_MPR121 cap = Adafruit_MPR121();

int currtouched10;

void setup () {

  mySoftwareSerial.begin(9600);
  Serial.begin(115200);

  //======MPR capacitive touch module==========
  while (!Serial) { // needed to keep leonardo/micro from starting too fast!
    delay(10);
  }

  Serial.println("Adafruit MPR121 Capacitive Touch sensor test");

  // Default address is 0x5A
  if (!cap.begin(0x5A)) {
    Serial.println("MPR121 not found, check wiring?");
    while (1);
  }
  Serial.println("MPR121 found!");
}
void loop() {
  currentTime = millis();
  showNumber(setTime);
  off();
}
void off() {
  currtouched10 = cap.touched() & (1 << 10);
  currentTime = millis();
  if (currtouched10 == 1024 && lastTouchStateOFF == 0) {
    lastDebounceTime = millis(); // save the time when button was pressed
  }
  if (currentTime - lastDebounceTime >= 50UL) { // if button was pressed longer than 50ms it's a real press.

    if (currtouched10 != offButtonState) { // read the value of the button. If not equal to offButtonState value then write  currtouched10 value to variable offButtonState
      offButtonState = currtouched10;
      Serial.print("OffButtonState:");
      Serial.println (offButtonState);
    }
  }

  //when switch is pressed
  if (offButtonState == 1024 &&  flag2 == 0) {

    flag1 = 0; //flag 1
    flag2 = 1; //flag 2
    pressedTime = millis(); //time from millis fn will be saved to firstTime var
    tapCounter++;
    tapCounter2++;
    Serial.println("Button pressed");
  }

  //when sw is released
  if (offButtonState == 0 && flag1 == 0)
  {
    releasedTime = millis(); //time from millis fn will be saved to releasetime var
    flag1 = 1; //flag 1
    flag2 = 0;  //flag 2

    Serial.println("Button realeased");

    timediff = releasedTime - pressedTime; //here we find the time gap between firstTime and releasedTime and stored to timediff var

  }

  //==================Press and hold========================
  if (offButtonState == 1024  && currentTime - pressedTime >= 60) //waiting time when button pressed)
  {
    Serial.println("First stage ON:");

    if (offButtonState == 1024  && screenControl == 1 && currentTime - pressedTime >= 2500UL && tapCounter2 % 2 == 0 ) // detect longpress when button is hold/ check if 7 seg indicator is on.
    {

      screenControl = 0;
      //pressedTime = currentTime;
      Serial.print("ONNNNNNNNNNNNNNNNNNNNNNNNNNNNN:");
      Serial.println(longPressCounter);
    }
  }

  if (offButtonState == 1024  && currentTime - pressedTime >= 60 )
  {
    Serial.println("First stage OF:");
    if (offButtonState == 1024 && screenControl == 0 && currentTime - pressedTime >= 2500UL && tapCounter % 2 == 0 )
    {
      screenControl = 1;

      Serial.print("OFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
      Serial.println(tapCounter);
    }
  }

  lastTouchStateOFF = currtouched10;

}
//====== Show minutes on the 7 segment display==========
void showNumber(int num)
{
  if (screenControl == 0) {
    sr.setAllHigh();
  }
  if (screenControl == 1) {
    digit2 = num % 10 ;
    digit1 = (num / 10) % 10 ;
    //Send them to 7 segment displays
    uint8_t numberToPrint[] = {digits[digit2], digits[digit1]};
    sr.setAll(numberToPrint);
  }
}

I appreciate every suggestion.

Please edit your post and post ALL the code.

But first, remove the extra blank lines and format the code using Ctrl-T in the Arduino editor, before copy/paste.

jremington:
Please edit your post and post ALL the code.

But first, remove the extra blank lines and format the code using Ctrl-T in the Arduino editor, before copy/paste.

Thanks for the comments. I have posted all the code.

I suggest you completely re-organise the code.

Create a function (let's call it checkButton() ) whose only purpose is to set the value of a variable (let's call it buttonPressType) to 'N' for no button press, 'S' for short button press and 'L' for long button press.

By putting it this into a function you can test it all on its own and get advice here with a very short program if it does not do what you want.

Then elsewhere in your program you can have functions to respond to the value in buttonPressType. Something like

void shortPressAction() {
   if (buttonPressType == 'S') {
      // do the stuff for a short press
  }
}
void longPressAction
   if (buttonPressType == 'L') {
      // do the stuff for a long press
  }
}

This also means that the logic of what happens when a button press happens is separate from the code for detecting the press and the logic can also be tested on its own.

The code in loop() could then be

void loop() {
   checkButton();
   shortPressAction();
   longPressAction();
}

...R

I have reorganised the code as you suggested, and after reorganisation was able to correct the code and make it work.
THANK YOU.

void loop() {
  checkButton();
  shortPressActionSTART();
  shortPressActionSTOP();
  longPressActionOn();
  longPressActionOff();
  notPressAction ();
}


void shortPressActionSTART() {
  if (buttonPressType == 'S') {
    startButtonWasPressed = 1;
    Serial.println("START");
  }
}
void shortPressActionSTOP() {
  if (buttonPressType == 'P'  ) {
    startButtonWasPressed = 0;
    Serial.println("STOP");


  }
}


void longPressActionOn () {
  if (buttonPressType == 'O') {
    screenControl = 1;
  }
}
void longPressActionOff () {
  if (buttonPressType == 'F') {
    screenControl = 0;
    startButtonWasPressed = 0;


    // Serial.println("Off");
  }
}


void notPressAction () {
  if (buttonPressType == 'S' || buttonPressType == 'P' ) { // if any button was pressed perform actions which are assigned for the button and set buttonPressType to 'N'
    buttonPressType = 'N';
  }
}
//======Long/Short press====
void checkButton() {
  currtouched10 = cap.touched() & (1 << 10);
  currentTime = millis();
  if (currtouched10 == 1024 && lastCurrtouched10 == 0) {
    lastDebounceTime = millis(); // save the time when button was pressed
    if ((millis() - lastDebounceTime) >= debounceTime) { // if button was pressed longer than 30ms it's a real press.
      if (currtouched10 != lastCurrtouched10) { // read the value of the button. If not equal to offButtonState value then write  currtouched10 value to variable offButtonState
        lastCurrtouched10 = currtouched10;
      }
    }
  }
  // Button pressed down
  if (currtouched10 == 1024 && flag2 == 0) {
    pressedTime = millis(); //time from millis fn will be saved to firstTime varw
    flag1 = 0; //flag 1
    flag2 = 1; //flag 2
    lngPressActive = 0; // every time button is pressed  variable lngPressActive resets to 0
    Serial.println("Button pressed");
  }
  // Button released
  if (currtouched10 == 0 && flag1 == 0 ) {
    releasedTime = millis(); //time from millis fn will be saved to releasetime var
    flag1 = 1; //flag 1
    flag2 = 0;  //flag 2
    // shortPressActive = 1;
    timediff = releasedTime - pressedTime; //here we find the time gap between firstTime and releasedTime and stored to timediff var
    Serial.println("Button realeased");
  }


  //=========Long press OFF=========
  if (currtouched10 == 1024 && buttonPressType != 'F' && (millis() - pressedTime) >= longPress ) {
    if (not lngPressActive) {
      buttonPressType = 'F';
      lngPressActive = 1;
      Serial.println("Long press off");
    }
  }


  //=========Long press ON=========


  if (currtouched10 == 1024 && (millis() - pressedTime) >= longPress ) {
    if (not lngPressActive) {
      buttonPressType = 'O';
      lngPressActive = 1;
      Serial.println("Long press on");
    }
  }

I'm not sure that I completely understand your idea regarding void checkButton() function.
Maybe it could be implemented in better way? Now it's working and it's much easier to assign events to buttons.

void shortPressActionSTART() {
  if (buttonPressType == 'S') {
    startButtonWasPressed = 1;
    Serial.println("START");
  }
}
void shortPressActionSTOP() {
  if (buttonPressType == 'P'  ) {
    startButtonWasPressed = 0;
    Serial.println("STOP");


  }
}


void longPressActionOn () {
  if (buttonPressType == 'O') {
    screenControl = 1;
  }
}
void longPressActionOff () {
  if (buttonPressType == 'F') {
    screenControl = 0;
    startButtonWasPressed = 0;


    // Serial.println("Off");
  }
}


void notPressAction () {
  if (buttonPressType == 'S' || buttonPressType == 'P' ) { // if any button was pressed perform actions which are assigned for the button and set buttonPressType to 'N'
    buttonPressType = 'N';
  }
}

oswa90:
I'm not sure that I completely understand your idea regarding void checkButton() function.
Maybe it could be implemented in better way? Now it's working and it's much easier to assign events to buttons.

Have you studied the code in the link I gave you in Reply #6 ?

Good to hear it is working.

...R

Yes, I have analyzed your suggested code.
I have used some fragments of the code, even though not understood how the line is proceeded as I couldn't find "not" statment in Arduino documentation ( != not this but "not").

The line which I couldn't understand:
if (not holdEventPast)
{
event = 3;
waitForUp = true;
ignoreUp = true;
DConUp = false;
DCwaiting = false;
//downTime = millis();
holdEventPast = true;
}

oswa90:
Yes, I have analyzed your suggested code.
I have used some fragments of the code, even though not understood how the line is proceeded as I couldn't find "not" statment in Arduino documentation ( != not this but "not").

The line which I couldn't understand:
if (not holdEventPast)
{
event = 3;
waitForUp = true;
ignoreUp = true;
DConUp = false;
DCwaiting = false;
//downTime = millis();
holdEventPast = true;
}

The "not" operator is the same as "!" operator in C++. Does that clear it up?

aarg:
The "not" operator is the same as "!" operator in C++. Does that clear it up?

Yes, thank you.
Now I'm sure. :slight_smile: