Using 2 flow meters

I have a project that requires setting up two flow meters to one Arduino Uno Rev3. I have set one up and it's working well, the code for it is below. My question is, how do I set up the code for the second Arduino? I am new to all of this. I'm assuming I essentially copy the code but use pin 3 as the interrupt pin instead of 2 for the second sensor. How exactly would this look? Also, can I power the second sensor using the 3.3V pin? The first one is connected to the 5V pin. Thanks!

int flowpin = 2; //This is the input pin on the Arduino
double flowRate; //Value to be calculated
volative int count; //Integer needs to be set as volative to ensure it updates correctly during interrupt process
void setup() {
    //setup code here, runs once
  pinMode(flowPin, INPUT);      //Sets the pin as input
  attachInterrupt(0, Flow, RISING);  //Set interrip for pin 2 (Interrupt pin 0)
  
}

void loop() {
    //main code to run repeatedly
  count = 0;    //makes counter start at 0
  interrups();  //enables interrupts
  delay (1000); //wait 1 second
  noInterrupts(); //Disable the interrups on the Arduino

  //math for useful readings
  flowRate = (count * 2.25);    //Count pulses in last second and multiply by 2.25mL
  flowRate = rlowRate * 60;     //Convert seconds to minutes, giving mL / minute
  flowRate = flowRate / 1000;   //Convert mL to Liters, giving L / minute

  Serial.println(flowRate);     //Print the variable flowRate to Serial
}

void Flow()
{
  count++;    //every time function runs, increase count by 1
}]

These are the flow sensors I'm using:


Please post a link to the datasheet for the sensor.
Why not use 5 volt? Why 3.3?

I put up a screenshot of the specifications of the sensor. Here's a link to the page. You are being redirected...

Hold on, I think I just realized I was not being smart about this haha. Sorry, I'm new to Arduino. If I have a pin in the 5V connecting a breadboard, can I just connect my second sensor to the breadboard at it will also be powered by the 5V?

That's the purpose of breadboard.
3.3V is too low.

That solves that issue, thank you!! So where do I input my new code for the second sensor running through interrupt pin 3?

Do I duplicate everything and change the flowRate variable to flowRateinitial or something, and copy each line for flowRatefinal?

Not with that exact code. It has a number of typos in it.

  1. "volative" should be "volatile"
  2. "flowPin" should be "flowpin"
  3. "interrups" should be "interrupts"
  4. "rlowRate" should be "flowRate"
  5. The square bracket right at the end should be deleted.

Before I post code to the forum I check it compiles.

2 Likes

The text told is a Hall effect sensor inside. Good.
Use pin 2 and 3 as they both provide interrupt services. One pin per sensor.

2 Likes

More less like that. For the second one use pin3 and attachInterrupt(1, Flow2, RISING);

1 Like

I was editing this code whilst others were answering.
This code should work. It compiles, uploads, and prints the expected info the the serial monitor. In my case the expected outputs are 0.00 because I haven't tested the activity on the input pins gets counted. I did simple duplication. It could be more elegant, but I didn't bother restructuring it to have an array of structs for each sensor and a calculate function.

const byte flowPinA = 2; //This is the input pin on the Arduino
const byte flowPinB = 3; //This is the input pin on the Arduino
double flowRateA; //Value to be calculated
double flowRateB; //Value to be calculated
volatile int countA; //Integer needs to be set as volatile to ensure it updates correctly during interrupt process
volatile int countB; //Integer needs to be set as volatile to ensure it updates correctly during interrupt process

void setup() {
  Serial.begin(115200);
  pinMode(flowPinA, INPUT);      //Sets the pin as input
  pinMode(flowPinB, INPUT);      //Sets the pin as input
  attachInterrupt(digitalPinToInterrupt(flowPinA), FlowA, RISING);
  attachInterrupt(digitalPinToInterrupt(flowPinB), FlowB, RISING);
}

void loop() {
  countA = 0;
  countB = 0;
  interrupts();  //enables interrupts
  delay (1000); //wait 1 second
  noInterrupts(); //Disable the interrups on the Arduino

  //math for useful readings
  flowRateA = (countA * 2.25);    //Count pulses in last second and multiply by 2.25mL
  flowRateA = flowRateA * 60;     //Convert seconds to minutes, giving mL / minute
  flowRateA = flowRateA / 1000;   //Convert mL to Liters, giving L / minute
  Serial.println(flowRateA);     //Print the variable flowRate to Serial

  flowRateB = (countB * 2.25);    //Count pulses in last second and multiply by 2.25mL
  flowRateB = flowRateB * 60;     //Convert seconds to minutes, giving mL / minute
  flowRateB = flowRateB / 1000;   //Convert mL to Liters, giving L / minute
  Serial.println(flowRateB);     //Print the variable flowRate to Serial

}

void FlowA() {
  countA++;    //every time function runs, increase count by 1
}

void FlowB() {
  countB++;    //every time function runs, increase count by 1
}

1 Like

Interrupts should be off for the shortest time that is practical. You could declare a couple of local variables localA and localB. Interrupts would only need to be off while you did localA = countA; and localB=countB;
Also the delay(1000) isn't going to give the best timing accuracy because there is some additional time taken to execute the other code in the loop. It might be better to poll millis() for >= 1000ms elapsed since the previous time you did the calculation.
IDK how much accuracy you would gain by making the above changes. It may not make a significant difference

I was brushing my teeth.

I think this is the big improvement!, using digitalPinToInterruot means one less thing to worry about and usually get wrong.

  attachInterrupt(digitalPinToInterrupt(flowPinB), FlowB, RISING);

I was going to suggest to @Dave_Lowther that the loop could, with a bit more code, also generate fake inputs so as to thoroughly test the sketch… then I noticed the loop does not run free. Bzzzt!

A place for a big step forward would be to rework the "interrupts on whilst a delay plays out" method.

If the counts were changed to unsigned, then the interrupts could just be left running. Start a sample period noting the counts, end it with a millis() or micros() based timing mechanism and calculate the number of pulses each counter accumulate and then… you could test it by using the rest of the time to generate the fake pulses.

a7

1 Like

@alto777 and @Dave_Lowther could you please explain the "poll millis()" part simply? Thanks for all your help thus far

1 Like

Here's @Dave_Lowther playing with @alto777. I added a fake signal generator, and redid the timing method.

Try it here

Wokwi_badge UA Test Flow Rate Sensors


The code:
// https://wokwi.com/projects/409384416604568577
// https://forum.arduino.cc/t/using-2-flow-meters/1302833

const byte flowPinA = 2;
const byte flowPinB = 3;

double flowRateA;
double flowRateB;

volatile unsigned int countA;
volatile unsigned int countB;

const byte signalA = 8;
const byte aControl = A0;

const byte signalB = 9;
const byte bControl = A1;

void setup() {
  Serial.begin(115200);
  Serial.println("\nWake up!\n");

  pinMode(flowPinA, INPUT);      //Sets the pin as input
  pinMode(flowPinB, INPUT);      //Sets the pin as input
  attachInterrupt(digitalPinToInterrupt(flowPinA), FlowA, RISING);
  attachInterrupt(digitalPinToInterrupt(flowPinB), FlowB, RISING);

  pinMode(signalA, OUTPUT);
  pinMode(signalB, OUTPUT);
}

bool measuring;
bool calculate;

unsigned int countAStart;
unsigned int countBStart;
unsigned int aTotal;
unsigned int bTotal;

unsigned long startTime;

void loop() {

  generateFakeSignals();

  if (!measuring) {
    noInterrupts();
    countAStart = countA;
    countBStart = countB;
    interrupts();
    measuring = true;
    calculate = false;
    startTime = millis();
  }
  else {  // measuring
    if (millis() - startTime > 1000) {
      noInterrupts();
      aTotal = countA - countAStart;
      bTotal = countB - countBStart;
      interrupts();

      calculate = true;
      measuring = false;
    }
  }

// time to report?
  if (!calculate)
    return; // nope. We outta here

// calculate and report
  //math for useful readings
  flowRateA = (aTotal * 2.25);    //Count pulses in last second and multiply by 2.25mL
  flowRateA = flowRateA * 60;     //Convert seconds to minutes, giving mL / minute
  flowRateA = flowRateA / 1000;   //Convert mL to Liters, giving L / minute
  Serial.print("flow A : ");
  Serial.print(flowRateA);     //Print the variable flowRate to Serial

  flowRateB = (bTotal * 2.25);
  flowRateB = flowRateB * 60;
  flowRateB = flowRateB / 1000;
  Serial.print("    flow B : ");
  Serial.println(flowRateB);
}

void FlowA() {
  countA++;    //every time function runs, increase count by 1
}

void FlowB() {
  countB++;    //every time function runs, increase count by 1
}

void generateFakeSignals()
{
  static unsigned long aLastEdge;
  static unsigned long bLastEdge;
  unsigned long now = micros();


  int periodHalf = analogRead(aControl);
  periodHalf = map(periodHalf, 0, 1023, 5000, 833);
  if (now - aLastEdge > periodHalf) {
    digitalWrite(signalA, digitalRead(signalA) == LOW ? HIGH : LOW);

    aLastEdge = now;
  }

  periodHalf = analogRead(bControl);
  periodHalf = map(periodHalf, 0, 1023, 5000, 833);
  if (now - bLastEdge > periodHalf) {
    digitalWrite(signalB, digitalRead(signalB) == LOW ? HIGH : LOW);

    bLastEdge = now;
  }
}

FWIW what took the most time was my decision to cut/paste/edit code. Always a losing proposition.

I just noticed calculate is just the inversion of measuring.

a7

1 Like

I hadn't seen @alto777's post while I was making the most simple example. I'll post it anyway. All it does is print "Tick" on the serial monitor once a second.

unsigned long timeWhenLastCalcDone = 0;
const unsigned calcDelay = 1000;

void setup() {
  Serial.begin(115200);
}

void loop() {
  unsigned long timeNow = millis();
  if ((timeNow - timeWhenLastCalcDone) >= calcDelay) {
    timeWhenLastCalcDone = timeNow;
    Serial.println("Tick"); // Remove this line
    // Put your loop code, with the delay(1000) removed, in here
    // Enable interrupts before you exit this block of code
  }
}

Does this all look correct?

const byte flowPinA = 2; //This is the input pin on the Arduino
const byte flowPinB = 3;
double flowRateA; //Value to be calculated
double flowRateB;
volatile int countA; //Integer needs to be set as volative to ensure it updates correctly during interrupt process
volatile int countB;

void setup() {
    //setup code here, runs once
  Serial.begin(115200);
  pinMode(flowPinA, INPUT);      //Sets the pin as input
  pinMode(flowPinB, INPUT);
  attachInterrupt(digitalPinToInterrupt(flowPinA), FlowA, RISING);  //Set interrupt for pin 2 (Interrupt pin 0)
  attachInterrupt(digitalPinToInterrupt(flowPinB), FlowB, RISING);
}

void loop() {
    //main code to run repeatedly
  countA = 0;    //makes counter start at 0
  countB = 0;
  unsigned long timeNow = millis();
  if ((timeNow - timeWhenLastCalcDone) >= calcDelay) {
    timeWhenLastCalcDOne = timeNow;
  interrupts();  //enables interrupts
  noInterrupts(); //Disable the interrupts on the Arduino
      //math for useful readings
    
  flowRateA = (countA * 2.25);    //Count pulses in last second and multiply by 2.25mL
  flowRateA = flowRateA * 60;     //Convert seconds to minutes, giving mL / minute
  flowRateA = flowRateA / 1000;   //Convert mL to Liters, giving L / minute
  Serial.println(flowRateA);     //Print the variable flowRate to Serial

  flowRateB = (countB * 2.25);
  flowRateB = flowRateB * 60;
  flowRateB = flowRateB / 1000;
  Serial.println(flowRateB);
  }
}

void FlowA(){
  countA++;    //every time function runs, increase count by 1
}

void FlowB() {
  countB++;
}
const byte flowPin[2] = {2, 3}; //This is the input pin on the Arduino

volatile unsigned int flowCount[2]; //Integer needs to be set as volative to ensure it updates correctly during interrupt process

void FlowA(){
  flowCount[0]++;    //every time function runs, increase count by 1
}

void FlowB() {
  flowCount[1]++;
}

void setup() {
  Serial.begin(115200);
  for (byte f=0; f<2; f++) {
    pinMode(flowPin[f], INPUT);      //Sets the pin as input 
  }
  attachInterrupt(digitalPinToInterrupt(flowPin[0]), FlowA, RISING);  //Set interrupt for pin
  attachInterrupt(digitalPinToInterrupt(flowPin[1]), FlowB, RISING);  //Set interrupt for pin
}

void loop() {
  unsigned long timeNow = millis();
  static unsigned long timeWhenLastCalcDone;
  if ((timeNow - timeWhenLastCalcDone) >= 1000) {
    timeWhenLastCalcDone = timeNow;
    for (byte f=0; f<2; f++) {
      noInterrupts(); //Disable the interrupts on the   
      unsigned int count = flowCount[f];
      flowCount[f] = 0;
      interrupts();  //enables interrupts
      //math for useful readings
      float flowRate = (count * 2.25) * 60 / 1000;    //Count pulses in last second and multiply by 2.25mL and convert to L/min
      Serial.println(flowRate);     //Print the variable flowRate to Serial
    }
  }
}

The Hall effect sensor's output is NPN open collector, so if you do not have a pullup resistor (10k) connected between pin 2 and 5V, you need:
pinMode(flowPin, INPUT_PULLUP);

AND:

attachInterrupt(0, Flow, FALLING);

I have 2 pullup resistors (one for each sensor). Can provide a picture of my setup if necessary