Flow meter sensor sketch help

Hi I I have been trying to figure out how to use a flow meter sensor with Particle and have had a few problems so far. I should mention I have not used flow sensors before and am just trying to wrap my head around it – and how interrupt timing works.

I saw from an earlier post ( Flow sensor library ) a Water flow sensor sketch, but when I try this code the water pulse count always reads 50, even when there is zero flow and up to 55 at high flow. It seems this code is not really working (perhaps because it was from a few years ago?). I’ve posted that sketch below.

I saw a more recent post ( Timer Based Interrupts syntax (adafruit flow meter) ) that recommends using Spark Interval Timers as a way of doing the interrupt counting. Which makes me think: is there a more “up to date” way of implementing such a flow sensor sketch? Any suggestions much appreciated.

    /*
        Water flow sensor test sketch

    */


    unsigned long oldTime;
    volatile unsigned int WaterPulseCount = 0;

    // conversion from pps to litres, plastic sensor (485 for metal)
    const float pulsesPerLiter = 450;

    // Spark Digial Pin D3 (D2 did not work)
    #define WATER_SENSOR_PIN	D3	// Water sensor digital pin

    // Define Spark variable - not sure "float" type works so define as INT
    // so decimal is shifted left with * 100 (so xx.yy becomes xxyy)
    float liters = 0;
    char liters_S[6];

    //-----------------------------------------------------------------------------
    // Water Sensor interrupts
    //-----------------------------------------------------------------------------
    void WaterPulseCounter(void)
    {
    	// Increment the water pulse counter
    	//detachInterrupt (WATER_SENSOR_PIN) ;
    	WaterPulseCount++;
    	//attachInterrupt(WATER_SENSOR_PIN, WaterPulseCounter, FALLING) ;
    }


    void setup()
    {
      Serial.begin(9600);
      
      Spark.variable("litersS", &liters_S, STRING);

      // Set Digital pin WATER_SENSOR_PINT to INPUT mode and set
      // interrupt vector (water flow sensor) for FALLING edge interrupt
      pinMode(WATER_SENSOR_PIN, INPUT);
      attachInterrupt(WATER_SENSOR_PIN, WaterPulseCounter, FALLING) ;
      oldTime = millis();
    }


    void loop()
    {
      unsigned long t;
      static unsigned int pc;

      t = (millis() - oldTime);
      if(t >= 1000)    			// Only process counters once per second
      {
        //Read water sensor pulse count and process
        if (WaterPulseCount != 0)		// Do nothing if water is not flowing!
        {
        detachInterrupt (WATER_SENSOR_PIN);	// Disable water flow interrupt to read value
        //Calculate litres and adjust for 1 sec offset, if any
        liters = (WaterPulseCount / pulsesPerLiter) * (t / 1000);
        oldTime = millis();				// Reset base delay time
        pc = WaterPulseCount;
        WaterPulseCount = 0;			// Reset the water pulse counter
        attachInterrupt(WATER_SENSOR_PIN, WaterPulseCounter, FALLING);

        sprintf(liters_S, "%4.3f", liters);
        
        Serial.print("WaterPulseCount= ");
        Serial.print(pc);
        Serial.print(", liters= ");
        Serial.print(liters,3);
        Serial.print(", liters_S= ");
        Serial.println(liters_S);
        }
      }
    }

Try setting pinMode(WATER_SENSOR_PIN, INPUT_PULLUP)

and I’d put oldTime = millis() before the if (WaterPulseCount != 0) check.

2 Likes

I liked how clean the spark interval timer solution was, so I tried to get that working. I seem to be getting much nicer values this time, the only problem is that my litersPerMinute values never seem to zero out when there is zero flow. It gets stuck on a low value like 0.3 - 1.1. – Any suggestions at how to make this more accurate?

#include <SparkIntervalTimer.h>
#define FLOWSENSORPIN D3


IntervalTimer msTimer;	

volatile uint32_t usLastTime  = 0;
volatile uint32_t usDeltaTime = 0;
volatile uint32_t msCount     = 0;
volatile double   revPerSec   = 0;
volatile double   revPerMS    = 0;
 float flowRate =0.0;
 float litersPerMinute=0.0;

void msISR() {
  revPerMS = msCount;
  msCount = 0;
}

void senseISR() {
  uint32_t us = micros();
  msCount++;
  usDeltaTime = us - usLastTime;
  usLastTime = us;
  revPerSec =  1000000.0 / usDeltaTime;
}

void setup() {
  Serial.begin(9600);

  msTimer.begin(msISR, 1000, uSec);  // trigger every 1000µs

  pinMode(FLOWSENSORPIN, INPUT_PULLUP);
  attachInterrupt(FLOWSENSORPIN, senseISR, FALLING);
  
}

void loop(){
    
  flowRate = (revPerSec * 2.25);        //Take counted pulses in the last second and multiply by 2.25mL 
  flowRate = flowRate * 60;         //Convert seconds to minutes, giving you mL / Minute
  litersPerMinute = flowRate / 1000;       //Convert mL to Liters, giving you Liters / Minute
    Serial.println(litersPerMinute);

}

That's because revPerSec is only ever calculated when you have at least one pulse trigger the pin interupt.
You need to find some logic that zeroes this value out when there is no flow and hence now senseISR() :wink:

Could you have the loop only calculate flowRate IF the interrupt has triggered in the past (Say 5-10 seconds)?
Else set flowRate to Zero.

@superliminal, personally I would use a Software Timer for the 1ms increment since it is accurate enough for this purpose. The timer ISR code could be used as is in the software timer callback. You could easily create a counter to do as @Rftop suggested. :wink:

1 Like