Reading Two Flow Sensors With an Arduino Uno

I've seen other forum posts on this subject, but I'm still new to programming and the everybody's code is really confusing to me.

So correct me if I'm wrong - In order to read the number of pulses the flow meter gives out when a fluid is running through it, you need a program that counts each time the Hall Effect sensor goes HIGH, right? If that's the case then why do you need to use an 'interrupt function'? This is where I get lost...

I'm trying to read two flow meters with an Arduino Uno, but I think what's messing me up is the interrupt function. Do I need separate interrupt functions for each flow sensor. To my understanding there are 3 interrupt pins on the Arduino Uno. One of these interrupt pins is digital pin 2, which is the one that works with one sensor, but when I try to add another interrupt function I can't read either flow meters. Any help or advice is very appreciated.

The code and flow meters I'm trying to use are from Liquid Flow Meter - Plastic 1/2 NPS Threaded : ID 828 : $9.95 : Adafruit Industries, Unique & fun DIY electronics and kits

This code works with one flow meter with its signal pin connected to digital pin 2:

/**********************************************************
This is example code for using the Adafruit liquid flow meters. 

Tested and works great with the Adafruit plastic and brass meters
    ------> http://www.adafruit.com/products/828
    ------> http://www.adafruit.com/products/833

Connect the red wire to +5V, 
the black wire to common ground 
and the yellow sensor wire to pin #2

Adafruit invests time and resources providing this open source code, 
please support Adafruit and open-source hardware by purchasing 
products from Adafruit!

Written by Limor Fried/Ladyada  for Adafruit Industries.  
BSD license, check license.txt for more information
All text above must be included in any redistribution
**********************************************************/


// which pin to use for reading the sensor? can use any pin!
#define FLOWSENSORPIN 2

// count how many pulses!
volatile uint16_t pulses = 0;
// track the state of the pulse pin
volatile uint8_t lastflowpinstate;
// you can try to keep time of how long it is between pulses
volatile uint32_t lastflowratetimer = 0;
// and use that to calculate a flow rate
volatile float flowrate;
// Interrupt is called once a millisecond, looks for any pulses from the sensor!
SIGNAL(TIMER0_COMPA_vect) {
  uint8_t x = digitalRead(FLOWSENSORPIN);
  
  if (x == lastflowpinstate) {
    lastflowratetimer++;
    return; // nothing changed!
  }
  
  if (x == HIGH) {
    //low to high transition!
    pulses++;
  }
  lastflowpinstate = x;
  flowrate = 1000.0;
  flowrate /= lastflowratetimer;  // in hertz
  lastflowratetimer = 0;
}

void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
  }
}

void setup() {
   Serial.begin(9600);
   Serial.print("Flow sensor test!");
   
   pinMode(FLOWSENSORPIN, INPUT);
   digitalWrite(FLOWSENSORPIN, HIGH);
   lastflowpinstate = digitalRead(FLOWSENSORPIN);
   useInterrupt(true);
}

void loop()                     // run over and over again
{ 

  float liters = pulses;
  liters /= 7.5;
  liters /= 60.0;

  Serial.print(liters); Serial.println(" Liters");
  delay(100);
}

This is the code I tried modifying to get it to work with two flow meters, but only the flow meter using pin 2 works. As you may see, I basically just double everything that the 1st flow meter was using and added '2' to the end:

#define FLOWSENSORPIN 2
#define FLOWSENSORPIN2 3 

// count how many pulses!
volatile uint16_t pulses = 0;
volatile uint16_t pulses2 = 0;
// track the state of the pulse pin
volatile uint8_t lastflowpinstate;
volatile uint8_t lastflowpinstate2;
// you can try to keep time of how long it is between pulses
volatile uint32_t lastflowratetimer = 0;
volatile uint32_t lastflowratetimer2 = 0;
// and use that to calculate a flow rate
volatile float flowrate;
volatile float flowrate2;
// Interrupt is called once a millisecond, looks for any pulses from the sensor!
SIGNAL(TIMER0_COMPA_vect) {
  uint8_t x = digitalRead(FLOWSENSORPIN);
  
  if (x == lastflowpinstate) {
    lastflowratetimer++;
    return; // nothing changed!
  }
  
  if (x == HIGH) {
    //low to high transition!
    pulses++;
  }
  lastflowpinstate = x;
  flowrate = 1000.0;
  flowrate /= lastflowratetimer;  // in hertz
  lastflowratetimer = 0;
}


SIGNAL(TIMER2_COMPA_vect) {
  uint8_t x2 = digitalRead(FLOWSENSORPIN2);

  if (x2 == lastflowpinstate2) {
    lastflowratetimer2++;
    return; // nothing changed!
  }
  
  if (x2 == HIGH) {
    //low to high transition!
    pulses2++;
  }
  lastflowpinstate2 = x2;
  flowrate2 = 1000.0;
  flowrate2 /= lastflowratetimer2;  // in hertz
  lastflowratetimer2 = 0;
}


void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
  }
}




void setup()
{
  Serial.begin(9600);
  
  pinMode(FLOWSENSORPIN, INPUT);
  digitalWrite(FLOWSENSORPIN, HIGH);
  lastflowpinstate = digitalRead(FLOWSENSORPIN);
  
  pinMode(FLOWSENSORPIN2, INPUT);
  digitalWrite(FLOWSENSORPIN2, HIGH);
  lastflowpinstate2 = digitalRead(FLOWSENSORPIN2);
  
  useInterrupt(true);

}


void loop() {
  

  float liters = pulses;
  liters /= 7.5;
  liters /= 60.0;

  float liters2 = pulses2;
  liters2 /= 7.5;
  liters2 /= 60.0;
 
  //Serial.println(level);
  //Serial.print(liters); Serial.println(" Liters");
  Serial.print(liters2); Serial.println(" Liters");
  
  delay(100);
 
}

I'm guessing the problem has something to do with this block of code?

SIGNAL(TIMER2_COMPA_vect) {
  uint8_t x2 = digitalRead(FLOWSENSORPIN2);

  if (x2 == lastflowpinstate2) {
    lastflowratetimer2++;
    return; // nothing changed!
  }
  
  if (x2 == HIGH) {
    //low to high transition!
    pulses2++;
  }
  lastflowpinstate2 = x2;
  flowrate2 = 1000.0;
  flowrate2 /= lastflowratetimer2;  // in hertz
  lastflowratetimer2 = 0;
}


void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
  }
}

If you made it this far through my giant mess of problems then I appreciate the time you took to at least give it a shot to solve. Thanks!

In order to read the number of pulses the flow meter gives out when a fluid is running through it, you need a program that counts each time the Hall Effect sensor goes HIGH, right?

Only if the flow meter uses hall effect sensors.

Do I need separate interrupt functions for each flow sensor.

If you want to keep the flow data separate, yes.

To my understanding there are 3 interrupt pins on the Arduino Uno.

There are two external interrupts, on pins 2 and 3.

This is the code I tried modifying to get it to work with two flow meters

That code is not using external interrupts. It is using timer interrupts.

As you may see, I basically just double everything that the 1st flow meter was using and added '2' to the end

No, you didn't. You used two different timers, but you only initialized one of the timers.

Thanks for the quick reply. So how would I initialize the second timer interrupt? Are external interrupts better to use than timer interrupts?

So how would I initialize the second timer interrupt?

Look at the ATMEL documentation.

I can't see why you can't read both pins in the same interrupt, though. Just move all the code in SIGNAL(TIMER2_COMPA_vect) into SIGNAL(TIMER0_COMPA_vect).

Thanks for the advice.

Unfortunately I can't read both sensors from the timer0. I can only read one flow sensor. When I have the 1st flow sesnor (FLOWSENSORPIN) connected to pin 2 I can read it but for some reason when I connect the 2nd flow sensor (FLOWSENSORPIN2) to pin 3 I can't read it and vice versa.

SIGNAL(TIMER0_COMPA_vect) {
  uint8_t x = digitalRead(FLOWSENSORPIN);
  
  if (x == lastflowpinstate) {
    lastflowratetimer++;
    return; // nothing changed!
  }
  
  if (x == HIGH) {
    //low to high transition!
    pulses++;
  }
  
  lastflowpinstate = x;
  flowrate = 1000.0;
  flowrate /= lastflowratetimer;  // in hertz
  lastflowratetimer = 0;



  uint8_t x2 = digitalRead(FLOWSENSORPIN2);
  
  if (x2 == lastflowpinstate2) {
    lastflowratetimer2++;
    return; // nothing changed!
  }
  

  if (x2 == HIGH) {
    //low to high transition!
    pulses2++;
  }


  lastflowpinstate2 = x2;
  flowrate2 = 1000.0;
  flowrate2 /= lastflowratetimer2;  // in hertz
  lastflowratetimer2 = 0;
  
}


void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
  }
}

[SOLVED]

This is the code that works with the Arduino Uno. With the signal pins of 2 flow meters from Liquid Flow Meter - Plastic 1/2 NPS Threaded : ID 828 : $9.95 : Adafruit Industries, Unique & fun DIY electronics and kits connected to digital pins 2 and 3, the Serial Monitor will display the amount of fluid that has passed through the flow meters individually. The code is probably really messy and over complicated, but that's because I modified it...

/*                   Red    ---> 5v 
   Flow meter 1 ---> Black ---> GND
            (Signal) Yellow ---> digital pin 2


                     Red ---> 5v
   Flow meter 2 ---> Black ---> GND
            (Signal) Yellow ---> digital pin 3
*/

#define FLOWSENSORPIN 2
#define FLOWSENSORPIN2 3 

// count how many pulses!
volatile uint16_t pulses = 0;
volatile uint16_t pulses2 = 0;
// track the state of the pulse pin
volatile uint8_t lastflowpinstate;
volatile uint8_t lastflowpinstate2;
// you can try to keep time of how long it is between pulses
volatile uint32_t lastflowratetimer = 0;
volatile uint32_t lastflowratetimer2 = 0;
// and use that to calculate a flow rate
volatile float flowrate;
volatile float flowrate2;
// Interrupt is called once a millisecond, looks for any pulses from the sensor!
SIGNAL(TIMER0_COMPA_vect) {
  uint8_t x = digitalRead(FLOWSENSORPIN);
  
  if (x == lastflowpinstate) {
    lastflowratetimer++;
    return; // nothing changed!
  }
  
  if (x == HIGH) {
    //low to high transition!
    pulses++;
  }
  
  lastflowpinstate = x;
  flowrate = 1000.0;
  flowrate /= lastflowratetimer;  // in hertz
  lastflowratetimer = 0;
}


SIGNAL(TIMER2_COMPA_vect) {
  
  uint8_t x2 = digitalRead(FLOWSENSORPIN2);
  
  if (x2 == lastflowpinstate2) {
    lastflowratetimer2++;
    return; // nothing changed!
  }
  

  if (x2 == HIGH) {
    //low to high transition!
    pulses2++;
  }


  lastflowpinstate2 = x2;
  flowrate2 = 1000.0;
  flowrate2 /= lastflowratetimer2;  // in hertz
  lastflowratetimer2 = 0;
}


void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
  }
}




void useInterrupt2(boolean b) {
  if (b) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR2A = 0xAF;
    TIMSK2 |= _BV(OCIE2A);
  } else {
    // do not call the interrupt function COMPA anymore
    TIMSK2 &= ~_BV(OCIE2A);
  }
}



void setup()
{
  Serial.begin(9600);
  
  pinMode(FLOWSENSORPIN, INPUT);
  digitalWrite(FLOWSENSORPIN, HIGH);
  lastflowpinstate = digitalRead(FLOWSENSORPIN);
  
  pinMode(FLOWSENSORPIN2, INPUT);
  digitalWrite(FLOWSENSORPIN2, HIGH);
  lastflowpinstate2 = digitalRead(FLOWSENSORPIN2);
  
  useInterrupt(true);
  useInterrupt2(true);

}


void loop() {
  

  float liters = pulses;
  liters /= 7.5;
  liters /= 60.0;

  float liters2 = pulses2;
  liters2 /= 7.5;
  liters2 /= 60.0;
 
  //Serial.println(level);
  Serial.print(liters); Serial.print(" Liters"); Serial.print("        "); Serial.print(liters2); Serial.println(" Liters");
  
  delay(100);
 
}

The code is probably really messy and over complicated, but that's because I modified it...

It is. It wastes two timers and two external interrupt pins unnecessarily. You need only one timer interrupt. Each time it fires, read both pins.

If you insist on wasting the two external interrupt pins, that is your business, but there is nothing in the code that requires the use of the external interrupt pins.

There is no reason to use two functions to set up two timers. They can both be set up in one function.

Not that there is any reason to use two timers. If the two pins can't be read in one interrupt, having two interrupts won't provide more time to read the two pins.

PaulS:
It is. It wastes two timers and two external interrupt pins unnecessarily. You need only one timer interrupt. Each time it fires, read both pins.

If you insist on wasting the two external interrupt pins, that is your business, but there is nothing in the code that requires the use of the external interrupt pins.

There is no reason to use two functions to set up two timers. They can both be set up in one function.

Not that there is any reason to use two timers. If the two pins can't be read in one interrupt, having two interrupts won't provide more time to read the two pins.

PAULS you give creative feedback lacking resolution to the question. You state he is wasting pins but offer no code solution or practical teaching to help with this issue. I too am having the same problem he is but I plan to add at least 5 more sensors.

When we reach out for help we are looking to learn (some people) others just want the solution so they can remain ignorant. none the less, a short explanation of what is being done wrong with a possible solution. He already knows he is doing something wrong with interrupt pins but HOW does he fix it? Thanks

Also I don't know why this was considered SOLVED when the code that was given in so dysfunctional. No offense Noob.

but offer no code solution or practical teaching to help with this issue

How much help do you need to use 1 timer, instead of two?

As to using external interrupt pins, you should know which pins are external interrupt pins, and not use them until you need them as external interrupt pins or until you have no other pins to use.

PaulS:
How much help do you need to use 1 timer, instead of two?

As to using external interrupt pins, you should know which pins are external interrupt pins, and not use them until you need them as external interrupt pins or until you have no other pins to use.

Sorry PAULS, that didn't come out right. Could you, by chance, demonstrate what you said by showing an example. I know it must be frustrating to have a firm grasp on coding and be able to see it with your eyes closed but I am like 1 week old in this stuff.

I am even taking a c++ course to learn this more but its going to take time. So any simple talking is greatly appreciated.

If I am correct on what you said here is how I changed my code to reflect your comment.

/*
Liquid flow rate sensor -DIYhacking.com Arvind Sanjeev

Added to by Artemas to support two flow sensors.

Measure the liquid/water flow rate using this code. 
Connect Vcc and Gnd of sensor to arduino, and the 
signal line to arduino digital pin 2.
 
 */

byte statusLed    = 13;

byte sensorInterrupt = 0;  // interrupt 0
byte sensorPin       = 2;  // digital pin 2
// The hall-effect flow sensor outputs approximately 4.5 pulses per second per
// litre/minute of flow.
float calibrationFactor = 4.5;
volatile byte pulseCount;  
float flowRate;
unsigned int flowMilliLitres;
unsigned long totalMilliLitres;
unsigned long oldTime;

byte sensorPin2       = 3;
// The hall-effect flow sensor outputs approximately 4.5 pulses per second per
// litre/minute of flow.
float calibrationFactor2 = 4.5;
volatile byte pulseCount2;
float flowRate2;
unsigned int flowMilliLitres2;
unsigned long totalMilliLitres2;
unsigned long oldTime2;

void setup()
{
  
  // Initialize a serial connection for reporting values to the host
  Serial.begin(115200);
   
  // Set up the status LED line as an output
  pinMode(statusLed, OUTPUT);
  digitalWrite(statusLed, HIGH);  // We have an active-low LED attached
  
  pinMode(sensorPin, INPUT);
  digitalWrite(sensorPin, HIGH);

  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0;
  totalMilliLitres  = 0;
  oldTime           = 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);

  
  
  pinMode(sensorPin2, INPUT);
  digitalWrite(sensorPin2, HIGH);

  pulseCount2        = 0;
  flowRate2          = 0.0;
  flowMilliLitres2   = 0;
  totalMilliLitres2  = 0;
  oldTime2           = 0;

  // The Hall-effect sensor is connected to pin 3 which uses interrupt 1.
  // 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((millis() - oldTime) > 1000)    // Only process counters once per second
  { 
    // Disable the interrupt while calculating flow rate and sending the value to
    // the host
    detachInterrupt(sensorInterrupt);
        
    // 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) / calibrationFactor;
    
    // 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.
    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 millilitres.
    flowMilliLitres = (flowRate / 60) * 1000;
    
    // Add the millilitres passed in this second to the cumulative total
    totalMilliLitres += flowMilliLitres;
      
    unsigned int frac;
    
    // Print the flow rate for GROW BED 1 this second in litres / minute
    Serial.print("Flow rate GROW BED 1: ");
    Serial.print(int(flowRate));  // Print the integer part of the variable
    Serial.print("L/min");
    Serial.print("\t");       // Print tab space

    // Print the cumulative total of GROW BED 1 litres flowed since starting
    Serial.print("Output Liquid Quantity: ");        
    Serial.print(totalMilliLitres);
    Serial.println("mL"); 
    Serial.print("\t");       // Print tab space
  Serial.print(totalMilliLitres/1000);
  Serial.print("L");
    

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

    
    // Disable the interrupt while calculating flow rate and sending the value to
    // the host
    detachInterrupt(sensorInterrupt);
        
    // 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)) * pulseCount2) / calibrationFactor;
    
    // 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.
    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 millilitres.
    flowMilliLitres = (flowRate / 60) * 1000;
    
    // Add the millilitres passed in this second to the cumulative total
    totalMilliLitres += flowMilliLitres;
      
    unsigned int frac2;
// flow meter 2
 // Print the flow rate for this second in litres / minute
    Serial.print("Flow rate FISHTANK: ");
    Serial.print(int(flowRate2));  // Print the integer part of the variable
    Serial.print("L/min 2");
    Serial.print("\t");       // Print tab space

    // Print the cumulative total of litres flowed since starting
    Serial.print("Output Liquid Quantity: ");        
    Serial.print(totalMilliLitres);
    Serial.println("mL"); 
    Serial.print("\t");       // Print tab space
  Serial.print(totalMilliLitres/1000);
  Serial.print("L");

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

/*
Insterrupt Service Routine
 */
void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}

My serial monitor shows:
0LFlow rate GROW BED 1: 0L/min Output Liquid Quantity: 3mL
0LFlow rate FISHTANK: 0L/min 2 Output Liquid Quantity: 3mL
0LFlow rate GROW BED 1: 8L/min Output Liquid Quantity: 147mL
0LFlow rate FISHTANK: 0L/min 2 Output Liquid Quantity: 147mL
so the pin 2 in the GROW BED pin 3 is FISHTANK, none the less, pin 3 still doesn't work.

You need to attach interrupts to two different pins, and have two different ISR's with two different counts.

//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();
  }

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

cattledog:
You need to attach interrupts to two different pins, and have two different ISR's with two different counts.

//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();
 }

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

Excellent CATTLEDOG! yes they both work! So, allow me to expand on this if I were to add subsequent flow meters, say 4 more, how would that look in comparison to the working code you gave above? more interrupts? I am using a MEGA2560 so I believe all digital pins can be used, yes?

if I were to add subsequent flow meters, say 4 more, how would that look in comparison to the working code you gave above? more interrupts? I am using a MEGA2560 so I believe all digital pins can be used, yes?

The MEGA2560 has six external interrupts. See attachInterrupt() - Arduino Reference

You definitely want to get into the habit of using the recommended syntax

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);	(recommended)

For more than 6 flow meters you will need to use pin change interrupts. I would recommend using a library if you you need them. Consider Nico Hood's PinChangeInterrupt library available through the library manager. It uses syntax which is similar to the external interrupts and is very easy to adopt if you are familiar using the external interrupts.

so I took what you had and added a third to everything but the third sensor won't give a value. The code looks like this:

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

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

void count1_ISR()
{
  count1++;
}

void count2_ISR()
{
  count2++;
}

void count3_ISR()
{
  count3++;
}
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)
  attachInterrupt(2, count3_ISR, RISING); //pin4 (Atmega328)
}

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

}
unsigned int getCount()
{
  noInterrupts();
  copyCount1 = count1;
  count1 = 0;
  copyCount2 = count2;
  count2 = 0;
  copyCount3 = count3;
  count1 = 0;
  interrupts();
}

I like how neat and simple your version is too. I think though maybe it isn't working because I only have two interrupts available to the MEGA2560?

Aslo I saw this code:

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

I know their block comments which isn't read by the program but could you elaborate for me please?

Thanks!

External interrupt pins. No interrupt on pin 4

Mega, Mega2560, MegaADK 2, 3, 18, 19, 20, 21

Aslo I saw this code:

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

That was just a way to provide some signals to the interrupt pins to demonstrate the code.

You should begin to think about using arrays to handle the multiple flow meters and the duplicated code.

cattledog:
External interrupt pins. No interrupt on pin 4

Mega, Mega2560, MegaADK 2, 3, 18, 19, 20, 21

You should begin to think about using arrays to handle the multiple flow meters and the duplicated code.

Arrays, i will read up on that.

As for interrupts on 18, 19, 20, 21 they don't show on the 2560. It skips from 13 to 22 but I see TX3, RX3, TX2, RX2 TX1, RX1, SDA and SCL on pins 14-21. Are these pins 18-21 to be used in my case and are they wired any differently than just plugging into those pins?

Again I thank you for the time. I have learned much in the last 30 minutes from you teaching this to me!

As for interrupts on 18, 19, 20, 21 they don't show on the 2560. It skips from 13 to 22 but I see TX3, RX3, TX2, RX2 TX1, RX1, SDA and SCL on pins 14-21. Are they (pins 18-21) to be used in my case and are they wired any differently than just plugging into those pins?

Here's a good pin out reference for the Mega 2560 http://pighixxx.com/megapdf.pdf

You can use digital pins 18,19,20 and 21 just like you use pins 2 and 3 for external interrupts. Like many pins on the various Arduino models, they have multiple functions.

You will see from the pin out image that the relationship between the pins 2,3,18,19,20,21 and what is called int0,int1,int2,int3,int4, and int5 is not what you would expect given that the ide maintains the int0 and int1 on pin 2 and 3 to stay in line with the AT328.

That is why you should use the recommended

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);

Could you, by chance, demonstrate what you said by showing an example.

Not without horribly confusing things. This thread WAS about using timer interrupts to read data from devices connected to pins that support, but weren't using, external interrupts.

Your code doesn't use timer interrupts. It does use external interrupts.

To show how to use one timer to read two different pins would not help.

PaulS:
This thread WAS about using timer interrupts to read data from devices connected to pins that support, but weren't using, external interrupts.

Sorry, I thought the thread was about reading two flow sensors simultaneously. Which was the exact same problem I had. Cattledog created and explained in detail the code required as well as the why behind it. the previous parts of this thread did little to resolve the issue in a clear cut way. Future noobs that come along will undoubtedly find their answer with Cattledogs input. The resolution seems to be directed at an interrupt ignorance, my ignorance that is.

Cattledog, thanks for the link I will check it out. Your answers are very thoughtful and helpful since your guidance is teaching rather than just responding. This forum is made great by people like you. Thanks!

cattledog:
You should begin to think about using arrays to handle the multiple flow meters and the duplicated code.

or (even better) a class...

this is easily convertible for flow:

// CLASS DEFINITION

class FanSpeed {
  
  public:
    void 
      setup(uint8_t irq_pin, void (*ISR_callback)(void), int value),
      handleInterrupt(void);
    double
      getSpeed(),
      getHertz();

  private:
    double
      _timeConstant = 60000000.0;
    uint32_t
      _lastMicros = 0UL,
      _interval = 60000000UL;
    void(*ISR_callback)();
};

void FanSpeed::setup(uint8_t irq_pin, void (*ISR_callback)(void), int value)
{
  attachInterrupt(digitalPinToInterrupt(irq_pin), ISR_callback, value);
}

inline void FanSpeed::handleInterrupt(void)
{
  uint32_t nowMicros = micros();
  _interval = nowMicros - _lastMicros;
  _lastMicros = nowMicros;
}

double FanSpeed::getSpeed()
{
  if (micros() - _lastMicros < 1000000UL) // has rotated in the last second
  {
    return _timeConstant / _interval;
  }
  else
  {
    return 0;
  }   
}

double FanSpeed::getHertz()
{
  if (micros() - _lastMicros < 1000000UL) // has rotated in the last second
  {
    return getSpeed() * 60.0;
  }
  else
  {
    return 0;
  }   
}

// PROGRAM START

FanSpeed* fan1;
uint8_t fan1pin = 2;

FanSpeed* fan2;
uint8_t fan2pin = 3;

void setup()
{
  Serial.begin(9600);
  pinMode(fan1pin, INPUT_PULLUP);
  fan1 = new FanSpeed();
  fan1->setup(fan1pin, []{fan1->handleInterrupt();}, FALLING);
  fan2 = new FanSpeed();
  fan2->setup(fan1pin, []{fan2->handleInterrupt();}, FALLING);
}

void loop()
{
  static uint32_t lastMillis = 0;
  if (millis() - lastMillis > 1000UL)
  {
    char message[64] = "";
    sprintf(message, "Fan1 Speed:%4d RPM\t%4d Hz", uint32_t(floor(fan1->getSpeed() + 0.5)), uint32_t(floor(fan1->getHertz() + 0.5)));
    sprintf(message, "Fan2 Speed:%4d RPM\t%4d Hz", uint32_t(floor(fan2->getSpeed() + 0.5)), uint32_t(floor(fan2->getHertz() + 0.5)));
    Serial.println(message);
    lastMillis = millis();
  }
}