Push button program

Hello,

I come across this cool code to demonstrate the use of a toggle switch.

When pressing the push button, the LED light connected to port 13 would go ON or OFF (depending on the previous state of the light).

What I don't understand is the delay code inside the while loop at the end of the program.
Whenever I remove it and then press the push button, the LED light isn't responding.

Can someone please explain to me the purpose of the delay code as well as that of the while loop ?

/*********************
Simple toggle switch
Created by: P.Agiakatsikas
*********************/

int button = 8;
int led = 13;
int status = 1;

void setup(){
pinMode(led, OUTPUT);
pinMode(button, INPUT_PULLUP); // set the internal pull up resistor, unpressed button is HIGH
}
void loop(){
//a) if the button is not pressed the false status is reversed by !status and the LED turns on
//b) if the button is pressed the true status is reveresed by !status and the LED turns off

if (digitalRead(button) == true) {
status = !status;
digitalWrite(led, status);
} while(digitalRead(button) == true);
delay(50); // keeps a small delay
}

The delay probably provides debounce.
By the way, digitalRead is documented to return HIGH or LOW. true happens to work currently.

The delay code is NOT INSIDE the while loop. The while loop does nothing, as there is nothing between the while condition and the ; This is saying while the button is still held down and for as long as the button is held down, do nothing. Then, wait 50 milliseconds before doing anything, time for the button to reach a stable state.

Coding like this makes it very difficult to build on, as it essentially blocks all other code you might add from executing. To take this example and use it for toggling anything, while doing other things, would be disastrous.

come across this cool code to demonstrate the use of a toggle switch.

It is OK until you want to do something else in loop(), at which point you will find that the code is blocked whilst the input is LOW

Yes, learning to use millis() would be better than delay(...) in the long run.

/* The Push-button example I wish I had when I started
 * by Perehama, credit to PieterP for the push-button class idea
 */

class IntervalTimer {
  public:
    IntervalTimer(unsigned long i);
    void synchronizeTimer();
    bool intervalComplete();
    void setInterval(unsigned long i);
    unsigned long elapsedTime();
  private:
    unsigned long _interval;
    unsigned long _timestamp;
};

class PushButton {
  public:
    PushButton(byte pinNumber);
    void inputPullup() const;
    bool isPushed();
    unsigned long durationHeld();
    void setDebounce(unsigned long i);
  private:
    IntervalTimer _timer;
    int _preState = HIGH;
    byte _debounceFlag = 0;
    const byte _ButtonPin;
    const static int _RisingEdge = HIGH - LOW;
    const static int _FallingEdge = LOW - HIGH;
};

IntervalTimer::IntervalTimer(const unsigned long i) : _interval(i) {}

void IntervalTimer::synchronizeTimer() {
  _timestamp = millis();
}
bool IntervalTimer::intervalComplete() {
  if (millis() - _timestamp >= _interval) {
    _timestamp += _interval;
    return true;
  }
  return false;
}

void IntervalTimer::setInterval(unsigned long i) {
  _interval = i;
}

unsigned long IntervalTimer::elapsedTime() {
  unsigned long x = millis() - _timestamp;
  return x;
}

PushButton::PushButton(byte pinNumber) : _timer(50UL), _ButtonPin(pinNumber) { }

void PushButton::inputPullup() const {
  pinMode(_ButtonPin, INPUT_PULLUP);
}

bool PushButton::isPushed() {
  bool b = false;
  if (_debounceFlag == 1) {
    if (_timer.intervalComplete()) _debounceFlag = 0;
  }
  else {
    int sample = digitalRead(_ButtonPin);
    int state = sample - _preState;
    if (state == _FallingEdge) {
      _timer.synchronizeTimer();
      b = true;
      _debounceFlag = 1;
    }
    else if (state == _RisingEdge) {
      _timer.synchronizeTimer();
      _debounceFlag = 1;
    }
    _preState = sample;
  }
  return b;
}

unsigned long PushButton::durationHeld() {
  unsigned long u = 0;
  if (_preState == LOW) {
    u = _timer.elapsedTime();
  }
  return u;
}

void PushButton::setDebounce(unsigned long i) {
  _timer.setInterval(i);
}

const byte ButtonPin = 8;
const byte LedPin = LED_BUILTIN;
byte ledFlag;

PushButton Button(ButtonPin);

void setup() {
  Button.inputPullup();
  pinMode(LedPin, OUTPUT);
}

void loop() {
  if (Button.isPushed()) {
    ledFlag = (ledFlag) ? 0 : 1;
  }
  (ledFlag) ? digitalWrite(LedPin, HIGH) : digitalWrite(LedPin, LOW);
}
1 Like

Perehama:

/* The Push-button example I wish I had when I started
  • by Perehama, credit to PieterP for the push-button class idea
    */

class IntervalTimer {
 public:
   IntervalTimer(unsigned long i);
   void synchronizeTimer();
   bool intervalComplete();
   void setInterval(unsigned long i);
   unsigned long elapsedTime();
 private:
   unsigned long _interval;
   unsigned long _timestamp;
};

class PushButton {
 public:
   PushButton(byte pinNumber);
   void inputPullup() const;
   bool isPushed();
   unsigned long durationHeld();
   void setDebounce(unsigned long i);
 private:
   IntervalTimer _timer;
   int _preState = HIGH;
   byte _debounceFlag = 0;
   const byte _ButtonPin;
   const static int _RisingEdge = HIGH - LOW;
   const static int _FallingEdge = LOW - HIGH;
};

IntervalTimer::IntervalTimer(const unsigned long i) : _interval(i) {}

void IntervalTimer::synchronizeTimer() {
 _timestamp = millis();
}
bool IntervalTimer::intervalComplete() {
 if (millis() - _timestamp >= _interval) {
   _timestamp += _interval;
   return true;
 }
 return false;
}

void IntervalTimer::setInterval(unsigned long i) {
 _interval = i;
}

unsigned long IntervalTimer::elapsedTime() {
 unsigned long x = millis() - _timestamp;
 return x;
}

PushButton::PushButton(byte pinNumber) : _timer(50UL), _ButtonPin(pinNumber) { }

void PushButton::inputPullup() const {
 pinMode(_ButtonPin, INPUT_PULLUP);
}

bool PushButton::isPushed() {
 bool b = false;
 if (_debounceFlag == 1) {
   if (_timer.intervalComplete()) _debounceFlag = 0;
 }
 else {
   int sample = digitalRead(_ButtonPin);
   int state = sample - _preState;
   if (state == _FallingEdge) {
     _timer.synchronizeTimer();
     b = true;
     _debounceFlag = 1;
   }
   else if (state == _RisingEdge) {
     _timer.synchronizeTimer();
     _debounceFlag = 1;
   }
   _preState = sample;
 }
 return b;
}

unsigned long PushButton::durationHeld() {
 unsigned long u = 0;
 if (_preState == LOW) {
   u = _timer.elapsedTime();
 }
 return u;
}

void PushButton::setDebounce(unsigned long i) {
 _timer.setInterval(i);
}

const byte ButtonPin = 8;
const byte LedPin = LED_BUILTIN;
byte ledFlag;

PushButton Button(ButtonPin);

void setup() {
 Button.inputPullup();
 pinMode(LedPin, OUTPUT);
}

void loop() {
 if (Button.isPushed()) {
   ledFlag = (ledFlag) ? 0 : 1;
 }
 (ledFlag) ? digitalWrite(LedPin, HIGH) : digitalWrite(LedPin, LOW);
}

Thank you so much for posting this.
You kind of anticipated my reaction to your first post.

I went through the program.
I don't see which PIN would be assign to the push button to set its pinMode to INPUT.
Do you have the schematic that comes with this program?

UKHeliBob:
It is OK until you want to do something else in loop(), at which point you will find that the code is blocked whilst the input is LOW

Thank you very much UKHeliBob
I wish I had as much knowledge as you do.
What is your secret ?

CanadaForces:
I went through the program.
I don't see which PIN would be assign to the push button to set its pinMode to INPUT.
Do you have the schematic that comes with this program?

The pin assignment is about 96 lines in... Just above setup(). Yes, the class coding may seem daunting and needlessly complicated for a novice programmer, so it was a bit tongue and cheek, but honestly, it gives a lot more guidance to where you want your programs to grow.
pinMode for the button is set for you with the line Button.inputPullup().
Schematic is pretty standard, see below. The pull-up resistor works like INPUT_PULLUP, only more reliable. The cap squares off the waveform, so high to low and low to high is more up and down and less sawtooth. The diode forces the switch to act only in pull-down fashion, in case of transients or static electricity etc.

What is your secret ?

No secret. I have just picked things up in my 45 years of programming

This has been attributed to Mark Twain:
"Good judgement is the result of experience and experience the result of bad judgement.”

In any case, whenever I hear from UKHeliBob, I listen.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.