Single shot input routine with debounce

I have an arduino uno, i have the input tied high with a resistor, the input is tied to a Banner sensor, when the sensor is blocked it pulls the input low. All of this can be seen on a ocilloscope and with a digital multimeter. However i think due to noise there is an occasional misread of the input. So i searched around and found a debounce routine. I thought i added it in correctly but alas it does not function. Was hoping that some of the more skilled people on this forum could look at the code and tell me what i did wrong.

The concept was to read the input, then check to see if the state has changed by comparing it to last known read, if the state had changed find out if it changed to low or high level, if it changes to a low level start debounce, if the debounce time elapses then the input was true and do the routine, if the input was a false trigger or if it goes to high level, clear the debounce time.

void loop() {

    curMillis = millis();
    buttonCCWpressed = false;
    buttonCWpressed = false;
    currentbutton = digitalRead(buttonCWpin);
    if ( currentbutton != previousbutton) {
        if (currentbutton == low){
            debounce++;
            if (debounce>50){
                previousbutton = currentbutton;
                buttonCWpressed = true;
        else{
            debounce = 0;
            buttonCWpressed = false;
            previousbutton = currentbutton;
    }
    actOnButtons();

}

void actOnButtons() {
    if (buttonCWpressed == true) {
        digitalWrite(directionPin, LOW);
        singleStep();
    }
}

Without the debounce the code works but it will randomly just trigger more than once which is why i thought to use the debounce. I have tried values of 5, 50 and 500 in the debounce number but it still fails to function. So i am sure it is probably something stupidly simple that i am missing. Thanks for taking the time to read this and i hope to see some answer.

I don’t know why your particular implementation doesn’t work but in general I’m suspicious of an over-engineered piece of code just to ‘debounce’ a switch.

Unless timing is of the essence (which is surprisingly rare in typical hobbyist Arduino projects) I just use a 50ms delay (which, as you have an oscilloscope you can confirm if this is more-or-less correct).

The example code you’ve supplied does all sorts of checks for changes in state and so forth but at the end of the day you just want something that ignores switch bounces that happen (in general) within the first 20ms (or less).

If you are getting false triggering after 50ms I suspect it’s something else not the switch (and you say you’ve tried 500ms, half a second, sounds impossible that a switch would bounce anywhere near that long unless you have a rubber switch!).

TBH as you have an oscilloscope you could be checking that switch bounce anyway. Trace the switch for one-time triggering then expand the time-base so you can see the individual bounces.

A small capacitor also helps your Arduino ignore bounces, you can try that too but beware of what I said at the start about over-engineered solutions! :slight_smile:

Well maybe my wording was off, what i meant to say is, since i have added the debounce portion to my code, the code in and of itself does not work. I have tried variations of numbers as i stated but no matter what numbers i place that piece of code does not function.

My first attempt with this code was:

If (debounce > 500){

When this didnt work i went and tried

if (debounce > 50){

When this didnt work i took it down to 5

if (debounce > 5){

Now if i take this check for debounce out of the code, then the code works, it does what it should but at times it would run through twice or three time. And it was very random.

curMillis = millis();
	buttonCCWpressed = false;
	buttonCWpressed = false;
	currentbutton = digitalRead(buttonCWpin);
	if ( currentbutton != previousbutton) {
		if (currentbutton == low){
			//debounce++;
			
			buttonCWpressed = true;
		else{
			debounce = 0;
			buttonCWpressed = false;
			previousbutton = currentbutton;
	}

The above code will work but with random occurances of running more than 1 time per input trigger. And the other part i might have missed here is that sometimes the item that is blocking the sensor will stick in front of the sensor thus keeping the circuit held low. This of course is when we have the event running more than 1 time per trigger.

One thing i have done is i used the serial println function and was viewing the input using the scope on the input wire and hyperterminal on the arduino. the scope did not show anything but on rare occasion you would see the input flutter. this is why i thoght of using the debounce. Hope that is much more clear for you.

You may need more variables to reflect the button and action state. currentButton and previousButton are used to eliminate physical bouncing, and buttonCWpressed reflects the accepted (debounced) logic button state. If you want a single action on a logic state change, you need another variable that tells actionTaken, to prevent further actions while the button remains pressed.

This is a different approach to reading and debouncing an input signal. Rather than debouncing, it checks for a period of stability in the signal before considering it valid. Works great on rising and falling edges. You only may need to modify “stableInterval” … I have it at 100ms which represents a maximum input frequency of 10Hz from your Banner sensor.

If you’re daring, give it a try (I’ve used pin 4 for input):

const int inputPin = 4;
const int ledPin = 13;
word inputState = 0xAAAA; // default to prevent false trigger on startup
word previousInputState = 0xAAAA;

word stableInterval = 100; // must be stable for stableInterval milliseconds to be valid

word readInterval;
unsigned long previousReadTime = 0;
unsigned long currentTime = 0;

void setup() {
  readInterval = constrain((stableInterval / 16), 1, 4000);
  pinMode(ledPin, OUTPUT);
  pinMode(inputPin, INPUT_PULLUP);
}

void loop() {
  inputStable();
}

void inputStable() {
  currentTime = millis();

  if (currentTime - previousReadTime > readInterval) { // it's time to take a reading
    inputState = (inputState << 1) | digitalRead(inputPin); // shift and read
    previousReadTime = currentTime;

    if (inputState != previousInputState) // input status has changed

      if (inputState == 0x7FFF) { // if rising and high for 15 consecutive reads
        digitalWrite(ledPin, HIGH);
      }

    if (inputState == 0x8000) { // if falling and low for 15 consecutive reads
      digitalWrite(ledPin, LOW);
    }
  }
}

In the original posted code, the debounce counter is not set back to zero in the case of a a true, debounced reading. Using auto format on your code helps understand the nested logic of the if/else statments

Try

void loop() {

  curMillis = millis();
  buttonCCWpressed = false;
  buttonCWpressed = false;
  currentbutton = digitalRead(buttonCWpin);
  if ( currentbutton != previousbutton) {
    if (currentbutton == low) {
      debounce++;
      if (debounce > 50) {
        previousbutton = currentbutton;
        buttonCWpressed = true;
        debounce = 0; //add this line
        else {
          debounce = 0;
          buttonCWpressed = false;
          previousbutton = currentbutton;
        }
        actOnButtons();

      }

      void actOnButtons() {
        if (buttonCWpressed == true) {
          digitalWrite(directionPin, LOW);
          singleStep();
        }
      }

Asuming (as it seems) you are using 2 different buttons the simplest thing is to ensure a short interval between successive reads. Have a look at how this is done in Several Things at a Time

...R