Home » IoT & Connected Hardware Resources » Designing and Building a Ventilator Flow Sensor from Home, Pt. 3

Designing and Building a Ventilator Flow Sensor from Home, Pt. 3

This is part three of the Designing an Intelligent IoT Ventilator from Home series

yellow cylinder illustration
Animation of v32 cutaway

There’s a Flow Sensor Shortage

In working on the Pressurizer proto-ventilator, one thing quickly became clear: There are two minimal metrics that need to be known at all times: pressure and volumetric flow.

Pressure we already measure, and in part two we discussed how to use a PID and a pressure sensor to make a minimal pressure-mode ventilator.

The next step is to add a flow sensor and do something clever with two PIDs in tandem to make a volume-mode or patient-initiated-mode ventilator, and that is what this blog post was supposed to be about. (And we’ll come back to that, and maybe it’ll even be as clever as promised.) However, we ran into a problem pretty fast: air-flow sensors are suddenly in short supply and high demand.

This isn’t really surprising. Ventilators are in high demand and in short supply, that’s the point of these blog posts, and pressure sensors are difficult to source as we mentioned in the previous post. The good news is that pressure sensors are still available in small quantities and seem to be more available every day.

The question then becomes: Can we make a flow sensor out of other sensors that are available along with some 3D-printer or CNC machined parts?

The answer turned out to be yes! And it’s even more affordable than the flow sensors that are unavailable.

Jumping Ahead: The Result and How to Use It

small black fitting with sensors wires connected on the side

We’ll come back to how we got to this result, but for those who genuinely don’t care (which is a shame, there are some fun animations below), the 3D printable parts can be found here in the g2core Pressurizer repo, the simulations of various designs can be viewed here on SimScale with a free account (many thanks to them for providing that service and providing free support).

With two I2C sensors and the printed piece, one can create a full flow sensor with temperature and pressure gauge as well, suitable for use on a ventilator or for testing/verifying functionality of a ventilator. That happens to be exactly the point of a project we’ve been working with called VentMon by Public Invention. To that end, one of the intended uses of this flow sensor is to become a backup option for VentMon in case the flow sensors they are using stay unavailable or aren’t available regionally for some.

For the sake of proof-of-concept, and to provide a simple testing and calibration mechanism, we created a python script that reads the sensors, calculates the flow and pressure, and logs the results. That along with some SparkFun Qwiic connectors and an pHat adapter board connected to a Raspberry Pi, and we were able to make a fully usable flow logger. It was the next logical step to make it log PIRDS to the VenMon data lake, so now it can log flow and pressure like a VentMon. (It’s missing some other metrics provided by a VentMon, however.)

Flow Sensor Requirements

Now to go back to how we got here. Before we could get started designing a flow sensor, we needed to have an idea of what capabilities and properties one needed to have. We’re also incorporating the gauge pressure sensor into the same mechanism in order to reduce the total part count.

We start with a list like the following:

  • We need to use pressure sensors that were readily available and easy to interface with:
    • For our purposes, this further is limited to those that had SPI or I2C interfaces with built-in temperature compensation to drastically reduce the amount of calibration required and remove the need for an analog circuit and all the issues involved in analog-to-digital conversion.
    • The design needs to not be tied to a specific form-factor of the sensor (hardware) and the software needs to be able to handle multiple sensor types with different interfaces, in case one type becomes unavailable, the flow sensor can still be sourced and made.
  • For this application, the pressure sensor needs to be capable of reading up to at least 60 cmH2O gauge (relative to atmospheric)
    • Many sensors are sold with limits expressed in PSI, and 1 PSI = 70.3 cmH2O, so 1 PSI is acceptable
  • We should be able to measure flow up to at 250SLM (standard liters per minute)
  • The flow measurement should be bidirectional and symmetric – we need to be able to sense inhalations as well as exhalations
  • Price isn’t a primary factor, but generally, with all other things being equal, less expensive parts should be preferred over more expensive ones
    • Things are never equal, so the technical difference between less expensive parts and more expensive parts needs to be carefully evaluated and determine what is considered acceptable
  • Mechanical connections for “breathing systems” according to ISO 5356 – basically 22mm OD tapered down on one end for being on the inside of a mating and 22mm ID tapered up on the other for being on the outside of a mating

Overall, this is a subset of a typical list of things to consider when sourcing parts for a hardware device, where you weigh the balance of price, availability, and suitability.

How We Got to This Design

There are several means of measuring flow. We carefully considered many of them and may even reevaluate a couple of them. What stood out fairly quickly is that Venturi-style differential pressure measurement seemed to be the most approachable from a mechanical standpoint (relatively simple geometric requirements, no moving parts) as well as an electrical/software one (differential pressure sensors are available in the same families as the gauge pressure sensors we’re already using).

venturi tube design illustration

A Venturi tube design (shown above) is rather easily created and tested, and mostly work fairly well, but it reveals an immediate problem: it’s unidirectional, meaning we could detect inhalation but not exhalation. It could be made into a bi-directional design with some clever arrangement of valves or with two differential pressure sensors, but that adds complexity that’s best avoided, so we looked on. There are a few things we learned in the process:

  • 3D Printing with FFF (Fused filament fabrication, the technology used by most inexpensive desktop 3D printers) is a viable option
  • The pressure-sensing “ports” are subject to hyperlocal pressure caused by airflow eddies, making the measurement particularly noisy, and we found a few references to using a “pressure ring” around the surface of the measurement zone to provide a pressure-equalized region that is outside of the flow
  • It is at this point that we learned about ISO 5356
colorful orifice plate flow sensor illustration

After a few iterations, we tried an orifice-plate inspired design, which is like a Venturi tube in that it also utilizes the Venturi effect, but is bidirectional. Instead of measuring the pressure directly in the middle of the constricted region, it measures the pressure in the recirculation zone of the vena contracta, which is the low-pressure region directly downstream of the orifice plate.

Unlike a typical orifice-plate design, we’ve tapered the interior faces of the orifice plate to make it 3D printable (shown below). This worked rather well and was indeed bidirectional, but this design has two problems:

  • With our differential pressure sensor that has a +-1PSI range, we are unable to detect the full range.
  • The measurement is rather noisy, particularly at slower flow rates.
  • Due to the geometry of the orifice-plate design, there’s more energy loss in the system.
orifice plate interior face illustration

So we started looking at the math for an orifice-plate, which also uses Bernoulli’s principle to create a pressure differential, like in a Venturi tube, but instead of measuring the least-restrictive and most restrictive areas directly, it measures pressure regions before and after a “plate” with a precisely sized restriction in it. The formula for the flow based on the pressure differential is isolated as K, which is computed differently for a Venturi tube than for an orifice-plate.

For the orifice plate, according to the Wikipedia article above:

flow based on the pressure differential formula

With Dt and Du being the inner diameter of the tube at the throat and upper regions, and At being the area of the throat, dens being the density of the air flowing through the tube, and Cdis being the discharge coefficient that’s unique to the geometry of the device (and mostly what we’re looking for when calibrating), and should be roughly between 0.6 and 0.85. We multiply K by 1000 to convert the meters3 to liters.

The formula, with K, is then:

flow equation

Where d is the measured pressure differential, in SLM. So if we chart that out (see interactive chart below) with the flow (in liters/min, SLM) on the Y-axis and the range we want to cover (+-250SLM) marked, then set the upper region inner diameter to 20mm (to leave room for the O.D. to be 22mm), and we add a few marks for common pressure sensor ranges (0.57psi and 1ps, 10psi is visible if you zoom out) you can see the shape of the input (on Y, in SLM) vs output (on X, in pascals).


var parameters = { “id”: “ggbApplet”, “width”:700, “height”:600, “showMenuBar”:false, “showAlgebraInput”:false, “showToolBar”:false, “customToolBar”:”0 73 62 | 1 501 67 , 5 19 , 72 75 76 | 2 15 45 , 18 65 , 7 37 | 4 3 8 9 , 13 44 , 58 , 47 | 16 51 64 , 70 | 10 34 53 11 , 24 20 22 , 21 23 | 55 56 57 , 12 | 36 46 , 38 49 50 , 71 14 68 | 30 29 54 32 31 33 | 25 17 26 60 52 61 | 40 41 42 , 27 28 35 , 6″, “showToolBarHelp”:false, “showResetIcon”:false, “enableLabelDrags”:false, “enableShiftDragZoom”:true, “enableRightClick”:false, “errorDialogsActive”:false, “useBrowserForJS”:false, “allowStyleBar”:false, “preventFocus”:false, “showZoomButtons”:true, “capturingThreshold”:3, // add code here to run when the applet starts “appletOnLoad”:function(api){ /* api.evalCommand(‘Segment((1,2),(3,4))’);*/ }, “showFullscreenButton”:true, “scale”:1, “disableAutoScale”:false, “allowUpscale”:false, “clickToLoad”:false, “appName”:”classic”, “showSuggestionButtons”:true, “buttonRounding”:0.7, “buttonShadows”:false, “language”:”en”, // use this instead of ggbBase64 to load a material from geogebra.org // “material_id”:”RHYH3UQ8″, // use this instead of ggbBase64 to load a .ggb file // “filename”:”myfile.ggb”, “ggbBase64″:”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”, }; // is3D=is 3D applet using 3D view, AV=Algebra View, SV=Spreadsheet View, CV=CAS View, EV2=Graphics View 2, CP=Construction Protocol, PC=Probability Calculator DA=Data Analysis, FI=Function Inspector, macro=Macros var views = {‘is3D’: 0,’AV’: 0,’SV’: 0,’CV’: 0,’EV2′: 0,’CP’: 0,’PC’: 0,’DA’: 0,’FI’: 0,’macro’: 0}; var applet = new GGBApplet(parameters, ‘5.0’, views); window.onload = function() {applet.inject(‘ggbApplet’)}; applet.setPreviewImage(‘data:image/gif;base64,R0lGODlhAQABAAAAADs=’,’https://www.geogebra.org/images/GeoGebra_loading.png’,’https://www.geogebra.org/images/applet_play.png’);

The above is interactive! Drag the sliders to test different parameters. It can also be viewed on the GeoGebra site.

One thing we can see with the throat diameter set to 10mm as in our previous design: the line is way too flat. All sensors are noisy, including these ones we’re using, and with the input being exponential then the noise in the lower ranges we end up with the noise reading +-40SLM with no airflow at all! We’re testing with +-1PSI sensors, and with an ideal sense range of slightly above +-250SLM, in order to make better use of our sensor range, we’ll need to lower the throat diameter.

Note that we first assumed a Cdis factor of 0.95, which later turned out to be way too high, which meant that the problem was actually worse than we thought.


With the new knowledge about the input/output curve, changing the diameters to be utilizing the pressure sensor better and having more of the sensor’s range outside of the noise range, we were still seeing a large noise, but at higher flow rates instead of the lower flow rates. So we turned to simulation to figure out what’s going on.

After some research about Computational Fluid Dynamics (CFD) packages available we found that SimScale was a good combination of approachable (web-based with a reasonable UI and documentation() and affordable (free, if you don’t mind your projects being publicly available). 

After a brief learning curve (where the SimScale in-product support chat helped out), we were able to get our models from Fusion to SimScale and the air-flow simulated. As you can see in the following animation there is a significant oscillation caused by an eddy in the region of the upstream half of the sensor inlet.

Model 1 simulation animated, highlighting the problematic pressure pulse

Another upside of simulation is that testing different geometries took minutes each. Even with rapid prototyping – it takes 2 ½ hours to 3D print a test sensor, and more to hook it up and run tests – that’s a fast turnaround.

Now that we’re on design 31 where the pressure sensing ports are closer together and the walls of the pressure plate as steep as possible while still being printable and the pressure plate is thinner (~1mm). The result looks like this:

Model 31 simulation animated, not the lack of pressure pulse

You can see that the flow is smoother and without upstream eddies the problematic pressure pulse. There is a low-pressure region of eddies downstream which is what causes the differential pressure and allows flow measurement, and it appears to be stable.

We believe it would be possible to make this general shape from more common parts such as PVC pipe and a carefully drilled thin plate and achieve similar quality output.

Verification and Calibration

Once we have a reasonable result from simulation, we print a design and (write and) run some monitoring software in Python (found in the repo here). We found two problems analyzing the raw data from the sensors:

  • The noise at no or near no flow is still noisy, but it’s a symmetric noise about a zero point
  • The zero-point is not the zero-output value from the sensor and may be different from sensor-to-sensor

Luckily, there are documented techniques from various vendors (such as this one from Honeywell) about how to handle the zeroing. In our case, we take the average of the first N samples read after the monitor starts (each time) and use that as the zero value, along with very slowly adjusting the zero value when the zero-adjusted value is within a configurable margin. Zeroing is important for the flow measurement as a non-zeroed value shows a “drift” and the integrated volume measurement will grow constantly when there’s actually no flow.

This leaves some signal noise. Analysis of data from the sensor with no flow shows that the noise is nearly symmetric over about 80ms when sampling at 500Hz, luckily without noticeable interference spikes, so we were able to gain an accurate and reasonably smooth signal by using a rolling average over 80ms of raw pressure samples. This creates a delay of roughly 40ms. For additional smoothing, you can add a secondary rolling average over 40ms of samples on the computed flow measurement. This improves the volume measurement noticeably.

So now we need to verify that our simulations are correct and calibrate the coefficients. That sounds like we’ll need some lab equipment, but remember this is being done at home on COVID-19 lockdown. So we need a simple but accurate solution.

Note that we have been given free-of-charge a VentMon by Public Invention, so we have cross-validated our results and testing against that, and it has been very helpful. Although Public Invention is giving away VentMons (and the design is open source) to pandemic response engineers, they are in short supply at the moment (partially for the same reasons we’re making this flow sensor in the first place), you’ll likely have to use the method outlined below or something similar.

What we came up with is indeed simple, but effective. We call it the “2-liter drop test,” and it consists of:

orange five gallon bucket with white tube taped to the side in front of a cardboard box
  • A plastic container (“jug”) that can hold more than 2-liters (1-gallons works nicely) with a lid that’s easily removed quickly yet can hold 2-liters of water without leaking when held upside-down.
  • A hole in the bottom of the jug that’s approximately 21mm in diameter.
  • A length of medical ventilator “circuit” (tubing) – in this case, ordered off of Amazon as CPAP tubing – or anything that can be fashioned to work as one and be air-tight
  • A measuring cup that is marked in metric that goes to at least 2-liters.
  • A few mechanical bits or a friend to hold the jug upside down
  • A place where you can dump 2L of water without damaging anything. A bucket to dump the water in if you wish to reuse it, and maybe to keep the mess down.
  • A funnel

The Setup is Simple:

five gallon orange bucket with inverted translucent jug attached to white tube taped onto the bucket
  1. Put a hole in the bottom of the jug that is just big enough to fit the ventilator tubing in, keeping in mind that we’ll be removing the ventilator tubing to fill the jug
    • Make sure that the tubing is air-tight when in place
  2. Put the sensor in place on the other end of the ventilator tubing – we want to avoid the risk of direct exposure to water
  3. If you don’t have a very patient friend to hold the jug for you, then a stick, some rope or pull-ties, and a 5-gallon bucket will do (see photos below)

The Test:

  1. Before each test, measure as precisely as possible 2L of water, close the lid on the jug, and pour the water into the jug with the funnel, being careful not to lose any
    • Tip: If you lift the funnel slightly the water will not “glug”
  2. Connect the non-sensor end of the ventilation tube is connected into the hole in the bottom of the jug snugly
  3. Start the sensor monitor software running and logging the readings
    • Note what K value you’re using the compute flow rate for this test
  4. Release the water from the jug
    • This will pull 2-liters of air through the hose as the water falls out, unless the jug “glugs,” which means some air came in from the other end. This means that the hose was blocked or kinked. If this happens, make sure air flows freely through the air tube and try again.
  5. Stop the sensor logging and analyze the data
    • You should see that the flow rate, when integrated over time, adds up to 2-liters
    • The calibration is to adjust the K factor so that the flow rate integration is accurately reading 2-liters
  6. Adjust the K factor in the software, and run the test again, until the final reading is correct
    • Be sure to reverse the sensor for half of the tests to ensure it’s reading symmetrically

Here’s a snapshot of one of our calibration runs:

plot chart of a ventilator flow calibration run

The 3D printable parts and other goodies can be found in the GitHub repo for the g2Core Pressurizer.

Next Steps

Now that we have a flow sensor, we can get back to the Pressurizer ventilator project. Specifically, with recent research showing that patient-initiated ventilation is best for COVID use, we’d like to implement that along with a breath-assistance mode.

We’d also like to test and see if it’s practical to make a flow sensor from hardware-store parts such as PVC pipe, as well as to make a version that can be made on a CNC milling machine, both broadening the availability of the flow sensor as well as making it more manufacturable.


Scroll to Top