Adding debounce for switch input

I have been having some trouble with EMI in my setup caused by a variable frequency drive running. In an attempt to alleviate this issue I have tried to implement a debounce for the inputs.

I have a couple issues, first, when I first power up the arduino one of the output turns on with no button pressed (specifically the TS function runs as if TSI is LOW) after a couple of resets of the arduino the debounce seems to run correctly (I currently have it set to 1000ms so that I can confirm it is working)

Here is my code I removed all functions but 1 to stay in the limit

#include <Bounce2.h>

Bounce debouncerTSI = Bounce();
Bounce debouncerPLI = Bounce();
Bounce debouncerDPI = Bounce();
Bounce debouncerJLI = Bounce();
Bounce debouncerCBI = Bounce();
Bounce debouncerBI = Bounce();
Bounce debouncerSHI = Bounce();

// Initialize Inputs:
const int DPI = 22; //Drill Press
const int PLI = 23; //Planer
const int JLI = 24; //Jointer
const int CBI = 25; //Crescent Bandsaw
const int BI = 26; //Oliver Bandsaw
const int DCI = 27; //Dust collection
const int ACPI = 28; //Air Compressor
const int TI = 29; //No longer doing transformer on the arduino pin 29 is available 
const int SHI = 30; // Oliver Shaper
const int TSI = 31; // Oliver Table Saw



// Intialize Outputs:
const int AC = 35; // Relay 1 Air Compressor
const int DC = 37; // Relay 2 Dust Collection 
const int DP = 39; // Relay 3 Drill Press
const int PL = 41; // Relay 4 Planer
const int JL = 43; // Relay 5 Jointer
const int CB = 45; // Relay  6 Crescent Bandsaw
const int B = 47; // Relay 7 Oliver Bandsaw
const int VFD = 49; // Relay 8 VFD end switch 
const int SH = 36; // Relay 9 Oliver Shaper
const int TS = 38; // Relay 10 Oliver Table Saw
const int T = 40; // No longer doing transformer on the arduino pin 40 available for output 
const int EStop = 42; // Estop to VFD
const int SLed = 13; // stop function LED


// Variables that will change:
int DPIState = 0; // drill press switch state
int PLIState = 0; // planer switch state
int JLIState = 0; // jointer switch state
int CBIState = 0; // crescent bandsaw switch state
int BIState = 0; // oliver bandsaw switch state
int DCIState = 0; //manual dust collection button
int ACPIState = 0; // unsure of the use for this integer
//int TIState = 0; // Tranformer no longer needed so this variable is not needed
int ACState = 0; //air compressor input
int SHIState = 0; // shaper input state
int TSIState = 0; // Table Saw input state

unsigned long TimerDPON = 0;
unsigned long TimerDPOFF = 0;
unsigned long TimerPLON = 0;
unsigned long TimerPLOFF = 0;
unsigned long TimerJLON = 0;
unsigned long TimerJLOFF = 0;
unsigned long TimerCBON = 0;
unsigned long TimerCBOFF = 0; 
unsigned long TimerBON = 0;
unsigned long TimerBOFF = 0;
unsigned long TimerSHON = 0;
unsigned long TimerSHOFF = 0; 
unsigned long TimerTSON = 0;
unsigned long TimerTSOFF = 0;

//Intialize analog signals

unsigned long previousMillis = 0;
unsigned long interval = 10000;


void setup(){  
 
//Drive all relays to High to begin
digitalWrite(T, HIGH);
digitalWrite(DC, HIGH);
digitalWrite(AC, HIGH);
digitalWrite(DP, HIGH);
digitalWrite(PL, HIGH);
digitalWrite(JL, HIGH);
digitalWrite(CB, HIGH);
digitalWrite(B, HIGH);
digitalWrite(VFD, HIGH);
digitalWrite(SH, HIGH);
digitalWrite(TS, HIGH);
digitalWrite(SLed, LOW); //Make sure the LED is turned off


// Pin Outputs to Ouputs
pinMode(T, OUTPUT);
pinMode(DC, OUTPUT);
pinMode(AC, OUTPUT);
pinMode(DP, OUTPUT);
pinMode(PL, OUTPUT);
pinMode(JL, OUTPUT);
pinMode(CB, OUTPUT);
pinMode(B, OUTPUT);
pinMode(VFD, OUTPUT);
pinMode(SH, OUTPUT);
pinMode(TS, OUTPUT);
pinMode(SLed, OUTPUT);

  // Pin Inputs to Inputs:
pinMode(DPI, INPUT_PULLUP);
pinMode(PLI, INPUT_PULLUP);
pinMode(JLI, INPUT_PULLUP);
pinMode(CBI, INPUT_PULLUP);
pinMode(BI, INPUT_PULLUP);
pinMode(DCI, INPUT_PULLUP);
pinMode(ACPI, INPUT_PULLUP);
pinMode(TI, INPUT_PULLUP); // transformer input no longer needed for use on the arduino 
pinMode(SHI, INPUT_PULLUP);
pinMode(TSI, INPUT_PULLUP);

debouncerTSI.attach(TSI);
debouncerTSI.interval(1000);

debouncerPLI.attach(PLI);
debouncerPLI.interval(1000);

debouncerDPI.attach(DPI);
debouncerDPI.interval(1000);

debouncerJLI.attach(JLI);
debouncerJLI.interval(1000);

debouncerCBI.attach(CBI);
debouncerCBI.interval(1000);

debouncerBI.attach(BI);
debouncerBI.interval(1000);

debouncerSHI.attach(SHI);
debouncerSHI.interval(1000);

}

void loop() {
debouncerTSI.update();
debouncerPLI.update();
debouncerDPI.update();
debouncerJLI.update();
debouncerCBI.update();
debouncerBI.update();
debouncerSHI.update();

TSIState = debouncerTSI.read();
PLIState = debouncerPLI.read();
DPIState = debouncerDPI.read();
JLIState = debouncerJLI.read();
CBIState = debouncerCBI.read();
BIState = debouncerBI.read();
SHIState = debouncerSHI.read();

DCIState = digitalRead(DCI);
ACPIState = digitalRead(ACPI);
ACState = digitalRead(AC);



 drillpres();
 planer();
 jointer();
 cbandsaw();
 obandsaw();
 shaper();
 tablesaw();
 
}


void tablesaw(){

  if (TSIState == LOW && BIState == HIGH && CBIState == HIGH && JLIState == HIGH && PLIState == HIGH && DPIState == HIGH && SHIState == HIGH) {
    // turn Planer relay on and Turn on Inverter
   unsigned long currentTSMillis = millis();
    digitalWrite(DC, LOW);
    digitalWrite(TS, LOW);
    if (currentTSMillis - TimerTSON >= 1000){
    digitalWrite(VFD, LOW);
    TimerTSOFF = millis();
  }
  }
  else if (DPIState == LOW || PLIState == LOW || JLIState == LOW || CBIState == LOW || BIState == LOW || SHIState == LOW){
    
  }
  else {
    digitalWrite(VFD, HIGH);
    unsigned long currentTSMillis = millis();
    if (currentTSMillis - TimerTSOFF >= 20000){
    digitalWrite(TS, HIGH);
    digitalWrite(DC, HIGH);
    TimerTSON = millis();
  }
}
}

The second issue that I have (and I had this issue before putting in the debounce) is that when I shutdown one of the switches for instance TS my program should turn off VFD it should wait 20 secs then turn off the TS output and then the DC output. What actually happens is that the VFD turns off and the DC turns off right away and the program waits 20 secs and turns off TS output. I just can no figure out why/how this is happening.

Thanks in advance
Scott

Here is the rest of the functions in my code

void drillpres(){  


  if (DPIState == LOW && PLIState == HIGH && JLIState == HIGH && CBIState == HIGH && BIState == HIGH && SHIState == HIGH && TSIState == HIGH ) {
    // turn DP relay on and Turn on Inverter
    unsigned long currentDPMillis = millis();
    digitalWrite(DP, LOW);
    if (currentDPMillis - TimerDPON >= 1000){
    digitalWrite(VFD, LOW);
    TimerDPOFF = currentDPMillis;
    }
  }
  else if (PLIState == LOW || JLIState == LOW || CBIState == LOW || BIState == LOW || SHIState == LOW || TSIState == LOW){
    
  }
  else {
    digitalWrite(VFD, HIGH);
    unsigned long currentDPMillis = millis();
    if (currentDPMillis - TimerDPOFF >= 5000){
    digitalWrite(DP, HIGH);
    TimerDPON = currentDPMillis;
    
  }
  }
}


void planer(){

  if (PLIState == LOW && DPIState == HIGH && JLIState == HIGH && CBIState == HIGH && BIState == HIGH && SHIState == HIGH && TSIState == HIGH) {
    // turn Planer relay on and Turn on Inverter
    unsigned long currentPLMillis = millis();
    digitalWrite(DC, LOW);
    digitalWrite(PL, LOW);
    if (currentPLMillis - TimerPLON >= 1000){
    digitalWrite(VFD, LOW);
    TimerPLOFF = millis();
    }
  }
  else if (DPIState == LOW || JLIState == LOW || CBIState == LOW || BIState == LOW || SHIState == LOW || TSIState == LOW){
    
 }
  else {
    digitalWrite(VFD, HIGH);
    unsigned long currentPLMillis = millis();
    if (currentPLMillis - TimerPLOFF >= 20000){
    digitalWrite(PL, HIGH);
    digitalWrite(DC, HIGH);
    TimerPLON = millis();
  
  }
  }
}
 


void jointer(){

  if (JLIState == LOW && PLIState == HIGH && DPIState == HIGH && CBIState == HIGH && BIState == HIGH && SHIState == HIGH && TSIState == HIGH) {
    // turn Planer relay on and Turn on Inverter
    unsigned long currentJLMillis = millis();
    digitalWrite(DC, LOW);
    digitalWrite(JL, LOW);
    if (currentJLMillis - TimerJLON >= 1000){
    digitalWrite(VFD, LOW);
    TimerJLOFF = millis();
  }
  }
  else if (DPIState == LOW || PLIState == LOW || CBIState == LOW || BIState == LOW || SHIState == LOW || TSIState == LOW){
    
  }
  else {
    digitalWrite(VFD, HIGH);
    unsigned long currentJLMillis = millis();
    if (currentJLMillis - TimerJLOFF >= 20000){
    digitalWrite(JL, HIGH);
    digitalWrite(DC, HIGH);
    TimerJLON = millis();
  
  }
  }
  }


void cbandsaw(){

  if (CBIState == LOW && JLIState == HIGH && PLIState == HIGH && DPIState == HIGH && BIState == HIGH && SHIState == HIGH && TSIState == HIGH) {
    // turn Planer relay on and Turn on Inverter
    unsigned long currentCBMillis = millis();
    digitalWrite(DC, LOW);
    digitalWrite(CB, LOW);
    if (currentCBMillis - TimerCBON >= 1000){
    digitalWrite(VFD, LOW);
    TimerCBOFF = millis();
  }    
  }
  else if (DPIState == LOW || PLIState == LOW || JLIState == LOW || BIState == LOW || SHIState == LOW || TSIState == LOW){
    
  }
  else {
    digitalWrite(VFD, HIGH);
    unsigned long currentCBMillis = millis();
    if (currentCBMillis - TimerCBOFF >= 20000){
    digitalWrite(CB, HIGH);
    digitalWrite(DC, HIGH);
    TimerCBON = millis();
  }
}
}

void obandsaw(){

  if (BIState == LOW && CBIState == HIGH && JLIState == HIGH && PLIState == HIGH && DPIState == HIGH && SHIState == HIGH && TSIState == HIGH) {
    // turn Planer relay on and Turn on Inverter
    unsigned long currentBMillis = millis();
    digitalWrite(DC, LOW);
    digitalWrite(B, LOW);
    if (currentBMillis - TimerBON >= 1000){
    digitalWrite(VFD, LOW);
    TimerBOFF = millis();
  } 
  }
  else if (DPIState == LOW || PLIState == LOW || JLIState == LOW || CBIState == LOW || SHIState == LOW || TSIState == LOW){
    
  }
  else {
    digitalWrite(VFD, HIGH);
    unsigned long currentBMillis = millis();
    if (currentBMillis - TimerBOFF >= 20000){
    digitalWrite(B, HIGH);
    digitalWrite(DC, HIGH);
    TimerBON = millis();
  }
  }
}

void shaper(){

  if (SHIState == LOW && BIState == HIGH && CBIState == HIGH && JLIState == HIGH && PLIState == HIGH && DPIState == HIGH && TSIState == HIGH) {
    // turn Planer relay on and Turn on Inverter
    unsigned long currentSHMillis = millis();
    digitalWrite(DC, LOW);
    digitalWrite(SH, LOW);
    if (currentSHMillis - TimerSHON >= 1000){
    digitalWrite(VFD, LOW);
    TimerSHOFF = millis();
  } 
  }
 else if (DPIState == LOW || PLIState == LOW || JLIState == LOW || CBIState == LOW || BIState == LOW || TSIState == LOW){
    
  }
  else {
    digitalWrite(VFD, HIGH);
    unsigned long currentSHMillis = millis();
    if (currentSHMillis - TimerSHOFF >= 20000){
    digitalWrite(SH, HIGH);
    digitalWrite(DC, HIGH);
    TimerSHON = millis();
  }
  }

}

I personally never liked libraries for simple stuff.

here is a simple debounce routine that is easy to understand and is non-blocking.

const byte button = 5;
const byte ledPin = 13;
void setup() {
  pinMode (button,INPUT_PULLUP);
  pinMode(ledPin,OUTPUT);
}

void loop() {
  byte buttonState = buttonCheck();
  if (buttonState == 1) {
    digitalWrite (ledPin,HIGH);
    delay (250);     // hate delay but its just an example
  } else {
    digitalWrite (ledPin,LOW);
  }
}

byte buttonCheck()   // Returns a positive result Once per press
{
  static int isButtonPressed = 0;

  if (digitalRead(button) == LOW)   //  button reads LOW when Pressed
  {
    isButtonPressed++;              // Increment Debounce variable
  } else {
    isButtonPressed = 0;            // if it bounces reset
  }

  if (isButtonPressed == 10)    //number of consecutive positive reads to be accepted as a legitimate press 
  {                                                // Adjust as needed
    return 1;               // Confirmed button Press
  } else {
    return 0;               // button not pressed
  }
}

There are several debounce libraries in playground.arduino.cc .

My debounce library is here.

If this was my problem I would go back to basics. I would get rid of all the debounce stuff and I would write a small function that reads and saves the switch values. Then I would add a temporary function to print the values so I could see what is happening. I would use that knowledge to decide what action (if any) I needed to get reliable switch values. You can use manual switching of the machine to stimulate [sic] any interference that might arise.

When I had reliable switch values I would then move on to the control logic.

This sort of complex IF statement can easily be a source of silly errors

if (TSIState == LOW && BIState == HIGH && CBIState == HIGH && JLIState == HIGH && PLIState == HIGH && DPIState == HIGH && SHIState == HIGH) {

There are 7 tests which means 127 ways to get it wrong and no way to check that parts of it work properly.

...R
Planning and Implementing a Program

I would go even further back to basics and investigate the EMI.

aarg:
I would go even further back to basics and investigate the EMI.

Implicit in my "back to basics" was a doubt whether EMI is the cause of the problem.

...R

Robin2:
Implicit in my "back to basics" was a doubt whether EMI is the cause of the problem.

...R

Right, but even if it isn't that problem, it can be the cause of so many other problems that it should be addressed anyway. That requires methods other than software scoping.

aarg:
I would go even further back to basics and investigate the EMI.

Robin2:
Implicit in my "back to basics" was a doubt whether EMI is the cause of the problem.

...R

So heres what I have done to help reduce the EMI, I am open to any and all suggestions. I will say though, the program (before the debounce was added) and the relay setup all function with no issues when the VFD is not powered up... When the VFD is powered I get erroneous switching of control relays i.e. when the TS is running the PL relay will switch on and off... So the EMI is doing something, however cleaning up the program is a must...

  • Braided shielded VFD cable from the drive to the motors
  • Bonded metal conduit from drive to all motors
  • twisted shielded (braided shield) control cable (shield is grounded just one 1 end)
  • all grounds in a star configuration
  • put the arduino into a metal enclosure that is grounded
  • added a new ground bar into the ground so that the VFD and all shield grounds could be isolated from the building ground

These things all helped a little but did not completely eliminate the problem.... Again I am open to suggestions if anyone has any.

Next steps
opto-isolators on the input of the Arduino
better isolated power supply for the arduino
opto-isolation on the low voltage relay board

Robin, to go back to your point I would love to get rid of all the of the If statements. However I need some help there. The If statements were added to make sure only one machine could be triggered at a time. Is there a way I can have the arduino run just the function that is being called for? Then I will need to re-look at my millis timers as they will probably not work correctly...

Just one thought on the EMI... you've covered the grounding very well. What about power distribution and filtering? Maybe it's getting into the Arduino through the power supply. Also, what does your input circuit look like? Do you have pullup resistors, if so what resistance value? Maybe the input resistance is too high, which would increase the susceptibility to noise.

ScottDamman:
Robin, to go back to your point I would love to get rid of all the of the If statements. However I need some help there. The If statements were added to make sure only one machine could be triggered at a time.

I'm sure the IF statements are necessary. What I was commenting on was putting them all in one statement. If it was my project I would cascade the tests like

if (TSIState == LOW) {
 if( BIState == HIGH) {
   if(CBIState == HIGH) {  
    // etc

That allows you to stick in Serial.print() statements to see if a particular stage in the logic is reached.

You may also see that one test is more significant than others and put it first so the program does not need to bother testing all the other things.

And I would make it more obvious with more meaningful names - something like

if (greenDevice == working) {

so that there was absolutely no need to wonder what TSIState represents or whether LOW means it is working or stopped. (Obviously replace greenDevice and working with words that are relevant for your project)

...R

If you're still looking for another example to debounce a switch or button, then read on :slight_smile:

For a project using a PIC mcu (yes not the Arduino, I know...) I wrote a simple button debounce library. It is written mostly in C. Debouncing a button does not block the main loop because it is using the millis function.

A handy function is to wire a button click (either long button click or short button click) to a function. So when your button/switch is clicked it will execute the function you wired it to.

Example code:

#include<Button.h> // Use '<' and '>' once put in the arduino installation folder
// #include"Button.h" // Files are put in your sketch folder

Button myButton;

void setup() 
{
  pinMode(13, OUTPUT);
  configureButton(&myButton, 8, PULL_DOWN); // Configure your button with a given I/O pin and your default state (note: it does NOT use internal pull-ups and pull-downs, you will have to apply them yourself)
  wireOnClick(&myButton, &toggleLedShortClick); // Wire a short button click to a function
  // wireOnLongClick(&myButton, &toggleLedLongClick); // Wire a long button click to a function

}

void loop() 
{
  boolean currentState = readButtonDebounce(&myButton); // Read button
  // Do something with currentState 
}

void toggleLedShortClick()
{
  digitalWrite(13, !digitalRead(13));
}

Maybe it can be useful for your project, otherwise just ignore this post :)!

Back on the EMI subject, did you try to put a low pass filter between your button and the arduino input?

Button.zip (1.34 KB)

Robin2:
I'm sure the IF statements are necessary. What I was commenting on was putting them all in one statement. If it was my project I would cascade the tests like

if (TSIState == LOW) {

if( BIState == HIGH) {
  if(CBIState == HIGH) { 
    // etc



That allows you to stick in Serial.print() statements to see if a particular stage in the logic is reached.

You may also see that one test is more significant than others and put it first so the program does not need to bother testing all the other things.

And I would make it more obvious with more meaningful names - something like


if (greenDevice == working) {



so that there was absolutely no need to wonder what TSIState represents or whether LOW means it is working or stopped. (Obviously replace `greenDevice` and `working` with words that are relevant for your project)

...R

Im going to clean up the program using your suggestions and see what is causing the issue. Thank you.

Im sure I will have more questions but first time to clean up the program and find out why the DC is turning off right away rather than waiting for the 20 secs like the program shows... something else in the program is turning it off just not sure where.

Did you really intend to "do nothing" in this if statement?:

  else if (DPIState == LOW || PLIState == LOW || JLIState == LOW || CBIState == LOW || BIState == LOW || SHIState == LOW) {

  }

aarg:
Did you really intend to "do nothing" in this if statement?:

  else if (DPIState == LOW || PLIState == LOW || JLIState == LOW || CBIState == LOW || BIState == LOW || SHIState == LOW) {

}

That else if is there to prevent the other else statement from turning the VFD off while one of the other functions is calling for it...

ScottDamman:
That else if is there to prevent the other else statement from turning the VFD off while one of the other functions is calling for it...

I hope that may be more obvious to you when you restructure your code.

...R

Robin2:
I hope that may be more obvious to you when you restructure your code.

...R

I wish it was more obvious, when I read through my code I see that each function can turn the VFD output HIGH in its else statement. So I was going down the path that if any of the functions are calling for VFD i.e. any of the switches are low I dont want to run the else of the other functions. Based on your comment I assume I dont need those but I need a little education then???

Thanks for the help