/** * Flow Meter * * An Arduino flow meter library that provides calibrated liquid flow and volume measurement with flow sensors. * * @author sekdiy (https://github.com/sekdiy/FlowMeter) * @date 14.07.2015 Initial release. * @version See git comments for changes. */ #ifndef FLOWMETER_H #define FLOWMETER_H #include "Arduino.h" /** * FlowSensorProperties * * Structure that holds essential information about a flow sensor. * Stores general sensor properties and calibration points. * * See file G34_Flow_rate_to_frequency.jpg for reference. */ typedef struct { double capacity; //!< capacity, upper limit of flow rate (in l/min) double kFactor; //!< "k-factor" (in (pulses/s) / (l/min)), e.g.: 1 pulse/s = kFactor * l/min double mFactor[10]; //!< multiplicative correction factor near unity, "meter factor" (per decile of flow) } FlowSensorProperties; extern FlowSensorProperties UncalibratedSensor; //!< default sensor extern FlowSensorProperties FS300A; //!< see documentation about FS300A/SEN02141B extern FlowSensorProperties FS400A; //!< see documentation about FS400A/USN-HS10TA /** * FlowMeter */ class FlowMeter { public: /** * Initializes a new flow meter object. * * @param pin The pin that the flow sensor is connected to (has to be interrupt capable, default: INT0). * @param prop The properties of the actual flow sensor being used (default: UncalibratedSensor). */ FlowMeter(unsigned int pin = 2, FlowSensorProperties prop = UncalibratedSensor); double getCurrentFlowrate(); //!< Returns the current flow rate since last reset (in l/min). double getCurrentVolume(); //!< Returns the current volume since last reset (in l). double getTotalFlowrate(); //!< Returns the (linear) average flow rate in this flow meter instance (in l/min). double getTotalVolume(); //!< Returns the total volume flown trough this flow meter instance (in l). /** * The tick method updates all internal calculations at the end of a measurement period. * * We're calculating flow and volume data over time. * The actual pulses have to be sampled using the count method (i.e. via an interrupt service routine). * * Flow sensor formulae: * * Let K: pulses per second per unit of measure (i.e. (1/s)/(l/min)), * f: pulse frequency (1/s), * Q: flow rate (l/min), * p: sensor pulses (no dimension/unit), * t: time since last measurements (s). * * K = f / Q | units: (1/s) / (l/min) = (1/s) / (l/min) * <=> | Substitute p / t for f in order to allow for different measurement intervals * K = (p / t) / Q | units: ((1/s)/(l/min)) = (1/s) / (l/min) * <=> | Solve for Q: * Q = (p / t) / K | untis: l/min = 1/s / (1/s / (l/min)) * <=> | Volume in l: * V = Q / 60 | units: l = (l/min) / (min) * * The property K is sometimes stated in pulses per liter or pulses per gallon. * In these cases the unit of measure has to be converted accordingly (e.g. from gal/s to l/min). * See file G34_Flow_rate_to_frequency.jpg for reference. * * @param duration The tick duration (in ms). */ void tick(unsigned long duration = 1000); void count(); //!< Increments the internal pulse counter. Serves as an interrupt callback routine. void reset(); //!< Prepares the flow meter for a fresh measurement. Resets all current values, but not the totals. /* * setters enabling continued metering across power cycles */ FlowMeter* setTotalDuration(unsigned long totalDuration); //!< Sets the total (overall) duration (i.e. after power up). FlowMeter* setTotalVolume(double totalVolume); //!< Sets the total (overall) volume (i.e. after power up). FlowMeter* setTotalCorrection(double totalCorrection); //!< Sets the total (overall) correction factor (i.e. after power up). /* * convenience methods and calibration helpers */ unsigned int getPin(); //!< Returns the Arduino pin number that the flow sensor is connected to. unsigned long getCurrentDuration(); //!< Returns the duration of the current tick (in ms). double getCurrentFrequency(); //!< Returns the pulse rate in the current tick (in 1/s). double getCurrentError(); //!< Returns the error resulting from the current measurement (in %). unsigned long getTotalDuration(); //!< Returns the total run time of this flow meter instance (in ms). double getTotalError(); //!< Returns the (linear) average error of this flow meter instance (in %). protected: unsigned int _pin; //!< connection pin (has to be interrupt capable!) FlowSensorProperties _properties; //!< sensor properties (including calibration data) unsigned long _currentDuration; //!< current tick duration (convenience, in ms) double _currentFrequency; //!< current pulses per second (convenience, in 1/s) double _currentFlowrate = 0.0f; //!< current flow rate (in l/tick), e.g.: 1 l / min = 1 pulse / s / (pulses / s / l / min) double _currentVolume = 0.0f; //!< current volume (in l), e.g.: 1 l = 1 (l / min) / (60 * s) double _currentCorrection; //!< currently applied correction factor unsigned long _totalDuration = 0.0f; //!< total measured duration since begin of measurement (in ms) double _totalVolume = 0.0f; //!< total volume since begin of measurement (in l) double _totalCorrection = 0.0f; //!< accumulated correction factors volatile unsigned long _currentPulses = 0; //!< pulses within current sample period }; /** * FlowSensorCalibration * * Convenience class for manipulating sensor properties. */ class FlowSensorCalibration { public: FlowSensorCalibration() {}; FlowSensorCalibration(FlowSensorProperties properties): _properties(properties) {}; FlowSensorCalibration* setProperties(FlowSensorProperties properties) { this->_properties = properties; return this; }; FlowSensorCalibration* setCapacity(double capacity) { this->_properties.capacity = capacity; return this; } FlowSensorCalibration* setKFactor(double kFactor) { this->_properties.kFactor = kFactor; return this; } FlowSensorCalibration* setMeterFactorPerDecile(unsigned int decile, unsigned int mFactor) { this->_properties.mFactor[decile] = mFactor; return this; } FlowSensorProperties getProperties() { return this->_properties; } double getCapacity() { return this->_properties.capacity; } double getKFactor() { return this->_properties.kFactor; } unsigned int getMeterFactorPerDecile(unsigned int decile) { return this->_properties.mFactor[decile]; } protected: FlowSensorProperties _properties; }; #endif // FLOWMETER_H