Timer Based Interrupts syntax (adafruit flow meter)

Hi friends,

I’m still a novice when it comes to interacting with physical sensors, in this case I am trying to get the small plastic flow sensor to work using the stock code found on their GitHub, with small modifications. I under stand that the flow meter works on the the principal of the flow spinning a wheel with magnets that induces a voltage every time it passes by the sensor. The code they provide i think is for an Arduino uno, now from my research i know that the interrupts are processor specific so i need to know how to modify the following code for it to work on my photon.

The stock code comes with LCD screen code that will display the sensor measurement, I’ve commented all of that out and am trying to get a reading on the serial monitor.

The two parts I still do not understand are the Signal, and the UseInterupt sections, so any explanation is greatly appreciated.

Here is the code i am using when i try to compile as is i get a few error readings.

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

// count how many pulses!
volatile int pulses = 0;
// track the state of the pulse pin
volatile int lastflowpinstate;
// you can try to keep time of how long it is between pulses
volatile int 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(FLOWSENSORPIN, ISR, CHANGE)
{
  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!");
   //lcd.begin(16, 2);
   
   pinMode(FLOWSENSORPIN, INPUT);
   digitalWrite(FLOWSENSORPIN, HIGH);
   lastflowpinstate = digitalRead(FLOWSENSORPIN);
   attachInterrupt(FLOWSENSORPIN, useInterrupt, CHANGE);
}

void loop()                     // run over and over again
{ 
  //lcd.setCursor(0, 0);
  //lcd.print("Pulses:"); lcd.print(pulses, DEC);
  //lcd.print(" Hz:");
  //lcd.print(flowrate);
  //lcd.print(flowrate);
  Serial.print("Freq: "); Serial.println(flowrate);
  Serial.print("Pulses: "); Serial.println(pulses, DEC);
  
  // if a plastic sensor use the following calculation
  // Sensor Frequency (Hz) = 7.5 * Q (Liters/min)
  // Liters = Q * time elapsed (seconds) / 60 (seconds/minute)
  // Liters = (Frequency (Pulses/second) / 7.5) * time elapsed (seconds) / 60
  // Liters = Pulses / (7.5 * 60)
  float liters = pulses;
  liters /= 7.5;
  liters /= 60.0;

/*
  // if a brass sensor use the following calculation
  float liters = pulses;
  liters /= 8.1;
  liters -= 6;
  liters /= 60.0;
*/
  Serial.print(liters); Serial.println(" Liters");
  //lcd.setCursor(0, 1);
  //lcd.print(liters); lcd.print(" Liters        ");
 
  delay(100);
}

There is an ingenious library that wraps the timer programming up in an easy to use way
https://build.particle.io/libs/SparkIntervalTimer/1.3.7

And I’d count the sensor revolutions (and measure the speed) this way

#include "SparkIntervalTimer.h"

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;

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

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

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

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

This makes much more sense to me, thank you for laying that our for me. I cant tell which variable holds the flow rate? Is it revPerSec? Or would i still have to do some calcualtions in the void loop section. Thanks again

@jjlee32, you could also use a Software Timer for the 1 second interval instead of SparkIntervalTimer. Either way will work well. :wink:

1 Like

That's it. I've left some work for you :wink:

According Software Timers they will be fine as long you have no other tasks that may get FreeRTOS' timing confused (e.g. long running or lots of concurring timer callbacks)

2 Likes

Thank you for your help.

2 Likes

When I try and compile I get three errors. All have to do with the ‘…’ in the void setup. I didnt modify the above code other than adding serial.begin(9600) so I can read the measurement.

here are the errors i am getting

/src/flowsensor.cpp: In function 'void setup()':
/src/flowsensor.cpp:30:3: error: expected primary-expression before '...' token
   usDeltaTime = us - usLastTime;
   ^

/src/flowsensor.cpp:30:3: error: expected ';' before '...' token
/src/flowsensor.cpp:37:3: error: expected primary-expression before '...' token
   ...
   ^

/src/flowsensor.cpp:37:3: error: expected ';' before '...' token
make[1]: *** [../build/target/user/platform-6src/flowsensor.o] Error 1
make: *** [user] Error 2

The “…” lines should not be in your code, delete them. They are placeholders to show where you might have other code in setup().

3 Likes

I did an implementation using a software timer as a watchdog to clear the revPerSec value after 500ms (in my case) of inactivity. Note: I also did away with the msCount since the update rate of my sensor was so low it was basically useless

Timer flowTimeout(500,flowTimeoutISR); 

volatile uint32_t usLastTime  = 0;  
volatile uint32_t usDeltaTime = 0;  
volatile double   revPerSec   = 0;  // calculated pulses/s using interval timing calculation

void flowTimeoutISR() {
  revPerSec = 0;
}

void flowSenseISR() {
  uint32_t us = micros();
  usDeltaTime = us - usLastTime;
  usLastTime = us;
  revPerSec =  1000000.0 / usDeltaTime;
  flowTimeout.resetFromISR();
}

void setup() {
  ...
  // don't need to start timer here - started when first reset in ISR

  pinMode(FLOWSENSORPIN, INPUT_PULLUP);
  attachInterrupt(FLOWSENSORPIN, flowSenseISR, FALLING);
  ...
}
1 Like