Debouncing

I seem to be confused about how to implement debouncing when using analogRead. I looked up many articles regarding debouncing, but they seem to address digitalRead, not analogRead. The link to my original post regarding how to wire the buttons is here. The following is my code so far, which works well, but there are multiple inputs detected.:

#include <XInput.h>

int analogPin= 0;
int raw= 0;
float SW1= 0;
int analogPin2= 1;
int raw2= 0;
float SW2= 0;
int analogPin3= 2;
int raw3= 0;
float SWE= 0;
int analogPin4= 3;
int raw4= 0;
float SWCC= 0;

void setup()
{
XInput.begin();
}

void loop()
{
raw= analogRead(analogPin);
if(raw<980 and raw<50)
{
XInput.press(BUTTON_A);
XInput.release(BUTTON_A);
}
if(raw<980 and raw>730)
{
XInput.press(BUTTON_B);
XInput.release(BUTTON_B);
}
if(raw<980 and raw<300 and raw>200)
{
XInput.press(BUTTON_X);
XInput.release(BUTTON_X);
}
if(raw<980 and raw<600 and raw>450)
{
XInput.press(BUTTON_Y);
XInput.release(BUTTON_Y);

}
raw2= analogRead(analogPin2);
if(raw2<980 and raw2<100)
{
XInput.press(BUTTON_LB);
XInput.release(BUTTON_LB);
}
if(raw2<980 and raw2>450)
{
XInput.press(BUTTON_RB);
XInput.release(BUTTON_RB);
}
if(raw2<980 and raw2<390 and raw2>150)
{
XInput.press(BUTTON_BACK);
XInput.release(BUTTON_BACK);
}
raw3= analogRead(analogPin3);
if(raw3<980 and raw3<100)
{
XInput.press(BUTTON_START);
XInput.release(BUTTON_START);
}
if(raw3<980 and raw3>700)
{
XInput.press(BUTTON_L3);
XInput.release(BUTTON_L3);
}
if(raw3<980 and raw3<660 and raw3>420)
{
XInput.press(BUTTON_R3);
XInput.release(BUTTON_R3);
}
if(raw3<980 and raw3<400 and raw3>120)
{
XInput.press(DPAD_UP);
XInput.release(DPAD_UP);
}
raw4= analogRead(analogPin4);
if(raw4<980 and raw4<100)
{
XInput.press(DPAD_DOWN);
XInput.release(DPAD_DOWN);
}
if(raw4<980 and raw4>510)
{
XInput.press(DPAD_LEFT);
XInput.release(DPAD_LEFT);
}
if(raw4<980 and raw4<500 and raw4>320)
{
XInput.press(DPAD_RIGHT);
XInput.release(DPAD_RIGHT);
}
if(raw4<980 and raw4<310 and raw4>110)
{
XInput.press(BUTTON_LOGO);
XInput.release(BUTTON_LOGO);
}
}

Thanks for using code tags. Because there is no inline documentation, can you please explain your approach? How does it work?

Edit - allow me to be more focused in my response. So you say, "how to implement debouncing when using analogRead...". My question, do you already fully understand digital debouncing so we don't have to explain that, or is it simply a matter of not understanding how the analogue nature might alter it in this case? In both cases, digital and analogue, the debounce logic is the same:

  1. A change of state is detected
  2. Register the change of state event
  3. Ignore any further state changes for some time interval (usually 10-20 ms)

I need to know what level of understanding you're on so the answer can specifically target the actual need. Or, is it the case that you do understand all of the above, but are too weak on C++ to translate the idea to code?

aarg:
Thanks for using code tags. Because there is no inline documentation, can you please explain your approach? How does it work?

I guess I should have given more details, but I wanted to keep this straightforward in regards to debouncing analog pins as opposed to digital pins.

Basically, I noticed that 2011 Lexus car owners can test if their steering wheel buttons work by using a multi-meter to read the resistance of each button they press. Using this finding, I used a 1K resistor on my breadboard to compare the analogRead reading of each button press. I included a breadboard view of this process below. Then I used ArduinoXInput to map Xbox XInput buttons.

PaulRB in my original post explains it best,

PaulRB:
For the switches, as you already figured out, you just need a known resistor for each arduino analog pin (between 5V and the pin). Try 10K, 4K7, 2K2, 1K... and see what analogRead() gives for each switch.

You can review all of the details regarding my project in my original topic.

So you double posted... not great. We shouldn't have to reference any previous articles to answer a question like that. I kind of feel that I wasted a lot of time with this because of what you didn't mention. You should definitely at least, have shared the link to previous topic in your first post.

Also, you completely ignored my questions, or chose not to answer them. Sorry, but that's really what it looks like from here.

aarg:
I need to know what level of understanding you're on so the answer can specifically target the actual need. Or, is it the case that you do understand all of the above, but are too weak on C++ to translate the idea to code?

I looked up many examples of digitalRead debouncing, but I feel like the example on this site explains it in the simplest form in regards to my beginner noob knowledge about debouncing. :slight_smile:
Basically, I am still confused about digitalRead debouncing and I assumed that it is familiar to analogRead debouncing. I feel like the simple example with booleans I referred to above is confusing me. :confused:

aarg:
You should definitely at least, have shared the link to previous topic in your first post.

I included the link, but I will include it again here.

I included the link,

Yes but not on the first post.

You are still not answering the questions you were asked by aarg, which is what do you understand.
In reply #1 he said:-

  1. A change of state is detected
  2. Register the change of state event
  3. Ignore any further state changes for some time interval (usually 10-20 ms)

So tell us do you understand this description as it applies to digital debouncing?
Because you do exactly the same thing with analogue debouncing.

Grumpy_Mike:
Yes but not on the first post.

You are still not answering the questions you were asked by aarg, which is what do you understand.
In reply #1 he said:-
So tell us do you understand this description as it applies to digital debouncing?
Because you do exactly the same thing with analogue debouncing.

Sorry about that, I will edit the first post to include the link.

I understand the concept of how debouncing works in general, but I don't know how to apply it, especially to my example. I learned Python and some Java, but yes, I am still very much a starter at coding. But I am able to figure out the code for the most part. So far in my Arduino journey, I understood pretty much everything to this point, but debouncing is giving me a struggle.

I feel like I could use a simple example of debouncing as a start. For some reason, all of the tutorials I found seemed very complex, except the one I linked. Can you guys provide a small tutorial here in regards to basic debouncing? :smiley:

P.S. My project is not at all serious, and I am surprised I achieved as much as I did with it already. If anything, I learned a lot from my first Arduino project. :slight_smile: And also, learning how to debounce the buttons in my project is just the first step. Trying to wire them so that they don't tangle when I turn the steering wheel in game is a whole different problem... ;D

For some reason, all of the tutorials I found seemed very complex, except the one I linked.

Maybe you understood it because it is a bad way to do debouncing and will not work all the time. It is much simpler than that.
After you detect a change then you don’t look at the input again for a time, normally about 20mS, but it depends on the nature of you switch, it could be longer. The very simples way is to delay for this amount of time after you detect the change before you look at the switch again, using the delay function.
What is troubling you about that?

The "several descriptions of debounce" given in this thread are all wrong! They all describe taking note of a state change, then ignoring any further state changes for some people of time. That is pretty much a$$backwards! The idea is to wait until there are NO state changes for some period of time.

sample the input
IF the state has changed start a timer.
ELSE IF the timer has expired, act on the state change

The whole idea is to ensure that the input has fully settled, and is completely stable, before acting on it.

RayLivingston:
The "several descriptions of debounce" given in this thread are all wrong! They all describe taking note of a state change, then ignoring any further state changes for some people of time. That is pretty much a$$backwards! The idea is to wait until there are NO state changes for some period of time.

sample the input
IF the state has changed start a timer.
ELSE IF the timer has expired, act on the state change

The whole idea is to ensure that the input has fully settled, and is completely stable, before acting on it.

At risk of starting a digression, I disagree. Your idea is not "the idea", it is "an idea" which may be the right answer in some situations. But I will explain why it is not always the best idea for a manual switch (there may be other situations as well). It boils down to what you are trying to detect, and what you are trying to reject. I will focus on the switch, for example a push button momentary type.

First of all, it is normally expected that there is no contact closure at all, unless someone activates the switch manually. In some environments there may be noise pulses that could register as a false actuation. What this means, is that very short pulses cannot be automatically identified either as externally produced noise, or as an extremely short switch actuation. This means that the designer must choose a default action for those, which is the essential difference between "my method" and "your method". There are unavoidable trade offs for those, which are not as cut and dried as you have implied in your last sentence above.

The two methods differ only in when a "registration" takes place, before, or after, the debounce time interval. You call this "taking note" of the event, you could also say, "recording" or "acting on" the event.

  • "my method" ... registration is immediate and guaranteed (within the several nanosecond input sampling interval at least... with a normal bouncy switch activation, if it misses the first pulse it will probably soon catch one of the following ones). This has important ramifications for the signal. First, there is minimal delay - it is possible to utilize the results of the switch closure immediately. Secondly, a short pulse will be interpreted as a switch activation. This makes it the fastest responding, and also the most sensitive response. If there is any pulse at all, it will register as an event. Thus it has no noise filtering
  • "your method" ... registration is delayed and conditional. In this case, there always a fixed delay - it is not possible to utilize the results of the switch closure immediately. A short pulse will always be ignored. This makes it the slowest responding, and also the least sensitive since a very light switch press will be ignored. However, this does give the benefit of providing noise filtering.

Neither method is "right" or "wrong". In the first case, the benefit is key sensitivity and speed, the disadvantage is that there is no noise filtering. In the second case, noise filtering is achieved but key sensitivity and response speed is sacrificed.

Consider the user experience of a very clunky switch that needs a very long debounce interval. User will press a switch, and expect an immediate response. If they don't get one, it gives the impression of sluggishness. It's true, the following switch press will be locked out even if it is registered immediately, but this would happen much less frequently and would be less frustrating than seeing a delay each and every time they use the switch. In this case, "my method" wins.

Can you guys provide a small tutorial here in regards to basic debouncing?

Why when it's on this same site?

Grumpy_Mike:
Maybe you understood it because it is a bad way to do debouncing and will not work all the time. It is much simpler than that.
After you detect a change then you don’t look at the input again for a time, normally about 20mS, but it depends on the nature of you switch, it could be longer. The very simples way is to delay for this amount of time after you detect the change before you look at the switch again, using the delay function.
What is troubling you about that?

I tried the simple way of delaying, and for my basic purposes, it does work. But if I would let's say want to make a character walk with the d-pad, the character would pause every step. Here is my updated code:

#include <XInput.h>

int analogPin= 0;
int raw= 0;
float SW1= 0;
int analogPin2= 1;
int raw2= 0;
float SW2= 0;
int analogPin3= 2;
int raw3= 0;
float SWE= 0;
int analogPin4= 3;
int raw4= 0;
float SWCC= 0;
int dd= 250;

void setup()
{
XInput.begin();
}

void loop()
{
raw= analogRead(analogPin);
if(raw<980 and raw<50)
{
XInput.press(BUTTON_A);
delay(dd);
XInput.release(BUTTON_A);
}
if(raw<980 and raw>730)
{
XInput.press(BUTTON_B);
delay(dd);
XInput.release(BUTTON_B);
}
if(raw<980 and raw<300 and raw>200)
{
XInput.press(BUTTON_X);
delay(dd);
XInput.release(BUTTON_X);
}
if(raw<980 and raw<600 and raw>450)
{
XInput.press(BUTTON_Y);
delay(dd);
XInput.release(BUTTON_Y);

}
raw2= analogRead(analogPin2);
if(raw2<980 and raw2<100)
{
XInput.press(BUTTON_LB);
delay(dd);
XInput.release(BUTTON_LB);
}
if(raw2<980 and raw2>450)
{
XInput.press(BUTTON_RB);
delay(dd);
XInput.release(BUTTON_RB);
}
if(raw2<980 and raw2<390 and raw2>150)
{
XInput.press(BUTTON_BACK);
delay(dd);
XInput.release(BUTTON_BACK);
}
raw3= analogRead(analogPin3);
if(raw3<980 and raw3<100)
{
XInput.press(BUTTON_START);
delay(dd);
XInput.release(BUTTON_START);
}
if(raw3<980 and raw3>700)
{
XInput.press(BUTTON_L3);
delay(dd);
XInput.release(BUTTON_L3);
}
if(raw3<980 and raw3<660 and raw3>420)
{
XInput.press(BUTTON_R3);
delay(dd);
XInput.release(BUTTON_R3);
}
if(raw3<980 and raw3<400 and raw3>120)
{
XInput.press(DPAD_UP);
delay(dd);
XInput.release(DPAD_UP);
}
raw4= analogRead(analogPin4);
if(raw4<980 and raw4<100)
{
XInput.press(DPAD_DOWN);
delay(dd);
XInput.release(DPAD_DOWN);
}
if(raw4<980 and raw4>510)
{
XInput.press(DPAD_LEFT);
delay(dd);
XInput.release(DPAD_LEFT);
}
if(raw4<980 and raw4<500 and raw4>320)
{
XInput.press(DPAD_RIGHT);
delay(dd);
XInput.release(DPAD_RIGHT);
}
if(raw4<980 and raw4<310 and raw4>110)
{
XInput.press(BUTTON_LOGO);
delay(dd);
XInput.release(BUTTON_LOGO);
}
delay(250);
}

aarg:
Why when it's on this same site?
https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce

Yes, I reviewed that example a couple of times. I guess the thing that's confusing me is that this example like many other examples use digitalRead to see if the button is LOW or HIGH. Since I am using analogRead, its not a simple LOW or HIGH comparison. That is what is getting me confused. For example, analogRead outputs random values even when I am not pressing any buttons. Usually its 0 or 1, but sometimes its 10 or even 15. If the lastButtonState of my analogRead value would essentially keep changing without me pressing any buttons, how would this be possible to implement?

Would I need to monitor values above 50, for example?

RayLivingston:
The "several descriptions of debounce" given in this thread are all wrong!

No they are not, it is your method that is sub optimal.

The whole idea is to ensure that the input has fully settled, and is completely stable, before acting on it.

Simply no.
For rapid response the whole idea is that you act as soon as you detect a state change. It matters not that subsequent to this the contacts will bounce you know you have to take action as soon as you see the first contact.
That first contact will, in your code, initiate an action or sequence of actions and these actions can carry on quite haply if the contact is bouncing or not. Why waste time waiting for the input to stabilise because you already know what that stable state is going to be eventually. For example it it triggers one or more analogue reads then they take 0.1mS each.

If you act on the first contact change then often, in all but the simplest code, the action will take longer than the contact bounce period and there will be no need to debounce at all. There seems to be a fetish here that all mechanical inputs must be debounced. This is not always necessary. In addition to the example I just sited there are many times when doing an action twice has no material effect of the function of the program.

But of course there are occasions where you do need to debounce in which case you have to ensure that sufficient time has elapsed before looking at the contact input again. This "sufficient time" is the same time you would have to wait in a "check if it has stabilised before you do anything" scenario, because if not there is no reliable way to to say the bouncing has stopped because you might just read the same level after a shorter delay because it will still be bouncing.

The method that makes the best of your processor's time, is of course, to make a note of the millis timer on the initial contact and then do not "look" at the button again until the "sufficient time" has elapsed. The simpler method is to just use a delay( "sufficient time" ) after the actions have been taken.

androidftw:
But if I would let's say want to make a character walk with the d-pad, the character would pause every step.

I don't understand what "a character walk with the d-pad" means.
Your default delay is 250mS is probbly way longer than it needs be.

What is the XInput.h library? What does it do?

if(raw3<980 and raw3<660 and raw3>420)

This is a bit silly because if raw3 < 660 then raw3 has to be < 980, so there is no need to have the first term in the test.

You make the same sorts of error all through that code like

if(raw4<980 and raw4<100)

Also the code seems unnecessary long and where did the requirement to read four analogue inputs come from? In the OP I thought you were only using one.

androidftw:
Yes, I reviewed that example a couple of times. I guess the thing that's confusing me is that this example like many other examples use digitalRead to see if the button is LOW or HIGH. Since I am using analogRead, its not a simple LOW or HIGH comparison. That is what is getting me confused. For example, analogRead outputs random values even when I am not pressing any buttons. Usually its 0 or 1, but sometimes its 10 or even 15. If the lastButtonState of my analogRead value would essentially keep changing without me pressing any buttons, how would this be possible to implement?

Would I need to monitor values above 50, for example?

A change of state is a change of state, whether 0 becomes 1, or X becomes greater than 42. Can you see that any logic you put around it would be the same?

But of course you don't count any change in input value as a change of state. You only count a change of input range, for example from X<42 to (X>=42 and X<66). In that example, a change from 41 to 53 would be a change of state, but a change from 55 to 62 would not be.

Grumpy_Mike:
I don't understand what "a character walk with the d-pad" means.
Your default delay is 250mS is probbly way longer than it needs be.

What is the XInput.h library? What does it do?

if(raw3<980 and raw3<660 and raw3>420)

This is a bit silly because if raw3 < 660 then raw3 has to be < 980, so there is no need to have the first term in the test.

You make the same sorts of error all through that code like

if(raw4<980 and raw4<100)

Also the code seems unnecessary long and where did the requirement to read four analogue inputs come from? In the OP I thought you were only using one.

I meant that when I move a character in a game, the character pauses while he/she walks.

But, yes 250 is probably too long.

The Xinput library just lets you emulate an Xbox 360 controller. I use this library to get vibration feedback from games as well.

Yes, I was just trying to establish the minimum and maximum analogRead values of the 4 analog inputs, but I see your point. I need 4 analog inputs because there are 4 separate switches on the wheel with similar resistance readings of each button. There is Switch 1 with the Seek+, Seek-, Volume+, Volume- buttons. There is Switch 2 with the Voice, Off Hook, On Hook, Mode buttons. There is the Switch with the Enter button toggle. There is also a separate Cruise Control Switch with four buttons.

Let's say that Volume+ and On Hook result in the same resistance readings, but on different switches. That's why there is 4 analog inputs.

aarg:
A change of state is a change of state, whether 0 becomes 1, or X becomes greater than 42. Can you see that any logic you put around it would be the same?

But of course you don't count any change in input value as a change of state. You only count a change of input range, for example from X<42 to (X>=42 and X<66). In that example, a change from 41 to 53 would be a change of state, but a change from 55 to 62 would not be.

Thanks for the clear explanation. In that case, I am less confused now, and I might be able to actually figure this out. ;D

aarg:
A change of state is a change of state, whether 0 becomes 1, or X becomes greater than 42. Can you see that any logic you put around it would be the same?

I tried implementing this logic to a new simple example, but it seems like it didn't turn out the correct way. When I press the button, I get multiple "Button Pressed!" messages with similar analogRead readings as if there is no debouncing.

Before I started, I noted what I needed to accomplish:

  • Trying to figure out if pressed analog button.

  • When you DON'T PRESS, reading is over > 1010.

  • When you PRESS, reading is under <= 1010.

Afterward, I started to follow the coding example from this site, and came up with the following:

const int analogPin = 0;
int reading = 0;

int currentReading;
int notPressed = 1010;

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;

void setup()
{
Serial.begin(9600);
}

void loop()
{
reading = analogRead(analogPin);

if(reading > notPressed){
	lastDebounceTime = millis();
	
	if((millis() - lastDebounceTime) > debounceDelay){
		
		if(reading != currentReading){
			currentReading = reading;
			
			if(currentReading < notPressed){
				Serial.print("Button Pressed!");
				Serial.println(currentReading);
			}
		}
	}
}
}