Skip to main content

Custom front end configuration

What this example shows​

The module's electrochemical-customConfig.py example builds its own SensorConfig instead of using a preset, and sends those settings to the LMP91000 analog front end and the ADC. Use it when you want to tune the gain, the internal zero or the zero offset yourself.

The example creates the sensor with its own SENSOR_CUSTOM and no adc_addr, so the module's default address is used. The example's values are the ozone cell's. For this board's cell, the SENSOR_NH3 preset's values below are a starting point. Three of them (ADS_GAIN_4_096V, INTERNAL_ZERO_20_PERCENT and BIAS_SIGN_POSITIVE) are not in the example's imports, so add them: ADS_GAIN_4_096V comes from ads1115, the other two from ElectrochemicalGasSensor.

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 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.

ArgumentSENSOR_NH3 value
nano_amperes_per_ppm40.0
internal_zero_calibration0
ads_gainADS_GAIN_4_096V
tia_gainTIA_GAIN_35_KOHM
rloadRLOAD_10_OHM
ref_sourceREF_EXTERNAL
internal_zeroINTERNAL_ZERO_20_PERCENT
bias_signBIAS_SIGN_POSITIVE
biasBIAS_0_PERCENT
fet_shortFET_SHORT_DISABLED
op_modeOP_MODE_3LEAD_AMP_CELL

The example code​

# FILE: electrochemical-customConfig.py
# AUTHOR: Fran Fodor @ Soldered
# BRIEF: Custom LMP91000 configuration example for any electrochemical sensor.
# LAST UPDATED: 2026-05-21

# Connecting diagram:
#
# Qwiic------------------------>Qwiic
# LMPEN------------------------>GPIO pin (see config_pin parameter below)

from machine import I2C, Pin
import time
from ElectrochemicalGasSensor import (
ElectrochemicalGasSensor, SensorConfig,
TIA_GAIN_35_KOHM, RLOAD_10_OHM, REF_EXTERNAL,
INTERNAL_ZERO_67_PERCENT, BIAS_SIGN_NEGATIVE, BIAS_0_PERCENT,
FET_SHORT_DISABLED, OP_MODE_3LEAD_AMP_CELL,
)
from ads1115 import ADS_GAIN_2_048V

# Custom sensor configuration — edit these values to match your sensor's datasheet.
#
# nanoAmperesPerPPM: Sensitivity from the sensor datasheet (nA/PPM)
# internalZeroCalibration: Voltage offset added to zero the reading in clean air
# adsGain: ADS1115 full-scale range (ADS_GAIN_6_144V .. ADS_GAIN_0_256V)
# tia_gain: TIA feedback resistor (TIA_GAIN_2_75_KOHM .. TIA_GAIN_350_KOHM)
# rload: Load resistor (RLOAD_10_OHM .. RLOAD_100_OHM)
# ref_source: Reference voltage source (REF_INTERNAL / REF_EXTERNAL)
# internal_zero: Internal zero percentage (INTERNAL_ZERO_20/50/67_PERCENT or _BYPASSED)
# bias_sign: Bias polarity (BIAS_SIGN_NEGATIVE / BIAS_SIGN_POSITIVE)
# bias: Bias percentage of Vref (BIAS_0_PERCENT .. BIAS_24_PERCENT)
# fet_short: FET short (FET_SHORT_DISABLED / FET_SHORT_ENABLED)
# op_mode: Operating mode (OP_MODE_3LEAD_AMP_CELL for normal operation)
SENSOR_CUSTOM = SensorConfig(
nano_amperes_per_ppm=-1000.0,
internal_zero_calibration=-0.0012,
ads_gain=ADS_GAIN_2_048V,
tia_gain=TIA_GAIN_35_KOHM,
rload=RLOAD_10_OHM,
ref_source=REF_EXTERNAL,
internal_zero=INTERNAL_ZERO_67_PERCENT,
bias_sign=BIAS_SIGN_NEGATIVE,
bias=BIAS_0_PERCENT,
fet_short=FET_SHORT_DISABLED,
op_mode=OP_MODE_3LEAD_AMP_CELL,
)

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

sensor = ElectrochemicalGasSensor(SENSOR_CUSTOM, i2c)

# -------------------------------------------------------------------------
# Setup
# -------------------------------------------------------------------------
print("Electrochemical Gas Sensor - Custom Config 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:
reading = sensor.getPPB()

print("Sensor reading: {:.5f} PPB".format(reading))

time.sleep_ms(2500)

🔗Open the complete example

electrochemical-customConfig.py at commit 623e1e6

→

The Thonny shell shows:

ShellMicroPython
Electrochemical Gas Sensor - Custom Config 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​

SensorConfig(nano_amperes_per_ppm, internal_zero_calibration, ads_gain, tia_gain, rload, ref_source, internal_zero, bias_sign, bias, fet_short, op_mode)returns SensorConfig

Holds one cell's settings: its sensitivity and zero offset for the ppm conversion, the ADC range, and the LMP91000 gain, load, reference, internal zero, bias, FET short and operating mode.

ReturnsThe configuration object to pass to ElectrochemicalGasSensor.

Parameters

ParameterDescription
nano_amperes_per_ppmCell sensitivity in nA per ppm, from the cell's datasheet.
internal_zero_calibrationVoltage offset added to zero the reading in clean air.
ads_gainADC full-scale range, ADS_GAIN_6_144V to ADS_GAIN_0_256V.
tia_gainAmplifier feedback resistor, TIA_GAIN_2_75_KOHM to TIA_GAIN_350_KOHM, or TIA_GAIN_EXTERNAL.
rloadLoad resistor, RLOAD_10_OHM to RLOAD_100_OHM.
ref_sourceREF_INTERNAL or REF_EXTERNAL. The ppm conversion assumes the board's 2.5 V reference.
internal_zeroINTERNAL_ZERO_20_PERCENT, _50_PERCENT, _67_PERCENT or INTERNAL_ZERO_BYPASSED.
bias_signBIAS_SIGN_NEGATIVE or BIAS_SIGN_POSITIVE.
biasBias as a percentage of the reference, BIAS_0_PERCENT to BIAS_24_PERCENT.
fet_shortFET_SHORT_DISABLED for normal operation, or FET_SHORT_ENABLED.
op_modeOP_MODE_3LEAD_AMP_CELL for normal operation; deep sleep, standby, 2-lead and temperature modes also exist.
ElectrochemicalGasSensor(sensor_type, i2c=None, adc_addr=0x49, config_pin=None)returns ElectrochemicalGasSensor

Creates the sensor object with any SensorConfig, a preset or your own. 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_typeSensorConfigYour configuration, here SENSOR_CUSTOM.
i2cI2CYour I2C object.
adc_addrintThe 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_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. Default None. Not used from 0x30 to 0x37.
begin()returns bool

Configures the ADC gain and writes the three LMP91000 register values built from your SensorConfig: through the board's bridge when the address is 0x30 to 0x37, or to the ADS1115 and the LMP91000 directly for any other address.

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.

getPPB()returns float

Takes one reading and converts it with your configuration's gain, internal zero, offset and sensitivity. A result below zero is returned as 0.

ReturnsThe concentration in ppb.

Putting it together​

begin() packs your SensorConfig into the LMP91000's three settings registers and writes them, and every reading uses the same fields for the conversion. Change one argument at a time and compare with the SENSOR_NH3 values above.