Carbon Monoxide Oxygen Analyser

A low-cost DIY analyser that measures oxygen and carbon monoxide in diving gas at the same time, built around an ESP32-C3.

View the code on GitHub Read the original write-up
The finished analyser: a 128 by 128 OLED showing O2 percent, cell millivolts, CO ppm and temperature, above illuminated power and calibration buttons.
Warning: this is a cheap, poorly tested DIY build with rough code, not a commercial product. The CO sensor's maker states it must not be used for life support. Think about what your life is worth before you rely on one.

Why

CO analysers sold for diving cost far more than comparable industrial safety analysers. So I built a dual analyser that tests a gas for O2 and CO in one go. The O2 side reuses a standard R22D oxygen cell that came out of my CCR after 12 months, which still has plenty of life left.

What it does

O2 and CO together

One sample, both readings: O2 % and CO ppm in large type, with the O2 cell's millivolts and the CO sensor's temperature.

Steady O2 reading

The O2 cell is read through a 16-bit ADC and averaged over 40 samples.

One-button calibration

Calibrate on air or pure oxygen; the analyser works out which from the cell's output and remembers it.

Factory-calibrated CO

Electrochemical CO sensor: 0–1000 ppm at 1 ppm resolution, expected life over 2 years.

Rechargeable

Runs on an 18650 cell, charged through a USB-C socket on the case.

Simple wiring

The display, ADC and CO sensor all share one I2C bus.

Calibration

Press CAL while the O2 cell is exposed to the calibration gas. The firmware decides which gas it is from the cell's output:

Cell outputTreated asCalibration value
7–13 mVAir20.7% O2
Above 44 mVOxygen98% O2
Anything elseErrorShows CAL ERROR and stops until restarted

The calibration is saved to flash and loaded at start-up. On the very first start, the analyser calibrates straight away.

Photos

Parts

PartNotes
ESP32-C3 Super MiniMicrocontroller
DFRobot CO sensor (SEN0466)I2C, address 0x74. Factory calibrated, 0–1000 ppm, 1 ppm resolution, expected life over 2 years
R22D oxygen cellReclaimed from a CCR; connected with a Molex connector
ADS1115 16-bit ADCI2C; reads the O2 cell differentially on A0–A1
1.5" 128×128 OLEDSH1107 driver, I2C
18650 battery holder / UPS moduleWith an 18650 cell
USB-C socketFor charging
M16 toggle switchPower
M16 momentary switchCalibration
Pelican-style caseCheap, but surprisingly decent
3D-printed internal assemblyHolds everything in the case; M4 threaded inserts
Stainless barbs, 4 × M6Gas sample connections

Wiring

ESP32-C3 pinConnects to
GPIO8 (SDA)SDA on the OLED, ADS1115 and CO sensor
GPIO9 (SCL)SCL on the OLED, ADS1115 and CO sensor
GPIO2Calibration button to GND (internal pull-up)
ADS1115 A0 / A1O2 cell + / −

Build and flash

The firmware is a basic Arduino-framework C++ program, written in VS Code with PlatformIO. It should port to other IDEs.

pio run -t upload
pio device monitor

Libraries: U8g2, Adafruit ADS1X15, RunningAverage and EasyButton, installed by PlatformIO. The DFRobot multi-gas sensor library (MIT licence) is included in src.

3D-print files (STL and Fusion 360): 3D-Printed-Assembly.zip

Known limitations