delay () and interrupts

I've read that the delay function will not run within an interrupt loop. In my sketch it appears to me that the interrupt is closed prior to the delay function yet the delays are not taking place. How could I edit my sketch to incorporate the delay (or its intended purpose- see sketch comments) and not disturb the interrupt's usefulness? Thank you.

// Disable a pressure switch controlled water pump to ignore small (ie: slow leak) flow rates.  Results in energy savings while accepting small water losses.
// Code adapted from below referenced and modified by Christopher Norkus
// reading liquid flow rate using Seeeduino and Water Flow Sensor from Seeedstudio.com
// Code adapted by Charles Gantt from PC Fan RPM code written by Crenn @thebestcasescenario.com
// http:/themakersworkbench.com http://thebestcasescenario.com http://seeedstudio.com

volatile int NbTopsFan; //measuring the rising edges of the signal
float Calc;                               
int inputPin = 2;    //The pin location of the sensor
int outPin = 4;   //The pin connected to the pump contactor coil.  The coil is wired in series with the pressure switch.

void rpm ()     //This is the function that the interupt calls 
{ 
  NbTopsFan++;  //This function measures the rising and falling edge of the hall effect sensors signal
} 
// The setup() method runs once, when the sketch starts
void setup() //
{ 
  pinMode(inputPin, INPUT); //initializes digital pin 2 as an input
  digitalWrite(inputPin, HIGH);  //enables internal pull up resistor
  pinMode(outPin, OUTPUT);  //initializes digital pin 4 as output
  Serial.begin(9600); //This is the setup function where the serial port is initialized,
  attachInterrupt(0, rpm, RISING); //and the interrupt is attached
} 
// the loop() method runs over and over again,
// as long as the Arduino has power
void loop ()    
{
  NbTopsFan = 0;   //Set NbTops to 0 ready for calculations
  sei();      //Enables interrupts
  delay (1000);   //Wait 1 second
  cli();      //Disable interrupts
  Calc = (NbTopsFan * 60 / 4.8 * 0.2642); //Pulse frequency (1/min) * 60(min/hr) / 4.8Q, = flow rate (L/hr) * 0.2642 (Gal/L) 
  Serial.print (Calc, DEC); //Prints the number calculated above
  Serial.print (" gal/hr\r\n"); //Prints "gal/hour" and returns a  new line
  if (Calc <30 )                //at 20 psi full demand flow from pressure tank is measured at 41 gph.  If flow < this keep pump off.  Experiments show that Calc 30 is a good value. 
    {
      digitalWrite(outPin, LOW);   //This allows the pump contactor to stay de-energized at low (ie: slow leak) flow rates and energized with actual water demand
      delay (10000);               //The delay allows for short pulses of demand (ie: car wash nozzle) to be ignored keeping the pump from surging repeatedly.
    }
  else if (Calc >= 30)
    {
      digitalWrite(outPin, HIGH);  //This energizes the coil, closes the contacts, and passes control to the pressure switch while the high flow condition exists.
      delay (10000);               //maybe redundant but intended to allow the  pump to run for 10 seconds beyond the demand signal and allowing tank to build reserve pressure.
    }
 }
1 Like

You can have a delay out in your code, just not in the interrupt service function itself. Delay relies on a timer interrupt to measure the time. Inside the ISR those are turned off so delay just sits and waits forever for an interrupt that's not coming.

When you disable interrupts with cli just before the delay call, you have the same problem. Delay is trying to wait 10 seconds, but from its point of view time is standing still.

sei();      //Enables interrupts
  delay (1000);   //Wait 1 second
  cli();      //Disable interrupts

This is some of the worst code floating around the internet, and I doubt it will ever go away. Enable interrupts-count for a second-disable interrupts is pure rubbish. You are not the first person to come to this forum with issues from running that flow meter code with interrupts disabled.

How could I edit my sketch to incorporate the delay (or its intended purpose- see sketch comments) and not disturb the interrupt's usefulness?

Study the example in the ide "blink without delay" and look at Robin 2's Tutorial of Demonstration Code for Several Things at the Same Time.

You need to use these millis() timers to replace the delay(10000) in the output.

Here is a better way to read interrupts for a one second period using the millis() timer of the "blink without delay" sketch and to get data from the interrupt with out it changing.

volatile unsigned long count = 0;
unsigned long copyCount = 0;

unsigned long lastRead = 0;
unsigned long interval = 1000;//one second

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");
  
  pinMode(2,INPUT_PULLUP);
  attachInterrupt(0, isrCount, RISING); //interrupt signal to pin 2
}

void loop()
{
  if (millis() - lastRead >= interval) //read interrupt count every second
  {
    lastRead  += interval;
    // disable interrupts, make copy of count, reenable interrupts
    noInterrupts();
    copyCount = count;
    count = 0;
    interrupts();
 
    Serial.println(copyCount);

  }
}

void isrCount()
{
  count++;
}

Thank you, I will look at your references to improve the sketch. I suppose that when one finds a sensor that suits a need and there is code referenced by the vendor to support it then the poorly written code proliferates along with posts by newbies like me seeking to fix it :confused:

cattledog,
I don't think things are as bad as you are making it sound.
I also don't think jumping to using the "blink without delay" method of coding is necessary.
This is very simple code that merely needs a duration of time to elapse in order to count interrupts occurring.
It is not trying to do anything complex like tasking.

tractorman1960,
using delay() in this case is perfectly acceptable and can help keep things simple and easy to follow.
All the code needs is a few tweaks with how it manages interrupt masking and how it deals with NbTopsFan.

The reason being that NbTopsFan is an int which is 2 bytes and modified in an ISR so interrupts must be masked when looking at it in the foreground code to ensure atomicity since the variable is more than one byte.
i.e. interrupts need to be masked when dealing with the counter but only need to be masked when dealing with the counter.

For example this small tweak should allow it work:

// http:/themakersworkbench.com http://thebestcasescenario.com http://seeedstudio.com

volatile int NbTopsFan; //measuring the rising edges of the signal
float Calc;
int inputPin = 2;    //The pin location of the sensor
int outPin = 4;   //The pin connected to the pump contactor coil.  The coil is wired in series with the pressure switch.

void rpm ()     //This is the function that the interupt calls
{
  NbTopsFan++;  //This function measures the rising and falling edge of the hall effect sensors signal
}
// The setup() method runs once, when the sketch starts
void setup() //
{
  pinMode(inputPin, INPUT); //initializes digital pin 2 as an input
  digitalWrite(inputPin, HIGH);  //enables internal pull up resistor
  pinMode(outPin, OUTPUT);  //initializes digital pin 4 as output
  Serial.begin(9600); //This is the setup function where the serial port is initialized,
  attachInterrupt(0, rpm, RISING); //and the interrupt is attached
}
// the loop() method runs over and over again,
// as long as the Arduino has power
void loop ()
{
  noInterrupts();    // mask interrupts to ensure both bytes of the int can be set atomicly
  NbTopsFan = 0;   //Set NbTops to 0 ready for calculations
  interrupts();    // re-enable interrupts

  delay (1000);   //Wait 1 second

  noInterrupts(); // disable interrupts so calculation sees a stable atomic NbTopsFan value
  Calc = (NbTopsFan * 60 / 4.8 * 0.2642); //Pulse frequency (1/min) * 60(min/hr) / 4.8Q, = flow rate (L/hr) * 0.2642 (Gal/L)
  interrupts(); // re-enable interrupts.

  Serial.print (Calc, DEC); //Prints the number calculated above
  Serial.print (" gal/hr\r\n"); //Prints "gal/hour" and returns a  new line
  if (Calc <30 )                //at 20 psi full demand flow from pressure tank is measured at 41 gph.  If flow < this keep pump off.  Experiments show that Calc 30 is a good value.
    {
      digitalWrite(outPin, LOW);   //This allows the pump contactor to stay de-energized at low (ie: slow leak) flow rates and energized with actual water demand
      delay (10000);               //The delay allows for short pulses of demand (ie: car wash nozzle) to be ignored keeping the pump from surging repeatedly.
    }
  else if (Calc >= 30)
    {
      digitalWrite(outPin, HIGH);  //This energizes the coil, closes the contacts, and passes control to the pressure switch while the high flow condition exists.
      delay (10000);               //maybe redundant but intended to allow the  pump to run for 10 seconds beyond the demand signal and allowing tank to build reserve pressure.
    }
 }

-- bill

bperrybap:
cattledog,
I don't think things are as bad as you are making it sound.
I also don't think jumping to using the "blink without delay" method of coding is necessary.
This is very simple code that merely needs a duration of time to elapse in order to count interrupts occurring.
It is not trying to do anything complex like tasking.

tractorman1960,
using delay() in this case is perfectly acceptable and can help keep things simple and easy to follow.
All the code needs is a few tweaks with how it manages interrupt masking and how it deals with NbTopsFan.

The reason being that NbTopsFan is an int which is 2 bytes and modified in an ISR so interrupts must be masked when looking at it in the foreground code to ensure atomicity since the variable is more than one byte.
i.e. interrupts need to be masked when dealing with the counter but only need to be masked when dealing with the counter.

For example this small tweak should allow it work:

// http:/themakersworkbench.com http://thebestcasescenario.com http://seeedstudio.com

volatile int NbTopsFan; //measuring the rising edges of the signal
float Calc;
int inputPin = 2;    //The pin location of the sensor
int outPin = 4;  //The pin connected to the pump contactor coil.  The coil is wired in series with the pressure switch.

void rpm ()    //This is the function that the interupt calls
{
  NbTopsFan++;  //This function measures the rising and falling edge of the hall effect sensors signal
}
// The setup() method runs once, when the sketch starts
void setup() //
{
  pinMode(inputPin, INPUT); //initializes digital pin 2 as an input
  digitalWrite(inputPin, HIGH);  //enables internal pull up resistor
  pinMode(outPin, OUTPUT);  //initializes digital pin 4 as output
  Serial.begin(9600); //This is the setup function where the serial port is initialized,
  attachInterrupt(0, rpm, RISING); //and the interrupt is attached
}
// the loop() method runs over and over again,
// as long as the Arduino has power
void loop ()
{
  noInterrupts();    // mask interrupts to ensure both bytes of the int can be set atomicly
  NbTopsFan = 0;  //Set NbTops to 0 ready for calculations
  interrupts();    // re-enable interrupts

delay (1000);  //Wait 1 second

noInterrupts(); // disable interrupts so calculation sees a stable atomic NbTopsFan value
  Calc = (NbTopsFan * 60 / 4.8 * 0.2642); //Pulse frequency (1/min) * 60(min/hr) / 4.8Q, = flow rate (L/hr) * 0.2642 (Gal/L)
  interrupts(); // re-enable interrupts.

Serial.print (Calc, DEC); //Prints the number calculated above
  Serial.print (" gal/hr\r\n"); //Prints "gal/hour" and returns a  new line
  if (Calc <30 )                //at 20 psi full demand flow from pressure tank is measured at 41 gph.  If flow < this keep pump off.  Experiments show that Calc 30 is a good value.
    {
      digitalWrite(outPin, LOW);  //This allows the pump contactor to stay de-energized at low (ie: slow leak) flow rates and energized with actual water demand
      delay (10000);              //The delay allows for short pulses of demand (ie: car wash nozzle) to be ignored keeping the pump from surging repeatedly.
    }
  else if (Calc >= 30)
    {
      digitalWrite(outPin, HIGH);  //This energizes the coil, closes the contacts, and passes control to the pressure switch while the high flow condition exists.
      delay (10000);              //maybe redundant but intended to allow the  pump to run for 10 seconds beyond the demand signal and allowing tank to build reserve pressure.
    }
}





-- bill




Thank you, Bill! I appreciate your practical approach in recognizing that I simply want to achieve a process improvement. As a beginner, it is exciting if anything I program works at all! I do enjoy learning about micro controllers and all that they are capable of. I don't have a programming background so I do the best I can and hope that I find helpful people like you willing to provide a leg-up from time to time.

chris

chris,
I'm not exactly sure what you wanting to accomplish with your code but
just keep in mind that delay() is synchronous; it goes away then then comes back after the delay but while it is gone nothing else can be done. It effectively halts the code for that period of time. So while you are doing the 10 second delay, the loop stops looping and so you are not monitoring the sensor during that 10 second period.

That may or may not be what you intended.
If you wanted to keep monitoring the sensor during that 10 second period, then things will get more complex and you will have to do things differently.

--- bill

bperrybap:
chris,
I'm not exactly sure what you wanting to accomplish with your code but
just keep in mind that delay() is synchronous; it goes away then then comes back after the delay but while it is gone nothing else can be done. It effectively halts the code for that period of time. So while you are doing the 10 second delay, the loop stops looping and so you are not monitoring the sensor during that 10 second period.

That may or may not be what you intended.
If you wanted to keep monitoring the sensor during that 10 second period, then things will get more complex and you will have to do things differently.

--- bill

Bill, it is a simple program and during the delay periods I intend for nothing else to happen. The application is to add flow control to a domestic water supply booster pump in addition to customary pressure control. There is a slow pressure leak in my piping system and the pump was running every 15 minutes or so (day and night) to restore peak usable pressure in a holding tank at which point the pressure switch "cut-out" setting would interrupt power to the pump. The flow sensor, power relay, and program prevent the pump from running and allows the pressure to drop below the "cut-in" setting until there is actual demand such as a faucet being opened. This system has resulted in significant power savings over the six month period since implementation.

Thanks again, I look forward to compiling the changes and saving the unnecessary and potentially damaging surging of my pump.

Chris

Thanks so much, Bill!

I uploaded your changes and all is working as I intended.

Chris