I am VERY new to Arduino and looking for some help with a sketch I am working on dispensing water. I need to dispense a certain amount of liquid (a minimum of 1L) measured with a Hall effect sensor pin-2 and then shut off supply via solenoid valve pin -5, recording the the amount of water dispensed.
Current set up is a solenoid valve upstream of a water flow sensor. I need to measure the water being distributed, after 1 L of water is distributed, I need the solenoid valve to shut off. (This is currently being performed by the program attached), currently the program outputs the amount of water as intended then delays and then repeats. I need the program to repeat AFTER a pushbutton is pressed, instead of the delay, but I'm not sure how to achieve this.
Edit: I've included a screenshot of the serial monitor. When the program is uploaded the solenoid valve (pin #5) is closed. the push button (pin #4/startpin) is pressed to open the valve. Water flows through the solenoid valve to water flow sensor (pin #2). Once 1000mL have passed, the solenoid valve (pin #5) is closed. A delay of 5 seconds occurs, and then the valve is open and water is passed through and measured again until 1000mL is passed, and repeat.
Instead of the delay occurring I need to have a push button reinitiate the process
You have posted a program but you have not told us what it actually does or what you want it to do that is different. There is no point looking at the code unless we know what to look for.
Thanks for the feed back, I edited the above to include more info. Basically, I am looking to repeat the loop function in the program via a pushbutton instead of the delay line that is currently there.
Do you need to display the flowrate? If not, you don't need all this code.
The OP's code auto formatted.:
/*
Liquid flow rate sensor -DIYhacking.com Arvind Sanjeev Measure the liquid/water flow rate using this code. Connect Vcc
and Gnd of sensor to arduino, and the signal line to arduino digital pin 2.
*/
byte statusLed = 13;
byte sensorInterrupt = 0;
// 0 = digital pin 2
byte sensorPin = 2;
int val = 0;
// put your button on pin 4
const int startpin = 4;
int buttonState = 0;
// The hall-effect flow sensor outputs approximately 4.5 pulses per second per
// litre/minute of flow.
float calibrationFactor = 4.5;
volatile byte pulseCount;
float flowRate;
unsigned int flowMilliLitres;
unsigned long totalMilliLitres;
unsigned long oldTime;
void setup() {
// Initialize a serial connection for reporting values to the host
Serial.begin(38400);
// Set up the status LED line as an output
pinMode(statusLed, OUTPUT);
// put your relay on pin 5
pinMode(5, OUTPUT);
pinMode(startpin, INPUT_PULLUP);
digitalWrite(statusLed, HIGH);
// We have an active-low LED attached
pinMode(sensorPin, INPUT);
digitalWrite(sensorPin, HIGH);
pulseCount = 0;
flowRate = 0.0;
flowMilliLitres = 0;
totalMilliLitres = 0;
oldTime = 0;
// The Hall-effect sensor is connected to pin 2 which uses interrupt 0.
// Configured to trigger on a FALLING state change (transition from HIGH
// state to LOW state)
attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
}
/** * Main program loop */
void loop()
{
// read the input pin:
int buttonState = digitalRead(startpin);
// fill button start if relay turn off
if (digitalRead(5) == LOW)
{ val = digitalRead(startpin);
digitalWrite(5, val);
}
if ((millis() - oldTime) > 1000)
// Only process counters once per second
{
// Disable the interrupt while calculating flow rate and sending the value to
// the host
detachInterrupt(sensorInterrupt);
// Because this loop may not complete in exactly 1 second intervals we calculate
// the number of milliseconds that have passed since the last execution and use
// that to scale the output. We also apply the calibrationFactor to scale the output
// based on the number of pulses per second per units of measure (litres/minute in
// this case) coming from the sensor.
flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;
// Note the time this processing pass was executed. Note that because we’ve
// disabled interrupts the millis() function won’t actually be incrementing right
// at this point, but it will still return the value it was set to just before
// interrupts went away.
oldTime = millis();
// Divide the flow rate in litres/minute by 60 to determine how many litres have
// passed through the sensor in this 1 second interval, then multiply by 1000 to
// convert to millilitres.
flowMilliLitres = (flowRate / 60) * 1000;
// Add the millilitres passed in this second to the cumulative total
totalMilliLitres += flowMilliLitres;
unsigned int frac;
Serial.print(buttonstate)
// Print the flow rate for this second in litres / minute
Serial.print("Flow rate: ");
Serial.print(int(flowRate));
// Print the integer part of the variable
Serial.print(".");
// Print the decimal point
// turn on pin 3 if flowrate is greater than 4
// Determine the fractional part. The 10 multiplier gives us 1 decimal place.
frac = (flowRate - int(flowRate)) * 10;
Serial.print(frac, DEC) ;
// Print the fractional part of the variable
Serial.print("L/min");
// Print the number of litres flowed in this second
Serial.print(" Current Liquid Flowing: ");
// Output separator
Serial.print(flowMilliLitres);
Serial.print("mL/Sec");
// Print the cumulative total of litres flowed since starting
Serial.print(" Output Liquid Quantity: ");
// Output separator
Serial.print(totalMilliLitres);
Serial.println("mL");
// check if flow volume is > 1 litres
if (totalMilliLitres > 1000)
{ digitalWrite(5, LOW);
delay(5000);
// reset volume to zero
totalMilliLitres = 0;
}
// Reset the pulse counter so we can start incrementing again
pulseCount = 0;
// Enable the interrupt again now that we’ve finished sending output
}
if digitalRead(
attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
}
}
/* Insterrupt
Service Routine
*/
void pulseCounter()
{
// Increment the pulse counter
pulseCount++;
}
The script posted has been uploaded to the Arduino and performs as expected. I just to need it to restart the loop when the push button (pin 4) is pushed, and ideally at line 95 when total milliitres > 1000 prompts line 96 the solenoid valve (pin 5) closes to stop printing recorded data until (pin 4) is pushed reinitiating the loop/measurement.
if (digitalRead(5)==LOW)
{
val = digitalRead(startpin);
digitalWrite(5, val);
}
What is pin 5 for? It is much easier to understand if you use names like you have with startpin
Why are you attaching and detaching the interrupt? It would be more usual to attach it in setup() and never detach it.
I need the program to repeat AFTER a pushbutton is pressed, instead of the delay, but I'm not sure how to achieve this.
You need a variable to keep track of the state of the system. The states seem to be waiting, starting and running ('W', 'S' and 'R'). If it is waiting then when you push the button it will change to starting. When it is starting it will turn on the relay and change to running. When it is finished running it will turn off the relay and change to waiting.
At the moment your button directly controls the machine, whereas it should really only change the state variable - and then only if it is in the waiting state.
pin #5 is the solenoid valve. To my understanding the code you are referring to allows the push button (startpin) to open the solenoid valve (pin 5) to allow water to flow to the flow sensor.
The detach interrupt is in place while calculating flow rate and sending data to host. It is enabled again after the data has been sent.
Interesting, I was viewing this as running vs. stopped and looking for the something to transition from stopped to running. I really appreciate the feedback, but I'm not quite how to translate that into this code. If there are any good examples or other threads that you think I may benefit from reading, please let me know!
I have found in my short time here that only a tiny percentage of problems posted have not been solved before - often many times over. A lot of finding answers on your own is just knowing what to put in the search box at the top of the page - trial and error, to be sure when one doesn't have the lexicon.
Also, the stickies at the head of every topic lead to their own vast troves.
HerbFlunderman:
The detach interrupt is in place while calculating flow rate and sending data to host. It is enabled again after the data has been sent.
You can enable and disable interrupts under program control. As I understand it, interrupts received while disabled will be serviced once reenabled. Interrupts received while detached are just gone.
You're right, Thanks Robin. I just deleted the detachinterrupt and the last attachinterrupt still works the same.
dougp:
You can enable and disable interrupts under program control. As I understand it, interrupts received while disabled will be serviced once reenabled. Interrupts received while detached are just gone.
This is very interesting, thank you for point me in this direction
As mentioned, problems like this are almost always solved using the state machine concept, millis() timing, and edge detection for your activation switch. You also need to be careful about disabling interrupts before accessing shared (volatile) variables and not trying to do too much while they are disabled.
Below is the framework for the code I’d use. You need to fill in your computation and printing details. Using enums and ‘switch()’ statements / ‘case’ clauses makes it very is to expand the state machine should more complex behavior be required in the future. I also personally find this framework format more aesthetically acceptable than the posted code.
const byte relayPin = 5;
const byte ledPin = 13;
const byte sensorPin = 2;
const byte sensorInterrupt = digitalPinToInterrupt(sensorPin);
const int startpin = 4;
const int totalFlowRequired = 1000;
const unsigned long samplePeriod = 1000;
byte lastButtonState;
unsigned long oldTime, totalMilliLitres;
volatile int pulseCount = 0;
enum machineState {waiting, dispensing};
machineState currentMachineState = waiting;
void setup() {
// Initialize a serial connection for reporting values to the host
Serial.begin(38400);
pinMode(startpin, INPUT_PULLUP);
pinMode(sensorPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, LOW);
digitalWrite(ledPin, HIGH); // Active-low LED
lastButtonState = digitalRead(startpin);
attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
}
void loop()
{
byte currentButtonState;
int currentCount;
unsigned long period;
currentButtonState = digitalRead(startpin);
switch (currentMachineState) {
case waiting:
if (currentButtonState != lastButtonState) {
if (currentButtonState == 0) {
currentMachineState = dispensing;
totalMilliLitres = 0;
noInterrupts();
pulseCount = 0;
interrupts();
digitalWrite(relayPin, HIGH);
oldTime = millis();
digitalWrite(ledPin, LOW);
// Print "Start Up" message to Serial as desired
}
}
break;
case dispensing:
period = millis() - oldTime;
if (period >= samplePeriod) {
noInterrupts();
currentCount = pulseCount;
pulseCount = 0;
interrupts();
oldTime += samplePeriod;
//
// Here you should use 'currentCount' and 'period' to compute
// how many mL flowed during the last ~1 second period. Add this number
// to 'totalMilliLitres' which is the running total. Can also use
// 'currentCount' and 'period' to compute flow RATE during this time.
// Print results to Serial as desired
//
//
if (totalMilliLitres >= totalFlowRequired) {
// Enough fluid has been dispensed
digitalWrite(relayPin, LOW);
digitalWrite(ledPin, HIGH);
currentMachineState = waiting;
// Print "Flow Stopped" message to Serial as desired
}
}
break;
default:
break;
}
lastButtonState = currentButtonState;
}
/* Insterrupt
Service Routine
*/
void pulseCounter()
{
// Increment the pulse counter
pulseCount++;
}
Get rid of the ‘oldTime = millis();’ line. Updating of ‘OldTime’ is handled with the ‘oldTime += samplePeriod;’ line:
if (period >= samplePeriod) {
noInterrupts();
currentCount = pulseCount;
pulseCount = 0;
interrupts();
oldTime += samplePeriod; // <--------------- KEEP THIS
//
flowRate = (1000.0 / period * currentCount) / calibrationFactor;
oldTime = millis(); // <----------------------- GET RID OF THIS
flowMilliLitres = (flowRate / 60) * 1000;
Your computation of quantity dispensed over each ~1 second period is rather convoluted. You convert pulses (which are a measurement of quantity) to a flow RATE which you then convert back to a quantity. That’s like going around your ear to scratch your elbow.
The problem starts with your choice of calibration factor:
// The hall-effect flow sensor outputs approximately 4.5 pulses per second per
// litre/minute of flow.
float calibrationFactor = 4.5;
Those are truly bizarre units. I assume the flow sensor is a spinning thingy exposed to the fluid’s flow. So, the number of pulses is directly proportional to the number of mL that have flowed passed the sensor. Rearranging your constant with some good old-fashioned "factor-label" conversion, I get:
const float calibrationFactor = 3.7037; // Units are mL / pulse;
Note the use of ‘const’. That number is not going to change while your program runs.
So now, multiplying ‘currentCount’ by ‘calibrationFactor’ directly gives you the number of mL that flowed during the last measurement period. Divide that by ‘period’ and you get the flow RATE for that period. That’s much more straight forward.
Instead of working so hard to print the float values, check out dtostrf()
Enjoy, have fun with your future Arduino projects!!!