Strange Interrupt Behaviour on attach

Have a strange issue that's got me scratching my head. Any help would be greatly appreciated.

I have a valve that dumps water for 5 minutes every half hour or so. The sketch sends an alarm if the valve doesn't dump within 1 hour. I built in a 10 minute hysteresis using detachInterrupt so that it isn't constantly interrupting for the entire 5 minute dump cycle. It is suppose to attachInterrupt after 10 minutes and wait for the next cycle.

I am using a vibration sensor. I tried 10K and 1K pullup to 5V. Grounding the input and to 5V. I tried rising edge and falling edge.

My problem is that after the interrupt is re-enabled, it runs the ISR and my timer is reloaded.

Trigger the interrupt.
vibration1Timer is loaded with millis correctly.
interrupt is detached correctly.
The first if statement behaves as expected.
When the second if statement becaomes true, it triggers the vibration1() routine even if the switch is hardwired to gnd.

const unsigned long vibrationDelay = 3600000; // Trigger siphon alert after no activity in 1 hour
const unsigned long interruptTimeout = 600000; // Disable interrupt for timeout period while siphon dumps
volatile unsigned long vibration1Timer = 0;
byte vibration1Value;

void setup()
{
  attachInterrupt(1, vibration1, FALLING); // Config interrupt for bell siphon vibration sensor
}

void loop()
{
  if ((millis() - vibration1Timer) >= vibrationDelay){
    vibration1Value = 0;
  } else {
    vibration1Value = 1;
  }

  if ((millis() - vibration1Timer) >= interruptTimeout){
    attachInterrupt(1, vibration1, FALLING);                               //doesn't work. Triggers interrupt routine instead.
  }
}

void vibration1() {
  vibration1Timer = millis();
  detachInterrupt(1);
}

The full sketch is quite long and take about 2 seconds to go through the loop. I tried to post but the forum complained I have too many characters. I don't believe any other parts of the sketch would be affecting this operation.

My problem is that after the interrupt is re-enabled, it runs the ISR and my timer is reloaded.

It might be possible that between detachinterrupt() and attachinterrupt(), an interrupt is generated and since you detached the interrupt so it is in pending mode. now when you attachinterrupt() the pending interrupt gets opportunity to run ISR.

if you want to ignore the interrupt just clear the interrupt flag before attachinterrupt() so that all the pending interrupts get ignored.

EIFR  |= (1<<INTF1)

If you want to ignore an interrupt, just define a volatile global control variable which the interrupt handler can test to see whether the interrupt should be ignored. This will work better than attaching and detaching interrupt handlers and trying to poke around in the pending interrupts register.

Chiumanfu:
so that it isn't constantly interrupting for the entire 5 minute dump cycle.

Why are you concerned about that?

Of course as you have not reset vibration1Timer it will look like it's retrigering.

Mark

void vibration1() {
  vibration1Timer = millis();
  detachInterrupt(1);
}

Interrupts are automatically disabled when an ISR is triggered and enabled when the ISR has run. The latter may be causing interrupts to be enabled at the end of the ISR.

The full sketch is quite long and take about 2 seconds to go through the loop

What is it that causes it to take 2 seconds each time through the loop() function ?

Without seeing what else your program does it is difficult to tell, but it is quite possible that you do not need to use an interrupt at all.

BijendraSingh:
if you want to ignore the interrupt just clear the interrupt flag before attachinterrupt() so that all the pending interrupts get ignored.

EIFR  |= (1<<INTF1)

I tried inserting that register setting just before the attachInterrupt. Same behavior.

PeterH:
If you want to ignore an interrupt, just define a volatile global control variable which the interrupt handler can test to see whether the interrupt should be ignored. This will work better than attaching and detaching interrupt handlers and trying to poke around in the pending interrupts register.

I have a other somewhat time sensitive operations in the sketch (flow counter counting pulses). If it constantly interrupts for the entire 5 minutes, it will cause problems.

holmes4:
Of course as you have not reset vibration1Timer it will look like it's retrigering.

I do not want to reset vibration1Timer. It is used to timeout the longer 1 hour delay as well. I should not need to reset vibration1Timer. The second IF statement will simple remain true and the attachInterrupt will run once every loop. Is there an issue calling attachInterrupt multiple times? hmm...

UKHeliBob:
Interrupts are automatically disabled when an ISR is triggered and enabled when the ISR has run. The latter may be causing interrupts to be enabled at the end of the ISR.

Without seeing what else your program does it is difficult to tell, but it is quite possible that you do not need to use an interrupt at all.

This doesn't seem to be the behavior I'm seeing. Once I detachInterrupt in the ISR, the interrupt does not trigger again until after the 10 minutes expires and the attachInterrupt is called. I tried inserting the interrupt register setting right before exiting the ISR, just in case this is actually happening but it made no difference.

The code is too long to post. I will try to clean out some comments and see if it will come in under the limit.

Any other ideas? I may have to take PeterH's suggestion and rethink the whole process.

Couldn't post the code so I attached the INO.

Thanks
Chiu

aquaponics.ino (18.4 KB)

I haven't yet had time to look at your code but your comments suggest to me that you have not thought sufficiently carefully about how interrupts should be used and how they work.

I successfully used the EIFR |= (1<<INTF1) technique to deal with a problem in code I had. if that piece of code is correct (I haven't checked) and it doesn't solve the problem then that brings me right back to my previous paragraph.

And your comment

I have a other somewhat time sensitive operations in the sketch (flow counter counting pulses). If it constantly interrupts for the entire 5 minutes, it will cause problems.

suggests that the overall organization of your code is not appropriate for your project.

...R

Robin2:
suggests that the overall organization of your code is not appropriate for your project.

This might very well be the case as it is my first arduino project although not my first embedded experience.

In any case, I stripped the sketch down to the minimum required to debug this section and it still exhibits the same behavior.

#include <Wire.h>              // For I2C Devices
#include <DS1307RTC.h>         // For DS1307 Real Time Clock
#include <Time.h>              // For Time functions

const unsigned long interruptTimeout = 60000; // Disable interrupt for timeout period while siphon dumps
volatile unsigned long vibration1Timer = 0;


void setup()
{
  Serial.begin(115200); //For debug
  attachInterrupt(1, vibration1, FALLING); // Config interrupt for bell siphon vibration sensor
}
   
void loop()
{
  Serial.print(F(">"));
  tmElements_t tm; // Calls RTC lib
  if (RTC.read(tm)) { // Read DS1307
  print2digits(tm.Hour);
  Serial.write(':');
  print2digits(tm.Minute);
  Serial.write(':');
  print2digits(tm.Second);
  Serial.print(" - ");
  Serial.print(tm.Day);
  Serial.write('/');
  Serial.print(tm.Month);
  Serial.write('/');
  Serial.print(tmYearToCalendar(tm.Year));
  Serial.print(",");
  } else {
    if (RTC.chipPresent()) {
    Serial.println(F("DS1307 Stopped"));
    } else {
    Serial.println(F("DS1307 read error!"));
    }
  }
  
  // Vibration Sensor 1

  if ((millis() - vibration1Timer) >= interruptTimeout){
    EIFR |= (1 << INTF1);
    attachInterrupt(1, vibration1, FALLING); //doesn't work. Triggers interrupt routine instead.
  }
  
  Serial.print(F(" "));
  Serial.print(F("Millis:"));
  Serial.print(millis());
  Serial.print(F(" "));
  Serial.print(F("timer:"));
  Serial.print(vibration1Timer);
  Serial.println("");
}

void vibration1() {
  vibration1Timer = millis();
  detachInterrupt(1);
}

void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
  Serial.print(number);
}

Expected Behavior:
capture interrupt
set vibration1Timer to millis
disable interrupt
wait 1 minute
reset interrupt buffer flag
enable interrupt
wait for new interrupt...

Observed behavior:
capture interrupt
set vibration1Timer to millis
disable interrupt
wait 1 minute
run ISR again with no interrupt pin state change. vibration1Timer is set to millis and interrupt is detached.
wait 1 minute
run ISR again with no interrupt pin state change. vibration1Timer is set to millis and interrupt is detached....

I have to note that I've noticed that if I let it run for a long time, it seems to stabilize... which points to a interrupt buffer queue problem. How can I get more info on EIFR |= (1 << INTF1);. Google didn't turn up much describing the syntax for this register setting.

Thanks again

How can I get more info on EIFR |= (1 << INTF1);. Google didn't turn up much describing the syntax for this register setting.

download above pdf, and in this pdf you will find EIFR (External Interrupt Flag Register).

attachInterrupt(1, vibration1, FALLING); //doesn't work. Triggers interrupt routine instead.

This is a strange comment. I always thought the purpose of attachInterrupt was to trigger an interrupt routine.

It is ALWAYS good debugging policy to assume that the Arduino is correctly doing what it is asked to do. In this case I would start by assuming there is a falling edge that triggers the interrupt. But perhaps it is not the falling edge that you are trying to catch.

You don't seem to have set pinMode() for the interrupt pin?

...R

attachInterrupt(1, vibration1, FALLING); //doesn't work. Triggers interrupt routine instead.

This is a strange comment. I always thought the purpose of attachInterrupt was to trigger an interrupt routine.

Surely the act of attaching the interrupt should not actually trigger it though, merely make it possible to trigger the interrupt when the interrupt pin state changes.

I fixed the issue and it is definitely a case where the Arduino is not behaving as advertised.

I am using a MEGA2560 r3 board. If you look at the schematic, Pin 3 (INT1) is tied to pin 7 of the ATMEGA2560 chip. Looking at the datasheet, pin 7 on the chip is actually INT5. When I clear the register for INT5, everything works exactly as expected.

I have no idea why the INT numbers were not translated directly... and why we need to clear the registers at all. It should be taken care of in the attachInterrupt routine IMO. There is a ticket open from 2011 on this exact "bug".
https://code.google.com/p/arduino/issues/detail?id=510

Note I didn't OR the bit in as per this thread.
http://forum.arduino.cc/index.php?topic=59217.0

Here is the working code snippet

const unsigned long interruptTimeout = 60000; // Disable interrupt for timeout period while siphon dumps
volatile unsigned long vibration1Timer = 0;

void setup()
{
  Serial.begin(115200); //For debug
  attachInterrupt(1, vibration1, FALLING); // Config interrupt for bell siphon vibration sensor
}
   
void loop()
{
  if ((millis() - vibration1Timer) >= interruptTimeout){
    EIFR = (1 << INTF5);
    attachInterrupt(1, vibration1, FALLING);
  }

  Serial.print("Millis:");
  Serial.print(millis());
  Serial.print(" ");
  Serial.print("timer:");
  Serial.print(vibration1Timer);
  Serial.println("");
  delay(500);
}

void vibration1() {
  vibration1Timer = millis();
  detachInterrupt(1);
}

Robin2:

attachInterrupt(1, vibration1, FALLING); //doesn't work. Triggers interrupt routine instead.

This is a strange comment. I always thought the purpose of attachInterrupt was to trigger an interrupt routine.

Not if your interrupt pin is not changing state.

Robin2:
It is ALWAYS good debugging policy to assume that the Arduino is correctly doing what it is asked to do. In this case I would start by assuming there is a falling edge that triggers the interrupt. But perhaps it is not the falling edge that you are trying to catch.

In this case the arduino was not doing what it was asked to do. I had to dig around in the schematic and uC datasheet to figure it out.

Of course, the first thing I did was confirm the physical interrupt state with my oscilloscope.

Robin2:
You don't seem to have set pinMode() for the interrupt pin?

You do not need to define pinMode for external interrupts. See this page for more info.

After a little more digging, the UNO does not have this issue.
Header pin 2 (INT0) use EIFR = (1 << INTF0);
Header pin 3 (INT1) use EIFR = (1 << INTF1);

On the Mega, it gets all mixed up.
Header pin 2 (INT0) use EIFR = (1 << INTF4);
Header pin 3 (INT1) use EIFR = (1 << INTF5);
Header pin 21 (INT2) use EIFR = (1 << INTF0);
Header pin 20 (INT3) use EIFR = (1 << INTF1);
Header pin 19 (INT4) use EIFR = (1 << INTF2);
Header pin 18 (INT5) use EIFR = (1 << INTF3);

Hope this helps someone save a few days of head scratching.

Chiumanfu:
I am using a MEGA2560 r3 board. If you look at the schematic, Pin 3 (INT1) is tied to pin 7 of the ATMEGA2560 chip. Looking at the datasheet, pin 7 on the chip is actually INT5. When I clear the register for INT5, everything works exactly as expected.

It does look like the Arduino folks screwed up here. If they had attached interrupt 5 to pin 3 things would have been clearer. I suspect they got tangled up in trying to be consistent between the Uno and the Mega.

Thanks for figuring this out.

...R