false inputs on interrupt[Solved]

I am using interrupts to monitor two relays about 300 feet away. I have enabled the internal pull ups and tied the pin to ground through the normally closed contacts on the relay. Interrupt on rising edge. I used millis() to debounce the relay. My problem is that occasionally I get a false trigger. Is this due to the distance of the relay? If so, how do I compensate? The following is my ISR.

void interruptHandler0(){
  if(millis()-lastMillis0>bounceDuration0){
    interruptWaveCount+=1;
    lastMillis0=millis();
  }
}

Try interfacing it through an optoisolator.

LarryD:
Try interfacing it through an optoisolator.

I don't happen to have any right now. I will order some and give it a try. In the mean time, any other suggestions?

Try interrupt on falling edge instead, when NO contacts close. Can add some additional pullup, like 10K down to 1K.

CrossRoads:
Try interrupt on falling edge instead, when NO contacts close. Can add some additional pullup, like 10K down to 1K.

I have tried interrupting on falling edge through N.O. contacts with the same results. I didn't have any extra pullup on it though.

You could try eliminating noise on your signal and ground wires with an external filter c/w series resistance on both wires:

I assume you use twisted pair, like cat-5 or cat-6.
If not, you have made a perfect aerial.

If you want fast detection, use a normal open contact.
The closing contact discharges the cable (and cap) faster.
I think a 10-100n to ground and 10k to +5volt will do.
Leo..

Hi,
does the relay have to be that far from the arduino, can you put the relay at the arduino and run the coil wires back to what ever you are connected to?
You may need to increase the voltage to the coil to over come the volt drop due to 600ft of wire.
Worth a try.

Tom..... :slight_smile:

Wawa:
I assume you use twisted pair, like cat-5 or cat-6.
If not, you have made a perfect aerial.

If you want fast detection, use a normal open contact.
The closing contact discharges the cable (and cap) faster.
I think a 10-100n to ground and 10k to +5volt will do.
Leo..

It is not a twisted pair...unfortunately I did not choose the cable and I do not have the option to redo it. I have the pull up already and I will try adding the capacitance and switching back to N.O. contacts.

TomGeorge:
Hi,
does the relay have to be that far from the arduino, can you put the relay at the arduino and run the coil wires back to what ever you are connected to?
You may need to increase the voltage to the coil to over come the volt drop due to 600ft of wire.
Worth a try.

Tom..... :slight_smile:

I am dealing with an existing installation with restrictions on what I can modify...moving the relays would be great, but it's not an option in this case.

Thank you everyone for your assistance. I will be returning to try these modifications in a couple days and I will report on the results. I will also continue monitoring this thread for any further suggestions. Thanks again.

Regarding the suggested circuit. The components were selected to give 94ms time constant. Reasoning for this is to eliminate contact bounce from the relay (could be 10ms+) and possible 60Hz line pickup (8.3ms full wave or 16.7ms 1/2 wave) or 10-20ms for 50Hz. Do not use the internal pullup as it is too weak and will affect the filter time constant. If faster response is needed, 2.2µF would give approx 44ms time constant on the rising signal. Falling signal time constants are 47ms and 22ms respectively.

http://hughestech.com/rc_calculator/

Good luck with your tests.

Hi,
Fine on the restrictions, can you fit a second relay at the arduino, is speed of response a problem?
Or use an optocoupler at the arduino end, either option will isolate the controller from the long length of wire to the remote input.

Tom.... :slight_smile:

TomGeorge:
Hi,
Fine on the restrictions, can you fit a second relay at the arduino, is speed of response a problem?
Or use an optocoupler at the arduino end, either option will isolate the controller from the long length of wire to the remote input.

Tom.... :slight_smile:

If I can't filter out the noise then I will try isolation. Speed isn't super critical. I'm leaning toward the optocoupler so voltage drop will be less of an issue.

dlloyd:
Regarding the suggested circuit. The components were selected to give 94ms time constant. Reasoning for this is to eliminate contact bounce from the relay (could be 10ms+) and possible 60Hz line pickup (8.3ms full wave or 16.7ms 1/2 wave) or 10-20ms for 50Hz. Do not use the internal pullup as it is too weak and will affect the filter time constant. If faster response is needed, 2.2µF would give approx 44ms time constant on the rising signal. Falling signal time constants are 47ms and 22ms respectively.

Online RC Time Constant Calculator - Gadget - Open Source Code

Good luck with your tests.

Thank you for the graphic and very detailed explanation. I am debouncing for a full 2 seconds in software because the relay will actuate for 1 second and then release and I want to ignore everything except the initial rising/falling edge. I know that with an input as slow as this I could repeatedly poll the pin instead of using interrupts, but when I tried I got the same result(random false triggers). I am looking for noise filter, not necessarily a full debounce circuit.

I probably should have posted my full sketch at the beginning of the thread. Here it is.

#include <EEPROM.h>

const int countUp=11;//increment relay coil output N.O.
const int countDown=10;//decrement relay coil output N.O.
const int powerReset=9;//future use - relay coil output N.C.
const int interrupt0Pin=2;
const int interrupt1Pin=3;

int bounceDuration0=2000;//interrupt0 debounce time
int bounceDuration1=2000;//interrupt1 debounce time
volatile unsigned long lastMillis0=0;//interrupt0 last interrupt time
volatile unsigned long lastMillis1=0;//interrupt1 last interrupt time

volatile unsigned int interruptWaveCount=0;//current wave count, updated in interruptHandler0
unsigned int lastWaveCount=0;//last non-volatile wave count
unsigned int waveCount=0;//stores non-volatile wave count during loops

volatile int interruptResetFlag=0;//reset pressed flag, set to 1 in interruptHandler1
int resetFlag=0;//non-volatile reset flag

int addr=0;//EEPROM address for storing current wave count
byte high=0;//stores high byte of 2 byte value read from memory
byte low=0;//stores low byte of 2 byte value read from memory
unsigned int memValCurrentAddr=0;//used to compare values in different memory addresses
unsigned int memValNextAddr=0;//used to compare values in different memory addresses
//##################################################################
void setup() {
  pinMode(4, INPUT);//not used, set as input because ground trace runs across this pin

  pinMode(interrupt0Pin, INPUT);
  pinMode(interrupt1Pin, INPUT);
  digitalWrite(interrupt0Pin,HIGH);
  digitalWrite(interrupt1Pin,HIGH);
  attachInterrupt(0, interruptHandler0, RISING);//increment input
  attachInterrupt(1, interruptHandler1, RISING);//reset input

  pinMode(countUp, OUTPUT);
  pinMode(countDown, OUTPUT);
  pinMode(powerReset, OUTPUT);//future use
  digitalWrite(countUp,LOW);//initialize relay to off
  digitalWrite(countDown,LOW);//initialize relay to off
  digitalWrite(powerReset,LOW);//initialize relay to off

  for(int i=0;i<1023;i+=2){//find EEPROM address of last known wave count
    high=EEPROM.read(i);
    low=EEPROM.read(i+1);
    memValCurrentAddr=word(high,low);//current address value
    high=EEPROM.read(i+2);
    low=EEPROM.read(i+3);
    memValNextAddr=word(high,low);//next address value
    if(memValCurrentAddr!=memValNextAddr-1){//is next address value == current address value + 1
      addr=i;//next address value != current address value + 1, this address contains current wave count
      break;//address found, exit for loop
    }//if
  }//for

  high=EEPROM.read(addr);//read high byte of last known wave count
  low=EEPROM.read(addr+1);//read low byte of last known wave count
  waveCount=word(high,low);//set non-volatile wave count == last known wave count
  lastWaveCount=waveCount;
  noInterrupts();//no interrupts while working with volatile variable
  interruptWaveCount=waveCount;//set volatile wave count == last known wave count
  interrupts();
}
//##################################################################
void loop() {
/*Wave count*/
  noInterrupts();
  waveCount=interruptWaveCount;//set current volatile wave count to non-volatile variable
  interrupts();
  for(int i=lastWaveCount;i<waveCount;i++){//cycle increment relay and update EEPROM
    addr+=2;
    if(addr==1024)addr=0;//if last EEPROM address reached, roll over to 0
    EEPROM.write(addr,highByte(waveCount));//write high byte of new wave count
    EEPROM.write(addr+1,lowByte(waveCount));//write low byte of new wave count
    digitalWrite(countUp,HIGH);//turn on increment relay for 1 second
    delay(500);
    digitalWrite(countUp,LOW);//turn off increment relay
    delay(500);//wait in case there were wave counts during reset
  }//for
  lastWaveCount=waveCount;//set last wave count == current wave count

/*Reset*/
  noInterrupts();
  resetFlag=interruptResetFlag;//set current reset flag to non-volatile variable
  interrupts();
  if(resetFlag==1){//if reset input detected, cycles decrement relay == number of increments
    for(int i=0;i<waveCount;i++){//cycle decrement relay == current non-volatile wave count
      EEPROM.write(addr,0);
      EEPROM.write(addr+1,0);
      addr-=2;
      digitalWrite(countDown,HIGH);
      delay(200);
      digitalWrite(countDown,LOW);
      delay(800);
    }//for
    noInterrupts();
    interruptWaveCount-=waveCount;//waves in reset == current volatile wave count - current non-volatile wave count
    interruptResetFlag=0;//set volatile reset flag == 0
    interrupts();
    resetFlag=0;//set non-volatile reset flag == 0
    lastWaveCount=0;//reset last wave count to 0
    waveCount=0;    //reset non-volatile wave count to 0
    addr=0;
  }//if
}//loop
//##################################################################
void interruptHandler0(){//increment interrupt handler
  if(millis()-lastMillis0>bounceDuration0){// has the debounce time elapsed
    interruptWaveCount+=1;//debounce time has elapsed increment volatile wave counter
    lastMillis0=millis();//set last interrupt time == current interrupt time
  }//if
}//interruptHandler0
void interruptHandler1(){//reset interrupt handler
  if(millis()-lastMillis1>bounceDuration1){// has the debounce time elapsed
    interruptResetFlag=1;//debounce time has elapsed, set reset flag == 1
    lastMillis1=millis();//set last interrupt time == current interrupt time
  }//if
}//interruptHandler1

Regarding the circuit, it is 100% a filter that would also eliminate contact bounce and AC pickup. It also dampens noise on the return GND connection.

The problem with generic software debounce, it will sense spontaneous noise, glitches, etc if it occurs before or after the ignore interval. So the first spike that comes along after power up gets sensed, then the software will apply a bounce ignore interval. During this interval, if the spikes keep coming in, they keep interrupting the program flow as the interrupt routine keeps getting called repeatedly. A long debounce interval will not solve this problem. However, a debounce routine that acts as a filter could work, but it will not prevent unwanted interrupts from firing.

Edit: Image added.

1 Like

Since I have so much time to play with, could I just do a delay and digitalRead inside the ISR to filter out spikes? If the input is still high after the delay then it must be a valid input. Perhaps like this:

void interruptHandler0(){
  delayMicroseconds(100000);
  if(digitalRead(interrupt0Pin)==HIGH&&millis()-lastMillis0>bounceDuration0){
    interruptWaveCount+=1;
    lastMillis0=millis();
  }
}

You can only read millis(), it won't update inside handler.

LarryD:
You can only read millis(), it won't update inside handler.

I thought delay() didn't work inside ISR because it uses interrupts, but delayMicroseconds() did work because it does not use interrupts.
http://www.arduino.cc/en/Reference/AttachInterrupt

True, millis() uses interrupts. But can be read.