Newbe Needs Help with Water Flow Sensor Program

OK before I get slammed, I have no programming experience and am learning as I go. I am also an old retired idiot so talk in simple terms and be nice :wink:

Here is my problem: I used the below sketch to calibrate two field effect water flow sensors. The program worked great and I calibrated the two sensors then installed them on my two geothermal heat pumps.

I wanted to be able to switch between the two sensors within the program (installed on pins 2 and 3) also wanted to add 5 analog water tank depth sensors. Plus add some calculations to the program.

Here is the problem: I can’t seem to add anything to the program without screwing up the timing and the results. I was reading up on functions and hoped I could create a function that could pass the interrupt and sensor pin then have the function write to an array with the values for the flow rate, elapsed time, and gallons consumed. I have failed miserably for the last two days. I just can’t figure it out. I am hoping that someone with some experience could help me or tell me I am on the wrong path.

Thanks in advance

 * Water Flow Gauge
 * Uses a hall-effect flow sensor to measure the rate of water flow and
 * output it via the serial connection once per second. The hall-effect
 * sensor connects to pin 2 and uses interrupt 0, and an LED on pin 13
 * pulses with each interrupt. Two volume counters and current flow rate
 * are also displayed on a 2-line by 16-character LCD module, and the
 * accumulated totals are stored in non-volatile memory to allow them to
 * continue incrementing after the device is reset or is power-cycled.
 * Two counter-reset buttons are provided to reset the two accumulating
 * counters. This allows one counter to be left accumulating indefinitely
 * as a "total" flow volume, while the other can be reset regularly to
 * provide a counter for specific events such as having a shower, running
 * an irrigation system, or filling a washing machine.
 * Copyright 2009 Jonathan Oxer <>
 * Copyright 2009 Hugh Blemings <>
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
1239.4L 8073.4L

// 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;

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;
float calibrationFactorHeat = 4.5*3.78541*1.1; //adjustment to gallons and calibration green wire heat pump
float calibrationFactorHot = (4.5*3.78541)* 1.30; //adjustment to gallons and calibration white wire hot water
volatile byte pulseCount;  

float flowRate;
unsigned int flowGallons;
unsigned long totalGallonsA;
unsigned long totalGallonsB;

unsigned long oldTime;
unsigned long ElapsedMilliseconds;

void setup()

  // Initialize a serial connection for reporting values to the host
  // 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;
  flowGallons   = 0;
  totalGallonsA = 0;
  totalGallonsB = 0;
  oldTime = 0;
  ElapsedMilliseconds = 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)
    totalGallonsA = 0;

  if(digitalRead(resetButtonB) == LOW)
    totalGallonsB = 0;

  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
    //lcd.setCursor(15, 0);
    // 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) / calibrationFactorHeat;
    flowRate = ((((millis() - oldTime) / 1000) * pulseCount) / calibrationFactorHeat);  
    // 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.
    // added next line
    ElapsedMilliseconds = ((millis()-oldTime) + millis());//corrected millis use for cals
           Serial.println("  (millis()-oldTime)");
    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 Gallons.
    // flowGallons = (flowRate / 60) * 1000;    
    flowGallons = (flowRate / 60) * 1000;
    //flowGallons = (flowRate / ElapsedMilliseconds) * 1000; //changed to this 
    // Add the Gallons passed in this second to the cumulative total
    totalGallonsA += flowGallons;
    totalGallonsB += flowGallons;
    // 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.println ("  pulseCount");
    // 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
    Serial.println("  GPM "); 
    // Print the number of litres flowed in this second
   // Serial.print(flowGallons);
   // Serial.println("  Flow Gallons ");    // Output separator

    // Print the cumulative total of litres flowed since starting
    Serial.println("  Total Gallons A");             // Output separator
    Serial.println("  Total Gallons A"); 

 //   if(int(flowRate) < 10)
 //   {
 //     lcd.print(" ");
 //   }


    // 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);
    Serial.println("");    // Output separator



 * 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

It looks as if that code just gathers data from 1 flow rate sensor.

It looks as if it counts the pulses for 1 second and then calculates the flow rate.

Have you tried writing a short function (perhaps just to print a message) and calling it from loop()? I don't see why that wouldn't work provided the function takes significantly less than 1 second to complete.

As it stands the code starts another 1 second count as soon as it finishes processing the previous data. It may not be necessary to check the flow rate that often.

The code could easily by modified so it never resets the count back to 0 which would allow it to measure the flow rate at irregular intervals. One option might be to measure the rate after every 100 or 1000 pulses.

It would help if you would post another version of your code without all the comments - but leave the present version where it is so the comments can be read if needed.


I am just learning about functions so my efforts have been hit or miss. I finally printed out the code last night and went through it line by line so I have a much better understanding of how it works. I had been working on the assumption the program started counting at the top of the program. Turns out the trigger is at the end of the program. I will try to write a new function today and see how well that works. If I can isolate the trigger and counter then I am home free.

BTW you are correct in everything that you said. It can only count one at sensor at a time. It counts pulses for exactly one second. then stops counting, drops out of the loop to do the calculations. My plan is to have it count once every 30 seconds for each sensor then calculate total gallons consumed for each sensor and keep a running total. From there I can determine power usage for getting the water from our well for the two open loop geothermal heat pumps. I just replaced our 25 year old geothermal heat pump so I am curious if it lives up to the hype.

BTW I can get simple test functions to work so I am making progress. At my age if I can remember something for more than a day I am lucky!


allen13331: Turns out the trigger is at the end of the program.

The location of the Interrupt Service Routine (ISR) function doesn't matter.

The simplest way to count pulses for a separate flow meter would be to repeat that ISR - crudely like this. Be aware that interrupt 1 is on pin 3. I'm assuming you only need to count pulses for 2 flow sensors. Things are a bit more complicated if you need more than2.

attachInterrupt(1, pulseCounter2, FALLING);

void pulseCounter2()
  // Increment the pulse counter