Oscilloscope and esp32 show raw and debounced button

Date: 2025-01-29 18:34:59 / Updated: 2025-01-29 18:34:59

This is a follow-up to my earlier post about why you need to debounce, where I showed the bouncing/chattering of a button on my oscilloscope. This time I connected an ESP32 and output both what it read directly from the button, as well as debouncing it in software. Source code for the ESP32 is at the end.

I'm using the same arcade button, one click took about 85ms, and the ESP32 read 41 transitions from HIGH to LOW, and LOW to HIGH. I actually had one that was 96! But I had already taken screen shots so I didn't record that one.

In the image, the first line in yellow is a direct reading from the button. The second line in cyan is what the ESP32 saw with a call to digitalRead(). The third line in green is debounced. I am using a 15ms debounce time here. The button needs to be stable for 15ms before I consider the state changed, which is why you see the debounced line stay HIGH so much longer then when the button was first pressed. Well, if you consider 25ms a long time. Same story for returning from LOW to HIGH when I let the button go.

In this case, the green line is a solid signal that only reads as two transitions. HIGH to LOW and LOW back to HIGH. One click.

A closer look at when I pressed the button. It always amazes me how much the button chatters. I wanted to keep the debounce transition on the screen, or you would have seen a boat load of transitions in that little cluster when the button is first pressed.

I was curious to see how quick the ESP was seeing the first state change. In this case, about 7 microseconds. That will be heavily dependent on your main loop and what other processing you are doing. Mine was very light.

And finally, a closer look at when I released the button.

Source code follows. I used PlatformIO, VSCode and the Arduino framework.

#include <Arduino.h>

#define SIG_OUT   12      //output pin for direct button read
#define SIG_DOUT  13      //output pin for debounced button
#define BUTTON    35      //pin button is on
#define DEBOUNCE_TIME 15  //milliseconds

#define FREQ 1000
#define HAS_EXPIRED(t,f)  (millis() - t >= f)    //t=timer f=freq; set t to millis() to reset

unsigned long timer=0;    //timer for outputting the counter
int pinStatus=-1;         //temp current button status as read from the button
int status=LOW;           //current button status as read from the button
int count=0;              //how many transitions

//debounce 
unsigned long lastDebounceTime=0;
int lastButtonState=HIGH;
bool buttonChanged=false;
int buttonState=HIGH;     //the debounced state of the button

void setup() {
  Serial.begin(115200); printf("\n");
  pinMode(SIG_OUT,OUTPUT);
  pinMode(SIG_DOUT,OUTPUT);
  pinMode(BUTTON,INPUT);
}

//Makes sure the button has held stable for DEBOUNCE_TIME before it assigns
//buttonState to the state of the button.
void debounceLoop(unsigned long now) {
  int reading=digitalRead(BUTTON);
  if(reading!=lastButtonState) {
    lastDebounceTime=now;
  }
  if((now-lastDebounceTime)>DEBOUNCE_TIME) {
    if(reading!=buttonState) {
      buttonChanged=true;
      buttonState=reading;
    }
  }
  lastButtonState=reading;
}

void loop() {
  long now=millis();
  if(HAS_EXPIRED(timer,FREQ)) {
    //only print every once in a while
    timer=now;
    Serial.printf("count=%d now=%ld\n",count,now);
  }
  pinStatus=digitalRead(BUTTON);      //current button status
  if(pinStatus!=status) {
    //status changed from what it was, record new state and count it
    status=pinStatus;
    count++;
  }
  debounceLoop(now);
  digitalWrite(SIG_OUT,status);       //directly to button
  digitalWrite(SIG_DOUT,buttonState); //debounced button 
}


Copyright © 2025, Lee Patterson