Reading from multiple flow meters and temperature sensors "at the same time"?

Hi all,

I’m currently working on a mini coolant system model project. A little background; the model consists of 3 temperature sensors (one at a secondary pump on a parallel line, a second at a radiator inlet, and a third at a radiator outlet) and two flow meters (one on the parallel line, another on the main line).

The temperature sensors are DS18B20s and the flow meters are these guys: G1/2 Water Flow sensor. I’m working with an Arduino Uno.

Posted below is my current test code that reads from two DS18B20s and two flow meters (tested with one flow meter though). Its a combination of the temperature sensor code from here and flow sensor code I modified a little bit from this post here (credits to dlloyd).

#include <OneWire.h>
#include <DallasTemperature.h>

// constants
#define ONE_WIRE_BUS 6 // Data bus pin for temperature sensor reading
const byte inputs[] = {2, 3}; // flow sensor input pins
const double pulseConstant[] = {7.5, 7.5}; // flow sensor pulse constants
const byte qty = sizeof(inputs);

OneWire oneWire(ONE_WIRE_BUS); // initialize OneWire to read for reading temperature sensors
DallasTemperature sensors(&oneWire); // directing the temperature sensors' location to the sensor library

// variables
long previousMillis = 0;
long previousMillis2 = 0;
long startTime[qty];
byte inputsState[qty];
byte inputsPrevious[qty];
double pulsePeriod[qty];
double pulseFrequency[qty];
double pulseMultiplier[qty];
double flowRate[qty];

void setup() {
  Serial.begin(9600);
  Serial.println("Temperature and Flow Sensor Read Out Testing");
  sensors.begin(); // initialize the temperature sensor library

  for (int i = 0; i < qty; i++) {
    pinMode(inputs[i], INPUT_PULLUP); // flow sensor pin locations declared as inputs
    pulseMultiplier[i] = 1.0 / (pulseConstant[i]); // multiplier to convert Hall effect sensor frequency into flow rate
  }


  clr(); // initialize arrays for flow rate calculations
}

void loop()
{

  if (millis() - previousMillis2 > 1000) {

    tempMeasure();  // read from DS18B20 sensors and print results
    previousMillis2 = millis();
    startTime[0] = micros();
  }
  
  else {
    
    flowMeasure();  // run flow tests, calculations and print results
  }

}


// functions -----------------------------------------------------

void flowMeasure() {

  for (int i = 0; i < qty; i++) {
    inputsState[i] = digitalRead(inputs[i]);                   // read the inputs

    if ((inputsState[i] == 1) && (inputsPrevious[i] == 0)) {   // if rising
      pulsePeriod[i] = (micros() - startTime[i]) * 0.000001;   // test duration (sec)
      pulseFrequency[i] = 1 / pulsePeriod[i];                  // input frequency (Hz)
      flowRate[i] = pulseFrequency[i] * pulseMultiplier[i];    //  1.0 / pulseConstant[i] (L/min)

      if (millis() - previousMillis > 250) {                   // update interval (milliseconds)
        Serial.print(i); Serial.print(": ");
        Serial.print(pulsePeriod[i], 6); Serial.print(" sec, ");
        Serial.print(pulseFrequency[i], 3); Serial.print(" Hz, ");
        Serial.print(flowRate[i], 3); Serial.print(" L/min, ");
        Serial.println();
        previousMillis = millis();
      }

      startTime[i] = micros();
    }

    inputsPrevious[i] = inputsState[i];
  }

}

void clr() {
  for (int i = 0; i < qty; i++) {
    startTime[i] = micros();
    inputsState[i] = 0;
    inputsPrevious[i] = 1;
    pulsePeriod[i] = 0;
    pulseFrequency[i] = 0;
    flowRate[i] = 0;
  }
}

void tempMeasure() {

  //Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures from all sensors on the bus
  //Serial.println("DONE");

  Serial.print("Temperature for Sensor 1 is: ");
  Serial.println(sensors.getTempCByIndex(0)); // first sensor on the bus
  Serial.print("Temperature for Sensor 2 is: ");
  Serial.println(sensors.getTempCByIndex(1)); // second sensor on the bus
}

The problem I’m having is with flow meter output after reading from the temperature sensors. The time it takes to output the temperature readings throws off the pulse period calculation for the flow meter afterwords, which is dependent on the micros() function. I’ve tried resetting the start time of the pulse period calculation after reading from the temperature sensors but it’s thrown out of wack (this little part below:).

if (millis() - previousMillis2 > 1000) {

    tempMeasure();  // read from DS18B20 sensors and print results
    previousMillis2 = millis();
    *****startTime[0] = micros();
  }

Without resetting the startTime, you get a really low flow rate reading because the pulse period is long (includes the time to output the temperature readings). Here’s a sample output of the code as posted:

Temperature and Flow Sensor Read Out Testing
0: 0.435332 sec, 2.297 Hz, 0.306 L/min, 
0: 0.034788 sec, 28.746 Hz, 3.833 L/min, 
Temperature for Sensor 1 is: 23.00
Temperature for Sensor 2 is: 23.06
0: 0.000688 sec, 1453.488 Hz, 193.798 L/min, 
0: 0.017900 sec, 55.866 Hz, 7.449 L/min, 
0: 0.017824 sec, 56.104 Hz, 7.481 L/min, 
0: 0.019008 sec, 52.609 Hz, 7.015 L/min, 
Temperature for Sensor 1 is: 23.00
Temperature for Sensor 2 is: 23.06
0: 0.007060 sec, 141.643 Hz, 18.886 L/min, 
0: 0.023296 sec, 42.926 Hz, 5.723 L/min, 
0: 0.023380 sec, 42.772 Hz, 5.703 L/min, 
0: 0.023768 sec, 42.073 Hz, 5.610 L/min

Anyone have any idea how I could sort this out? I haven’t been able to think of a way around this, or I’m not seeing the logic. Thanks for reading!

I think there are two options.

I'm guessing that the flow rate calculation can be suspended briefly while other things are being done. So, rather than carry over the pulse count on the occasions when you do the temperature stuff you could restart the pulse counting. And you may get away with only doing the temperature calcs every 10 secs rather than every second. That would mean the flow would be read continuously for 10 seconds and then there would be a short gap before flow measurements resume. This system would mean minimal change to your existing code.

The other option is to use interrupts to detect the pulses from the flow meters so that the counting happens in the background. Nick Gammon has a good tutorial on interruupts.

...R

Hi,
Can you draw a diagram of how your flows and temperatures are configured in the project please?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks...Tom...... :slight_smile:

The motivation behind dlloyds code was to read large numbers of flow meters without interrupts. With only two meters, there is no reason not to use the more standard approach of using interrupts and counting multiple pulses over a time period, rather than determining flow rate from a pulse period.

The temperature readings can be significantly faster if you read by address instead of by index.

Also, setting the temperature resolution to 11 bits will give increments of .125 degree C resolution for rounding to .1 C This is faster than a 12 bit resolution and gives accuracy sufficient for most applications.

TomGeorge:
Hi,
Can you draw a diagram of how your flows and temperatures are configured in the project please?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Thanks...Tom...... :slight_smile:

Here's a basic physical diagram of the circuit. Additional temperature sensors would be parallel with the single wired example (sharing a single pin) while flow meters would be wired individually on their own pins.

And here's a diagram of the model configuration:

cattledog:
The motivation behind dlloyds code was to read large numbers of flow meters without interrupts. With only two meters, there is no reason not to use the more standard approach of using interrupts and counting multiple pulses over a time period, rather than determining flow rate from a pulse period.

The temperature readings can be significantly faster if you read by address instead of by index.

Also, setting the temperature resolution to 11 bits will give increments of .125 degree C resolution for rounding to .1 C This is faster than a 12 bit resolution and gives accuracy sufficient for most applications.

Thanks for the temperature reading tips, I plan on optimizing the code a little once I get things working properly.

Since I'm using two flow meters, I initially planned on using interrupts since the Uno conveniently has two pins to support the external interrupt requests (pin 2 & 3). Is it possible to enable and disable interrupts on the pins individually though? This was my initial interrupt based code for two flow sensors:

volatile int mainPulseCount; //measuring the rising edges of the signal for the main line
volatile int secPulseCount;  //measuring the rising edges of the signal for the secondary line
float mainFlowRead;
float secFlowRead;                               
int mainFlowPin = 2;    //pin location of flow rate meter on main line
int secFlowPin = 3;     //pin location of flow rate meter on secondary line

 

// The setup() method runs once, when the sketch starts
void setup() 
{ 
  Serial.begin(9600);
  
  pinMode(mainFlowPin, INPUT); //initializes digital pin 2 as an input
  pinMode(secFlowPin, INPUT); //initializes digital pin 3 as an input
  
  attachInterrupt(digitalPinToInterrupt(2), mainRPM, RISING); //attaching main pulse counter interrupt to pin 2
  attachInterrupt(digitalPinToInterrupt(3), secRPM, RISING);  //attaching secondary pulse counter interrupt to pin 3
} 



// the loop() method runs over and over again,
// as long as the Arduino has power
void loop ()    
{
  mainPulseCount = 0;   //resets pulse count for main flow sensor
  secPulseCount = 0;    // resets pulse count for secondary flow sensor
  
  sei();      //Enables interrupts
  delay (1000);   //Wait 1 second to count pulses at each flow sensor
  cli();      //Disable interrupts
  
  mainFlowRead = (mainPulseCount / 7.5); //(Main pulse frequency / 7.5Q) = flow rate in L/min
  secFlowRead = (secPulseCount / 7.5); //(sec pulse frequency / 7.5Q) = flow rate in  L/min

  Serial.print ("Flow reading at main line: ");
  Serial.print (mainFlowRead, 2); //Prints the number calculated above
  Serial.println (" L/min");
  Serial.print ("Flow reading at secondary line: ");
  Serial.print (secFlowRead, 2); //Prints the number calculated above
  Serial.println (" L/min");
}



void mainRPM ()     //This is the function that the interupt calls 
{ 
  mainPulseCount++;  //This function measures the rising and falling edge of the hall effect sensors signal
}

void secRPM ()     //This is the function that the interupt calls 
{ 
  secPulseCount++;  //This function measures the rising and falling edge of the hall effect sensors signal
}

The problem here is both interrupts cannot run simultaneously and with them alternating each other I think it's messing up the pulse count over one second... Could be wrong though? I would rather work with the interrupts method as it seems simpler.

The problem here is both interrupts cannot run simultaneously and with them alternating each other I think it’s messing up the pulse count over one second… Could be wrong though?

The two interrupts will work independently. While interrupts are disabled during an ISR’s execution, any other interrupts will be flagged and executed in its turn. See Gammon Forum : Electronics : Microprocessors : Interrupts

sei();      //Enables interrupts
  delay (1000);   //Wait 1 second to count pulses at each flow sensor
  cli();      //Disable interrupts

Reading interrupt counts accumulated during a delay period is not ideal because it is blocking for the rest of your code. It is better to read the counts with a timer like in the “Blink without delay” example.

Here is some example code which reads two interrupts every second. I have jumpered some pwm pins to the interrupt pins to provide test signals.

//pulse counts changed within count_ISR's
volatile unsigned int count1;
volatile unsigned int count2;

//variables for protected copies of counts
unsigned int copyCount1;
unsigned int copyCount2;

void count1_ISR()
{
  count1++;
}

void count2_ISR()
{
  count2++;
}
void setup() {

  Serial.begin(115200);
  
  //send test signal to interrupt pins
  analogWrite(5,127);//980 hz timer 0 jumper pin 5 to pin 2
  analogWrite(9,127);//490 hz timer 2 jumper pin 9 to pin 3
  
  attachInterrupt(0, count1_ISR, RISING); //pin2 (Atmega328)
  attachInterrupt(1, count2_ISR, RISING); //pin3 (Atmega328)
  
}

void loop() {
  static unsigned long lastSecond;
  if (micros() - lastSecond >= 1000000L)
  {
    lastSecond += 1000000;
    getCount();
    Serial.println(copyCount1);
    Serial.println(copyCount2);
    Serial.println();
  }

}
void getCount()
{
  noInterrupts();
  copyCount1 = count1;
  count1 = 0;
  copyCount2 = count2;
  count2 = 0;
  interrupts();
}

cattledog:
The two interrupts will work independently. While interrupts are disabled during an ISR’s execution, any other interrupts will be flagged and executed in its turn. See Gammon Forum : Electronics : Microprocessors : Interrupts

sei();      //Enables interrupts

delay (1000);  //Wait 1 second to count pulses at each flow sensor
  cli();      //Disable interrupts



Reading interrupt counts accumulated during a delay period is not ideal because it is blocking for the rest of your code. It is better to read the counts with a timer like in the "Blink without delay" example.

Here is some example code which reads two interrupts every second. I have jumpered some pwm pins to the interrupt pins to provide test signals.


//pulse counts changed within count_ISR’s
volatile unsigned int count1;
volatile unsigned int count2;

//variables for protected copies of counts
unsigned int copyCount1;
unsigned int copyCount2;

void count1_ISR()
{
  count1++;
}

void count2_ISR()
{
  count2++;
}
void setup() {

Serial.begin(115200);
 
  //send test signal to interrupt pins
  analogWrite(5,127);//980 hz timer 0 jumper pin 5 to pin 2
  analogWrite(9,127);//490 hz timer 2 jumper pin 9 to pin 3
 
  attachInterrupt(0, count1_ISR, RISING); //pin2 (Atmega328)
  attachInterrupt(1, count2_ISR, RISING); //pin3 (Atmega328)
 
}

void loop() {
  static unsigned long lastSecond;
  if (micros() - lastSecond >= 1000000L)
  {
    lastSecond += 1000000;
    getCount();
    Serial.println(copyCount1);
    Serial.println(copyCount2);
    Serial.println();
  }

}
void getCount()
{
  noInterrupts();
  copyCount1 = count1;
  count1 = 0;
  copyCount2 = count2;
  count2 = 0;
  interrupts();
}

Thanks for the example, I tested it out. I’ve integrated interrupts back into my code similar to the example and removed the pulse timing stuff:

#include <OneWire.h>
#include <DallasTemperature.h>

// constants
#define ONE_WIRE_BUS 6 // Data bus pin for temperature sensor reading
OneWire oneWire(ONE_WIRE_BUS); // initialize OneWire to read for reading temperature sensors
DallasTemperature sensors(&oneWire); // directing the temperature sensors' location to the sensor library
const int mainFlowPin = 2;    //pin location of flow rate meter on main line
const int secFlowPin = 3;     //pin location of flow rate meter on secondary line

// variables
volatile int mainPulseCount; //measuring the rising edges of the signal for the main line
volatile int secPulseCount;  //measuring the rising edges of the signal for the secondary line
unsigned int copyMainPulseCount;
unsigned int copySecPulseCount;
double mainFlowRead;
double secFlowRead;



void setup() {
  Serial.begin(9600);
  Serial.println("Temperature and Flow Sensor Read Out Testing");
  sensors.begin(); // initialize the temperature sensor library

  pinMode(mainFlowPin, INPUT); //initializes digital pin 2 as an input
  pinMode(secFlowPin, INPUT); //initializes digital pin 3 as an input

  attachInterrupt(digitalPinToInterrupt(2), mainRPM, RISING); //attaching main pulse counter interrupt to pin 2
  attachInterrupt(digitalPinToInterrupt(3), secRPM, RISING);  //attaching secondary pulse counter interrupt to pin 3
}


void loop()
{
  static unsigned long lastSecond;

  if (micros() - lastSecond >= 1000000)
  {
    lastSecond += 1000000;
    getCount();

    mainFlowRead = (copyMainPulseCount / 7.5); //(Main pulse frequency / 7.5Q), = flow rate in L/min
    secFlowRead = (copySecPulseCount / 7.5); //(sec pulse frequency / 7.5Q), = flow rate in L/min

    tempMeasure();
    
    Serial.print ("Flow reading at main line: ");
    Serial.print (mainFlowRead, 2); //Prints the number calculated above
    Serial.println (" L/min");
    Serial.print ("Flow reading at secondary line: ");
    Serial.print (secFlowRead, 2); //Prints the number calculated above
    Serial.println (" L/min");
  }

}


// functions -----------------------------------------------------

void mainRPM ()     //This is the function that the interupt calls
{
  mainPulseCount++;  //This function measures the rising and falling edge of the hall effect sensors signal
}

void secRPM ()     //This is the function that the interupt calls
{
  secPulseCount++;  //This function measures the rising and falling edge of the hall effect sensors signal
}

void getCount()
{
  noInterrupts();
  copyMainPulseCount = mainPulseCount;
  mainPulseCount = 0;
  copySecPulseCount = secPulseCount;
  secPulseCount = 0;
  interrupts();
}

void tempMeasure() {

  //Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures from all sensors on the bus
  //Serial.println("DONE");

  Serial.print("Temperature for Sensor 1 is: ");
  Serial.println(sensors.getTempCByIndex(0)); // first sensor on the bus
  Serial.print("Temperature for Sensor 2 is: ");
  Serial.println(sensors.getTempCByIndex(1)); // second sensor on the bus
}

I can print readings from both temperature and flow sensors together without issue, for the most part. In testing this code I’m only using one flow sensor though (a spare one separate from the model). In blowing into it, it seems to build up the separate count in both interrupts. There is nothing connected to pin 3.

Temperature and Flow Sensor Read Out Testing
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.69
Flow reading at main line: 0.00 L/min
Flow reading at secondary line: 0.00 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.69
Flow reading at main line: 0.00 L/min
Flow reading at secondary line: 0.00 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 0.00 L/min
Flow reading at secondary line: 0.00 L/min
Temperature for Sensor 1 is: 24.62
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 0.00 L/min
Flow reading at secondary line: 0.00 L/min
Temperature for Sensor 1 is: 24.62
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 0.00 L/min
Flow reading at secondary line: 0.00 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 0.93 L/min
Flow reading at secondary line: 0.67 L/min
Temperature for Sensor 1 is: 24.62
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 6.27 L/min
Flow reading at secondary line: 1.33 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 8.40 L/min
Flow reading at secondary line: 1.60 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.69
Flow reading at main line: 8.93 L/min
Flow reading at secondary line: 5.60 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 9.20 L/min
Flow reading at secondary line: 9.20 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 9.60 L/min
Flow reading at secondary line: 9.60 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 9.87 L/min
Flow reading at secondary line: 9.87 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 9.87 L/min
Flow reading at secondary line: 9.87 L/min
Temperature for Sensor 1 is: 24.62
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 9.87 L/min
Flow reading at secondary line: 9.87 L/min
Temperature for Sensor 1 is: 24.62
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 10.00 L/min
Flow reading at secondary line: 10.00 L/min
Temperature for Sensor 1 is: 24.62
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 8.53 L/min
Flow reading at secondary line: 8.53 L/min
Temperature for Sensor 1 is: 24.62
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 3.87 L/min
Flow reading at secondary line: 3.87 L/min
Temperature for Sensor 1 is: 24.56
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 0.27 L/min
Flow reading at secondary line: 0.27 L/min
Temperature for Sensor 1 is: 24.62
Temperature for Sensor 2 is: 24.75
Flow reading at main line: 0.00 L/min
Flow reading at secondary line: 0.00 L/min

Any idea why this is? Or is it because pin 3 is unoccupied?

Any idea why this is? Or is it because pin 3 is unoccupied?

Pin 3 is probably floating.

The flow meters documentations shows 10K pull ups on the pulse output lines. You can set
pinMode(3, INPUT_PULLUP) and it will keep it high during your testing when not connected to a flow meter with stronger pullups.

Hi,
Looking at your flow diagram, have you got a one way valve in the length of pipe the you bypass with the Boost Pump, to stop fluid just flowing around the local circuit?

Tom... :slight_smile:

cattledog:
Pin 3 is probably floating.

The flow meters documentations shows 10K pull ups on the pulse output lines. You can set
pinMode(3, INPUT_PULLUP) and it will keep it high during your testing when not connected to a flow meter with stronger pullups.

Sorry for the late response! Finally got around to testing again and it appears that was the case. Wired up two flow meters with pull up resistors and they look to be reporting fine. Thanks!

TomGeorge:
Hi,
Looking at your flow diagram, have you got a one way valve in the length of pipe the you bypass with the Boost Pump, to stop fluid just flowing around the local circuit?

Tom... :slight_smile:

No, but we realized our boost pump wasn't truly parallel to the main pump so we've since added a line from the secondary inner loop to the reservoir tank so the boost pump will be able to draw more water in order to increase the flow of the main line. At least that's the idea.

Hi Vmorr,

Interresting project, i would like to use your code for my own project, could you make it available to me?

j.a.kuiphof@gmail.com it would be much appreciated.

With Regards, Ko.

Don't hold your breath. Vmorr made only 6 posts and hasn't been back since 2015.