Skip to main content

Calibrate the zero point

What this example shows​

The module's electrochemical-calibrateSensor.py example adds your own zero offset to every reading. Run it where there is none of the target gas and adjust CUSTOM_CALIBRATION until the reading is just barely 0 ppb.

The example creates the sensor with the module's ozone preset, SENSOR_O3, adc_addr=0x49 and config_pin=32. SENSOR_NH3 is the preset for this board's cell, and the ppm conversion uses the preset's sensitivity (-1000 nA/ppm for SENSOR_O3, 40 nA/ppm for SENSOR_NH3). As shipped, the example's readings are therefore not ammonia values. To read ammonia, change SENSOR_O3 to SENSOR_NH3 in the import line and in the line that creates the sensor. The example opens I2C bus 0 on pin 22 (SCL) and pin 21 (SDA).

The module talks to the board's ATtiny404 bridge only when adc_addr is from 0x30 to 0x37. Any other value, such as the 0x49 this example passes, is taken as the I2C address of an ADS1115 that the module talks to directly, with config_pin set low while it configures the LMP91000. On this board the ADS1115 sits on the header's SDA2/SCL2 pins, which the Qwiic connectors do not reach, so this example does not run over the Qwiic cable. Wire the controller to the header instead: 3.3 V to 3V3, GND to GND, the example's SDA pin (21) to SDA2 and its SCL pin (22) to SCL2. The example then runs as written, with no change to the address.

The connecting diagram in the example's comments wires LMPEN to GPIO 32. On this board, leave LMPEN unconnected: a 10 kΩ resistor already pulls it to GND, and the ATtiny404 is connected to it too.

The example code​

# FILE: electrochemical-calibrateSensor.py
# AUTHOR: Fran Fodor @ Soldered
# BRIEF: Sensor zero-point calibration example.
# LAST UPDATED: 2026-05-21

# Connecting diagram:
#
# Qwiic------------------------>Qwiic
# LMPEN------------------------>32 (GPIO pin, required for configuration)

from machine import I2C, Pin
import time
from ElectrochemicalGasSensor import ElectrochemicalGasSensor, SENSOR_O3

# This value is added to the voltage read from the ADC.
# In an environment with 0 of the target gas, the reading after calibration
# should be just barely 0 PPB — adjust this value until that is the case.
CUSTOM_CALIBRATION = 0

# Initialize I2C
i2c = I2C(0, scl=Pin(22), sda=Pin(21))

# Custom ADC address (0x49 default) and GPIO pin for LMPEN
sensor = ElectrochemicalGasSensor(SENSOR_O3, i2c, adc_addr=0x49, config_pin=32)

# -------------------------------------------------------------------------
# Setup
# -------------------------------------------------------------------------
print("Electrochemical Gas Sensor - Calibration Example")

if not sensor.begin():
print("ERROR: Can't init the sensor! Check connections!")
while True:
time.sleep_ms(100)

# Apply the zero-point calibration offset
sensor.setCustomZeroCalibration(CUSTOM_CALIBRATION)

print("Sensor initialized successfully!")

# -------------------------------------------------------------------------
# Main loop
# -------------------------------------------------------------------------
while True:
reading = sensor.getPPB()

# O3 is typically measured in PPB
# Switch to getPPM() if PPM is more relevant for your use case
print("Sensor reading: {:.5f} PPB".format(reading))

time.sleep_ms(2500)

🔗Open the complete example

electrochemical-calibrateSensor.py at commit 623e1e6

→

The Thonny shell shows:

ShellMicroPython
Electrochemical Gas Sensor - Calibration Example
Sensor initialized successfully!

Then a line starting with Sensor reading: follows every 2.5 seconds, in ppb to five decimals.

If begin() returns False, the example prints ERROR: Can't init the sensor! Check connections! instead and stops.

Functions used​

ElectrochemicalGasSensor(sensor_type, i2c=None, adc_addr=0x49, config_pin=None)returns ElectrochemicalGasSensor

Creates the sensor object. adc_addr decides how begin() talks to the board: from 0x30 to 0x37 it uses the ATtiny bridge protocol; any other value is used as the ADC's own I2C address.

ReturnsThe sensor object.

Parameters

ParameterTypeDescription
sensor_typeSensorConfigThe cell preset. SENSOR_NH3 is the preset for this board's cell.
i2cI2CYour I2C object.
adc_addrintThe address the module talks to. Default 0x49; the example passes 0x49. On this board the ADDRESS switch sets the bridge's address from 0x30 to 0x37.
config_pinint or NoneA GPIO pin number the module sets low while it configures the LMP91000 and high afterwards, when adc_addr is outside 0x30 to 0x37; the example passes 32. Default None. Not used from 0x30 to 0x37.
begin()returns bool

With an address from 0x30 to 0x37, checks that the bridge answers and sends it the ADC and analog front end settings from the preset. With any other address, starts the ADS1115 on that address directly and configures the LMP91000. Call it once before any reading.

ReturnsTrue when every step succeeded. False when the bridge did not answer OK within 500 ms or reported an error, when starting the ADS1115 failed, or when configuring the analog front end failed.

setCustomZeroCalibration(calibration)returns None

Sets the voltage added to every reading after the front end's internal zero is removed. It replaces the preset's internal_zero_calibration, which is 0 for SENSOR_NH3.

ReturnsNothing.

Parameters

ParameterTypeDescription
calibrationfloatOffset in volts. Choose it so clean air reads just barely 0.
getPPB()returns float

Takes one reading with your offset applied and returns it in parts per billion. A result below zero is returned as 0.

ReturnsgetPPM() multiplied by 1000.

getPPM()returns float

The same reading in ppm. The example names it in a comment as an alternative.

ReturnsThe gas concentration in ppm.

Putting it together​

Keep in mind that a result below zero is printed as 0, so a reading of exactly 0 can hide an offset that is too low. Once clean air reads just barely 0, call setCustomZeroCalibration() with that value after begin() in your own program.