drivers.SEN66.adafruit_sen6x.SEN6x¶
- class drivers.SEN66.adafruit_sen6x.SEN6x(i2c: smbus2.SMBus | int, address: int = SEN6X_I2C_ADDRESS)¶
Base class for the Sensirion SEN6x environmental-sensor family.
Implements the protocol-level features that are common to the whole SEN6x family — SEN60, SEN63C, SEN65,
SEN66, and SEN68 — including start/stop measurement, thedata_readypoll, the 32-bit device-status register, fan cleaning, serial number and product name lookup, firmware version, the SHT-heater readback, and the per-word CRC8 used by every Sensirion command. Device-specific measurement commands are added by subclasses such asSEN66. Although named after the original CircuitPython driver, this port talks directly to ansmbus2.SMBushandle.- Variables:
_address (
int) – 7-bit I2C address (0x6Bfor SEN6x,0x6Cfor SEN60)._i2c_bus (
smbus2.SMBus) – Open SMBus handle used for all I2C read/write transfers._serial_number (
str | None) – Cached ASCII serial number, populated lazily on first access ofserial_number._product_name (
str | None) – Cached product name string, populated lazily on first access ofproduct_name._measurement_started (
bool) –Trueoncestart_measurement()has been issued without a matchingstop_measurement(); used to guard idle-mode-only commands.
Example
>>> from smbus2 import SMBus >>> bus = SMBus(1) >>> dev = SEN6x(bus) >>> dev.serial_number '...'
Note
On Linux the SEN6x lives at I2C address
0x6B(SEN6X_I2C_ADDRESS); the legacy SEN60 lives at0x6C(SEN60_I2C_ADDRESS). The constructor sleeps_SENSOR_STARTUP_TIME(1 s) before returning so that the very first command issued by the caller hits a sensor that has fully booted.Bind the driver to an SMBus handle and wait for sensor boot.
- Parameters:
i2c (
smbus2.SMBus | int) – Either an already-opensmbus2.SMBusinstance or an integer bus number; in the latter case the constructor opensSMBus(int(i2c))itself.address (
int) – 7-bit I2C address of the SEN6x device. Defaults toSEN6X_I2C_ADDRESS(0x6B), shared by SEN63C/SEN65/SEN66/SEN68; passSEN60_I2C_ADDRESS(0x6C) for a SEN60.
- Returns:
None
- Raises:
OSError – Propagated from
smbus2.SMBuswhen an integer bus number is given but the underlying/dev/i2c-*device cannot be opened.
Example
>>> from smbus2 import SMBus >>> bus = SMBus(1) >>> dev = SEN6x(bus)
Note
Blocks for
_SENSOR_STARTUP_TIMEseconds (1 s) to cover the worst-case SEN6x power-on time before the first Sensirion command (e.g.0x0021start_measurement) is sent.- static _crc8(data: bytes) int¶
Compute the Sensirion CRC8 checksum for an arbitrary byte string.
Implements the byte-wise CRC8 used by every Sensirion air-quality sensor: polynomial
0x31(x**8 + x**5 + x**4 + 1), initial value0xFF, no input or output reflection, no final XOR. Each input byte is XORed into the running CRC, then the CRC is shifted left 8 times with the polynomial XORed in whenever the high bit was set, and finally masked to 8 bits.- Parameters:
data (
bytes) – Raw input bytes — typically a single big-endian 16-bit word — whose CRC8 is required.- Returns:
8-bit unsigned CRC value (0..255) suitable for appending after data on the wire, or for comparing to the CRC byte that follows data in a read response.
- Return type:
- Raises:
None –
Example
>>> SEN6x._crc8(b"\xBE\xEF") 146
Note
Used directly by
_write_command()to protect every argument word and by_read_data()to validate every response word.
- _read_data(num_words: int, execution_time: float = _TIME_STANDARD) list[int]¶
Read and CRC-verify a sequence of 16-bit words from the SEN6x.
Sensirion sensors stream their responses as
[MSB, LSB, CRC8]triplets per 16-bit word, so this helper sleeps execution_time first (to let the previous command finish), issues a singlenum_words * 3bytesmbus2.i2c_msg.read, then validates every word’s CRC8 (polynomial0x31, init0xFF) before unpacking each pair into a big-endian unsigned integer.- Parameters:
num_words (
int) – Number of 16-bit words the device is expected to return after the most recent command;num_words * 3bytes are read off the wire.execution_time (
float) – Seconds to sleep before issuing the read so the device has had time to prepare the response. Defaults to_TIME_STANDARD(20 ms).
- Returns:
List of num_words 16-bit unsigned integers decoded from the I2C response in protocol order.
- Return type:
- Raises:
RuntimeError – If the CRC8 of any returned word does not match the CRC byte from the device, indicating bus corruption.
OSError – Propagated from
smbus2.SMBus.i2c_rdwron an underlying I2C error.
Example
>>> dev._read_data(1) [...]
Note
CRC is the same Sensirion variant used in
_write_command(); one CRC byte follows every word in the response.
- _write_command(command: int, data: list[int] | None = None, execution_time: float = _TIME_STANDARD) None¶
Send a Sensirion 16-bit command, with optional CRC-protected data.
Packs the command big-endian into a
bytearray; if a data payload is supplied, each 16-bit word is also packed big-endian and followed by its CRC8 byte (polynomial0x31, init0xFF) before being concatenated to the buffer. The whole buffer is then sent in a singlesmbus2.i2c_msg.writetransfer and the call sleeps for execution_time so the sensor has time to act before the host issues the matching read.- Parameters:
command (
int) – 16-bit Sensirion command ID (the value already encodes the Sensirion command CRC bits; callers pass constants such as_START_MEASUREMENT/0x0021).data (
list[int] | None) – Optional list of 16-bit argument words to append after the command; each word is CRC8-protected on the wire. Defaults toNone(command only).execution_time (
float) – Seconds to sleep after the write transfer so the device can execute the command. Defaults to_TIME_STANDARD(20 ms).
- Returns:
None
- Raises:
OSError – Propagated from
smbus2.SMBus.i2c_rdwrif the I2C transaction fails (NACK, bus error, etc.).
Example
>>> dev._write_command(0x0021)
Note
All SEN6x command IDs are 16-bit big-endian; per-word CRC8 is mandatory for both writes and reads. The polynomial is
0x31(x**8 + x**5 + x**4 + 1) with0xFFinitialisation, matching the Sensirion air- quality sensor convention.
- check_sensor_errors() None¶
Raise if any critical sensor-error flags are currently set.
Reads
device_statusonce and raises aRuntimeErrorif either the fan-error bit is set (which invalidates every channel) or any of the PM, gas, or RH+T error bits are set. Intended to be called immediately before a measurement read so callers can fail fast rather than persisting bogus values.- Parameters:
None
- Returns:
Returns silently when no critical error bits are set.
- Return type:
None
- Raises:
RuntimeError – When the fan-error bit is set (“Fan error detected - measurements unreliable”), or when any of the PM, gas, or RH+T error bits are set (“Sensor errors detected: …”).
OSError – Propagated from the underlying SMBus transfer used by
device_status.
Example
>>> dev.check_sensor_errors()
Note
Internally triggers Sensirion command
0xD206(device_status) viadevice_status; does not clear any flags.
- clear_device_status() None¶
Clear all sticky error and warning flags in the status register.
Sensirion error flags are “sticky” — once set they persist across the underlying condition disappearing, until the host explicitly clears them or the sensor is reset. This call issues the clear command; flags whose underlying condition is still active will be re-asserted on the next status read.
- Parameters:
None
- Returns:
None
- Raises:
OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.clear_device_status()
Note
Sensirion command
0xD210(clear_device_status) with an execution delay of_TIME_STANDARD(20 ms).
- reset() None¶
Reset the SEN6x sensor and wait for it to come back up.
Issues the Sensirion reset command, clears the local
_measurement_startedflag and the cached serial number and product name, then sleeps_SENSOR_STARTUP_TIMEso the sensor has time to reboot before further commands are sent. All on-device configuration values (temperature offset, VOC/NOx tuning, ambient pressure/altitude, CO2 ASC state, etc.) are restored to their factory defaults.- Parameters:
None
- Returns:
None
- Raises:
OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.reset()
Note
Sensirion command
0xD304(reset) with an execution time of_TIME_RESET(1.2 s); a further 1 s startup sleep is applied here, matching the constructor.
- start_fan_cleaning() None¶
Spin the fan up to maximum for 10 s to blow out accumulated dust.
Spins the SEN6x fan up to its maximum speed for ten seconds so any dust that has settled in the PM channel is blown clear. The command is only valid when the sensor is in idle mode; callers are expected to wait at least 10 seconds after invoking this before calling
start_measurement()again.- Parameters:
None
- Returns:
None
- Raises:
RuntimeError – If the sensor is currently in measurement mode (
self._measurement_startedisTrue); callers muststop_measurement()first.OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.stop_measurement() >>> dev.start_fan_cleaning()
Note
Sensirion command
0x5607(start_fan_cleaning) with an execution delay of_TIME_STANDARD(20 ms); the actual cleaning runs for ~10 s on the device side.
- start_measurement() None¶
Switch the sensor into continuous measurement mode.
Sends the Sensirion start measurement command and sets the
_measurement_startedflag so subsequent calls become no-ops; the sensor then continuously updates its internal result registers and raises thedata_readyflag once per measurement cycle.- Parameters:
None
- Returns:
None
- Raises:
OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.start_measurement()
Note
Sensirion command
0x0021(start_measurement) with an execution delay of_TIME_START_MEASUREMENT(50 ms); first valid measurement frame is available about_FIRST_MEASUREMENT_DELAY(1.1 s) later.
- stop_measurement() None¶
Return the sensor to idle mode so configuration can be changed.
Sends the Sensirion stop measurement command and clears the
_measurement_startedflag. Many configuration commands (CO2 FRC/ASC, temperature acceleration, VOC/NOx tuning, altitude, fan cleaning, …) require the device to be in idle mode and must be preceded by this call.- Parameters:
None
- Returns:
None
- Raises:
OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.stop_measurement()
Note
Sensirion command
0x0104(stop_measurement) with an execution time of_TIME_STOP_MEASUREMENT(1 s).
- property data_ready: bool¶
Whether a fresh measurement frame is waiting on the SEN6x.
- Parameters:
None
- Returns:
Truewhen the sensor has produced a new measurement that has not yet been read;Falseif the sensor is in idle mode or the previous frame has already been consumed.- Return type:
- Raises:
RuntimeError – From
_read_data()on a CRC mismatch.OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.start_measurement() >>> dev.data_ready True
Note
Sensirion command
0x0202(data_ready); only the least-significant bit of the returned word carries the ready flag.
- property device_status: DeviceStatus¶
Current 32-bit device status register, parsed into a typed object.
- Parameters:
None
- Returns:
A fresh
DeviceStatuswrapping the two 16-bit words just read from the device, exposing individual fan/PM/gas/RH+T/CO2 flags and theDeviceStatus.errorsandDeviceStatus.warningsaggregates.- Return type:
- Raises:
RuntimeError – From
_read_data()on a CRC mismatch.OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.device_status.errors False
Note
Sensirion command
0xD206(device_status); the two response words are concatenated MSB-first into a 32-bit value before being handed toDeviceStatus.
- property error_status_description: dict[str, str]¶
Human-readable summary of which error flags are set and why it matters.
- Parameters:
None
- Returns:
Mapping from short error tag (
"fan","pm","gas","rht","co2") to a short English sentence explaining what the flag means for the measurement results; entries for flags that are not set are omitted, so an empty dict means the sensor is healthy.- Return type:
- Raises:
RuntimeError – From
_read_data()on a CRC mismatch while reading the status register.OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.error_status_description {}
Note
Backed by Sensirion command
0xD206(device_status); the descriptions exposed here match the wording in the SEN66 datasheet.
- property product_name: str¶
The SEN6x product name string as reported by the device.
- Parameters:
None
- Returns:
ASCII product name (for example
"SEN66") with trailing null bytes stripped. Cached after the first read.- Return type:
- Raises:
RuntimeError – From
_read_data()on a CRC mismatch.OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.product_name 'SEN66'
Note
Sensirion command
0xD014(get_product_name); the response is 32 bytes (16 words) of null-padded ASCII.
- property serial_number: str¶
The SEN6x serial number as an ASCII string (up to 32 characters).
- Parameters:
None
- Returns:
ASCII serial number read from the sensor, with any trailing null bytes stripped. The value is cached after the first successful read so subsequent accesses are free.
- Return type:
- Raises:
RuntimeError – From
_read_data()on a CRC mismatch.OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.serial_number '...'
Note
Sensirion command
0xD033(get_serial_number); the response is 32 bytes (16 words) of null-padded ASCII.
- property sht_heater_measurements: dict[str, float | None]¶
RH+T values produced while the SHT internal heater is active.
Available on firmware >= 4.0 only; intended to monitor the progress of an SHT heater cycle (used to drive moisture out of the RH+T sensor). While heating is still in progress the sensor returns the unknown sentinel value for one or both channels, which is surfaced here as
None.- Parameters:
None
- Returns:
Mapping with keys
"humidity"(relative humidity, percent) and"temperature"(degrees Celsius); either value isNonewhen heating has not yet finished.- Return type:
- Raises:
RuntimeError – From
_read_data()on a CRC mismatch.OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.sht_heater_measurements {'humidity': 35.4, 'temperature': 24.8}
Note
Sensirion command
0x6790(activate_sht_heater); response uses scale factors 100 for humidity and 200 for temperature.
- property version: tuple[int, int]¶
The on-device firmware version as a
(major, minor)tuple.- Parameters:
None
- Returns:
(major_version, minor_version)pair decoded from the high and low bytes of the single 16-bit response word.- Return type:
- Raises:
RuntimeError – From
_read_data()on a CRC mismatch.OSError – Propagated from the underlying SMBus transfer.
Example
>>> dev.version (4, 0)
Note
Sensirion command
0xD100(read_version); the response packs major in the high byte and minor in the low byte of one word.