Using two flow sensors connected to one Arduino.

Hi.

My friend and I, are trying to figure out a way to use two flow sensor to read a fuel consumption. The first one is for main fuel flow into a pump. The other one is for return fuel from the injector and back to the fuel tank. So, if we call the main fuel sensor for sens1 and the return for sens2 the mathematics will be like this: sens1-sens2=actually fuel consumption.

How do we program two sensors together. How can we get one of the sensor values to be subtracted from the other sensor value?

We tried to use "NbTopsFan++" but it did not work.

We use two of these sensors FCH-m-POM-LC -0,8 LPM 97478617 : B.I.O-TECH Flowmeter

Thanks for your help 8)

Best regards, Fredrik

The short answer is get one sensor working, then add another, then use the '-' operator on the results. But there's v. similar posting recently that might be relevant: http://arduino.cc/forum/index.php/topic,123039.0.html

Something like this?

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);

byte sensorInterruptA  = 0;  
byte sensorInterruptB  = 1;
byte sensorPinA        = 2;
byte sensorPinB        = 3;

float calibrationFactor = 50; 

volatile byte pulseCount;

float flowRate;
unsigned int turFuel;
unsigned int returFuel;

unsigned long oldTime;

void setup()
{
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print("                ");
  
  Serial.begin(38400); 
  
  pulseCount    = 0;
  flowRate      = 0.0;
  turFuel       = 0;
  returFuel     = 0;
  oldTime       = 0;
  
  attachInterrupt(sensorInterruptA, pulseCounter, FALLING);
  attachInterrupt(sensorInterruptB, pulseCounter, FALLING);
}

//hovedprogrammet i loop. 

void loop()
{
  if((millis() - oldTime > 1000)
  {
    detachInterrupt(sensorInterrupt);
    
    flowRate = ((1000.0 / (millis() - oldtime)) * pulseCounat) / calibrationFactor;
    
    oldTime =millis();
    
    flowrate = (turFuel - returFuel) / 8500;
    
    unsigned int frac;
    
    Serial.print(int(flowRate));
    Serial.print(".");            
    frac = (flowRate - int(flowRate)) * 10;
    Serial.print(frac, DEC) ;
    
    Serial.print(" ");             
    Serial.print(flowRate);
    
     lcd.setCursor(0, 0);
    lcd.print("                ");
    lcd.setCursor(0, 0);
    lcd.print("Flow: ");
    if(int(flowRate) < 10)
    {
      lcd.print(" ");
    }
    lcd.print((int)flowRate);   
    lcd.print('.');             
    lcd.print(frac, DEC) ;      
    lcd.print(" L");
    lcd.print("/min");
    
    pulseCount = 0;
    
    attachInterrupt(sensorInterrupt, pulseCounterA, FALLING);
    attachInterrupt(sensorInterrupt, pulseCounterB, FALLING);
  }
}

void pulseCounter()
{
  pulseCount++;
}

I think we can use PinChangeInt

One Digital input for each FlowSensor

Lets say the variable to hold Fuel Consumption is 'fc'.
First PinChangeInt (The forward) will add 1 to fc, and the second (The return) will substract 1 from fc

The main loop only check for time periode.
When the periode reached, calculate the real fuel consumption basd on fc and your sensor spec (pulse/galon). That if you want consumption per time

Alternatively, if you easy about acuration, you can put freq devider (prescaler) between the Forward-Sensor and First PinChangeInt.

I'm pretty sure soon you will also want to look at the RPM.

Have a nice hack
-bino-

I think I am into something here:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);

byte sensorA = 0;
byte sensorB = 1;
byte sensorPinA = 2;
byte sensorPinB = 3;

float calibrationFactor = 50;

volatile byte pulseCount;

float flowRate;
unsigned int turFuel;
unsigned int returFuel;

unsigned long oldTime;

void setup()
{
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");

Serial.begin(38400);

pinMode(sensorA, INPUT);
pinMode(sensorB, INPUT);

pulseCount  = 0;
flowRate    = 0.0;
turFuel     = 0;
returFuel   = 0;
oldTime     = 0;

attachInterrupt(sensorA, pulseCounter, FALLING);
attachInterrupt(sensorB, pulseCounter, FALLING);
}

//hovedprogrammet i loop.

void loop()
{
if((millis() - oldTime > 1000))
{
detachInterrupt(sensorA);
detachInterrupt(sensorB);

flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;

oldTime =millis();

flowRate = (sensorA - sensorB) / 8500;

unsigned int frac;

Serial.print(int(flowRate));
Serial.print(".");
frac = (flowRate - int(flowRate)) * 10;
Serial.print(frac, DEC) ;

Serial.print(" ");
Serial.print(flowRate);

lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("Flow: ");
if(int(flowRate) < 10)
{
lcd.print(" ");
}
lcd.print((int)flowRate);
lcd.print('.');
lcd.print(frac, DEC) ;
lcd.print(" L");
lcd.print("/min");

pulseCount = 0;

attachInterrupt(sensorA, pulseCounter, FALLING);
attachInterrupt(sensorB, pulseCounter, FALLING);
}

void pulseCounter()
{
  pulseCount++;
}

Or what do you think? I get a problem in one of the last lines in this code. Can anyone help with that problem?

Regards, Fredrik.

Why you Attach two interupt pin to the same Function ( pulseCounter() ) ?
SensorPinA have to always increase the counter, and
SensorPinB have to alwayas decrease the counter.

I did it using PinChangeInt Library,
but here is the basic operation using attachInterupt

long lastMillis=0;
long curMillis=0;
int RptInterval=1000 ; 

int SensorPinFwd = 2;
int SensorPinRtr = 3;
int FlowClick = 0;

void setup(){
    
    pinMode(SensorPinFwd, INPUT);
    pinMode(SensorPinRtr, INPUT);


    attachInterrupt(SensorPinFwd, FuncFlowFwd, FALLING);
    attachInterrupt(SensorPinRtr, FuncFlowRtr, FALLING);

    lastMillis=millis();
    curMillis=lastMillis;

    /*
    And do other setup for serial and LCD here
    */
}

void loop(){
    curMillis = millis();
    if (curMillis-lastMillis >RptInterval) {
        doReport;
        lastMillis = curMillis;
    }
    
}

void FuncFlowFwd(){
    FlowClick=FlowClick+1;
}
void FuncFlowRtr(){
    FlowClick=FlowClick-1;
}
void doReport(){
    /*
    Do any calculation and report/display here
    */
}

Thanks for help binooetomo, but I can't get this to work properly. Actually, nothing work properly here right now. This works like a charmike a charm: WaterFlowGauge/WaterFlowGauge.pde at master · practicalarduino/WaterFlowGauge · GitHub

I have also tried to modify my first sketch to work, but nothing happends after compiling, uploading and opening the serial monitor.

I am so far here:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);

byte sensorInterruptA = 0;
byte sensorInterruptB = 1;
byte sensorPinA = 2;
byte sensorPinB = 3;

float calibrationFactor = 4.5;

volatile byte pulseCount;

float flowRate;
unsigned int flowRateA;
unsigned int turFuel;
unsigned int returFuel;

unsigned long oldTime;

void setup()
{
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");

Serial.begin(9600);

pinMode(sensorPinA, INPUT);
pinMode(sensorPinB, INPUT);
digitalWrite(sensorPinA, HIGH);
digitalWrite(sensorPinB, HIGH);

pulseCount           = 0;
pulseCount           = 0;
flowRate             = 0.0;
sensorInterruptA     = 0;
sensorInterruptB     = 0;
oldTime              = 0;

attachInterrupt(sensorInterruptA, pulseCounter, FALLING);
attachInterrupt(sensorInterruptB, pulseCounter, FALLING);
}

//hovedprogrammet i loop.

void loop()
{
  if((millis() - oldTime > 1000))
  {
  detachInterrupt(sensorInterruptA);
  detachInterrupt(sensorInterruptB);

  oldTime =millis();

  flowRate = (sensorInterruptA - sensorInterruptB) / 8500;
  
  flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount) / calibrationFactor;
  
  flowRateA = (flowRate / 60) * 1000;
  
  unsigned int frac;

  Serial.print(int(flowRateA));
  Serial.print(".");
  frac = (flowRateA - int(flowRateA)) * 10;
  Serial.print(frac, DEC) ;
  
  Serial.print(" ");
  Serial.print(flowRateA);

  lcd.setCursor(0, 0);
  lcd.print(" ");
  lcd.setCursor(0, 0);
  lcd.print("Flow: ");
  if(int(flowRate) < 10)
  {
    lcd.print(" ");
  }
  lcd.print((int)flowRate);
  lcd.print('.');
  lcd.print(frac, DEC) ;
  lcd.print(" L");
  lcd.print("/min");

  pulseCount = 0;

  attachInterrupt(sensorInterruptA, pulseCounter, FALLING);
  attachInterrupt(sensorInterruptB, pulseCounter, FALLING);
  }
}
  void pulseCounter()
{
  pulseCount++;
}

Regards, Fredrik

fiddled a bit with your code:

  • use #define for those vars that are not changeable
  • made 2 different IRQ's one for counting the IN pulses and one for counting the out pulses
  • changed some var names to reflect what they meant like litersperminute
  • use a copy of millis() in the if block as millis() might change during the block (accuracy)
  • probably screwed up the math but you should be able to fix that relative easily
  • made some vars local instead of global (reduce scope)

have a look and give it a try (not tested as I don't have an I2CLCD nearby :slight_smile:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,16, 2);

// as these are const one better use #defines
#define IRQ_A 0
#define IRQ_B 1
#define PIN_A 2
#define PIN_B 3

// a separate counter per channel
volatile unsigned long countIN = 0;
volatile unsigned long countOUT = 0;
unsigned long oldTime  = 0;

void setup()
{
  lcd.begin(16, 2);

  Serial.begin(9600);

  pinMode(PIN_A, INPUT);
  pinMode(PIN_B, INPUT);
  digitalWrite(PIN_A, HIGH);
  digitalWrite(PIN_B, HIGH);

  attachInterrupt(IRQ_A, CounterIN, FALLING);
  attachInterrupt(IRQ_B, CounterOUT, FALLING);
}

void loop()
{
  // use a var for value of millis, as it might change in the if .... block
  // due to slow serial prints
  unsigned long now = millis();
  if((now - oldTime > 1000))
  {
    unsigned long duration = now - oldTime;
    oldTime = now;

    // use names that indicate the unit!
    // don't know if all math is correct anymore
    // 8500 pulses per liter?
    float liters = (countIN - countOUT) * 0.000117647; // faster than divide by 8500.0
    float litersPerMinute = (1000 * liters)/duration / 60; // optimized 16.66667*liters/duration

    Serial.print(duration);
    Serial.print(",");  // separate by comma's so you can copy output to Excel easily.
    Serial.println(litersPerMinute, 3);     // print a float with three decimals

    int LPM = litersPerMinute;               // implicit conversion to int
    int frac = (litersPerMinute-LPM)*10;
    lcd.setCursor(0, 0);
    lcd.print("Flow L/min: ");
    lcd.print(LPM);  
    lcd.print(".");    
    lcd.print(frac);  
    lcd.print(" ");
  }
}

void CounterIN()
{
  countIN++;
}

void CounterOUT()
{
  countOUT++;
}

Hi Rob and thanks for your help with this!! :slight_smile: This looks very good, but this sketch did not work as I hoped it would, but I think you are into something good there. I will try to understand more of it tomorrow. I dont either have the LCD, but my companion will test it when he finnishes his work in the North Sea :stuck_out_tongue:

Regards, Fredrik

The code is definitely not correct (some parts are missing) but should inspire you to improve your coding skills.

Hopes it helps! Succes!

PS, please post your final result when it works

robtillaart:
The code is definitely not correct (some parts are missing) but should inspire you to improve your coding skills.

Hopes it helps! Succes!

PS, please post your final result when it works

Thanks so much :slight_smile: Sure, I will post the final result, if other also are planning to do similar 8)

Regard, Fredrik

That's the idea of open source, sharing and learning and building on top of each other.

#define IRQ_A 0
#define IRQ_B 1
#define FlowA 2
#define FlowB 3

volatile unsigned long countIN = 0;
volatile unsigned long countOUT = 0;
unsigned long oldTime  = 0;
float flowRate;

volatile byte pulseCountIN;
volatile byte pulseCountOUT;
volatile byte pulseCount;

float liters;

void setup()
{
  Serial.begin(57600);
  
  pulseCountIN = 0;
  pulseCountOUT = 0;
  pulseCount = 0;
  flowRate = 0.0;

  pinMode(FlowA, INPUT);
  pinMode(FlowB, INPUT);
  digitalWrite(FlowA, HIGH);
  digitalWrite(FlowB, HIGH);
  
  liters = 0.0;

  attachInterrupt(IRQ_A, CounterIN, FALLING);
  attachInterrupt(IRQ_B, CounterOUT, FALLING);
}

void loop()
{
  unsigned long now = millis();
  if((now - oldTime > 1000))
  {
    unsigned long duration = now - oldTime;
    oldTime = now;

    liters = (countIN - countOUT); 
    float litersPerMinute = (1000 * liters)/duration; 
    Serial.println(litersPerMinute, 3);    
    int LPM = litersPerMinute;           
    
    pulseCount = pulseCountIN - pulseCountOUT;
       

    flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount);
    unsigned int frac = (flowRate - int(flowRate))*10;
    Serial.print("pulseCount: ");
    Serial.print(pulseCount, DEC);
    Serial.println(" ");

        
    pulseCountIN = 0;
    pulseCountOUT = 0;
    
    attachInterrupt(IRQ_A, CounterIN, FALLING);
    attachInterrupt(IRQ_B, CounterOUT, FALLING);  
  }
}

void CounterIN()
{
  countIN++;
  pulseCountIN++;
}

void CounterOUT()
{
  countOUT++;
  pulseCountOUT++;
}

This is my last version of the sketch, and now it works! Thanks so much for your help :smiley: I will keep developing this sketch to work more properly. I added two more volatile bytes, because the first sketch did not work.

Super that it works now,

some remarks

if((now - oldTime > 1000))

I would use >= 1000
and the additional ()'s do not add anything :slight_smile:

no need to

    attachInterrupt(IRQ_A, CounterIN, FALLING);
    attachInterrupt(IRQ_B, CounterOUT, FALLING);

at the end of every loop, as you do not detach them (you shouldn't)

writing

float litersPerMinute = (1000.0 * liters)/duration;

makes explicit that the math is done in float (it is, as liters is already float, but just looking at the original line it could have been integer math)

for the readability I would group the math and the display statements in the loop, something like

void loop()
{
  unsigned long now = millis();
  if((now - oldTime >= 1000))
  {
    // TIME MATH
    unsigned long duration = now - oldTime;
    oldTime = now;

    // FLOW MATH
    liters = (countIN - countOUT); 
    float litersPerMinute = (1000.0 * liters)/duration; 
    int LPM = litersPerMinute;           
    pulseCount = pulseCountIN - pulseCountOUT;
    pulseCountIN = 0;
    pulseCountOUT = 0;

    flowRate = (1000.0 / duration) * pulseCount);
    unsigned int frac = (flowRate - int(flowRate))*10;

    // DISPLAY RESULTS
    Serial.println(litersPerMinute, 3);    
    Serial.print("pulseCount: ");
    Serial.print(pulseCount, DEC);
    Serial.println("  ");  // to remove trailing digit when printed on lcd iso serial
  }
}

How many pulses do the devices give per liter?

Thanks. I have cleaned up a bit more here, but its not perfect yet. The whole system is not accurate enough. I need the sensors to be very accurate for this project, and I think it is something inside here who makes the difference:

#define IRQ_A 0
#define IRQ_B 1
#define FlowA 2
#define FlowB 3

volatile unsigned long countIN = 0;
volatile unsigned long countOUT = 0;
unsigned long oldTime  = 0;
float flowRate;



volatile float pulseCountIN;
volatile float pulseCountOUT;
volatile float pulseCount;

float liters;

void setup()
{
  Serial.begin(57600);
  
  pulseCountIN = 0.0;
  pulseCountOUT = 0.0;
  pulseCount = 0.0;
  flowRate = 0.0;

  pinMode(FlowA, INPUT);
  pinMode(FlowB, INPUT);
  digitalWrite(FlowA, HIGH);
  digitalWrite(FlowB, HIGH);
  
  liters = 0.0;

  attachInterrupt(IRQ_A, CounterIN, FALLING);
  attachInterrupt(IRQ_B, CounterOUT, FALLING);
}

void loop()
{
  unsigned long now = millis();
  if(now - oldTime >= 1000)
  {
    unsigned long duration = now - oldTime;
    oldTime = now;

    liters = (countIN - countOUT); 
    float litersPerMinute = (1000 * liters)/duration;    
    int LPM = litersPerMinute;           
    
    pulseCount = (pulseCountIN - pulseCountOUT) / 10000.0 * 3600;
       

    flowRate = ((1000.0 / (millis() - oldTime)) * pulseCount);
    unsigned int frac;
    Serial.print("Liter per time: ");
    Serial.println(pulseCount, 4);
      
    pulseCountIN = 0;
    pulseCountOUT = 0;
     
  }
}

void CounterIN()
{
  countIN++;
  pulseCountIN++;
}

void CounterOUT()
{
  countOUT++;
  pulseCountOUT++;
}

When I connect the hoses and sensors to the water faucet, the sensors gives me this: results
Liter per time: 23.4000
Liter per time: 24.1200
Liter per time: 24.8400
Liter per time: 25.2000
Liter per time: 25.5600
Liter per time: 24.8400

...and that is not what I had expected.
The engines I develop this flow meter to, uses between 3 and 50 liters per hour.

Do you understand what I have done wrong?

Cheers, Fredrik

Liter per time: 24.8400
...and that is not what I had expected.
The engines I develop this flow meter to, uses between 3 and 50 liters per hour.

24 is between 3 and 50, so ?? Be aware that this is the consumption per hour!

The device gives 10.000 pulses per liter according to spec. So the formula seems OK
pulseCount = (pulseCountIN - pulseCountOUT) / 10000.0 * 3600;

better split the two (and give the vars good names) to debug them seperately.

float LPH_IN = pulseCountIN/ 10000.0 * 3600; // pulses to Liter/hour
float LPH_OUT = pulseCountOUT/ 10000.0 * 3600;

The fact that you get ~24 Liter means that the incounter get far more pulses than the outcounter. This makes sense as the device consumes fuel.

About Accuracy
The minimum consumption it can detect is one pulse. If you measure a delta of 1 pulse per second it equals a difference of 1/10000*3600 = 0.36L

Look at your measurements

Liter per time: 23.4000
Liter per time: 24.1200
Liter per time: 24.8400
Liter per time: 25.2000
Liter per time: 25.5600
Liter per time: 24.8400

and you see the delta's between the measurements are 0.36 or 0.72. That is one or two pulses difference.

The best way to have a better accuracy is to measure the fuel-consumption over the last minute * 60. A delta of 1 pulse will result in a delta of 0.006 L
The best way to do this is to make measurements per second and use them to fill a circular buffer of 60 elements.

Delta meter with a circular buffer.

#define IRQ_A 0
#define IRQ_B 1
#define FlowA 2
#define FlowB 3

volatile unsigned int countIN = 0;
volatile unsigned int countOUT = 0;
unsigned long oldTime  = 0;

unsigned long totalPulses = 0;

unsigned int circBuf[60];
uint8_t idx = 0;

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

  pinMode(FlowA, INPUT);
  pinMode(FlowB, INPUT);
  digitalWrite(FlowA, HIGH);
  digitalWrite(FlowB, HIGH);

  attachInterrupt(IRQ_A, CounterIN, FALLING);
  attachInterrupt(IRQ_B, CounterOUT, FALLING);
}

void loop()
{
  unsigned long now = millis();
  if(now - oldTime >= 1000)
  {
    unsigned long duration = now - oldTime;
    oldTime = now;

    // store usage of last second in circular buffer
    idx++;
    if (idx == 60) idx = 0;
    // disable interrupts while reading & reset counters.
    cli();
    circBuf[idx] = countIN - countOUT; 
    countIN = 0;
    countOUT = 0;
    sei();

    totalPulses += circBuf[idx];

    long pulsesLastMinute = 0;
    for (uint8_t i=0; i<60; i++) 
    {
      pulsesLastMinute += circBuf[i];
    }
    // LPH based upon last minute
    float LPH_M = pulsesLastMinute/10000.0 * 60;
    // LPH based upon last second
    float LPH_S = circBuf[idx]/10000.0 * 3600;
    // Total Liters since start
    float liters = totalPulses/10000.0;

    // DISPLAY USAGE 
    Serial.print(now);
    Serial.print("\tLPH_M:\t");  
    Serial.print(LPH_M, 3);
    Serial.print("\tLPH_S:\t");
    Serial.print(LPH_S, 3);
    Serial.print("\tLiters:\t");
    Serial.println(liters, 3);
  }
}

void CounterIN()
{
  countIN++;
}

void CounterOUT()
{
  countOUT++;
}

Thanks for very good help robtillaart! 8) 8) 8)

I Tried this sketch with my sensors, but one of them shows something like 100 times higher value than the other(about 700 liters per secund). It does not seems that the subtraction function works either as it did before.

Regards, Fredrik.

What happens if you switch the lines ?
just to test if this happens in the sensor/wiring or in the software?

100 times higher could mean more noise on the line, which is seen as pulses for the interrupt handler. (Or leakage?)
Is the factor a constant or irregular?. If it is a constant (exact 100x) you might just have 2 different devices !
If it is irregular it looks more like noise.

You can add this modification to the sketch to remove the noise. It ignores pulses that are too short after each other.
Be aware that micros() returns multiples of 4. So testing with 8,9,10,11 give the same behaviour.

void CounterIN()
{
  static unsigned long lastTime = 0;
  if (micros()  - lastTime > 10) // to be adjusted
  {
    lastTime = micros();
    countIN++;
  }
}

void CounterOUT()
{
  static unsigned long lastTime = 0;
  if (micros()  - lastTime > 10) 
  {
    lastTime = micros();
    countOUT++;
  }
}

Hi all,
i would like to make a two way diesel consumption meter like this.
Has anyone made this project and can confirm it works?
Thanks in advance