├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── examples ├── Calibration │ └── Calibration.cpp ├── Multi │ └── Multi.cpp ├── Simple │ └── Simple.cpp └── Simulator │ └── Simulator.cpp ├── keywords.txt ├── library.json ├── library.properties ├── platformio.ini └── src ├── FlowMeter.cpp ├── FlowMeter.h ├── FlowSensorCalibration.h ├── FlowSensorProperties.cpp └── FlowSensorProperties.h /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=Arduino 2 | *.cpp linguist-language=Arduino 3 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to the FlowMeter library 2 | 3 | All pull requests are welcome. 4 | Please follow the pull request template. 5 | 6 | If you have a question or suggestion, feel free to open an issue on Github. 7 | Please follow the issue template. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected behavior 2 | 3 | ## Observed behavior. 4 | 5 | ## Steps to reproduce the problem. 6 | 7 | ## Arduino (or other IDE) version info 8 | 9 | ## Arduino (or compatible) hardware info -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Description** 8 | A clear and concise description of what the bug is. 9 | 10 | **Reproduction** 11 | Steps to reproduce the behavior: 12 | 1. Connect '...' 13 | 2. Measure '...' 14 | 3. Error '...' 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **Hardware (please complete the following information):** 23 | - Board type 24 | - Sensor type 25 | - Pins used 26 | 27 | **Software (please complete the following information):** 28 | - OS 29 | - IDE 30 | - Other libraries 31 | 32 | **Minimal example to reproduce (please complete the following information):** 33 | - Code to reproduce 34 | - Wiring to reproduce 35 | 36 | **Additional context** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Tackles issue # 2 | 3 | ## changes being applied by this pull request 4 | 5 | - 6 | - 7 | - 8 | 9 | ## people involved 10 | 11 | @user/repository -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # IDEs 31 | .pio 32 | .vscode 33 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "wiki"] 2 | path = wiki 3 | url = https://github.com/sekdiy/FlowMeter.wiki.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 sekdiy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlowMeter Library [![Version](https://img.shields.io/badge/FlowMeter-1.3.0-blue.svg)](https://github.com/sekdiy/FlowMeter) [![analytics](http://www.google-analytics.com/collect?v=1&t=pageview&dl=https%3A%2F%2Fgithub.com%2Fsekdiy%2FFlowMeter&cid=2238D739-76DE-4205-9768-2F3277FA2561&tid=UA-65656434-2&aip=1&dt=README)]() 2 | 3 | **FlowMeter** is an Arduino library that provides calibrated flow and volume measurement with flow sensors. 4 | 5 | You can use it to count flow and volume of liquids and gases (although the documentation focuses on applications using liquids) and can support multiple flow sensors at the same time. 6 | 7 | It also works as a totalizer, accumulating total volume and average flow rate over the run time of your project. 8 | 9 | A provision for calibration helps you to get the most out of your sensor. You can even estimate the recent and overall error margin. 10 | 11 | The library is intended for use with flow sensors that provide a frequency output signal proportional to flow, although other types of sensors could be made to work. 12 | 13 | ## A Simple Example 14 | 15 | Getting started with **FlowMeter** is easy. Take a look at this simple example: 16 | 17 | ```c++ 18 | void setup() { 19 | // prepare serial communication 20 | Serial.begin(115200); 21 | 22 | // get a new FlowMeter instance for an uncalibrated flow sensor on pin 2 23 | Meter = new FlowMeter(2, UncalibratedSensor, MeterISR, RISING); 24 | } 25 | 26 | void loop() { 27 | // wait between output updates 28 | delay(period); 29 | 30 | // process the (possibly) counted ticks 31 | Meter->tick(period); 32 | 33 | // output some measurement result 34 | Serial.print("Currently "); 35 | Serial.print(Meter->getCurrentFlowrate()); 36 | Serial.print(" l/min, "); 37 | Serial.print(Meter->getTotalVolume()); 38 | Serial.println(" l total."); 39 | 40 | // 41 | // any other code can go here 42 | // 43 | } 44 | ``` 45 | 46 | In the above example, a flow sensor is assumed to be connected to the `INT0` pin. The corresponding object `Meter` is updated every `period` (in milliseconds, e.g. 1000ms). 47 | 48 | So after every measurement `period`, the current *flow rate* and the total *volume* are printed out. 49 | 50 | Please read on in the [examples](https://github.com/sekdiy/FlowMeter/wiki/Examples) section of the library's [documentation pages](https://github.com/sekdiy/FlowMeter/wiki). 51 | 52 | ## Installing the library 53 | 54 | Just check out the [**FlowMeter**](https://github.com/sekdiy/FlowMeter) Repository on GitHub (or download the ZIP archive) and copy it to your `libraries/` folder (usually within your Arduino sketchbook). 55 | 56 | See the [installation](https://github.com/sekdiy/FlowMeter/wiki/Installation) section in the [documentation pages](https://github.com/sekdiy/FlowMeter/wiki) for more. 57 | 58 | ## Unit of measure 59 | 60 | The **FlowMeter** library expresses flow in the unit **l/min**. 61 | Most units of measure can be derived by simple conversion (just try not to measure in [Sverdrups](https://en.wikipedia.org/wiki/Sverdrup)). 62 | 63 | As an example, conversion between **l/min** and US **gal/min** can be done with a factor of *3.78541178*, conversion from **min** to **s** with a factor of *60*. 64 | 65 | ```math 66 | 3.78541178 l/min ≈ 1 gal/min ≈ 0.0167 gal/s. 67 | ``` 68 | 69 | Please make sure you consult the [documentation](https://github.com/sekdiy/FlowMeter/wiki/Properties) in order to further understand how the library works. 70 | 71 | ## Should you calibrate your own sensor? 72 | 73 | The **FlowMeter** library can be used with many different flow sensors right away. Some [sensor examples](https://github.com/sekdiy/FlowMeter/wiki/Sensors) are listed in the documentation. 74 | 75 | For many projects you don't need to worry about calibration. But it still makes sense to be aware of the limitations that come with an uncalibrated sensor in a metering application. 76 | 77 | It's easy to calibrate yourself. Preferrably you'd do this after installing the sensor into your project. The flow meter then benefits from increased precision within the viscosity and flow range of your application. 78 | 79 | There's a [complete how-to](https://github.com/sekdiy/FlowMeter/wiki/Calibration) in the documentation. 80 | 81 | ![Calibration Example: Irrigation with FS400A](https://github.com/sekdiy/FlowMeter/wiki/images/FS400A-calibration.jpg) 82 | 83 | ## Documentation 84 | 85 | For further details please take a look at the **FlowMeter** [documentation pages](https://github.com/sekdiy/FlowMeter/wiki). 86 | 87 | Also, the library source code (in the folder [`src/`](src/)) and the examples (in the folder [`examples/`](examples/)) are fully documented. 88 | -------------------------------------------------------------------------------- /examples/Calibration/Calibration.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include // https://github.com/sekdiy/FlowMeter 3 | 4 | // enter your own sensor properties here, including calibration points 5 | FlowSensorProperties MySensor = {60.0f, 5.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; 6 | FlowMeter *Meter; 7 | 8 | // timekeeping variables 9 | long period = 1000; // one second (in milliseconds) 10 | long lastTime = 0; 11 | 12 | // define an 'interrupt service routine' (ISR) 13 | void MeterISR() { 14 | // let our flow meter count the pulses 15 | Meter->count(); 16 | } 17 | 18 | void setup() { 19 | // prepare serial communication 20 | Serial.begin(115200); 21 | 22 | // get a new FlowMeter instance for an uncalibrated flow sensor on pin 2 23 | Meter = new FlowMeter(2, MySensor, MeterISR, RISING); 24 | } 25 | 26 | void loop() { 27 | // do some timekeeping 28 | long currentTime = millis(); 29 | long duration = currentTime - lastTime; 30 | 31 | // wait between display updates 32 | if (duration >= period) { 33 | 34 | // process the counted ticks 35 | Meter->tick(duration); 36 | 37 | // output some measurement result 38 | Serial.println("FlowMeter - current flow rate: " + String(Meter->getCurrentFlowrate()) + " l/min, " + 39 | "nominal volume: " + String(Meter->getTotalVolume()) + " l, " + 40 | "compensated error: " + String(Meter->getCurrentError()) + " %, " + 41 | "duration: " + String(Meter->getTotalDuration() / 1000) + " s."); 42 | 43 | // prepare for next cycle 44 | lastTime = currentTime; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/Multi/Multi.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include // https://github.com/sekdiy/FlowMeter 3 | 4 | // connect a flow meter to an interrupt pin (see notes on your Arduino model for pin numbers) 5 | FlowMeter *Meter1; 6 | FlowMeter *Meter2; 7 | 8 | // set the measurement update period to 1s (1000 ms) 9 | const unsigned long period = 1000; 10 | 11 | // define an 'interrupt service handler' (ISR) for every interrupt pin you use 12 | void Meter1ISR() { 13 | // let our flow meter count the pulses 14 | Meter1->count(); 15 | } 16 | 17 | // define an 'interrupt service handler' (ISR) for every interrupt pin you use 18 | void Meter2ISR() { 19 | // let our flow meter count the pulses 20 | Meter2->count(); 21 | } 22 | 23 | void setup() { 24 | // prepare serial communication 25 | Serial.begin(115200); 26 | 27 | // get a new FlowMeter instance for an uncalibrated flow sensor and let them attach their 'interrupt service handler' (ISR) on every rising edge 28 | Meter1 = new FlowMeter(2, UncalibratedSensor, Meter1ISR, RISING); 29 | 30 | // do this setup step for every FlowMeter and ISR you have defined, depending on how many you need 31 | Meter2 = new FlowMeter(3, UncalibratedSensor, Meter2ISR, RISING); 32 | } 33 | 34 | void loop() { 35 | // wait between output updates 36 | delay(period); 37 | 38 | // update the flow measurement calculations 39 | Meter1->update(period); 40 | Meter2->update(period); 41 | 42 | // fetch and output some measurement results 43 | Serial.println("Meter 1 currently " + String(Meter1->getCurrentFlowrate()) + " l/min, " + String(Meter1->getTotalVolume())+ " l total."); 44 | Serial.println("Meter 2 currently " + String(Meter2->getCurrentFlowrate()) + " l/min, " + String(Meter2->getTotalVolume())+ " l total."); 45 | 46 | // 47 | // any other code can go here 48 | // 49 | } 50 | -------------------------------------------------------------------------------- /examples/Simple/Simple.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include // https://github.com/sekdiy/FlowMeter 3 | 4 | // connect a flow meter to an interrupt pin (see notes on your Arduino model for pin numbers) 5 | FlowMeter *Meter; 6 | 7 | // set the measurement update period to 1s (1000 ms) 8 | const unsigned long period = 1000; 9 | 10 | // define an 'interrupt service handler' (ISR) for every interrupt pin you use 11 | void MeterISR() { 12 | // let our flow meter count the pulses 13 | Meter->count(); 14 | } 15 | 16 | void setup() { 17 | // prepare serial communication 18 | Serial.begin(115200); 19 | 20 | // get a new FlowMeter instance for an uncalibrated flow sensor on pin 2 21 | Meter = new FlowMeter(2, UncalibratedSensor, MeterISR, RISING); 22 | } 23 | 24 | void loop() { 25 | // wait between output updates 26 | delay(period); 27 | 28 | // update the flow measurement calculations 29 | Meter->update(period); 30 | 31 | // fetch and output some measurement results 32 | Serial.println("Currently " + String(Meter->getCurrentFlowrate()) + " l/min, " + String(Meter->getTotalVolume())+ " l total."); 33 | 34 | // 35 | // any other code can go here 36 | // 37 | } 38 | -------------------------------------------------------------------------------- /examples/Simulator/Simulator.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include // https://github.com/sekdiy/FlowMeter 3 | 4 | // let's provide our own sensor properties, including calibration points for error correction 5 | FlowSensorProperties MySensor = {60.0f, 4.5f, {1.2, 1.1, 1.05, 1, 1, 1, 1, 0.95, 0.9, 0.8}}; 6 | FlowMeter *Meter; 7 | 8 | void setup() { 9 | // prepare serial communication 10 | Serial.begin(115200); 11 | 12 | // let's pretend there's a calibrated flow sensor connected to pin 3 13 | Meter = new FlowMeter(3, MySensor); 14 | } 15 | 16 | void loop() { 17 | // randomly change simulation period and pulse rate 18 | long frequency = random(MySensor.capacity * MySensor.kFactor); // Hz 19 | long period = random(3 * frequency, 5000); // ms 20 | 21 | // simulate random flow meter pulses within a random period 22 | for (long i = 0; i < (long) ((float) period * (float) frequency / 1000.0f); i++) { 23 | Meter->count(); 24 | } 25 | 26 | // wait that random period 27 | delay(period); 28 | 29 | // update the flow measurement calculations 30 | Meter->update(period); 31 | 32 | // fetch and output some measurement results 33 | Serial.println("FlowMeter - period: " + String(Meter->getCurrentDuration() / 1000.0f, 3) + "s, " + 34 | "frequency: " + String(Meter->getCurrentFrequency(), 0) + "Hz, " + 35 | "volume: " + Meter->getCurrentVolume() + "l, " + 36 | "flow rate: " + Meter->getCurrentFlowrate() + "l/min, " + 37 | "error: " + Meter->getCurrentError() + "%, " + 38 | "total duration: " + Meter->getTotalDuration() / 1000.0f + "s, " + 39 | "total volume: " + Meter->getTotalVolume() + "l, " + 40 | "average flow rate: " + Meter->getTotalFlowrate() + "l/min, " + 41 | "average error: " + Meter->getTotalError() + "%."); 42 | } 43 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For FlowMeter 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | FlowMeter KEYWORD1 10 | FlowSensorProperties KEYWORD1 11 | FlowSensorCalibration KEYWORD1 12 | 13 | ####################################### 14 | # Methods and Functions (KEYWORD2) 15 | ####################################### 16 | 17 | getPin KEYWORD2 18 | getCurrentFlowrate KEYWORD2 19 | getCurrentFrequency KEYWORD2 20 | getCurrentVolume KEYWORD2 21 | getCurrentDuration KEYWORD2 22 | getCurrentError KEYWORD2 23 | 24 | getTotalVolume KEYWORD2 25 | getTotalDuration KEYWORD2 26 | getTotalFlowrate KEYWORD2 27 | getTotalError KEYWORD2 28 | 29 | setTotalVolume KEYWORD2 30 | setTotalDuration KEYWORD2 31 | setTotalError KEYWORD2 32 | 33 | tick KEYWORD2 34 | count KEYWORD2 35 | reset KEYWORD2 36 | 37 | ####################################### 38 | # Instances (KEYWORD2) 39 | ####################################### 40 | 41 | UncalibratedSensor KEYWORD2 42 | FS300A KEYWORD2 43 | FS400A KEYWORD2 44 | 45 | ####################################### 46 | # Constants (LITERAL1) 47 | ####################################### 48 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "FlowMeter", 3 | "version": "1.3.0", 4 | "description": "An Arduino flow meter library that provides calibrated liquid flow and volume measurement with flow sensors.", 5 | "keywords": "Sensors, Flowmeter", 6 | "repository": 7 | { 8 | "type": "git", 9 | "url": "https://github.com/sekdiy/FlowMeter.git" 10 | }, 11 | "authors": 12 | [ 13 | { 14 | "name": "sekdiy", 15 | "email": "sek@sekdiy.de", 16 | "maintainer": true 17 | } 18 | ], 19 | "examples": 20 | [ 21 | { 22 | "name": "Simple", 23 | "base": "examples/Simple", 24 | "files": ["Simple.cpp"] 25 | }, 26 | { 27 | "name": "Multi", 28 | "base": "examples/Multi", 29 | "files": ["Multi.cpp"] 30 | }, 31 | { 32 | "name": "Simulator", 33 | "base": "examples/Simulator", 34 | "files": ["Simulator.cpp"] 35 | }, 36 | { 37 | "name": "Calibration", 38 | "base": "examples/Calibration", 39 | "files": ["Calibration.cpp"] 40 | } 41 | ], 42 | "export": 43 | { 44 | "exclude": ["wiki"] 45 | }, 46 | "license": "MIT", 47 | "frameworks": "arduino", 48 | "platforms": "*" 49 | } 50 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=FlowMeter 2 | version=1.3.0 3 | author=sekdiy 4 | maintainer=sekdiy 5 | sentence=An Arduino flow meter library that provides calibrated liquid flow and volume measurement with flow sensors. 6 | paragraph=This Flow Meter library is primarily intended for use with impeller flow sensors, although other types of sensors could be made to work. 7 | category=Sensors 8 | url=https://github.com/sekdiy/FlowMeter 9 | architectures=* 10 | includes=FlowMeter.h 11 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = simple 13 | 14 | [env] 15 | framework = arduino 16 | monitor_speed = 115200 17 | platform = atmelavr 18 | board = sparkfun_promicro16 19 | 20 | [env:simple] 21 | build_src_filter = ${env.build_src_filter} +<../examples/Simple/> 22 | 23 | [env:multi] 24 | build_src_filter = ${env.build_src_filter} +<../examples/Multi/> 25 | 26 | [env:simulator] 27 | build_src_filter = ${env.build_src_filter} +<../examples/Simulator/> 28 | 29 | [env:calibration] 30 | build_src_filter = ${env.build_src_filter} +<../examples/Calibration/> 31 | -------------------------------------------------------------------------------- /src/FlowMeter.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Flow Meter 3 | * 4 | * An Arduino flow meter library that provides calibrated liquid flow and volume measurement with flow sensors. 5 | * 6 | * @author sekdiy (https://github.com/sekdiy/FlowMeter) 7 | * @date 14.07.2015 Initial release. 8 | * @version See git comments for changes. 9 | */ 10 | 11 | #include 12 | #include "Arduino.h" 13 | #include "FlowMeter.h" // https://github.com/sekdiy/FlowMeter 14 | 15 | FlowMeter::FlowMeter(unsigned int pin, FlowSensorProperties prop, void (*callback)(void), uint8_t interruptMode) : 16 | _pin(pin), // store pin number 17 | _properties(prop), // store sensor properties 18 | _interruptCallback(callback), 19 | _interruptMode(interruptMode) 20 | { 21 | pinMode(this->_pin, INPUT_PULLUP); // initialize interrupt pin as input with pullup 22 | 23 | if (this->_interruptCallback != NULL) { // if ISR callback has been provided, attach it 24 | attachInterrupt(digitalPinToInterrupt(this->_pin), this->_interruptCallback, this->_interruptMode); 25 | } 26 | 27 | this->reset(); // ignore pulses generated during initialisation 28 | } 29 | 30 | FlowMeter::~FlowMeter() { 31 | if (this->_interruptCallback != NULL) { // if ISR callback has been provided, detach it 32 | detachInterrupt(digitalPinToInterrupt(this->_pin)); 33 | } 34 | } 35 | 36 | double FlowMeter::getCurrentFlowrate() { 37 | return this->_currentFlowrate; // in l/min 38 | } 39 | 40 | double FlowMeter::getCurrentVolume() { 41 | return this->_currentVolume; // in l 42 | } 43 | 44 | double FlowMeter::getTotalFlowrate() { 45 | return this->_totalVolume / (this->_totalDuration / 1000.0f) * 60.0f; // in l/min 46 | } 47 | 48 | double FlowMeter::getTotalVolume() { 49 | return this->_totalVolume; // in l 50 | } 51 | 52 | /** 53 | * The update method updates all internal calculations at the end of a measurement period. 54 | * 55 | * We're calculating flow and volume data over time. 56 | * The actual pulses have to be sampled using the count method (i.e. via an interrupt service routine). 57 | *s 58 | * Flow sensor formulae: 59 | * 60 | * Let K: pulses per second per unit of measure (i.e. (1/s)/(l/min)), 61 | * f: pulse frequency (1/s), 62 | * Q: flow rate (l/min), 63 | * p: sensor pulses (no dimension/unit), 64 | * t: time since last measurements (s). 65 | * 66 | * K = f / Q | units: (1/s) / (l/min) = (1/s) / (l/min) 67 | * <=> | Substitute p / t for f in order to allow for different measurement intervals 68 | * K = (p / t) / Q | units: ((1/s)/(l/min)) = (1/s) / (l/min) 69 | * <=> | Solve for Q: 70 | * Q = (p / t) / K | untis: l/min = 1/s / (1/s / (l/min)) 71 | * <=> | Volume in l: 72 | * V = Q / 60 | units: l = (l/min) / (min) 73 | * 74 | * The property K is sometimes stated in pulses per liter or pulses per gallon. 75 | * In these cases the unit of measure has to be converted accordingly (e.g. from gal/s to l/min). 76 | * See file G34_Flow_rate_to_frequency.jpg for reference. 77 | * 78 | * @param duration The update duration (in ms). 79 | */ 80 | void FlowMeter::update(unsigned long duration) { 81 | /* sampling */ 82 | noInterrupts(); // going to change interrupt variable(s) 83 | volatile unsigned long pulses = this->_currentPulses; // sample current pulses from counter 84 | this->_currentPulses = 0; // reset pulse counter after successful sampling 85 | interrupts(); // done changing interrupt variable(s) 86 | 87 | /* normalisation */ 88 | double seconds = duration / 1000.0f; // normalised duration (in s, i.e. per 1000ms) 89 | double frequency = pulses / seconds; // normalised frequency (in 1/s) 90 | 91 | /* determine current correction factor (from sensor properties) */ 92 | unsigned int decile = floor(10.0f * frequency / (this->_properties.capacity * this->_properties.kFactor)); // decile of current flow relative to sensor capacity 93 | unsigned int ceiling = 9; // highest possible decile index 94 | this->_currentCorrection = this->_properties.kFactor / this->_properties.mFactor[min(decile, ceiling)]; // combine constant k-factor and m-factor for decile 95 | 96 | /* update current calculations: */ 97 | this->_currentFlowrate = frequency / this->_currentCorrection; // get flow rate (in l/min) from normalised frequency and combined correction factor 98 | this->_currentVolume = this->_currentFlowrate / (60.0f/seconds); // get volume (in l) from normalised flow rate and normalised time 99 | 100 | /* update statistics: */ 101 | this->_currentDuration = duration; // store current update duration (convenience, in ms) 102 | this->_currentFrequency = frequency; // store current pulses per second (convenience, in 1/s) 103 | this->_totalDuration += duration; // accumulate total duration (in ms) 104 | this->_totalVolume += this->_currentVolume; // accumulate total volume (in l) 105 | this->_totalCorrection += this->_currentCorrection * duration; // accumulate corrections over time 106 | } 107 | 108 | void FlowMeter::count() { 109 | this->_currentPulses++; // this should be called from an interrupt service routine 110 | } 111 | 112 | void FlowMeter::reset() { 113 | noInterrupts(); // going to change interrupt variable(s) 114 | this->_currentPulses = 0; // reset pulse counter 115 | interrupts(); // done changing interrupt variable(s) 116 | 117 | this->_currentFrequency = 0.0f; 118 | this->_currentDuration = 0.0f; 119 | this->_currentFlowrate = 0.0f; 120 | this->_currentVolume = 0.0f; 121 | this->_currentCorrection = 0.0f; 122 | } 123 | 124 | unsigned int FlowMeter::getPin() { 125 | return this->_pin; 126 | } 127 | 128 | unsigned long FlowMeter::getCurrentDuration() { 129 | return this->_currentDuration; // in ms 130 | } 131 | 132 | double FlowMeter::getCurrentFrequency() { 133 | return this->_currentFrequency; // in 1/s 134 | } 135 | 136 | double FlowMeter::getCurrentError() { 137 | // error (in %) = error * 100 138 | // error = correction rate - 1 139 | // correction rate = k-factor / correction 140 | return (this->_properties.kFactor / this->_currentCorrection - 1) * 100; // in % 141 | } 142 | 143 | unsigned long FlowMeter::getTotalDuration() { 144 | return this->_totalDuration; // in ms 145 | } 146 | 147 | double FlowMeter::getTotalError() { 148 | // average error (in %) = average error * 100 149 | // average error = average correction rate - 1 150 | // average correction rate = k-factor / corrections over time * total time 151 | return (this->_properties.kFactor / this->_totalCorrection * this->_totalDuration - 1) * 100; 152 | } 153 | 154 | FlowMeter* FlowMeter::setTotalDuration(unsigned long totalDuration) { 155 | this->_totalDuration = totalDuration; 156 | return this; 157 | } 158 | 159 | FlowMeter* FlowMeter::setTotalVolume(double totalVolume) { 160 | this->_totalVolume = totalVolume; 161 | return this; 162 | } 163 | 164 | FlowMeter* FlowMeter::setTotalCorrection(double totalCorrection) { 165 | this->_totalCorrection = totalCorrection; 166 | return this; 167 | } 168 | -------------------------------------------------------------------------------- /src/FlowMeter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Flow Meter 3 | * 4 | * An Arduino flow meter library that provides calibrated liquid flow and volume measurement with flow sensors. 5 | * 6 | * @author sekdiy (https://github.com/sekdiy/FlowMeter) 7 | * @date 14.07.2015 Initial release. 8 | * @version See git comments for changes. 9 | */ 10 | 11 | #ifndef _FLOWMETER_H_ 12 | #define _FLOWMETER_H_ 13 | 14 | #include "FlowSensorProperties.h" 15 | #include "FlowSensorCalibration.h" 16 | 17 | /** 18 | * FlowMeter 19 | */ 20 | class FlowMeter { 21 | public: 22 | /** 23 | * Initializes a new flow meter object. 24 | * 25 | * @param pin The pin that the flow sensor is connected to (has to be interrupt capable, default: INT0). 26 | * @param prop The properties of the actual flow sensor being used (default: UncalibratedSensor). 27 | * @param callback The interrupt callback handler 28 | */ 29 | FlowMeter(unsigned int pin = 2, FlowSensorProperties prop = UncalibratedSensor, void (*callback)(void) = NULL, uint8_t interruptMode = RISING); 30 | ~FlowMeter(); // Cleans up a flow meter object. 31 | 32 | double getCurrentFlowrate(); // Returns the current flow rate since last tick (in l/min). 33 | double getCurrentVolume(); // Returns the current volume since last tick (in l). 34 | 35 | double getTotalFlowrate(); // Returns the (linear) average flow rate in this flow meter instance (in l/min). 36 | double getTotalVolume(); // Returns the total volume flown trough this flow meter instance (in l). 37 | 38 | void update(unsigned long duration = 1000); // Updates all internal calculations at the end of a measurement period. 39 | void count(); // Increments the internal pulse counter. Serves as an interrupt callback routine. 40 | void reset(); // Prepares the flow meter for a fresh measurement. Resets all current values, but not the totals. 41 | 42 | /** 43 | * deprecated methods 44 | */ 45 | 46 | void tick(unsigned long duration = 1000) { update(duration); } 47 | 48 | /* 49 | * setters enabling continued metering across power cycles 50 | */ 51 | 52 | FlowMeter* setTotalDuration(unsigned long totalDuration); // Sets the total (overall) duration (i.e. after power up). 53 | FlowMeter* setTotalVolume(double totalVolume); // Sets the total (overall) volume (i.e. after power up). 54 | FlowMeter* setTotalCorrection(double totalCorrection); // Sets the total (overall) correction factor (i.e. after power up). 55 | 56 | /* 57 | * convenience methods and calibration helpers 58 | */ 59 | 60 | unsigned int getPin(); // Returns the Arduino pin number that the flow sensor is connected to. 61 | 62 | unsigned long getCurrentDuration(); // Returns the duration of the current tick (in ms). 63 | double getCurrentFrequency(); // Returns the pulse rate in the current tick (in 1/s). 64 | double getCurrentError(); // Returns the error resulting from the current measurement (in %). 65 | 66 | unsigned long getTotalDuration(); // Returns the total run time of this flow meter instance (in ms). 67 | double getTotalError(); // Returns the (linear) average error of this flow meter instance (in %). 68 | 69 | protected: 70 | unsigned int _pin; // connection pin (has to be interrupt capable!) 71 | FlowSensorProperties _properties; // sensor properties (including calibration data) 72 | void (*_interruptCallback)(void); // interrupt callback 73 | uint8_t _interruptMode; // interrupt mode (LOW, CHANGE, RISING, FALLING, HIGH) 74 | 75 | unsigned long _currentDuration; // current tick duration (convenience, in ms) 76 | double _currentFrequency; // current pulses per second (convenience, in 1/s) 77 | double _currentFlowrate = 0.0f; // current flow rate (in l/tick), e.g.: 1 l / min = 1 pulse / s / (pulses / s / l / min) 78 | double _currentVolume = 0.0f; // current volume (in l), e.g.: 1 l = 1 (l / min) / (60 * s) 79 | double _currentCorrection; // currently applied correction factor 80 | 81 | unsigned long _totalDuration = 0.0f; // total measured duration since begin of measurement (in ms) 82 | double _totalVolume = 0.0f; // total volume since begin of measurement (in l) 83 | double _totalCorrection = 0.0f; // accumulated correction factors 84 | 85 | volatile unsigned long _currentPulses = 0; // pulses within current sample period 86 | }; 87 | 88 | #endif // _FLOWMETER_H_ 89 | -------------------------------------------------------------------------------- /src/FlowSensorCalibration.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Flow Meter 3 | * 4 | * An Arduino flow meter library that provides calibrated liquid flow and volume measurement with flow sensors. 5 | * 6 | * @author sekdiy (https://github.com/sekdiy/FlowMeter) 7 | * @date 14.07.2024 Initial release. 8 | * @version See git comments for changes. 9 | */ 10 | 11 | #ifndef _FLOWSENSORCALIBRATION_H_ 12 | #define _FLOWSENSORCALIBRATION_H_s 13 | 14 | #include "FlowSensorProperties.h" 15 | 16 | /** 17 | * FlowSensorCalibration 18 | * 19 | * Convenience class for manipulating sensor properties. 20 | */ 21 | class FlowSensorCalibration { 22 | public: 23 | FlowSensorCalibration() {}; 24 | FlowSensorCalibration(FlowSensorProperties properties): _properties(properties) {}; 25 | 26 | FlowSensorCalibration* setProperties(FlowSensorProperties properties) { 27 | this->_properties = properties; 28 | return this; 29 | }; 30 | 31 | FlowSensorCalibration* setCapacity(double capacity) { 32 | this->_properties.capacity = capacity; 33 | return this; 34 | } 35 | 36 | FlowSensorCalibration* setKFactor(double kFactor) { 37 | this->_properties.kFactor = kFactor; 38 | return this; 39 | } 40 | 41 | FlowSensorCalibration* setMeterFactorPerDecile(unsigned int decile, unsigned int mFactor) { 42 | this->_properties.mFactor[decile] = mFactor; 43 | return this; 44 | } 45 | 46 | FlowSensorProperties getProperties() { 47 | return this->_properties; 48 | } 49 | 50 | double getCapacity() { 51 | return this->_properties.capacity; 52 | } 53 | 54 | double getKFactor() { 55 | return this->_properties.kFactor; 56 | } 57 | 58 | unsigned int getMeterFactorPerDecile(unsigned int decile) { 59 | return this->_properties.mFactor[decile]; 60 | } 61 | 62 | protected: 63 | FlowSensorProperties _properties; 64 | }; 65 | 66 | #endif // _FLOWSENSORCALIBRATION_H_ 67 | -------------------------------------------------------------------------------- /src/FlowSensorProperties.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Flow Meter 3 | * 4 | * An Arduino flow meter library that provides calibrated liquid flow and volume measurement with flow sensors. 5 | * 6 | * @author sekdiy (https://github.com/sekdiy/FlowMeter) 7 | * @date 14.07.2015 Initial release. 8 | * @version See git comments for changes. 9 | */ 10 | 11 | #include "FlowSensorProperties.h" 12 | 13 | FlowSensorProperties UncalibratedSensor = {60.0f, 5.0f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; 14 | FlowSensorProperties FS300A = {60.0f, 5.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; 15 | FlowSensorProperties FS400A = {60.0f, 4.8f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}; 16 | FlowSensorProperties FS400A_cal = {30.0f, 4.25f, {1.01695, 1.01695, 1.01695, 1.01695, 1, 0.99767, 0.99767, 0.99767, 1, 1.01695}}; 17 | FlowSensorProperties FHKCS_1mm_0deg = {0.4, 39.7, {1.0, 0.995, 0.9925, 0.992, 0.9925, 0.994, 0.995, 0.9975, 1.00125, 1.006}}; 18 | -------------------------------------------------------------------------------- /src/FlowSensorProperties.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Flow Meter 3 | * 4 | * An Arduino flow meter library that provides calibrated liquid flow and volume measurement with flow sensors. 5 | * 6 | * @author sekdiy (https://github.com/sekdiy/FlowMeter) 7 | * @date 14.07.2015 Initial release. 8 | * @version See git comments for changes. 9 | */ 10 | 11 | #ifndef __FLOWSENSORPROPERTIES_H_ 12 | #define __FLOWSENSORPROPERTIES_H_ 13 | 14 | /** 15 | * FlowSensorProperties 16 | * 17 | * Structure that holds essential information about a flow sensor. 18 | * Stores general sensor properties and calibration points. 19 | * 20 | * See file G34_Flow_rate_to_frequency.jpg for reference. 21 | */ 22 | typedef struct { 23 | double capacity; // capacity, upper limit of flow rate (in l/min) 24 | double kFactor; // "k-factor" (in (pulses/s) / (l/min)), e.g.: 1 pulse/s = kFactor * l/min 25 | double mFactor[10]; // multiplicative correction factor near unity, "meter factor" (per decile of flow) 26 | } FlowSensorProperties; 27 | 28 | extern FlowSensorProperties UncalibratedSensor; // default sensor 29 | extern FlowSensorProperties FS300A; // see documentation about FS300A/SEN02141B 30 | extern FlowSensorProperties FS400A; // see documentation about FS400A/USN-HS10TA 31 | extern FlowSensorProperties FS400A_cal; // see https://github.com/sekdiy/FlowMeter/wiki/Calibration#2-calibration-example-irrigation-with-fs400a 32 | extern FlowSensorProperties FHKCS_1mm_0deg; // see https://github.com/sekdiy/FlowMeter/wiki/Calibration#3-calibration-example-digimesa-fhksc 33 | 34 | #endif // __FLOWSENSORPROPERTIES_H_ 35 | --------------------------------------------------------------------------------