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
}
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 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.
@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
@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.
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().