Go Down

Topic: One button, press and press+hold analog input (Read 683 times) previous topic - next topic

MorganS

I think the other posters are dragging you backwards here. You already have a perfectly good function to read the analog input and return which button is pressed. You already have an adequate method of debouncing and you already have the initial press detected separately from the holding-it-down state.

Those 3 things are usually very difficult to get across to Arduino beginners, so most of the advice so far is addressing those 3 problems.

You have a large if() statement which detects that the button being pressed is a new button. Add an else to it for the case when the button is pressed but it's not a new button. My attempt is below. This just looks at one button, but I think you can set it up easily enough for your other buttons.
Code: (not tested) [Select]
int getButton()
{

  int i, z, sum ;
  int button;

  sum = 0;
  for (i = 0; i < 4; i++)
  {
    sum += analogRead(A0);        // will have 5 buttons through resistors on one input
  }
  z = sum / 4;
  if (z > 650 && z < 690) button = 0;             //aplication requires rest at 3 volts//
  else if (z > 840 && z < 860)  button = 1;       // analog input range for a button
  else if (z > 970 && z < 1010) button = 2;       //will have 5 total buttons

  else button = 0;
  return button;

}


void loop(void)
{
  int button, button2, pressed_button;
  static unsigned long buttonPressTime;
  static boolean buttonWasHeldDown = false;
  const unsigned long BUTTON_LONG_PRESS = 2000; //milliseconds - the duration of a 'long' press
  button = getButton();
  if (button != old_button)
  {
    delay(50);                   // debounce
    button2 = getButton();

    if (button == button2)
    {
      old_button = button;
      pressed_button = button;
      Serial.println(pressed_button);
      if (pressed_button == 1)
      {
        ble.print("AT+BleHidControlKey=");
        ble.println("VOLUME+");

      }

      if (pressed_button == 2)
      {
        ble.print("AT+BleHidControlKey=");
        ble.println("VOLUME-");
      }
    }
    buttonWasHeldDown = false;
    buttonPressTime = millis(); //record when the button was initially pressed
  } else {
    //the button is not a new button - is it held down long enough?
    //Well, first we are only interested in one or two buttons that have the long-press function
    if(button == 1)
    {
      if(!buttonWasHeldDown)
      {
        //so far, we have not detected it as a held-down button
        if(millis() - buttonPressTime > BUTTON_LONG_PRESS)
        {
          buttonWasHeldDown = true; //set this so we don't do the held-down action more than once
          Serial.println("button was held down");
          //do the held-down action
        }       
      }
    }
  }
}
"The problem is in the code you didn't post."

Matt1995

MorganS, thank you for your reply. Before I read your post, I actually had finally thought about exactly
what your explaining..the present code is already doing most everything. I think what is confusing me
is that in all the example code I can find and study, they use "LOW" and "HIGH" for detecting the
button press. Are these low/high designations just logic toggles, or is it actually detecting the 0-5v input range??? Can I use an integer, like "0" as HIGH and "1" as LOW?  Also, is the code looking for a "constant stream" of data from the button? OR is it just looking
for a state change? If all it needs is the "state change", then the current code I posted already does this
for each button. In serial monitor, if I press the first button, it reports a button "1". It will ONLY report a
button "0" when the button is released. It doesn't keep scrolling eg"1111111111111111" if i hold the button down.
 If it did this,
then I could potentially, lets say increase the volume extremely quickly if the button was held down. I dont want this(i dont want it to act like a TV remote that if you hold the button down, it increments volume). I just want a long press for one button to perform an additional task, like "next track".

I'll see if I can merge your code example and make something happen. I greatly appreciate the help
here. It's been days trying to figure this out..wife is getting annoyed at all the time spent lol.

PaulS

Quote
Are these low/high designations just logic toggles, or is it actually detecting the 0-5v input range??? Can I use an integer, like "0" as HIGH and "1" as LOW?
HIGH and LOW are just names, with values of 1 and 0, respectively. They represent a pin voltage of 5V or 0V, respectively (for 5V Arduinos).

Using the names is preferred, to make it clear that you are comparing pin states and that you know what the possible states are.

Since you don't actually have pin states, you have a different problem.
The art of getting good answers lies in asking good questions.

Robin2

in all the example code I can find and study, they use "LOW" and "HIGH" for detecting the
button press.
I had hoped that my examples in Reply #5 would have shown you that digitalRead() and analogRead() can be used in a way that is equivalent to each other.

In the Arduino LOW is defined as 0 and HIGH as 1. And digitalRead() returns a value that is either 0 (LOW) or 1 (HIGH).

What your code does when it detects a HIGH or a LOW or an analog reading above a certain value is up to you.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Matt1995

In the Arduino LOW is defined as 0 and HIGH as 1. And digitalRead() returns a value that is either 0 (LOW) or 1 (HIGH).

What your code does when it detects a HIGH or a LOW or an analog reading above a certain value is up to you.

...R
Ok, I didn't realize this, that now makes much more sense, at least for digitalRead().
In the link you posted, here are some snippets from that code:

    boolean buttonVal = HIGH;   // value read from button
    boolean buttonLast = HIGH;  // buffered value of the button's previous state


    buttonVal = digitalRead(buttonPin);
   // Button pressed down
   if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)


Now knowing that my input is analog, and button "off" is not "0" (i cannot pullup the pin to 5v for
 button off), how do I go about translating these statements? For each HIGH and LOW statement, do I
have to designate an analog range? eg if(buttonVal > 600 && buttonVal <700) ? If that is the case, what do I declare for the 2 boolean variables? Seems like there should be a better way to detect a press, so you dont
have to type out each "range" everytime a comparison needs to be made...which is what MorganS I believe is hinting at...currently a button number is generated when a button is pressed, then a "0" gets returned
when it is released.

PaulS

Quote
currently a button number is generated when a button is pressed, then a "0" gets returned
"Generated" isn't really correct. "Determined" is.

You already know which switch is pressed - you have it's number - if any. You can determine that it is the same number as last time, or it isn't. When the number changes (the current number isn't the same as the previous number), you would take the same action as you would take if the switch state changed, were you using digital switches.
The art of getting good answers lies in asking good questions.

Matt1995

#21
Sep 12, 2017, 04:30 pm Last Edit: Sep 12, 2017, 04:43 pm by Matt1995
So I tried MorganS's code example and what happened is what i was thinking might...It doesn't do the long press action, it immediately goes to the " if pressed_button == 1" command. If I change that button number to a
different one(thats not declared in the "getButton" function), the long press does work.

I currently have 2 pushbuttons with a breadboard and resistors for my inputs, so I can test at least
2 different buttons. This modified code now has button 1 for one hardware button, and button 3 for
my second hardware button. I want button 1 press/release to trigger "volume+" then press and hold button 1 to do "volume-" .
Code: [Select]
void loop(void)
{
  int button, button2, pressed_button;
  static unsigned long buttonPressTime;
  static boolean buttonWasHeldDown = false;
  const unsigned long BUTTON_LONG_PRESS = 2000; //milliseconds - the duration of a 'long' press
  button = getButton();
  if (button != old_button)
  {
    delay(50);                   // debounce
    button2 = getButton();

    if (button == button2)
    {
      old_button = button;
      pressed_button = button;
      Serial.println(pressed_button);
      if (pressed_button == 1)
      {
        ble.print("AT+BleHidControlKey=");
        ble.println("VOLUME+");

      }

      if (pressed_button == 3)
      {
        ble.print("AT+BleHidControlKey=");
        ble.println("PLAYPAUSE");
      }
    }
    buttonWasHeldDown = false;
    buttonPressTime = millis(); //record when the button was initially pressed
  } else {
    //the button is not a new button - is it held down long enough?
    //Well, first we are only interested in one or two buttons that have the long-press function
    if(button == 1)
    {
      if(!buttonWasHeldDown)
      {
        //so far, we have not detected it as a held-down button
        if(millis() - buttonPressTime > BUTTON_LONG_PRESS)
        {
          buttonWasHeldDown = true; //set this so we don't do the held-down action more than once
          Serial.println("button was held down");
          //do the held-down action
              ble.print("AT+BleHidControlKey=");
              ble.println("VOLUME-") ;
        }        
      }
    }
  }
}


/**************************************************************************/
/*!
    Checks for user input via the Steering Wheel Control wire
*/
/**************************************************************************/

int getButton()          
 {
 
  int i, z, sum ;
  int button;

  sum = 0;
  for (i=0; i < 4; i++)
  {
     sum += analogRead(A0);    
  }
  z = sum / 4;
  if (z > 650 && z < 690) button = 0;                                              
  else if (z > 840 && z < 860)  button = 1;                                          
  else if (z > 970 && z < 1010) button = 3;  
              
  else button = 0;
  return button;
 
 }


gfvalvo

#22
Sep 12, 2017, 04:34 pm Last Edit: Sep 12, 2017, 04:43 pm by gfvalvo
Just to expand the conversation (and for my own curiosity) -- why are you attempting to do an inherently digital thing (detect pressed / not-pressed) by analog means? Are you really that short of digital GPIO that you can't assign each button its own digital input?

Doing so would immediately solve your problem of mapping different input voltage ranges to individual button pressed / not-pressed states. And, it would have the added benefit of being able to detect if more than one button is being pressed at a time. I don't see any provision for doing so in your current code -- it would be difficult since 5 buttons represent 32 different combinations of pressed / not-pressed. Given 5 buttons, I guarantee that some user will try to press more than one of them simultaneously. At that point the operation of your system will be undefined.


Matt1995

Just to expand the conversation (and for my own curiosity) -- why are you attempting to do an inherently digital thing (detect pressed / not-pressed) by analog means? Are you really that short of digital GPIO that you can't assign each button its own digital input?

Doing so would immediately solve your problem of mapping different input voltage ranges to individual button pressed / not-pressed states. And, it would have the added benefit of being able to detect if more than one button is being pressed at a time. I don't see any provision for doing so in your current code -- it would be difficult since 5 buttons represent 32 different combinations of pressed / not-pressed. Given 5 buttons, I guarantee that some user will try to press more than one of them simultaneously. At that point the operation of your system will be undefined.


I think I get what your're saying, but the hardware config is a steering wheel with media buttons on it, that outputs to one wire, multiplexed through different resistances on each button..maybe i'm not really understanding. I suppose if each wheel button had it's own output wire... ?

gfvalvo

You mean something like this?

If so, that's unfortunate. You'll just have to keep pounding that square peg into the round hole. As I said, consider the multiple buttons pressed scenario.

Matt1995

You mean something like this?

If so, that's unfortunate. You'll just have to keep pounding that square peg into the round hole. As I said, consider the multiple buttons pressed scenario.

Yes that's exactly how it works. I *could* do  two buttons pressed at the same time, but received analog
voltage can't overlap any other button value..I have no way to adjust the resistance values per switch, as
that is all built into the steering wheel switches. So the only other real option is to use more than one
function per switch value, such as double click, press/hold, press/release. Having more than one click do something may get complicated as you'd have to remember how many clicks does what on the same button..

Press/release and Press/hold could give a total of 10 different operations, with 5 available buttons. Not sure
how much interaction with a phone/tablet you want to do while driving! 

gfvalvo

I *could* do  two buttons pressed at the same time .....
My point wasn't that you should add MORE functionality by encoding multiple button presses into even more actions. It's that you should consider what will happen IF (really when) the user presses two or more buttons at the same time. Will something unexpected (bad) happen? Like I said, at this point it's undefined and that's never a good thing in control applications.

Robin2

how do I go about translating these statements?
That code depends on the value in buttonVal. You just need to use your analogRead() to put a suitable value in buttonVal in place of using digitalRead(). So, something like this
Code: [Select]
boolean buttonVal = HIGH;   // value read from button
 boolean buttonLast = HIGH;  // buffered value of the button's previous state

  analogVal = analogRead(analogInputPin);
  if (analogVal > NNN) {
    buttonVal = LOW;  // assuming LOW means that the button was pressed
  }
 
   // Button pressed down
   if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)


...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Matt1995

Thank you all for your help. I really do appreciate it. Using Morgan's code example, I was able to get
it to work for the most part. Only one issue..To trigger the press/hold event, it first triggers the push/release event. This code is setup for pushbutton 1 to go volume+ with press/release, then volume-
with press/hold. So to be clear, if i press/hold it for volume down, it first does volume up, wait the hold time, then it does volume down. Thinking this should be an easy fix? 
Code: [Select]
void loop(void)
{
  int button, button2, pressed_button;
  static unsigned long buttonPressTime;
  static boolean buttonWasHeldDown = false;
  const unsigned long BUTTON_LONG_PRESS = 2000; //milliseconds - the duration of a 'long' press
  button = getButton();
  if (button != old_button)
  {
    delay(50);                   // debounce
    button2 = getButton();

    if (button == button2)
    {
      old_button = button;
      pressed_button = button;
      Serial.println(pressed_button);
      if (pressed_button == 1)
      {
        ble.print("AT+BleHidControlKey=");
        ble.println("VOLUME+");

      }

      if (pressed_button == 2)
      {
        ble.print("AT+BleHidControlKey=");
        ble.println("PLAYPAUSE");
      }
    }
    buttonWasHeldDown = false;
    buttonPressTime = millis(); //record when the button was initially pressed
  } else {
    //the button is not a new button - is it held down long enough?
    //Well, first we are only interested in one or two buttons that have the long-press function
    if(button == 1)
    {
      if(!buttonWasHeldDown)
      {
        //so far, we have not detected it as a held-down button
        if(millis() - buttonPressTime > BUTTON_LONG_PRESS)
        {
          buttonWasHeldDown = true; //set this so we don't do the held-down action more than once
          Serial.println("button was held down");
          ble.print("AT+HidBleControlKey=");
          ble.println("VOLUME-");
        }       
      }
    }
  }
}
     



/**************************************************************************/
/*!
    Checks for user input via the Steering Wheel Control wire
*/
/**************************************************************************/

int getButton()         
 {
 
  int i, z, sum ;
  int button;

  sum = 0;
  for (i=0; i < 4; i++)
  {
     sum += analogRead(A0);   
  }
  z = sum / 4;
  if (z > 650 && z < 690) button = 0;                                             
  else if (z > 840 && z < 860)  button = 1;                                         
  else if (z > 970 && z < 1010) button = 2;   
             
  else button = 0;
  return button;
 
 }

PaulS

Quote
Thinking this should be an easy fix? 
It might be if your quick press and long press functions for one switch did similar things. But, volume up for short press and volume down for long press are not similar things.

You could wait for the release event to do anything. If the switch that was released had been held a short time, do one thing. If it had been held a long time, do something else. Nothing would happen on press, except for recording the time that the press started.
The art of getting good answers lies in asking good questions.

Go Up