Spirometer with CO2 Analysis Instructable

The following is an instructable for a spirometer with CO2 analysis capabilities. Spirometry is the measure of volume and speed in which a user can inhale and exhale air. In addition to general spirometer measurements, this spirometer has capnographic abilities. Capnography is the measurement of concentration of CO2 in respiratory gases. Specifically, the spirometer is able to record CO2 expelled in parts per million (up to 50,000 ppm), which is measured from the built in CO2 sensor within the spirometer. The spirometer also records exhale pressure in kilopascals, which is determined via the pressure sensor. These two values are the crux of all the data calculations and analysis by the spirometer, which uses CO2 concentration, pressure, and time in order to determine a user’s instantaneous rate of breathing. From there, the amount of air (in liters) the user can blow out in a singular breath, also known as Forced Vital Capacity (FVC), and the rate of breathing are calculated. With the use of an LED display, the spirometer has the ability to show real-time calculations to the user and operator. It also possesses cloud storage and data streaming abilities via a connected microprocessor.

This spirometer uses Arduino for software (IDE and code/sketch) and hardware (microprocessor and board). To download the Arduino IDE, please refer to this link.

The spirometer also utilizes 3D printed parts. The vast majority of commercially available 3D printers are compatible with this instructable. Ensure beforehand that your 3D printer can open and process .stl files.

Bill of Materials (Unit costs revised July 2020)

Additionally, 2 x 3 Dupont jumper wire housing, basic workbench tools, zip ties, soldering materials, and your 3D printer software/filament are needed to complete this project.

Step 1: IDE and Sketch

We start this instructable by discussing the software of the spirometer and its implementation. For this step, you will need a computer and a stable internet connection.

Section 1A:

First we need to download the program that runs the spirometer. Assuming you already have the Arduino IDE installed, download the sketch from the GitHub repository or download the file Spirometer.ino from the open source Google Drive folder. In order to properly upload or make edits to the sketch through the Arduino IDE, ensure you have enabled the OLED screen support AND ensure the correct boards/ports are selected. The process to check these two items and change their respective settings is detailed below.

Section 1B:

To enable the OLED screen support (or check to make sure it is done), from the menu bar, go to “Sketch” > “Include Library” > “Manage Libraries,” and select “Library Manager” (Figure 1B.1). Once there, search for “U8g2.” If the OLED screen support is not enabled, there will be an “Install” button (Figure 1B.2), in which case you should install the latest version of the library. If you see an “Update” button, make sure to update the U8g2 library installation (Figure 1B.3). The window will also indicate if this library is installed and fully updated (Figure 1B.4). Once the U8g2 library is installed and updated, the OLED screen support is enabled.

Figure 1B.1
Figure 1B.2
Figure 1B.3
Figure 1B.4

Additionally, ensure the correct boards and ports are selected. Make sure the USB cable is connected to the Arduino Board and the computer (Figure 1B.5). If the program has never been uploaded to the Arduino board, the screen will be blank. If it has previously been uploaded, it will appear similar to Figure 1B.5 as shown. Then, from the menu bar, go to “Tools” > “Boards,” and select an Arduino type, Arduino Uno, for example (Figure 1B.6). Then, also from “Tools” in the menu bar, go “Port” and ensure that an Arduino of the same type is selected there as well (Figure 1B.7).

Figure 1B.5
Figure 1B.6
Figure 1B.7

Section 1C:

Once the desired sketch is written in the IDE, upload it to the Arduino Board. Before uploading, follow Section 1B. Repeat this section (and section 1B) each time the sketch is updated.

Figure 1C.1

Step 2: Modify the Sketch

Section 2A:

If you do not plan on modifying or editing the sketch, you may move on to Step 3. Modifying the sketch includes changing input pin locations, changing what the LED screen displays, adding other calculations that the spirometer should do or modifying existing ones, etc.

Looking into the sketch of the downloaded code shows how the calculations displayed on the LED were determined. As previously mentioned at the beginning of the instructable, the main sources of input data are CO2 expelled in parts per million from the user, and pressure in kilopascals as measured by the pressure sensor. Using these two variables, the spirometer is able to calculate a number of different breathing characteristics.

Before these values are calculated, it is important to see how these two variables are determined. CO2 readings are measured directly from the sensor, as seen in line 209. The high concentration and low concentration are added, but the high concentration is multiplied by 256 in order to convert bytes of data to the actual numerical reading. This determination was based on the instruction manual of the CO2 sensor listed in the BOM. If your sensor is different, use the manual to determine how to find the parts per million of CO2 expended by the user of the device.

Section 2B:

The following bullet points detail how each value is calculated in the provided code. Should you want to modify the spirometer’s output units (for example, changing kilopascals to Standard atmospheres) or dimensions of the spirometer tubing, etc. please review this section carefully.

  • We calculate the pressure of breathing in kilopascals by the different specifications of our pressure sensor, in addition to the voltage measured by the pressure sensor and the voltage by which the measured voltage is offset. These two variables are the only non-constant variables that determine pressure. The formula used is given below:
Figure 2B.1

where P is the outputted pressure in kilopascals, and Vmeasured is the voltage measured by the sensor in Volts, and Voffset is the voltage by which the measured voltage is offset, in order to determine the pressure. Both Vmeasured and Voffset are determined by multiplying the differential pressure, Pdifferential and the initial differential pressure, P0, by the ratio 5/1023 because when reading an analog voltage from an input pin, the value is returned as a number from 0 to 1023; for a range of 0 V to 5 V. Essentially, the ratio converts the number read by the sensor into an actual voltage.

After subtracting Vmeasured by Voffset, their difference is multiplied by the ratio of rangegauge/Vrange, where rangegauge is a constant 25 kpa, and represents the range of the pressure sensor. This value will change based on the make and model of each pressure sensor. Vrange is a constant 2.25 V and represents the voltage range of the pressure sensor. The range is 2.25 V because its range for negative pressure is 0.25 V to 2.5 V and range for positive pressure is 2.5 V to 4.75 V. Once more, this value might vary, depending on what pressure sensor is used. Thus, based on the constants from the pressure sensor used, the formula for pressure in kilopascals is calculated as such:

Figure 2B.2
  • The instantaneous rate of the users breathing is calculated via the formula below:
Figure 2B.3

where air is the density of ϱair in kg/m3 (1.225).

Based on the equation above it is clear that volume flow is mass flow over ϱ. We can continue to rewrite the equation above by expanding the equation for mass flow rate and considering unit conversions:

Figure 2B.4

where P is the pressure in pascals, ϱair is the density of air in kg/m3, and A2 and A1 are the areas of the smaller and larger tubes which the user breathes in. These holes are made in later in the instructable when the mouthpiece is 3D printed in Step 4, and while it is recommended that the measurements listed in the 3D prints are followed, should you use different dimensions, determine each tubes’s radius in meters, square the radii, and multiply this value by π. Then, change the values of areas 2 and 1 (small and large, respectively) in lines 36 and 35 in the code.

All the values in the mass flow rate (pressure, air density, and areas) are already known, thus it is easy to calculate mass flow rate and therefore the instantaneous rate of breathing (in liters per second).

  • The final value calculated in the code is the Forced Vital Capacity (FVC), or the total amount of air (in liters) expelled per singular breath.

The logic behind calculating FVC is relatively simple. When the user is in between breaths, their instantaneous rate of breathing is zero liters per second since they are not expending air. When the user is breathing, their rate of breathing is greater than zero. Thus, in the sketch, whenever the instantaneous rate of breathing (calculated above), is greater than 0.1 liters per second (used instead of 0.0 to mitigate error), the microprocessor will continue adding volume until the rate of breathing goes below 0.1 which is when breathing stops. This value is stored, and the subsequent breath’s volume gets measured using the same method.

Section 2C:

There are even more modifications that you as a user could make to this sketch, specifically, what the LED screen displays, and the update frequency.

To change what the specific displays read or where the text is located on the screen, refer to lines 77-84. To change the (x,y) coordinates, change the inputs in the setCursor() function. To change the text, change the inputs in the print() function. Make sure to keep the quotes inside the print() function, and also make sure the text printed on the screen is not too long. That will result in the text “going off the screen,” and not being visible on the LED.

To change the update frequency, refer to lines 99-116. Each if statement has a value for the variable current_tenth, which is the tenth of a second at which a value is updated. To customize when these values are updated, simply change the value of current_tenth to a value between 0 and 9 inclusive. However, to ensure the microprocessor is not overloaded with data, it is ideal not to have values changing too frequently or have too many values changing at the same or similar times.

Step 3: Build the Board

Now we will transition to assembling the hardware of the spirometer.

Section 3A:

Start with your blank shield as shown below (Figure 3A.1).

  Figure 3A.1

Next, solder the pressure sensor on one edge of the board. Ensure one side of the pressure sensor’s mounts lie on the holes adjacent to A0 to A3 (the analog input holes) respectively. Additionally, ensure that the three adjacent mounts from A1 to A3 are soldered to the board (but not the fourth at A0), and that the two of the mounts on the other side are soldered for support (Figure 3A.2). On the bottom side of the board, solder A1, A2 and A3 to the adjacent hole where one of the mounts of the pressure sensor is, in order to connect the pressure sensor to the trace (Figure 3A.3), which ensures the pressure sensor is electronically connected. 

For best results in later steps, ensure that neither of the nipples of the pressure sensor come into contact with the board itself as tubing will be attached to the nipples in future steps. This is especially the case with the nipple closest to the board, where there should visually be a couple of millimeters of space between the nipple in question and the board. While not impossible, this may be a bit difficult, so it is acceptable to preemptively attach the airline tubing (as detailed in Section 3B) to the nipple closest to the board prior to soldering.

Figure 3A.2 (top view)
Figure 3A.3 (bottom view)

On the board, place stacking headers around the outside perimeter of the board. Specifically, place a total of six stacking pins on the board. The first four pins will be put prongs-down (relative to the top of the board): a stacking pin of length 8 on the digital holes labeled 0 to 7, a stacking pin of length 10 on holes labeled 8 to SCL, a stacking pin of length 6 on analog holes (A0 to A5), and a stacking pin of length 8 on the power holes from A6 to Vin. Additionally, place two stacking pins prongs-up, both of length 3 directly above the ICSP pins on the Arduino Uno board– which should be distinctly labeled or boxed on the shield (Figures 3A.4 and 3A.5).

Figure 3A.4 (top view)
Figure 3A.5
Figure 3A.5 (front side view)

Next, take the LED and put a (normal) pin of length 4 underneath the LED (Figure 3A.6).

Figure 3A.6

Attach the appended pins on the LED screen to holes above Digital holes 4 to 7 (Figure 3A.7).

Figure 3A.7

Section 3B:

Next, solder all the loose stacking pins in place. It is not necessary to solder each hole of each pin in place. 

Solder the following pins underneath (from the bottom of) the board. For the pin of length six (that lies from A0 to A5), it is only necessary to solder the pins adjacent to A1 through A3. These are the same holes that should be next to the soldering of the 3 prongs of the pressure sensor (on the other side of the board, however). For the pin for length 8 that lies from A6 to Vin, solder the two ends only– the holes adjacent to A6 and Vin. The same goes for the pin of length 10 that lies next to holes SCL to 8– only solder the ends (SCL and 8). Solder all the holes of the pin of length 8 that lies from 0 to 7. Refer to the figure below (Figure 3B.1) for an illustrative example.

Figure 3B.1

The only two pins left to be soldered are the two pins of length 3 that lie directly above the ICSP pins on the Arduino Uno board. Solder all six holes of these two pins in place from the top of the board (Figure 3B.2). If needed, the LED display can be removed and replaced if it lays on top of the pins and makes it difficult to solder.

Figure 3B.2

Next, stack the shield on top of the Arduino Uno board. All the pins should align with the holes/pins from the Arduino (ie A0 to A6 pin on the shield goes over the associated A0 to A6 holes on the Arduino, etc.) (Figures 3B.3 and 3B.4).

Figure 3B.3 (top view)
Figure 3B.4 (front side view)

Cut 2 pieces of airline tubing, each approximately 2 ft in length with scissors. Take the tubes and fit one through each nipple of the pressure sensor (Figure 3B.5). If you find it difficult to place the tube into the lower nipple because of interference with the board, consider resoldering the pressure sensor so that it fits higher on the board, which will allow for the airline tubing to attach to the nipple of the sensor (Figure 3B.6).

Figure 3B.5
Figure 3B.6

Section 3C:

Place the board to the side for now.

Focusing now on the CO2 sensor, connect the adapter (that comes with the CO2 sensor) to the CO2 sensor as shown (Figure 3C.1). Ensure that the connection occurs on the correct side of the adapter.

Figure 3C.1

Next, attach Dupont jumper wires to the other side of the adapter (Figure 3C.2).

Figure 3C.2

Attach the end of the Dupont wires to the wire housing (Figure 3C.3). Make sure the associated wires connect to their associated port in the 2 x 3 wire housing. The diagram below represents the arrangement to put each colored wire in (gray represents an empty port) (Figure 3C.4). Also note that these ports are associated with the sketch that comes with instructable. Any modifications to the input pins on the sketch might require you to put wires into different ports.

Figure 3C.3
Figure 3C.4

Next, place wire housing into their associated male pins from the Arduino board (Figure 3C.5). Once more, ensure that the appropriate pins are put in their associated housing ports in the schematic (Figure 3C.6). Simply ensure that the alignment of the jumper box on the Arduino matches these figures. It might be necessary to adjust the OLED display slightly in order to fit the pins into the ports. It might also be easier to take out the OLED, then attach the wire housing male and females together, then replace the OLED display in its original position.

Figure 3C.5 and Figure 3C.6

Step 4: 3D Printing

This next step will detail the printing process for the 3D parts needed to build the spirometer.

Section 4A:

We will first print the 3D files using a 3D printer. To access the files needed to print the files click the link here for the open source Google Drive folder. The following are the .stl files to be printed:

This equates to a total of 6 parts to be printed.

Refer to your 3D printer manual and FAQs for instructions on the setup, printing, and clean up for your specific make and model.

Some of the parts (particularly the CO2 holders) are more compatible with certain 3D printers when rotated at different orientations. You might have to adjust the orientation of the files to whatever is most compatible with your type of printer.

Additionally, because of differences in error in every 3D printer, it might be necessary to adjust some of the dimensions of each part and test several iterations of the design if some of the parts don’t fit into others.

All parts (particularly the CO2 holder, which is separated into two parts) were specifically designed so there was minimal scaffolding. Depending on your printer and printing orientation, however, it might be necessary to ensure all parts and holes are cleared of any filament or scaffolding before proceeding.

Section 4B:

Next, glue together the CO2 holders by their non-forked, long ends such that both parts stack on top of one another (Figure 4B.1). Any type of strong glue will suffice, however super glue is recommended for this step and all following steps involving gluing.

Figure 4B.1

Step 5: Assembling the Spirometer

The home stretch of this spirometer building involves connecting all the parts from previous steps together.

Section 5A:

First, take the CO2 holder made and glued in Step 4, and slide it onto the CO2 sensor (Figure 5A.1). Ensure the holder is SLID onto the sensor from the bottom and not “popped in,” or else the holder might break from external pressure.

Figure 5A.1

Set the CO2 sensor to the side. Stick in the airline tubing (attached in Step 3) into the two holes of the 3D printed spirometer body. Ensure that the correct airline tube goes into the correct hole on the body. The “top” tube on the pressure sensor should align with the “top” hole on the spirometer, and the “bottom” tube with the “bottom” hole (Figure 5A.2). When inserting the tubing, look through the top of the hole to ensure that each tube just gets through the hole: the tubes should not extend past the opening to a significant degree nor be too loose in the hole (Figure 5A.3). Super glue these tubes in place (Figure 5A.4).

Figure 5A.2
Figure 5A.3
Figure 5A.4

Set the spirometer body to the side. Take the 3D printed inner cap and add a few dabs of superglue to the inner edge. Then stick the cap onto the top of the 3D printed removable mouthpiece (Figures 5A.5 and 5A.6). If the parts don’t fit, consider adjusting the dimensions of these parts and reprinting.

Figure 5A.5
Figure 5A.6

Then, attach the bottom of the removable mouthpiece to the top of the 3D printed spirometer body (Figure 5A.7). Do NOT attach with superglue as this mouthpiece is intended to be removed between usages to be washed. The mouthpiece, which was specifically designed to be removable, is both dishwasher and soap friendly (if using ABS filament). Again, if the parts don’t fit, consider adjusting the dimensions of these parts and reprinting.

Figure 5A.7

Next, super glue the bottom of the spirometer body to the top of the 3D printed vinyl bottom (Figures 5A.8 and 5A.9). Once more, if the parts don’t fit, consider adjusting the dimensions of these parts and reprinting.

Figure 5A.8
Figure 5A.9

Then, cut 9 inches of the vinyl tubing with a cutting tool (hacksaw, table saw, simple carpet knife, etc.). (Figure 5A.10). This is where the CO2 sensor and holder combination will be placed inside. We cut 9 inches in length in order to ensure everything was contained within the tube with extra room in case future iterations of the spirometer had more contents in the vinyl tubing.

Figure 5A.10

Finally, connect the bottom of the vinyl bottom 3D printed part with one side of the vinyl tubing. On the other side, insert the CO2 sensor (Figure 5A.11). Ensure that the CO2 sensor is not inserted too far in the tube as this will make it difficult for it to be removed in the future.

Figure 5A.11

Note that in between uses of individuals, use a new cardboard mouthpiece. Insert these mouthpieces by attaching to the 3D printed removable mouthpiece part. In between several uses, remove and wash the removable mouthpiece.

Your product should now look like this (Figure 5A.12):

Figure 5A.12

Step 6: How to use the Spirometer

This step details how to use the spirometer after assembly. In order to ensure all assembly steps were properly followed, use this step to verify the spirometer is functional.

Section 6A:

To start, plug in the spirometer into the USB port of the Arduino board and the other end of the cord into a computer (Figure 6A.1). Wait one minute in order to give the CO2 sensor and Arduino board time to warm up. Ensure that the most updated sketch has been uploaded to the Arduino board (as detailed in Section 1B and 1C) before proceeding.

Figure 6A.1

It is preferable to have data printed and saved on the computer, as opposed to just being printed in real time on the OLED display. To have values printed on the screen, open the Arduino IDE, and navigate to the menu bar and go to “Tools” > “Serial Monitor” to have the values displayed in text format (Figure 6A.2). For a graphical format, go to “Tools” > “Serial Plotter.” For these steps to work correctly, the procedures outlined in Section 1B must have been followed and executed (Figure 6A.3). You could copy and paste these text values into excel or a .CSV file, or take screenshots of the serial plotter. Examples of the Serial Monitor (Figure 6A.4) and Serial Plotter (Figure 6A.5) are also shown below.

Figure 6A.2
Figure 6A.3
Figure 6A.4
Figure 6A.5

After one minute, breathe into the tube of the spirometer (Figure 6A.6). As a user breathes into the tube, the real-time values of each variable being measured will be displayed on the OLED screen, and a running set of data will be printed onto the terminal of the serial monitor or serial plotter if previously enabled. Ensure that prior to breathing, a new cardboard mouthpiece has been attached to the end of the tube.

Figure 6A.6

Each row of values printed is one set of data, and each second, an additional row is added. The values printed on the table from left to right are instantaneous rate (L/sec), CO2 concentration in ppm, volume of current breath (L), and volume of previous breath (L).

Once the desired data is attained and saved, simply unplug the USB cord from the Arduino and computer. Make sure to also take off the cardboard tube.

Section 6B:

In order to confirm that the values measured by the spirometer are accurate, there are a couple tests that will confirm its accuracy.

If the following tests return the correct values, it can be assumed that the spirometer was built correctly, the changes implemented are accurate, and the CO2 sensor is functional:

  1. When testing the CO2 concentration outside, the measured amount on the LED screen should show approximately 400 ppm.
  2. When testing the CO2 concentration inside, the measured amount on the LED screen should show anywhere in or around the range of 400 – 1,000 ppm.
  3. When blowing into or in the direction of the tube, the rate of breathing, measured CO2 concentration, and volumes of current and previous breaths, should all increase on the LED screen.

Section 6C:

Here’s a video demonstrating how to use the spirometer. It provides insight on set-up, usage, and post-usage.

Step 7: 3D Printing and Assembling Part 2

This step is an optional (but highly recommended) step to make additional 3D printed parts to hold the spirometer together. Completing this step will make using and moving the spirometer around much easier.

Section 7A:

We will first print the 3D files using a 3D printer. To access the files needed to print the files click the link here for the open source Google Drive folder. The following are the .stl files to be printed:

This equates to a total of 4 parts to be printed.

The following are similar reminders as listed in Step 4:

Refer to your 3D printer manual and FAQs for instructions on the setup, printing, and clean up for your specific make and model.

Some of the parts are more compatible with certain 3D printers when rotated at different orientations. You might have to adjust the orientation of the files to whatever is most compatible with your type of printer.

Additionally, because of differences in error in every 3D printer, it might be necessary to adjust some of the dimensions of each part and test several iterations of the design if some of the parts don’t fit into others.

All parts were specifically designed so there was minimal scaffolding. Depending on your printer and printing orientation, however, it might be necessary to ensure all parts and holes are cleared of any filament or scaffolding before proceeding.

Section 7B:

Once all parts have been printed, configure the wires and adapter above the board in a similar way as shown below (Figure 7B.1). Everything should be as compact as possible.

Figure 7B.1

Next place the TopBox part on the top of the board and wires/adapter (Figure 7B.2 and 7B.3). Ensure the 3D printed part is aligned correctly such that the OLED screen is visible and all the ports on the side align.

Figure 7B.2 (top view)
Figure 7B.3 (bottom view)

Place the BottomBox part on the bottom of the board (Figure 7B.4). It should attach with the TopBox part and all the wires along with the adapter and board should fit snugly within the box. If not, it might be helpful to rearrange these items within the box. Keep in mind that error on each 3D printer might mean that the items in the box might not fit, so resizing the box might be necessary as well.

Figure 7B.4

Next add a total of four 3 mm Hex screws (20 mm length), one to each corner, of the BottomBox part to connect to the TopBox part (Figure 7B.5). If the holes on the box are too small due to errors in 3D printing, drill the holes with a 7/64 inch drill tip, or adjust the size on your 3D printing software and reprint.

Figure 7B.5

Next, slide the two Ring parts onto the tube. Ensure that the flat edge sides are oriented in the same direction. They should be about 1-2 inches apart, and the ring closest to the end of the tube should be about 1 inch away from the end (Figure 7B.6).

Figure 7B.6

Next, glue the BottomBox to the flat edges on the rings (Figure 7B.7).

Figure 7B.7

Finally zip tie the airline tubes against the vinyl tubing behind and under the 3D printed box. To do this twist the airline tubing once and place 2 zip ties on the tubing behind the 3D printed box– one on the inner loop, the part where the loop of the tubing “intersects itself” (Figure 7B.8) and then another one on the inner loop (Figure 7B.9). Ensure that when putting on the zip ties and looping the tubing that air flow through the tubes is not constricted. In order to ensure that the zip ties are not constricting air, retest the spirometer using the instructions in Step 6– the same results should be seen.

Figure 7B.8
Figure 7B.9

Cut the excess zip tie (Figure 7B.10). The final product should look like the image below (Figure 7B.11).

Figure 7B.10
Figure 7B.11


It might be useful to do a couple steps out of order than they were presented in the instructable:

  1. The nipples should be placed on the pressure sensor before the pressure sensor is soldered on
  2. The OLED screen should be the last thing placed on the board. First solder on all parts and attach all wires on

Additionally, because of small errors made during the making of this instructable, some images might not correlate exactly with the previous steps. Specifically, the soldering might not be put on the right pins/areas of the board. When in doubt, aire on the side of using what the instructable says and not what the pictures show.

Share This Article