Skip to main content

Two sensors

What this example shows​

The module's electrochemical-twoSensors.py example creates two sensor objects on one I2C bus, each with its own address and pin, starts both, and prints both concentrations in ppb every 2.5 s. Sensor 1 is created with adc_addr=0x4A and config_pin=25, sensor 2 with adc_addr=0x49 and config_pin=32.

Both sensors use SENSOR_O3, the preset for the ozone cell. The module calculates each reading from the preset's sensitivity and gain settings; the preset for this board's chlorine cell is SENSOR_CL2.

The example opens I2C bus 0 with SCL on pin 22 and SDA on pin 21. Change them to match your board.

The example code​

# FILE: electrochemical-twoSensors.py
# AUTHOR: Fran Fodor @ Soldered
# BRIEF: Two O3 sensor PPB reading example using two breakouts on one I2C bus.
# LAST UPDATED: 2026-05-21

# Connecting diagram:
#
# Qwiic------------------------>Qwiic
# LMPEN------------------------>25 (GPIO pin, required for I2C configuration)
#
# Qwiic------------------------>Qwiic (same bus)
# LMPEN------------------------>32 (GPIO pin, required for I2C configuration)
#
# Set different ADC I2C addresses on each breakout using the address jumpers.
# Both LMP91000 chips share address 0x48 — the LMPEN pin selects which one
# is active during configuration, so a GPIO pin is required for each sensor.

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

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

# Sensor 1: ADC address 0x4A, LMPEN on pin 25
sensor1 = ElectrochemicalGasSensor(SENSOR_O3, i2c, adc_addr=0x4A, config_pin=25)

# Sensor 2: ADC address 0x49, LMPEN on pin 32
sensor2 = ElectrochemicalGasSensor(SENSOR_O3, i2c, adc_addr=0x49, config_pin=32)

# -------------------------------------------------------------------------
# Setup
# -------------------------------------------------------------------------
print("Electrochemical Gas Sensor - Two Sensors Example")

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

print("Sensor 1 initialized successfully!")

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

print("Sensor 2 initialized successfully!")

# -------------------------------------------------------------------------
# Main loop
# -------------------------------------------------------------------------
while True:
reading1 = sensor1.getPPB()
reading2 = sensor2.getPPB()

print("Sensor 1 reading: {:.5f} PPB".format(reading1))
print("Sensor 2 reading: {:.5f} PPB".format(reading2))

time.sleep_ms(2500)

🔗Open the complete example

Examples/electrochemical-twoSensors.py at commit 623e1e6

→

The example's comments wire each board's LMPEN to a GPIO and set the two addresses with address jumpers. On this board a 10 kΩ resistor holds LMPEN low and it is wired to the ATtiny404, so you leave it unconnected, and the board's own address is set with the ADDRESS switch (see Hardware details).

Expected result:

ShellMicroPython
Electrochemical Gas Sensor - Two Sensors Example
Sensor 1 initialized successfully!
Sensor 2 initialized successfully!

Then two lines every 2.5 s: Sensor 1 reading: and Sensor 2 reading: , each followed by that sensor's concentration in ppb with five decimals and PPB. If a sensor's begin() fails, the script prints ERROR: Can't init sensor 1! Check connections! (or sensor 2) and stops.

Functions used​

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

Creates a sensor object. Nothing is sent over I2C until begin(). The example creates two, both on the same I2C object.

ReturnsThe sensor object.

Parameters

TypeNameDescription
SensorConfigsensor_typeThe cell preset. The example passes SENSOR_O3 for both; this board's SGX-4CL2 cell has the preset SENSOR_CL2.
I2Ci2cA ready machine.I2C object; the example passes the same one to both. When left out, the module creates I2C(0, scl=Pin(22), sda=Pin(21)) on the esp32 port and on the one other board it recognises by its name (pins 22 and 21 there too); on esp8266, I2C(scl=Pin(5), sda=Pin(4)); on any other board it raises Board not recognized, please pass an I2C object manually.
intadc_addrThe I2C address, 0x49 when left out; the example passes 0x4A and 0x49. For an address from 0x30 to 0x37, the range of this board's ADDRESS switch, begin() talks to the board's ATtiny404; for any other address it looks for an ADS1115 at that address and the LMP91000 at 0x48 on the bus you pass.
intconfig_pinA GPIO for the LMP91000's MENB input: the module drives it low while it configures the LMP91000 directly, and high otherwise; the example passes 25 and 32. None when left out. Unused for addresses 0x30 to 0x37.
sensor.begin()returns bool

Checks that the board answers, sets the preset's ADC gain and data rate, and configures the LMP91000 with the preset's settings. The example calls it once for each sensor before reading.

ReturnsTrue when every step succeeded, False otherwise.

sensor.getPPB()returns float

Takes one reading and converts it to ppb: getPPM() times 1000. getPPM() subtracts the internal zero voltage, adds the zero calibration, divides by the amplifier gain and the preset's sensitivity, and returns 0.0 for a result below zero.

ReturnsThe gas concentration in ppb.

This example also uses I2C and Pin from MicroPython's machine module and time.sleep_ms(), which are not part of this library and come with MicroPython itself.

Putting it together​

Each sensor object keeps its own preset, address and pin, so the loop reads them one after the other with the same function.