diff --git a/adafruit_ags02ma.py b/adafruit_ags02ma.py index 8a45b47..25919f3 100644 --- a/adafruit_ags02ma.py +++ b/adafruit_ags02ma.py @@ -27,6 +27,11 @@ """ +try: + from busio import I2C +except ImportError: + pass + import time import struct from micropython import const @@ -44,10 +49,10 @@ _AGS02MA_CRC8_POLYNOMIAL = const(0x31) -def _generate_crc(data): +def _generate_crc(data: int) -> int: """8-bit CRC algorithm for checking data - :param bytearray data: The data to generate a CRC for + :param int data: The data to generate a CRC for """ crc = _AGS02MA_CRC8_INIT @@ -64,13 +69,47 @@ def _generate_crc(data): class AGS02MA: - """Driver for the AGS02MA air quality sensor + """Driver for the AGS02MA air quality sensor. + + .. warning:: + I2C communication rate cannot be higher than 30KHZ. Refer to + https://cdn-shop.adafruit.com/product-files/5593/datasheet+ags02ma.pdf + Section 3. :param ~busio.I2C i2c_bus: The I2C bus the AGS02MA is connected to. :param int address: The I2C device address. Defaults to :const:`0x1A` + + :raises RunTimeError: When the sensor could not be found + + + **Quickstart: Importing and using the device** + + Here is an example of using the :class:`AGS02MA` class. + First you will need to import the libraries to use the sensor + + .. code-block:: python + + import board + import busio + from adafruit_ags02ma import AGS02MA + + Once this is done you can define your `busio.I2C` object and define your sensor object + + .. code-block:: python + + i2c = busio.I2C(board.SCL, board.SDA, frequency=20_000) + ags = AGS02MA(i2c, address=0x1A) + + Now you have access to the :attr:`gas_resistance` and :attr:`TVOC` attributes + + .. code-block:: python + + res = ags.gas_resistance + tvoc = ags.TVOC + """ - def __init__(self, i2c_bus, address=AGS02MA_I2CADDR_DEFAULT): + def __init__(self, i2c_bus: I2C, address: int = AGS02MA_I2CADDR_DEFAULT) -> None: self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) self._buf = bytearray(5) @@ -80,30 +119,40 @@ def __init__(self, i2c_bus, address=AGS02MA_I2CADDR_DEFAULT): except RuntimeError as exc: # a CRC error or something! raise RuntimeError("Failed to find AGS02MA - check your wiring!") from exc - def firmware_version(self): + def firmware_version(self) -> int: """Return 24-bit value which contains the firmware version""" return self._read_reg(_AGS02MA_VERSION_REG, 30) @property - def gas_resistance(self): + def gas_resistance(self) -> int: """The resistance of the MEMS gas sensor""" return self._read_reg(_AGS02MA_GASRES_REG, 1500) * 100 # in 0.1Kohm @property - def TVOC(self): # pylint: disable=invalid-name - """The calculated Total Volatile Organic Compound measurement, in ppb""" + def TVOC(self) -> int: # pylint: disable=invalid-name + """The calculated Total Volatile Organic Compound measurement, in ppb + + :raises RunTimeError: When the sensor still preheating + """ val = self._read_reg(_AGS02MA_TVOCSTAT_REG, 1500) status = val >> 24 - # print(hex(status)) + if status & 0x1: raise RuntimeError("Sensor still preheating") return val & 0xFFFFFF - def set_address(self, new_addr): + def set_address(self, new_addr: int) -> None: """Set the address for the I2C interface, from 0x0 to 0x7F + The sensor supports modifying the I2C address. After sending + this command, the new address will take effect immediately, + New address will remain after powering the sensor off. - :param int new_addr: THe new address + :param int new_addr: The new address + + :raises: ValueError: When selected address is not in the range 0x00-0x7F """ + if new_addr not in range(1, 128): + raise ValueError("Selected address must be between 0x00 and 0x7F") _buf = bytearray( [ @@ -119,11 +168,14 @@ def set_address(self, new_addr): with self.i2c_device as i2c: i2c.write(_buf) - def _read_reg(self, addr, delayms): + def _read_reg(self, addr: int, delayms: int) -> int: """Read a register :param int addr: The address to read :param int delayms: The delay between writes and reads, in milliseconds + + :raises RunTimeError: When CRC check have failed. + """ with self.i2c_device as i2c: @@ -131,9 +183,9 @@ def _read_reg(self, addr, delayms): i2c.write(self._addr) time.sleep(delayms / 1000) i2c.readinto(self._buf) - # print([hex(x) for x in self._buf]) + if _generate_crc(self._buf) != 0: raise RuntimeError("CRC check failed") val, crc = struct.unpack(">IB", self._buf) # pylint: disable=unused-variable - # print(hex(val), hex(crc)) + return val