Problem with debouncing after interruption

Hi community,

I'm making a water flow controller to dosage water into a home brewery. I'm using an Arduino Uno R3, 16x2 LCD keypad Shield, Flowsensor YF-S201 and a solenoid valve.

I built a menu structure with the following:
1 - Main Menu:
1.1 - Settings
1.1.1 - flow sensor factor adjustment
1.1.2 - language
1.2 - Flow Control
1.2.1 Manual Control: press a button to open the valve manually
1.2.2 Automatic Control: define volume and program does all the job
1.3....other menus to read other sensors

To read the flow sensor input I'm using an interruption of 1s and counting the rising pulses, it works properly alone. What happens is that after desactivate the interruption (when total volume is achieved for example) my debouncing delays just don't work anymore and my menu get crazy.

In my understand, after desactivating the interruption the delay() or millis() should work fine, don't they? They should not work only inside the ISR.

I wonder if anyone can help?

OBS. because of the number of menus I'm not posting the whole code here, see below the code from the manual control menu, if "up" is pressed open valve and start measuring, if "down" is pressed close valve, if "back" is pressed return to previous menu:

attachInterrupt (0,incpulso, RISING); // interruption description

/*
void incpulso()
 {
   counter++;
 }
*/
.
.
.
// --- MANUAL CONTROL MENU FUNCTION --- 
void manual()
{
  boolean valve_ctrl = 0;//valve flag
  Serial.println("manual");
  //PRINTING STATUS ON LCD
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("VALVE:");
  if (digitalRead(valve)== 0)
    {
      lcd.print("ON ");  
      Serial.println(digitalRead(valve));    
    }
  else if (digitalRead(valve)==1)
    {
      lcd.print("OFF");
      Serial.println(digitalRead(valve));
    }
  lcd.setCursor(0,1);
  lcd.print("T:");
  lcd.print(total);
  lcd.print("L ");
  lcd.print("F:");
  lcd.print(flow);
  total = 0; //ZEROING TOTAL FOR NEW MEASURING
  while (analogRead(A0) < 400 || analogRead(A0) > 800) //WHILE BT IS DIFFERENCE OF "BACK"
  {
    delay(t1);//DEBOUNCING DELAY T1 = 100ms
    if (((analogRead(A0) > 100 && analogRead(A0) < 200))|| (valve_ctrl == 1))  //"UP" BT PRESSED
      {
        delay(t1);
        valve_ctrl = 1;//valve flag
        lcd.setCursor(6,0);
        lcd.print("ON ");     
        digitalWrite(valve,LOW);//open rele valve to flow
        
        //COUNTER
        sei();//enable interruption
        unsigned long currentMillis = millis();
        while (millis() < currentMillis + 1000)
        {
        }
        cli();//unable interruption;
     
        //CALCULATIONS                  
        flow = counter/k_factor;//convert to L/min
        volume = flow/60;//define volume in 1s
        total = (total + (volume*1000));//transform volume to ml and sum into total
        lcd.setCursor(2,1);
        lcd.print(total/1000);
        lcd.setCursor(10,1);
        lcd.print(flow);
        if (analogRead(A0) > 200 && analogRead(A0) < 400) // check "down" bt pressed after 1st cycle
          {
            valve_ctrl = 0;    
          }
      }
    else if (analogRead(A0) > 200 && analogRead(A0) < 400) // "down" pressed
      {
        delay(t1); //debouncing delay
        valve_ctrl = 0;//valve flag
        lcd.setCursor(6,0);
        lcd.print("OFF");   
        digitalWrite(valve,HIGH);//close valve and so flow
      }
  }
 //when left is pressed
 delay(t1);//debouncing
 digitalWrite(valve, HIGH);//close valve to go back to menu;
 total = 0;
 flow = 0;
 volume = 0;
 counter = 0;
 flow_control(); //go to flow control menu to select between manual/automatic
}

because of the number of menus I'm not posting the code here

I guess people will not post replies in that case :wink:

Just added part of the code.
Thanks in advance!

I cannot begin to discern your issue without seeing the entire sketch.

ToddL1962:
I cannot begin to discern your issue without seeing the entire sketch.

Better yet: @cpicolo, please post an MRE that demonstrates the problem.

Your use of interrupts is the problem. Don't.

It is much easier to deal with switch bounce if you poll the input. You will not be able to use delay(), so study the Blink Without Delay tutorial to learn how to avoid that.

I smell recursion

void manual()
{
  ...
  ...
  flow_control(); //go to flow control menu to select between manual/automatic
}

@cpicolo, is manual() called from flow_control() ?

is manual() called from flow_control() ?

@sterretje, yes.

I believe that flowmeter uses a hall effect sensor (3 wires) that does not bounce, it is normally pulled HIGH with a pullup resistor and switches LOW when activated. So you need pullup resistor or set pinMode to INPUT_PULLUP and FALLING in the interrupt setup. Do you have that?

@jca34f The debouncing is for the menu control buttons (analog read from the LCD keypad shield) not for the flow reading. The thing is that after calling the interruption for flow reading the debouncing delay just don't work anymore.

cpicolo:
@jca34f The debouncing is for the menu control buttons (analog read from the LCD keypad shield) not for the flow reading. The thing is that after calling the interruption for flow reading the debouncing delay just don't work anymore.

Again, please post your sketch.

The debouncing is for the menu control buttons (analog read from the LCD keypad shield)

Why do you believe that the analogReadings from the keypad shield need debouncing?

Given the proper choice of thresholds for the different buttons debounce should not be necessary.

cattledog:
Why do you believe that the analogReadings from the keypad shield need debouncing?

Given the proper choice of thresholds for the different buttons debounce should not be necessary.

Okay, I admit I've never tried that kind of button setup, but why would that be the case? I don't get it.

@OP,
just wondering how you did the 1 sec ISR.
my guess is that maybe it is using the same timer/counter than the millis or the delay.
In my opinion, the 1 sec ISR must be enabled all the time, otherwise, your flowmeter count is not accurate.
can you post the code or at least the 1sec ISR?
Regards

For button, I recommend using the button library instead of interrupt. See For beginners: The simple way to program for multiple buttons [code example] - Introductory Tutorials - Arduino Forum

@OP,
should disable the interrupt only when the 1 sec delay is done.
something like this (pseudo-code):
if elapsed time > 1000ms {
disable interrupt
do the flow calculation
display stuff
reset the flowmeter counter
enable the interrupt
}

To read a button push, put the button on an interrupt line. When the button is pushed, it activates an interrupt routine that just toggles a boolean, say to buttonPushed = TRUE, and sets a variable timeNow = millis(). Whenever the interrupt routine starts, check timeNow against millis(), and if not enough time has passed, don't toggle the boolean.

Now go take care of the actual code for the button push by polling in the loop, to see if buttonPushed is True. When done with that code, be sure to set it FALSE. And don't forget to use the volatile variable declaration.

jrdoner:
To read a button push, put the button on an interrupt line. When the button is pushed, it activates an interrupt routine that just toggles a boolean, say to buttonPushed = TRUE, and sets a variable timeNow = millis(). Whenever the interrupt routine starts, check timeNow against millis(), and if not enough time has passed, don't toggle the boolean.

Now go take care of the actual code for the button push by polling in the loop, to see if buttonPushed is True. When done with that code, be sure to set it FALSE. And don't forget to use the volatile variable declaration.

You are right, the only problem is that any controller has a limitation regarding interrupts inputs number.
Rgrds

jrdoner:
To read a button push, put the button on an interrupt line.

You can't do that with a "resistorplexed" button. You could conceivably set up some interrupt on the analog comparator, but it may or may not be possible because you have to gate the "no button" voltage.

cpicolo:
@sterretje, yes.

You should not do that; it's called recursion and eventually will result in a crash because you run out of memory. When you call flowcontrol(), a little bit of data is saved in memory. flowcontrol() calls manual() and again a little bit of data is save in memory. manual() calls flowcontrol() again when it is finished and again a bit of data is saved in memory. And so on and so on.

Once manual() is finished, it should go back to flowcontrol(), not call flowcontrol() again. Functions do return to the function that called them so there is no need to call flowcontrol() at the end of manual().