Adjustable water flow sensor with simple relay control for pumps

Ive got this shield on my arduino uno. Im using the adafruit flow meter. Im very new to arduino and i have almost no programming experience to speak of. I am able to load the simple flow metrer sketches that ive found scouring the internet to use with this flow meter, and im able to get the lcd to produce the proper output. I'd like to take this project a bit farther. using the buttons on the shield, id like to create a menu to allow me to set a specific water volume then turn on a relay to control a pump, sense the volume, then shut off the pump once the desired level has been reached. like i said i have no programming experience. I just need some guidance here.

that's a simple compare statement between total flow amount and set amount. Set amount can be adjusted by using buttons and displayed on the lcd. As you already have sorted out he hard part which is counting the pulses and displaying the total amount the rest should be easy.

look at this post (still ongoing conversation but you should get the general idea of how your shield works and how to compare a setpoint to a reading)
http://forum.arduino.cc/index.php?topic=329761.0

you say both volume and level.

flow is very simple.

level is simple.

whichever you choose, it should be pretty simple.

dave-in-nj:
you say both volume and level.

flow is very simple.

level is simple.

whichever you choose, it should be pretty simple.

Wow thanks for telling me how simple it is. Now I'm an expert arduino coder!

wbarber69:
Wow thanks for telling me how simple it is. Now I'm an expert arduino coder!

you're welcome.

don't forget to add [SOLVED] on the subject line.

since two people told you it is simple, you might get the jist.

of course, you do need to pick flow or level.

you will find that every post that makes fundamental assumptions gets pointed back on track. it is not meant to be harsh. you asked for help, we are pointing you towards that end. deal with it.

gpop1:
that's a simple compare statement between total flow amount and set amount. Set amount can be adjusted by using buttons and displayed on the lcd. As you already have sorted out he hard part which is counting the pulses and displaying the total amount the rest should be easy.

look at this post (still ongoing conversation but you should get the general idea of how your shield works and how to compare a setpoint to a reading)
Irrigation lcd menu - Programming Questions - Arduino Forum

very valuable stuff in that post. thanks for your help. im gonna need a bit more help since that project is not anywhere close to what im doing. Im not using one-wire moisture sensors. i dont need a percentage value but a litre value. and im not really sure how to tell the arduino how to use the flowrate calculations to decide when and how to fire off the relay. All i have at the moment is 3 different sketches (one for flow meter, one for lcd shield and button assignments, and one from that other project) the only thing they have in common so far is they all use an lcd. I have no clue what im doing. Im sure ive pointed that out 3 times already. And assuming that this is easy is very wrong. Just because a lot of people go into the arduino with some basic coding knowledge doesnt mean this is easy. I bought an html4 book 17 years ago. That as far as my coding skills go. I understand pin assignments and stuff, from other projects ive hacked together, but i have no idea how to take 3 different sketches (none of which really do what i want, with the exception of flow rate) and turn them into what i really need, which is:

input desired gallons/liters
turn on relay
read flow sensor
reach desired galls/liters
turn off relay

I know it should be simple but there is a lot of code dedicated to just flow rate. and then i have to use buttons that all hail from one analogue pin by way of resistance values. its just a bit over my head.

wbarber69:
I know it should be simple but there is a lot of code dedicated to just flow rate. and then i have to use buttons that all hail from one analogue pin by way of resistance values. its just a bit over my head.

your raw input is on an pulse input ?

9,875 pulses is 'x' volume..

if(pulse_count >= 9875){
digitalWrite(pump,LOW)
}

there is the heart.

int pulse_total = 0;

change that with your serial inputs.

if(pulse_count >= pulse_total){ //pulse_total is change by serial input.
digitalWrite(pump,LOW)
}

you still have a better start than most people so post the lcd, buttons, flow meter codes you have (please hit reply then cut and copy the codes using the </> icon) Im sure one of us can look at the codes and do a rough splice to get you a starting point) Then we can help you understand how to modify that into the finished sketch.

here is a good reference of what the flow rate code does and how it handles pulses. it uses external buttons to clear values, which is not what i need. it also outputs flowrate and calculated total volumes. all i need is the part that calculates the volume.

#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Specify the pins for the two counter reset buttons and indicator LED
byte resetButtonA = 11;
byte resetButtonB = 12;
byte statusLed    = 13;

byte sensorInterrupt = 0;  // 0 = pin 2; 1 = pin 3
byte sensorPin       = 2;

// 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 totalMilliLitresA;
unsigned long totalMilliLitresB;

unsigned long oldTime;

void setup()
{
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print("                ");
  
  // 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);
  digitalWrite(statusLed, HIGH);  // We have an active-low LED attached
  
  // Set up the pair of counter reset buttons and activate internal pull-up resistors
  pinMode(resetButtonA, INPUT);
  digitalWrite(resetButtonA, HIGH);
  pinMode(resetButtonB, INPUT);
  digitalWrite(resetButtonB, HIGH);
  
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, HIGH);

  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0;
  totalMilliLitresA = 0;
  totalMilliLitresB = 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()
{
  if(digitalRead(resetButtonA) == LOW)
  {
    totalMilliLitresA = 0;
    lcd.setCursor(0, 1);
    lcd.print("0L      ");
  }
  if(digitalRead(resetButtonB) == LOW)
  {
    totalMilliLitresB = 0;
    lcd.setCursor(8, 1);
    lcd.print("0L      ");
  }
  
  if( (digitalRead(resetButtonA) == LOW) || (digitalRead(resetButtonB) == LOW) )
  {
    digitalWrite(statusLed, LOW);
  } else {
    digitalWrite(statusLed, HIGH);
  }
  
  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);
    //lcd.setCursor(15, 0);
    //lcd.print("*");
    
    // 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
    totalMilliLitresA += flowMilliLitres;
    totalMilliLitresB += flowMilliLitres;
  
    // During testing it can be useful to output the literal pulse count value so you
    // can compare that and the calculated flow rate against the data sheets for the
    // flow sensor. Uncomment the following two lines to display the count value.
    //Serial.print(pulseCount, DEC);
    //Serial.print("  ");
    
    // Write the calculated value to the serial port. Because we want to output a
    // floating point value and print() can't handle floats we have to do some trickery
    // to output the whole number part, then a decimal point, then the fractional part.
    unsigned int frac;
    
    // Print the flow rate for this second in litres / minute
    Serial.print(int(flowRate));  // Print the integer part of the variable
    Serial.print(".");             // Print the decimal point
    // 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

    // Print the number of litres flowed in this second
    Serial.print(" ");             // Output separator
    Serial.print(flowMilliLitres);

    // Print the cumulative total of litres flowed since starting
    Serial.print(" ");             // Output separator
    Serial.print(totalMilliLitresA);
    Serial.print(" ");             // Output separator
    Serial.println(totalMilliLitresB);
    
    lcd.setCursor(0, 0);
    lcd.print("                ");
    lcd.setCursor(0, 0);
    lcd.print("Flow: ");
    if(int(flowRate) < 10)
    {
      lcd.print(" ");
    }
    lcd.print((int)flowRate);   // Print the integer part of the variable
    lcd.print('.');             // Print the decimal point
    lcd.print(frac, DEC) ;      // Print the fractional part of the variable
    lcd.print(" L");
    lcd.print("/min");
    
    lcd.setCursor(0, 1);
    lcd.print(int(totalMilliLitresA / 1000));
    lcd.print("L");
    lcd.setCursor(8, 1);
    lcd.print(int(totalMilliLitresB / 1000));
    lcd.print("L");

    // Reset the pulse counter so we can start incrementing again
    pulseCount = 0;
    
    // Enable the interrupt again now that we've finished sending output
    attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
  }
}

/**
 * Invoked by interrupt0 once per rotation of the hall-effect sensor. Interrupt
 * handlers should be kept as small as possible so they return quickly.
 */
void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}

Here is the code that comes with the lcd shield as an example of how to define the buttons through A0

//Sample using LiquidCrystal library
#include <LiquidCrystal.h>

/*******************************************************

This program will test the LCD panel and the buttons
Mark Bramwell, July 2010

********************************************************/

// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);      // read the value from the sensor 
 // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
 // we add approx 50 to those values and check to see if we are close
 if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
 // For V1.1 us this threshold
/*
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 250)  return btnUP; 
 if (adc_key_in < 450)  return btnDOWN; 
 if (adc_key_in < 650)  return btnLEFT; 
 if (adc_key_in < 850)  return btnSELECT;  
*/
 //For V1.0 comment the other threshold and use the one below:

 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 195)  return btnUP; 
 if (adc_key_in < 380)  return btnDOWN; 
 if (adc_key_in < 555)  return btnLEFT; 
 if (adc_key_in < 790)  return btnSELECT;   



 return btnNONE;  // when all others fail, return this...
}

void setup()
{
 lcd.begin(16, 2);              // start the library
 lcd.setCursor(0,0);
 lcd.print("Push the buttons"); // print a simple message
}
 
void loop()
{
 lcd.setCursor(9,1);            // move cursor to second line "1" and 9 spaces over
 lcd.print(millis()/1000);      // display seconds elapsed since power-up


 lcd.setCursor(0,1);            // move to the begining of the second line
 lcd_key = read_LCD_buttons();  // read the buttons

 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     lcd.print("RIGHT ");
     break;
     }
   case btnLEFT:
     {
     lcd.print("LEFT   ");
     break;
     }
   case btnUP:
     {
     lcd.print("UP    ");
     break;
     }
   case btnDOWN:
     {
     lcd.print("DOWN  ");
     break;
     }
   case btnSELECT:
     {
     lcd.print("SELECT");
     break;
     }
   case btnNONE:
     {
     lcd.print("NONE  ");
     break;
     }
 }

}

Sounds like you are struggling with putting the whole thing together. This is a basic problem with any project. People get frustrated because they are looking at the whole picture rather than all the little ones. Start with a sketch that you have that is working and add things to it one piece at a time and test each piece. If you have a sketch that measures flow rate and displays it, then add the function that tells you how much has gone through the sensor. Then add the code to turn on and off your valve. Work with the simplest steps to start. As you gain the experience of doing these simple additions, your additions will become bigger and bigger until all of a sudden you are writing code.

I have found menus to be a bit daunting, although I have written a couple. I have never used it, but there is a menu library with example code here: Arduino Playground - Menu Library. I'd add the menu last and get all the other functions working and then start on the harder things like a menu.

What scale would you like to work with. If gallons which one. Also what ballpark range do you require

Ex. 300 to 1000 us,gallons

how do you determine how much volume you need in the first place ?

if your tank is empty, you need 'litres' to fill it. "x"

then you have to measure how many litres leave it, call that "y"

you have a simple x/y problem. (pun intended)

the other option is to put in a sensors to detect water level. then when it is at one point, turn on the valve and let the pump run until the 'full' point is reached. you can record the quantity, but it would not be needed in the control process.

this would be the level method, and is much easier in some cases than the quantity (volume) method.
specifically because you have to have two sensors that detect volume and they have to be dead on. one that enters the tank, and you control with that one. and one that leaves the tank, and that one is for measurement only.

If one reads 1% higher than the other, eventually the tank will either overflow or become empty.

if you are mixing two things, like soap and water for a dishwasher, different story.

I did not see if you have both an odometer, a value that never gets re-set, and also a trip meter, one that you can re-set.

read about batch processes. just the overview to get the idea.

PV or Process Variable
SV or SetPoint Variable

make PV a separate variable, one that re-sets only after PV=SV
so that you finish your batch process.

pass PV over to the LCD as one line.
all the calculaiton are on the calculation side,
the display only ever gets the one line of PV.

in that way, all the bits of the program act independently from each other.

gpop1:
What scale would you like to work with. If gallons which one. Also what ballpark range do you require

Ex. 300 to 1000 us,gallons

More like 20-30 gallons max. I'll probably just leave it all in metric though since it's easier to work with digitally. And in the end it'll be easier for my other calculations down the road.

dave-in-nj:
how do you determine how much volume you need in the first place ?

if your tank is empty, you need 'litres' to fill it. "x"

then you have to measure how many litres leave it, call that "y"

you have a simple x/y problem. (pun intended)

the other option is to put in a sensors to detect water level. then when it is at one point, turn on the valve and let the pump run until the 'full' point is reached. you can record the quantity, but it would not be needed in the control process.

this would be the level method, and is much easier in some cases than the quantity (volume) method.
specifically because you have to have two sensors that detect volume and they have to be dead on. one that enters the tank, and you control with that one. and one that leaves the tank, and that one is for measurement only.

If one reads 1% higher than the other, eventually the tank will either overflow or become empty.

if you are mixing two things, like soap and water for a dishwasher, different story.

No I need this to allow a set amount of liquid through based on user input. And it can definitely be set by the liter.

I know you're giving me shit about my spelling in some of my posts. But take into mind that my Linux test laptop that I use sometimes has a Danish keyboard on it and some of the punctuation and spell check is way off from what I've used my whole life. It was a throw away that was given to me. My bad.

wbarber69:
More like 20-30 gallons max. I'll probably just leave it all in metric though since it's easier to work with digitally. And in the end it'll be easier for my other calculations down the road.

ok so that's about 90L to 135L as a set point.

gpop1:
ok so that's about 90L to 135L as a set point.

Well yeah, but I want to be able to set the set point manually using the buttons on the lcd shield.

yep I guessed that. I don't have anything to test this on so im just guessing at some of the code but its something to work with

test this and see what happens

press select to get to the set point, use up and down to set, then select to return to main screen, press right button to run. If it works then the led on 13 should light and stay lit until the flow reach's the set point.

ive left a lot of code in there that does nothing but will be useful later

don't panic if it doesn't work as Im not good at this just list what happens or doesn't and I will try to fix it.

#include <Wire.h>
#include <LiquidCrystal.h>


/*******************************************************

This program will test the LCD panel and the buttons
Mark Bramwell, July 2010

********************************************************/

// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

byte sensorInterrupt = 0;  // 0 = pin 2; 1 = pin 3
byte sensorPin       = 2;
float calibrationFactor = 4.5;
float flowRate=0;
volatile byte pulseCount=0;
unsigned long flowMilliLitres=0;
unsigned long totalMilliLitresA=0;
byte screen=0;
int set_point=100;
byte edit=0;
unsigned long oldTime=0;
byte key_input;
byte statusLed    = 13;
int total_litres=0;
byte display_run=0;
// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);      // read the value from the sensor 
 if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
 if (adc_key_in < 50)   return btnRIGHT;  
 if (adc_key_in < 195)  return btnUP; 
 if (adc_key_in < 380)  return btnDOWN; 
 if (adc_key_in < 555)  return btnLEFT; 
 if (adc_key_in < 790)  return btnSELECT;   



 return btnNONE;  // when all others fail, return this...
}

void setup()
{
 lcd.begin(16, 2);              // start the library
 lcd.clear();
 lcd.setCursor(0,0);
 Serial.begin (9600);
  attachInterrupt(sensorInterrupt, pulseCounter, FALLING);

}
 
void loop()
{
pinMode(statusLed, OUTPUT);
  
 lcd_key = read_LCD_buttons();  // read the buttons

 switch (lcd_key)  // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     break;
     }
   case btnLEFT:
     {
    key_input=1;
     break;
     }
   case btnUP:
     {
    if (edit=1)
      {set_point++;}
     break;
     }
   case btnDOWN:
     {
   if (edit=1)
      {set_point--;}
     break;
     }
   case btnSELECT:
     {
 if (screen==1){
   screen = 0;
 edit=0;}
 else
 {screen=1;
 edit =1;}
     break;
     }
   case btnNONE:
     {
    //used for speed
     break;
     }
 }
if (screen<0){screen=0;}
if (screen>1){screen=1;}
if (set_point<60){set_point=60;}
if (set_point>150) {set_point=150;}



 if(key_input==1){
   totalMilliLitresA =0;
   total_litres=0;
   display_run=1;
 digitalWrite(statusLed, HIGH);}
   
   if (total_litres>set_point){
     display_run=0;
 digitalWrite(statusLed, LOW);}
 
  

  if((millis() - oldTime) > 1000)    // Only process counters once per second
  { 
    
    detachInterrupt(sensorInterrupt);
    flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;
    oldTime = millis();
    // convert to millilitres.
    flowMilliLitres = (flowRate / 60) * 1000;
    
    // Add the millilitres passed in this second to the cumulative total
    totalMilliLitresA += flowMilliLitres;

    // Print the flow rate for this second in litres / minute
   // Serial.print(flowRate,2);

    // Print the number of litres flowed in this second
   // Serial.print(" ");             // Output separator
   // Serial.print(flowMilliLitres);

    // Print the cumulative total of litres flowed since starting
   // Serial.print(" ");             // Output separator
   // Serial.print(totalMilliLitresA);
   if (display_run!=0){
    total_litres=totalMilliLitresA / 1000;
   }
      

    // Reset the pulse counter so we can start incrementing again
    pulseCount = 0;
    
    // Enable the interrupt again now that we've finished sending output
    attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
  }
  lcd.clear();
    lcd.setCursor(0, 0);
  switch (screen){
    case 0:
    lcd.print("Flow: ");
    lcd.print(flowRate,2);
    lcd.setCursor(8, 0);
    lcd.print(total_litres);
    lcd.print("L");
    lcd.setCursor(3, 1);
    lcd.print(set_point);
    lcd.print("L");   
  break;
  
  case 1:
  lcd.print(set_point);
    lcd.print("L");
     lcd.setCursor(0, 1);
     lcd.print("select to set");
  break;
  }
  
}
void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}