Problems with interrupt function

Good afternoon, I watered my home greenhouse, but I ran into such a problem that I can’t divide the different flow rates into two buttons (so that when you press the first button, one volume is filled, and when you press the second, another volume
I use one water flow sensor and one pump with a relay
here is the sketch

#define button_pin 3
#define relay_pin 6

#define button_pinnn 7

boolean butt_flag = 0;
boolean butt;
boolean flag = 0;
boolean butt_flagg = 0;
boolean buttt;
boolean flagg = 0;
unsigned long last_press;
volatile double waterFlow;

void setup() {
Serial.begin(9600); //baudrate

pinMode(button_pin, INPUT_PULLUP);
pinMode(button_pinnn, INPUT_PULLUP);
pinMode(relay_pin, OUTPUT);
attachInterrupt(0, pulse, FALLING); //DIGITAL Pin 2: Interrupt 0

waterFlow = 0.000;

}
void pulse() //measure the quantity of square wave
{
waterFlow += 1.0 / 3510.0;
}

void loop() {

butt = !digitalRead(button_pin);

if (butt == 1 && butt_flag == 0 && millis() - last_press > 1000) {
butt_flag = 1;
flag = !flag;
last_press = millis();

digitalWrite(relay_pin, flag);
}
if (butt == 0 && butt_flag == 1) {
butt_flag = 0;
}

if(waterFlow < 0.4){
Serial.print("waterFlow:");
Serial.print(waterFlow);
Serial.println(" L");
delay(1000);
}
else {
digitalWrite(relay_pin, 0);
waterFlow = 0.000;
}

butt = !digitalRead(button_pinnn);

if (butt == 1 && butt_flag == 0 && millis() - last_press > 1000) {
butt_flag = 1;
flag = !flag;
last_press = millis();

digitalWrite(relay_pin, flag);
}
if (butt == 0 && butt_flag == 1) {
butt_flag = 0;
}

if(waterFlow < 0.5){
Serial.print("waterFlow:");
Serial.print(waterFlow);
Serial.println(" L");
delay(1000);
}
else {
digitalWrite(relay_pin, 0);
waterFlow = 0.000;
}

}

What do you suppose you get if an interrupt happens while this instruction group is being executed and another interrupt occurs?

the debugger ignores the following conditions with (if<0.5) , don't know why...
maybe it's an attachinterrupt??

Welcome to the forum

Your topic was MOVED to its current forum category which is more appropriate than the original as it has nothing to do with Installation and Troubleshooting of the IDE

Please follow the advice given in the link below when posting code, in particular the section entitled 'Posting code and common code problems'

Use code tags (the </> icon above the compose window) to make it easier to read and copy for examination

Please follow the advice given in the link below when posting code, in particular the section entitled 'Posting code and common code problems'

Use code tags (the </> icon above the compose window) to make it easier to read and copy for examination

1 Like

You have to read both buttons at the beginning of the loop and set state variables to indicate the buttons were pressed.
Otherwise, button presses may be ignored. For example, this: butt = !digitalRead(button_pinnn); is preceded by delay statements so that any button press that occurs during such delays will be lost.

Here you have another problem: volatile double waterFlow;. This is updated in an Interrupt Service Routine and read in the loop() (main). But, it is larger a byte so, on an 8 bit machine, it could result in a corrupt read operation. (See theme: non-atomic read operations or here volatile - Arduino Reference). Anyway, the solution is to make a copy of the variable in the loop while interrupts are suspended and use the copy.

1 Like

I'm not sure if I really understand what the functionality shall be:

Do you want

press button 1

  • switch on pump
  • and keep pump switched on
  • until a volume1 (a certain amount of liters) is measured by your flow-sensor

press button 2

  • switch on pump
  • and keep pump switched on
  • until a volume2 (a certain amount of liters) is measured by your flow-sensor

I emphasise certain amount of liters

volume (=liters) is something different than water-flow

which is how many Liters per second

If yes my approach would be

  • to assign the number of flow-meter pulses that represent the volume to a variable

and then inside the interrupt service-routine
check if value is still above zero
if above zero decrement this variable by 1
if zero is reached set a boolean flag variable "stopPump" to true

your main-loop is checking for this flag-variable to then switch off the pump.
inside your mainloop

  • disable interrupts
  • assign the value flow-meter-countdown-variable to another variable
  • enable interrupts

the disabling/enabling of the interrupt has to be finished as fast as possible to not loose pulses of the flowmeter.
After having the copy of the actual number of flow-meter-countdown-variable you can take time as long as you want to
process this number

best regards Stefan

1 Like

Thank you for your help. did so, at least he sees that the different buttons, one button he counts, but on the other pin relay turns off and does not count the flow, I know what to do, but I think it is a matter of interruption, but do not know how to solve, could you write a small example of a fix? would be very grateful to you

const int button_pin = 3; // button pin
const int relay_pin = 6; // relay pin

const int button_pinnn = 7; // button pin
boolean butt_flag = 0; // button button press flag
boolean butt; // button state variable
boolean flag = 0; // mode flag
boolean butt_flagg = 0; // button checkbox
boolean buttt; // a variable that checks button status
boolean flagg = 0;
unsigned long last_press; // timer for the debounce filter
volatile double waterFlow;
volatile double waterFloww;
 void setup() {
 Serial.begin(9600); //baudrate

  pinMode(button_pin, INPUT_PULLUP); 
  pinMode(button_pinnn, INPUT_PULLUP); 
  pinMode(relay_pin, OUTPUT); 
  double nn=2;
 attachInterrupt(0, pulse, CHANGE); //DIGITAL Pin 2: Interrupt 0
 attachInterrupt(0, pulsee, FALLING); //DIGITAL Pin 2: Interrupt 0
 waterFlow = 0.000;
 waterFloww = 0.000;
 
 }
  void pulse() //measure the quantity of square wave
  
 {
 waterFlow += 1.0 / 3510.0;
     if(waterFlow < 0.03 ){
 Serial.print("waterFlow:");
 Serial.print(waterFlow);
 Serial.println(" L ");
 delay(1000); 
   }
   else {
    digitalWrite(relay_pin, 0);
    waterFlow = 0.000;
    
 }
   }
  
  

int pulsee() {
waterFloww += 1.0 / 3510.0;
    if(waterFloww < 0.04 && buttt == 1 ){
 Serial.print("waterFloww:");
 Serial.print(waterFloww);
 Serial.println("L");
 delay(1000); 
   }
   else {
    digitalWrite(relay_pin, 0);
    waterFloww = 0.000;
   } 
 
} 
  
 void loop() {

    butt = ! digitalRead(button_pin); // read current button position

  if (butt == 1 && butt_flag == 0 && millis() - last_press > 1000) { // if the button is DOWN, but has been released before
    butt_flag = 1; // remember that the button was pushed
    flag = !flag; // invert the flag
    last_press = millis(); // remember the time   
    digitalWrite(relay_pin, flag); // send signal to relay_ LED pin
  }
  
  if (butt == 0 && butt_flag == 1) { // if the button is LOWERED and has been previously pressed
    butt_flag = 0; // remember releasing the button
  }
  
 
    buttt = !digitalRead(button_pinnn); // read the current button position   
    if (buttt == 1 && butt_flagg == 0 && millis() - last_press > 1000) { // if the button is PUSHED, but was previously STOPPED
    butt_flagg = 1; // remember that the button was pushed
    flagg = !flagg; // invert the flag
    last_press = millis(); // remember the time

    digitalWrite(relay_pin, flagg); // send a signal to the relay_pin
  }
  if (buttt == 0 && butt_flagg == 1) { // if button is LOWERED and previously was DOWN
    butt_flagg = 0; // remember releasing the button
  }

   
 }

Waterflow is 4 bytes long. Your Arduino must process each byte with several instructions. An interrupt will change Waterflow value in the midst of being tested by the "if" code.

Consider changing your logic to just count the interrupts in your interrupt code. Then in your "loop()" code, turn interrupts off, save the interrupt count to a new variable and turn the interrupts back on. Then use your copy of the count to compute the volume that has been pumped.

1 Like

you cannot specify two conditions on the same external interrupt

this probably will not work because waterFlow is initally 0 and will always be reset to 0 by the else statement. Printing in an ISR is unreliable.

The main logic is not clear enough. You have two buttons and they both have an effect on the relay_pin. Can you say in words what should happen when the buttons are pressed and how this is dependent on the waterFlow, the length of time the buttons are pressed for, etc.

I'm guessing that button 1 should deliver a metered quantity of water. Button 2 should do the same. Maybe there is some special handling if button 2 is pressed before the activity started by button 1 is finished.

1 Like

I understand correctly that you cannot call the interrupt function more than once?
I just have the following logic, button calls are processed in the loop, and after the loop, water is already moving and counting liters this is in the pulse and pulseee function

The pin is pin 2. Only one interrupt service routine can be called based on activity on that pin. Choose only one of FALLING, RISING or CHANGE.

1 Like

no.
If you define an interrupt you define it for rising or fallling or changing.

Defining for more than one behaviour is similar to telling your barber
color all my hair green, color all my hair red, color all my hair blue.
doesn't make sense

the interrupt service function is called each time
either
a rising-edge
or
a falling-edge
or
change of an edge (which includes rise and fall)
occurs

I don'tunderstand what you want to say with that

is this a correct description of your wanted functionality
please answer with "yes correct"§
or in case of no

write down a description in this style

as an addition: trying to be fast by writing quickly too short answers is in fact slowing down finding the solution
best regards Stefan

1 Like

Yes, this is indeed the logic.
there is a choice to press the first button or the second (each button has its own volume according to the condition else)

-I press the first button (its function is in the loop)

  • the relay and pump are starting
  • the interrupt function counts the thread for the first button by condition (if the condition is not met, the block follows otherwise)
    the cycle repeats

I press the second button:
the same should be done, but it considers the volume as with the first button, when it should be different. Or it doesn't turn on at all. at least separates them now
best regards, PHAROS

So here is a code that works as described above

If a button is pressed set a countervariable to the amount of lowmeter-pulses that equals your desired volume

switch on pump
set a flag-variable PumpRunning to true

print volume once every second with a non-blocking timer

interrupt -service-function counts down pulses until zero
then switch pump of
done


const byte button1Pin = 3;
const byte button2Pin = 7;
const byte relayPin   = 6;
const byte waterflowPin = 2;

const byte pressed   = LOW;
const byte unpressed = HIGH;

const byte PumpOn  = LOW; // change to HIGH if relay is HIGH-active
const byte PumpOff = HIGH;// change to LOW if relay is HIGH-active


unsigned long PulseSum1 = 30000; // don't know the real value
unsigned long PulseSum2 = 50000; // don't know the real value

volatile unsigned long ISR_Counter = 0;
unsigned long CounterSnapShot = 0;
volatile boolean PumpRunning = false;
unsigned long PrintTimer = 0;


void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

unsigned long MyTestTimer =  0;                   // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void pulse() {
  if (ISR_Counter > 0) {
    ISR_Counter--;
  }
  else {
    PumpRunning = false;
  }
}

void CalculatePrintVolume() {
  Serial.println("replace with calculation pulses to Liter");
}


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();

  pinMode(button1Pin,INPUT_PULLUP);
  pinMode(button2Pin,INPUT_PULLUP);
  pinMode(waterflowPin,INPUT_PULLUP);
  
  pinMode(relayPin,OUTPUT);
    
  attachInterrupt(digitalPinToInterrupt(waterflowPin), pulse, FALLING); //DIGITAL Pin 2: Interrupt 0
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);

  if (!PumpRunning) { // check if there is idle-mode
    if (digitalRead(button1Pin) == pressed && digitalRead(button2Pin) == unpressed) {
      ISR_Counter = PulseSum1; // set counter for volume 1
      PumpRunning = true; // change flag which has the additional effect of debouncing 
      digitalWrite(relayPin, PumpOn);
    }

    if (digitalRead(button2Pin) == pressed && digitalRead(button1Pin) == unpressed) {
      ISR_Counter = PulseSum2; // set counter for volume 2
      PumpRunning = true; // change flag which has the additional effect of debouncing 
      digitalWrite(relayPin, PumpOn);
    }
  }

  if (PumpRunning) { 
    if ( TimePeriodIsOver(PrintTimer, 1000) ) { // check if 1000 milliseconds of time has passed by
      noInterrupts(); // disable interrupts
      CounterSnapShot = ISR_Counter; // make a quick copy of actual counter
      // immidiately after making the copy of ISR_counter
      // enable interrupts again
      interrupts();
      CalculatePrintVolume(); // lots of time for calculations
    }
  }
  else { // countdown is at zero switch pump off
    digitalWrite(relayPin, PumpOff);
  }
}

best regards Stefan

1 Like

This probably will not work as expected.

I'm too lazy to think about.
Either you post what you mean or it stays undiscovered until I need to use this code in reality

It's only, the read of ISR_Counter in the main sketch is a critical section, you have to disable interrupts, make a local copy, then enable interrupts again.

that is what I'm doing here

or do you mean that inside the ISR interrupts have to be disabled?
I think inside the ISR interrupts are disabled

I came here late. I was referring to the code in pulse(), in reply #16. An earlier version?

the sequence is

assign ISR_Counter a value > 0
assign PumpRunning = true;
after that switch on pump => still some additional time until the flow-meter creates pulses

ISR_Counter reaches 1
doing the last ISR_Counter--;

now ISR_Counter is zero
=> PumpRunning = false;

maincode switches off pump
flow-meter might count some more pulses as the pump is not immidiately on rpm zero
doesn't matter because ISR_counter is zero => ISR sets PumpRunning = false;
would again execute switching pump off

if at next iteration of loop a button is pressed start again with sequence
assign ISR_Counter a value > 0
assign PumpRunning = true;
after that switch on pump => still some additional time until the flow-meter creates pulses

as ISR_Counter is > 0 just count down again

so where do you see a problem?