Averaged measurement
What this example shows
The module's electrochemical-averagedMeasurement.py example takes several readings, waits between them, and prints their average in ppb. Averaging smooths out the noise of a single reading.
The example creates the sensor with the module's ozone preset, SENSOR_O3, and no adc_addr, so the module's default address is used. Its comment says to change the preset to match your sensor type: SENSOR_NH3 is the preset for this board's ammonia 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 default 0x49 this example uses, is taken as the I2C address of an ADS1115 that the module talks to directly. 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 GND or a GPIO pin. 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-averagedMeasurement.py
# AUTHOR: Fran Fodor @ Soldered
# BRIEF: Averaged PPM reading example using the built-in averaging function.
# LAST UPDATED: 2026-05-21
# Connecting diagram:
#
# Qwiic------------------------>Qwiic
# LMPEN------------------------>GND or GPIO pin (see config_pin parameter below)
from machine import I2C, Pin
import time
from ElectrochemicalGasSensor import ElectrochemicalGasSensor, SENSOR_O3
# How many readings to average and how many seconds to wait between them
NUM_READINGS = 5
SECS_BETWEEN_READINGS = 3
# Initialize I2C
i2c = I2C(0, scl=Pin(22), sda=Pin(21))
# Create sensor object - change SENSOR_O3 to match your sensor type
# Available types: SENSOR_CO, SENSOR_NO2, SENSOR_SO2, SENSOR_O3,
# SENSOR_NO, SENSOR_H2S, SENSOR_NH3, SENSOR_CL2
sensor = ElectrochemicalGasSensor(SENSOR_O3, i2c)
# -------------------------------------------------------------------------
# Setup
# -------------------------------------------------------------------------
print("Electrochemical Gas Sensor - Averaged Measurement Example")
if not sensor.begin():
print("ERROR: Can't init the sensor! Check connections!")
while True:
time.sleep_ms(100)
print("Sensor initialized successfully!")
# -------------------------------------------------------------------------
# Main loop
# -------------------------------------------------------------------------
while True:
# getAveragedPPM is a blocking call — it waits between each reading
reading = sensor.getAveragedPPB(NUM_READINGS, SECS_BETWEEN_READINGS)
# If PPM is more relevant for your sensor, use:
# reading = sensor.getAveragedPPM(NUM_READINGS, SECS_BETWEEN_READINGS)
print("Sensor reading: {:.5f} PPB".format(reading))
time.sleep_ms(2500)
🔗Open the complete example
electrochemical-averagedMeasurement.py at commit 623e1e6
The Thonny shell shows:
Electrochemical Gas Sensor - Averaged Measurement Example
Sensor initialized successfully!
Then a line starting with Sensor reading: appears with the average in ppb. Five readings with 3 seconds after each take 15 seconds, plus the 2.5 second pause, so a new line arrives about every 17.5 seconds.
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 ElectrochemicalGasSensorCreates 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
| Parameter | Type | Description |
|---|---|---|
sensor_type | SensorConfig | The cell preset. SENSOR_NH3 is the preset for this board's cell. |
i2c | I2C | Your I2C object. |
adc_addr | int | The address the module talks to. Default 0x49, which this example uses. On this board the ADDRESS switch sets the bridge's address from 0x30 to 0x37. |
config_pin | int or None | A GPIO pin number the module sets low while it configures the LMP91000 and high afterwards, when adc_addr is outside 0x30 to 0x37. Default None. Not used from 0x30 to 0x37. |
begin()returns boolWith 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.
getAveragedPPB(num_measurements=5, seconds_delay=2)returns floatAverages num_measurements readings, waiting seconds_delay seconds after each one, and returns the result in ppb. It blocks until all readings are done.
ReturnsgetAveragedPPM() multiplied by 1000.
Parameters
| Parameter | Type | Description |
|---|---|---|
num_measurements | int | How many readings to average. Default 5. |
seconds_delay | int | Seconds to wait after each reading. Default 2. |
getAveragedPPM(num_measurements=5, seconds_delay=2)returns floatThe same average in ppm. The example shows it in a comment as an alternative.
ReturnsThe averaged concentration in ppm.
Parameters
| Parameter | Type | Description |
|---|---|---|
num_measurements | int | How many readings to average. Default 5. |
seconds_delay | int | Seconds to wait after each reading. Default 2. |
Putting it together
Use the averaged functions when a steady value matters more than a fast one. They block while they wait, so keep NUM_READINGS × SECS_BETWEEN_READINGS short if your program has other work to do.