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:
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 ElectrochemicalGasSensorCreates a sensor object. Nothing is sent over I2C until begin(). The example creates two, both on the same I2C object.
ReturnsThe sensor object.
Parameters
| Type | Name | Description |
|---|---|---|
SensorConfig | sensor_type | The cell preset. The example passes SENSOR_O3 for both; this board's SGX-4CL2 cell has the preset SENSOR_CL2. |
I2C | i2c | A 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. |
int | adc_addr | The 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. |
int | config_pin | A 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 boolChecks 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 floatTakes 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.