Bouncy Debounce with 2 Momentary Buttons and Interrupt Routine

I cannot seem to get a clean button press recognition using the examples for Debouncing on the forum. I have two momentary buttons tied to digital pins 14 and 15 and I have internal-pullups enabled (but I have also tried with external 10K resistors) and the other end of the buttons to GND. I also have the +5v side of each button tied to Interrupt 1 through N4148 diodes. I have the ISR triggering on the falling edge.

My code works fine but every 4th-5th button press, even with very slow purposeful button presses, the release triggers another interrupt. Looking at this with a scope the transients giving the false trigger seem to be occurring 200 microseconds BEFORE the definitive button press. It almost looks like I am hesitating on the press. Since it happens before not after, I dont think the debounce delay has any effect. I changed buttons and types a few times with no help. Should I reverse the polarity of the buttons and have them pulled down to ground and then put a capacitor across ?

/*
 Button 1 connected to D14 (input with pull up but also tried external 10K resistor to +5V
 Button 2 connected to D15 (input with pull up but also tried external 10K resistor to +5V
 */

#define SERIAL_BAUD 115200

const int PWM_button = 14;   //for changing PWM pin action
const int DIR_button = 15;   //for changing DIR pin direction
const int INT1_Pin = 3;  //only interrupt available on Moteino

const int PWM_Out_Pin = 5;      
const int DIR_Out_Pin = 7;
const int DutyCycle = 127;

int button1;
int button2;

int lastPWM_buttonState = LOW;   // the previous button1 from the input pin
int lastDIR_buttonState = LOW;   // the previous button1 from the input pin

unsigned long last_micros_INT1;
unsigned long debouncing_time = 250; 

boolean INT1_flag = false;
boolean pwm_flag = false;  //intial state is off
boolean dir_flag = false; // initial state is off 

void setup() {
  Serial.begin(SERIAL_BAUD);
  pinMode(PWM_button,INPUT_PULLUP);
  pinMode(DIR_button,INPUT_PULLUP);
  pinMode(INT1_Pin,INPUT_PULLUP);
  pinMode(PWM_Out_Pin, OUTPUT);
  pinMode(DIR_Out_Pin, OUTPUT);

  // set initial state
  analogWrite(PWM_Out_Pin, LOW);  
  digitalWrite(DIR_Out_Pin, LOW);

  attachInterrupt(1, debounce_INT1, FALLING);  
}

void loop() {
  if (INT1_flag){ 
    button1 = !digitalRead(PWM_button); 
    button2 = !digitalRead(DIR_button); 
    ProcessButtons();  
    INT1_flag = false;  //reset for next interrupt  
    last_micros_INT1 = 0; // reset for next interrupt 
  }
} 

void ProcessButtons()  
{
  if (button1)  
  {
    if (pwm_flag == false){  //if last PWM state was OFF --> set PWM_Out_Pin to ON
      analogWrite(PWM_Out_Pin,DutyCycle);
      pwm_flag = true;  // set to new state
    }
    else {
      analogWrite(PWM_Out_Pin, LOW);
      pwm_flag = false;  // set to new state
    }
  }

  if (button2)  
  {
    if (dir_flag == false){  
      digitalWrite(DIR_Out_Pin,HIGH);
      dir_flag = true;  // set to new state
    }
    else {
      digitalWrite(DIR_Out_Pin, LOW);
      dir_flag = false;  // set to new state
    }
  }  
}


void debounce_INT1(){
  if(((unsigned long)(micros() - last_micros_INT1) >= debouncing_time * 1000)) {
    last_micros_INT1 = micros();
    INT1_flag = true;  //set flag but make sure to reset it later
  }
}

Why do you believe you need interrupts for button handling?

This would most certainly depend on the construction of the button. Some switches are lubricated with a conductive lubricant which would potentially produce tentative contacts before the main conductive parts (which may be carbon, not metal) fully contact.

Using interrupts inappropriately to detect extremely slow events, such as manual pushbuttons or encoders, is going to be something of an exercise in futility. It can be done, but the code gets ridiculous. Here is my example of polled, de-bounced pushbutton and asynchronous LED flashing code:

// Blink without "delay()" - multi!

const int led1Pin =  13;    // LED pin number
const int led2Pin =  10;
const int led3Pin =  11;
const int button1 =  4;
int led1State = LOW;        // initialise the LED
int led2State = LOW;
int led3State = LOW;
char bstate1 = 0;
unsigned long count1 = 0;   // will store last time LED was updated
unsigned long count2 = 0;
unsigned long count3 = 0;
unsigned long bcount1 = 0; // button debounce timer.  Replicate as necessary.

// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check 
boolean timeout(unsigned long *marker, unsigned long interval) {
  if (millis() - *marker >= interval) { 
    *marker += interval;    // move on ready for next interval
    return true;       
  } 
  else return false;
}

// Deal with a button read; true if button pressed and debounced is a new event
// Uses reading of button input, debounce store, state store and debounce interval.
boolean butndown(char button, unsigned long *marker, char *butnstate, unsigned long interval) {
  switch (*butnstate) {               // Odd states if was pressed, >= 2 if debounce in progress
  case 0: // Button up so far, 
    if (button == HIGH) return false; // Nothing happening!
    else { 
      *butnstate = 2;                 // record that is now pressed
      *marker = millis();             // note when was pressed
      return false;                   // and move on
    }

  case 1: // Button down so far, 
    if (button == LOW) return false; // Nothing happening!
    else { 
      *butnstate = 3;                 // record that is now released
      *marker = millis();             // note when was released
      return false;                   // and move on
    }

  case 2: // Button was up, now down.
    if (button == HIGH) {
      *butnstate = 0;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 1;               // jackpot!  update the state
        return true;                  // because we have the desired event!
      }
      else 
        return false;                 // not done yet; just move on
    }

  case 3: // Button was down, now up.
    if (button == LOW) {
      *butnstate = 1;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 0;               // Debounced; update the state
        return false;                 // but it is not the event we want
      }
      else 
        return false;                 // not done yet; just move on
    }
  default:                            // Error; recover anyway
    {  
      *butnstate = 0;
      return false;                   // Definitely false!
    }
  }
}

void setup() {
  pinMode(led1Pin, OUTPUT);      
  pinMode(led2Pin, OUTPUT);      
  pinMode(led3Pin, OUTPUT);      
  pinMode(button1, INPUT);      
  digitalWrite(button1,HIGH);        // internal pullup all versions
}

void loop() {
  // Toggle LED if button debounced
  if (butndown(digitalRead(button1), &bcount1, &bstate1, 10UL )) {
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW; 
    } 
    digitalWrite(led1Pin, led1State);
  } 

  // Act if the latter time (ms) has now passed on this particular counter,
  if (timeout(&count2, 300UL )) {
    if (led2State == LOW) {
      led2State = HIGH;
    }
    else {
      led2State = LOW; 
    } 
    digitalWrite(led2Pin, led2State);
  } 

  if (timeout(&count3, 77UL )) {
    if (led3State == LOW) {
      led3State = HIGH;
    }
    else {
      led3State = LOW; 
    } 
    digitalWrite(led3Pin, led3State);
  } 
}

Here is Button Watcher, a tool that lets you see how much debounce time your button needs.
Next post I will put a up a small (work in progress) library with button class and example code.

Ho-Ho-Ho!

// This is button_watcher, a test for button bounce --- free and open code Oct 25, 2014 by GFS
// Revised Nov 7, 2014 including many comments on operation of the sketch.

// This sketch can be used to check buttons for needed debounce time.
// If you get multiple UP and DOWN messages for 1 press or 1 release 
// then increase debounce time which is in microseconds.
// The goal is to have 1 and only 1 DOWN message for any press and
// 1 and only 1 UP message for any release.

/*
As you press and release your button/switch/jumper,

You will see are some lines that start with ^ or _ and a number.
Lines that start with ^ are button release and microseconds down before the release.
Lines that start with _ are button press and microseconds before the press.

Those lines are only information to show bounces the routine filtered out.
They are not considered stable UP or DOWN yet.

If the debounce time is too short, you will see multiple messages with UP or DOWN
in them. Increase debounce time. Different buttons will need different debounce.

You can always debounce extra. Remember debounce values is microseconds.

Lines that have the word UP or DOWN tell how many times the sketch checked the
button since last stable change. This is just to give the user some idea as to
how fast the sketch is running those checks, already slowed down by Serial printing.
Meaning....
If you add other code, you can see the effect in the checks made.
*/

byte ledPin = 13;
byte buttonPin = 7; // my button is a jumper wire grounded on the metal USB port box.

unsigned long btNow, btStart, btDebounce = 10000UL; // bt = button time. 10ms debounce

byte pinStates; // always the current state is in bit 0 and the previous is in bit 1
// value --- meaning with pin mode INPUT_PULLUP.
// 0 --- no change, pressed now and pressed last check
// 1 --- changed, released now, pressed last check
// 2 --- changes, pressed now, released last check
// 3 --- no change, released now and released last check

byte buttonState; // this only changes when the pin states are stable for some time.
byte buttonPrev; // this only changes when the pin states are stable for some time.

unsigned long checkCounter; // how many checks since last stable change


void setup( void )
{
  Serial.begin( 115200 );
  Serial.println( "\n  Button Bounce Check" );

  pinMode( ledPin, OUTPUT );  // default os LOW
  pinMode( buttonPin, INPUT_PULLUP );  // default is pullup HIGH
  pinStates = 1; // sets initial pin state as not pressed

  // buttonState logic will be 0=false for stable not pressed, 1=true for stable pressed
}

inline void buttonHandler( void ) // just so the cleanliness police don't freak out
{
  pinStates &= 1; // mask off the old previous state bit, only bit 0 data is left
  pinStates <<= 1; // move current pin state bit left 1 into previous pin state bit

  pinStates += digitalRead( buttonPin ); // read the current pin state into bit 0

  btNow = micros(); // time at loop start and read pin state to within 4 usecs

    switch ( pinStates )  // IDE autoformat indents this line. Why?
  {
  case  0 :  // 0 --- no change, pressed now and pressed last check
    if ( btNow - btStart >= btDebounce )
    {
      buttonState = 1; // button is pressed now long enough to be considered stable
      btStart = btNow;  
    }
    break;

  case  1 :  // 1 --- changed, released now, pressed last check
    Serial.print( '^' ); // show the bounce 
    Serial.println( btNow - btStart ); // show the micros since last stable or change
    btStart = btNow;  
    break;

  case  2 :  // 2 --- changes, pressed now, released last check
    Serial.print( '_' ); // show the bounce
    Serial.println( btNow - btStart ); // show the micros since last stable or change
    btStart = btNow; 
    break;

  case  3 :  // 3 --- no change, released now and released last check
    if ( btNow - btStart >= btDebounce )
    {
      buttonState = 0; // button is released now long enough to be considered stable
      btStart = btNow;  
    }
    break;
  }
}

void loop( void )
{
  buttonHandler( ); // this code only watches the button, job never finished
  checkCounter++;

  // this if() block only watches buttonState for change, job never finished
  if ( buttonState != buttonPrev ) // button stable state change event
  {
    buttonPrev = buttonState;
    digitalWrite( ledPin, buttonState );
    Serial.print( F( "\nButton is " ));
    if ( buttonState ) // if not zero, it is true
    {
      Serial.print( F( " DOWN " ));
    }
    else
    {
      Serial.print( F( " UP " ));
    }
    Serial.print( F( "after " ));
    Serial.print( checkCounter );
    Serial.println( F( " checks.\n" ));
    checkCounter = 0UL;
  }
}

Example for using blinker and button classes.

// ioclasses demo io_events_2  Dec 21, 2014 by GoForSmoke
/*
  This sketch uses 2 classes from ioclasses.h to make led13
 blink and grounded jumpers on pins 6 & 7 to control the rate.
 Ground pin 6 and the rate adds 100ms. Pin 7 subtracts 100ms.
 
 Button output is a state value for use in sketch code.
 -1, Undecided, pin state changed at least debounce-time ago.
 0, button currently still not pressed.
 1, button currently still pressed.
 2, button released since last checked by sketch. 
 3, button pressed since last checked by sketch.
 The sketch doesn't have to track previous output state. 
 */

#include <IOClasses.h>

// led13( blink-rate, arduino-pin, port, mask-to-toggle-PIN-reg ); 
ioblinker led13( 500, 13, 'B', 0x20 ); // pin 13 is port B pin 5 
// button-name( arduino-pin, millis-debounce );
iobutton  goslower( 6, 20UL );
iobutton  gofaster( 7, 20UL );
iobutton  *buttons[ 2 ] = 
{ 
  &goslower, &gofaster 
};
byte buttonIdx, buttonRead;

unsigned long val;


void setup( void )
{
  Serial.begin( 115200 );
  Serial.println( );
  Serial.println( F( "\n  Startup\n" ));
  // object starts set pinmodes and states
  led13.startBlinker( );
  led13.setPin( LOW );
  gofaster.startButton();
  goslower.startButton();
}

void loop( void )
{
  // gotta run them
  led13.runBlinker( );

  // by only running 1 button per loop(), many buttons don't load it.
  buttons[ buttonIdx ]->runButton( ); // only running 1 button per loop

  // now using button data to change blink rate
  if ( !buttonIdx ) // only check the button that was run
  { 
    buttonRead = buttons[ buttonIdx ]->readOutput();
    if ( buttonRead == 3 ) // 1st read since pressed
    {
      Serial.print( F( "slower " ));
      val = led13.getWait();
      if ( val < 9900UL )
      {
        val += 100UL;
      }    
      else
      {
        val = 10000UL;
      }
      led13.setBlinker( val ); 
      Serial.println( val );
    }
  }

  if ( buttonIdx ) // only check the button that was run
  { 
    // now using button data to change blink rate
    byte buttonRead = buttons[ buttonIdx ]->readOutput();
    if ( buttonRead == 3 ) // 1st read since pressed
    {
      Serial.print( F( "faster " ));
      val = led13.getWait();
      if ( val > 100UL )
      {
        val -= 100UL;
      }    
      else
      {
        val = 0;
      }
      led13.setBlinker( val ); 
      Serial.println( val );
    }
  }

  buttonIdx ^= 1; // toggles bit 0
}

And the library .h and .cpp files. Again, the library is set to be added to.

// ioclasses library, ongoing work Dec 21, 2014 by GoForSmoke

#ifndef ioclasses_h
#define ioclasses_h

class ioblinker
{
private:
  unsigned long startMillis;
  unsigned long waitMillis;
  unsigned char arduPin;
  char port;
  unsigned char pinsToggleMask;

public:
  ioblinker( unsigned long, unsigned char, char, unsigned char );
  void setBlinker( unsigned long wM ); // wM == 0UL turns the blink off
  void startBlinker( void );
  void setPin( unsigned char );
  void runBlinker( void ); // this runs every time loop() runs
  unsigned long getWait( void );
};

class iobutton
{
private:
  unsigned char arduPin;
  unsigned char stateHistory; // bit 0 = now, bit 1 = prev
  unsigned long startMillis;
  unsigned long debounceMillis; 
  char buttonOut; // 5-state as below

public:
  iobutton( char, unsigned long ); // pin, debounce millis
  void startButton( void );
  void runButton( void );
  char readOutput( void ); // 3-state UNDECIDED = -1, OFF = 0, ON = 1
// 5-state UNDECIDED = -1, OFF = 0, ON = 1, justOFF = 2, justON = 3
};


#endif
// ioclasses library, ongoing work Dec 21, 2014 by GoForSmoke

#include "Arduino.h"
#include "ioclasses.h"

//    blinker =================================================

ioblinker::ioblinker( unsigned long w, unsigned char a, char p, unsigned char m )
{
  startMillis = 0UL;
  waitMillis = w; 
  arduPin = a;
  port = p;
  pinsToggleMask = m;
};

void ioblinker::setBlinker( unsigned long w )
{
  waitMillis = w; 
};

void ioblinker::startBlinker( void )
{
  pinMode( arduPin, OUTPUT );
  startMillis = millis();
};

void ioblinker::setPin( unsigned char s )
{
  waitMillis = 0;
  digitalWrite( arduPin, s );
};


void ioblinker::runBlinker( void )
{
  if ( !waitMillis )  return;

  if ( millis() - startMillis >= waitMillis )
  {
    switch ( port | 32 )
    {
    case 'b' :
      PINB = pinsToggleMask;  // writes 1 to each bit in the PINSB register that will get toggled
      break;
    case 'c' :
      PINC = pinsToggleMask;  // writes 1 to each bit in the PINSB register that will get toggled
      break;
    case 'd' :
      PIND = pinsToggleMask;  // writes 1 to each bit in the PINSB register that will get toggled
      break;
    default :
      waitMillis = 0UL;
    }
    startMillis += waitMillis;
  }
};

unsigned long ioblinker::getWait( void )
{
  return waitMillis;
};

//    end blinker =============================================

//    button ================================================

iobutton::iobutton( char ap, unsigned long dbm )
{
  arduPin = ap;
  debounceMillis = dbm;
  buttonOut = -1;
};

void iobutton::startButton( void )
{
  pinMode( arduPin, INPUT_PULLUP );
}

void iobutton::runButton( void )
{
  stateHistory &= 1;  // clears all but the last read
  stateHistory <<= 1; // shifts last read to bit 1
  stateHistory += digitalRead( arduPin ); // current state to bit 0

  switch ( stateHistory ) // set for INPUT_PULLUP 
  {
  case 0 : // low - low     pressed
  case 3 : // high - high   released
    if ( buttonOut < 0 )
    {
      if ( millis() - startMillis >= debounceMillis )
      {
        if ( stateHistory == 0 )
        {
          buttonOut = 3; // button pressed - just changed
        }
        else
        {
          buttonOut = 2; // button released - just changed
        }
      }
    }
    break;
  case 1 : // high - low   if state change, debounce!
  case 2 : // low - high 
    buttonOut = -1;
    startMillis = millis() & 0xFF;
  }
};

char iobutton::readOutput( void )
{
  if ( buttonOut < 0 )  return buttonOut;
  startMillis = buttonOut;
  if ( buttonOut > 1 )  buttonOut -= 2; // see change only once 
  return startMillis; // debounce over, it's not being used
};

//    end button ============================================

[quote author=Coding Badly date=1419486349 link=msg=2015363]
Why do you believe you need interrupts for button handling?[/quote]
This is also where I start from.

There is a simple debounce system in several things at a time and the more recent planning and implementing a program does not need any.

Debouncing is only a problem if the software can detect two events too close together AND if the second event can cause the code to do something unwanted. Changing a state variable when the first event is detected and ignoring events until the state changes again is the simplest solution and it is something your code would probably need in any case - so no extra work and no (or very little) extra code.

...R

Robin2:
Debouncing is only a problem if the software can detect two events too close together AND if the second event can cause the code to do something unwanted. Changing a state variable when the first event is detected and ignoring events until the state changes again is the simplest solution and it is something your code would probably need in any case - so no extra work and no (or very little) extra code.

...R

Once the code is non-blocking and efficient, most contact switches/buttons will look like they're being turned on and off by demented imps at every contact. You can get or make buttons that won't but is extra hardware worth saving a few lines of code? I doubt it.

My debounce looks for pin state change and starts a timer. Only if the pin stays stable for the few millis set as debounce time (the longest bounce expected plus some) will the button/switch be considered to have finished changing. Any state change prior to that just restarts the timer.

In my library version the button "runs itself" and sketch code simply reads an output value.
-1 = undecided. The pin has changed less than debounce millis ago, check again later.
0 = OFF = not pressed and was not pressed last time it was checked.
1 = ON = pressed and was pressed last time it was checked.
2 = OFF = not pressed and was either pressed or undecided last time it was checked.
3 = ON = pressed and was either not pressed or undecided last time it was checked.

Output values 2 and 3 mean your sketch does not have to keep track of the last button check.
The read output function only changes a 2 or 3 to a 0 or 1 on user read. But it can be -1, 2 or 3 by event.

GoForSmoke:
Once the code is non-blocking and efficient, most contact switches/buttons will look like they’re being turned on and off by demented imps at every contact.

My debounce looks for pin state change

I agree about the Imps. My approach is to use a state variable that is an integral part of the project rather than one specially dedicated to the button. For example if the button is to switch on an led use the time the led is ON as the buffer against the Imps.

…R

GoForSmoke:
Once the code is non-blocking and efficient, most contact switches/buttons will look like they're being turned on and off by demented imps at every contact. You can get or make buttons that won't but is extra hardware worth saving a few lines of code? I doubt it.

My debounce looks for pin state change and starts a timer. Only if the pin stays stable for the few millis set as debounce time (the longest bounce expected plus some) will the button/switch be considered to have finished changing. Any state change prior to that just restarts the timer.

In my library version the button "runs itself" and sketch code simply reads an output value.
-1 = undecided. The pin has changed less than debounce millis ago, check again later.
0 = OFF = not pressed and was not pressed last time it was checked.
1 = ON = pressed and was pressed last time it was checked.
2 = OFF = not pressed and was either pressed or undecided last time it was checked.
3 = ON = pressed and was either not pressed or undecided last time it was checked.

Output values 2 and 3 mean your sketch does not have to keep track of the last button check.
The read output function only changes a 2 or 3 to a 0 or 1 on user read. But it can be -1, 2 or 3 by event.

Many thanks to all for the advice. I use interrupt as the button controls the "emergency" all stop to my project. I am using an RF Moteino plus my loop is monitoring a variety of sensors plus controlling a high current (15A) H-bridge driver. When I need to shut down the motor, all else in the Loop has to be stopped. I though unsing the interrupt was the simplest approach.

GoForSmoke -- could you help me understand what you mean by "button 'runs itself' " ??

k1jos:
When I need to shut down the motor, all else in the Loop has to be stopped.

I would much prefer a proper safety switch on the motor power supply which also sends a signal to the Arduino so it can shut down gracefully.

...R

I use interrupt as the button controls the "emergency" all stop to my project.

An emergency stop never needs to be debounced. Any contact (break) means the equipment is stopped.

...plus controlling a high current (15A) H-bridge driver.

And you are trying to implement an emergency stop using software? Is your goal to start an accidental fire?

Robin2:
I agree about the Imps. My approach is to use a state variable that is an integral part of the project rather than one specially dedicated to the button. For example if the button is to switch on an led use the time the led is ON as the buffer against the Imps.

...R

I am also filtering out false signals for both press and release even while other tasks run between checks.
My approach only accepts definite button press/release and is over in 20 ms for dirty buttons.

This is for human paws, how close does it have to be? If I need better, I'd use better sensors.

k1jos:
Many thanks to all for the advice. I use interrupt as the button controls the "emergency" all stop to my project. I am using an RF Moteino plus my loop is monitoring a variety of sensors plus controlling a high current (15A) H-bridge driver. When I need to shut down the motor, all else in the Loop has to be stopped. I though unsing the interrupt was the simplest approach.

GoForSmoke -- could you help me understand what you mean by "button 'runs itself' " ??

Well really the button class code runs the button so your sketch only sets it up and calls the run function in loop(). As far as the programmer is concerned, "the button runs itself" and the sketch is able to check and use the output whenever it wants. The sketch is concerned with the output, not how it is achieved.

And emergency stop should be mechanical and cut all power as instantly as possible. If the project is running off a power strip with a switch, that switch could be your emergency cutoff. The last thing you want is the very thing you are trying to stop because things went wrong to be the very thing to stop itself.

You don't need an interrupt for this project. You can have one but you don't need it.

BTW, a jammed motor will try to draw massive current. An overcurrent cutout circuit or fuse will be faster than your hand reaching for a switch.

k1jos:
I use interrupt as the button controls the "emergency" all stop to my project.

Which is entirely irrelevant to the use of interrupts, as I explain here.

And as others have pointed out, an "emergency stop" is a normally closed switch in series with the main contactor.

Paul__B:
Which is entirely irrelevant to the use of interrupts, as I explain here.

And as others have pointed out, an "emergency stop" is a normally closed switch in series with the main contactor.

Thanks. I understand that for most code, polling is fast enough. I read the post you linked to including NickG's replies as well as BullDogLowell to you. It doesn't seem like a black and white issue. The comments by BullDog in the thread your pointed to:

I have a wireless network comprised of NRF24L01 devices communicating to a serial gateway attached to a home automation controller, connected to the internet. The radio calls (puts and takes) and ack's are sometimes quite lengthly and block the devices for what seems like eons (it is actually usually way less than a second in real time). I use interrupts to activate routines because of all that blocking resulting from the radio/gateway/controller/internet lag-time. Trying to hold the button down for a second was not conducive to anyone else activating the devices (scene controllers).

This is exactly the situation I have with my RF plus multi-sensor project. My term "Emergency" stop was misleading and incorrect. My project involves the rotation of a 150 lb antenna with a slow speed high current (15A) motor controlled remotely by the RF Moteino pair. I monitor magnetic compass headings and I need to stop the motor almost on a dime via H-bridge breaking (another story). My button press to STOP has to be done asap and not wait for the RF packets/acknowledgements/sensor acks, etc to complete in the loop. I use States as much as possible but the RF Packets are quite slow. Maybe polling for the motor stop button press would work fine but I see nothing wrong with using the hardware interrupt in this case.

The motor is sophisticated as it has a slip clutch that allows an adjustment to set the stall current. However stopping at the right point is of major importance so the slip clutch/stall current is purely for current limit safety. Thats why I use the ACS714 current sernsor.

I know that questions out of full context can be misleading but it seems as lengthy prologue is the best way to not get any advice. Sorry for that.

I still would like to know what GoForSmoke meant by "button runs itself"...

Hope all had a good Christmas holiday

k1jos:
My project involves the rotation of a 150 lb antenna with a slow speed high current (15A) motor controlled remotely by the RF Moteino pair. I monitor magnetic compass headings and I need to stop the motor almost on a dime via H-bridge breaking (another story). My button press to STOP has to be done asap and not wait for the RF packets/acknowledgements/sensor acks, etc to complete in the loop.

Presumably one of the Moteinos is near you and the other one is near the antenna.

Which one will the "emergency" switch be connected to?

If the stop message has to go through the wireless channel then you will have to design the wireless comms to be fast enough. Using an interrupt to detect a switch press won't affect that.

...R

Robin2:
Presumably one of the Moteinos is near you and the other one is near the antenna.

Which one will the "emergency" switch be connected to?

If the stop message has to go through the wireless channel then you will have to design the wireless comms to be fast enough. Using an interrupt to detect a switch press won't affect that.

...R

That's an excellent point. Out of the box, the speed of the motor is such that is takes about 15 seconds to make a 360 revolution (24 degrees/sec). I want to slow it down with PWM so that its about 2-3 degrees per second. Yes, the motor ON/OFF is at the base station and goes through the wireless channel. I will have this both manual on/off but also semi-automated: the remote Moteino reads the magentic sensor and sends back a running average (10 data points) to the base. The base station Moteino has a keypad input to set the 'desired' compass direction (CW or CCW) and heading. I guess my precision for stopping the motor is not to go beyond 2-3 degrees from the selected heading. I have therefore 1 second which has to include the remote sensor reading delay, delay due to remote RF packet transmission and then base RF packet reception delay plus acknowledgment delay. Complicating everything further is at the remote Moteino (I am using the MegaMoteino) there is a proprietary high power radio antenna switching box (relays, etc) that communicates via a proprietary RS-485 packet scheme where it sends a "heartbeat packet " every 200 msec to the Moteino and then expects an echo of the same packet (or modified packet containing new instructions) precisely within 30msec-60 msec after or it shuts down - this is a huge time killer. I am already working on a antenna switch replacement or easier to use a second remote microArduino just to handle the heartbeat issue. I didn't want to bring this all in to my simple question about debouncing an interrupt button but for my first Arduino project this has taken me about 6 months of steep learning but well worth every effort.

Based on what you have said in Reply #16 there are several parts of this jigsaw that need to be sorted out.

I presume you wrote the code for the Moteino that is attached to the motor, as well as the code for the control Moteino.

Why not use the "heartbeat" transmissions to actually send the data in both directions.

If this was my project I would sit down and design a sensible data package that does not waste any time.

I made a system for remote control for model trains that receives data from my PC every 200 millisecs, sends it to several locomotives, receives a reply from each and sends the replies back to the PC.

...R

Just for info, Nick Gammon has an article on switches and debouncing

He shows how to debounce in hardware with a 1 uF cap for 36ms debounce (36 ms recovery time) and some nice oscilloscope shots of his findings.

I do have to say, if I want instant results, I'll be using the capacitor! That said, practically all of the button apps I know can wait at least 50 ms and a few lines of code is cheaper than a cap.