# SPDX-FileCopyrightText: 2020 Bryan Siepert for Adafruit Industries # # SPDX-License-Identifier: MIT """ `adafruit_hts221` ================================================================================ Helper library for the HTS221 Humidity and Temperature Sensor * Author(s): Bryan Siepert Implementation Notes -------------------- **Hardware:** * `Adafruit HTS221 - Temperature & Humidity Sensor Breakout Board `_ **Software and Dependencies:** * Adafruit CircuitPython firmware for the supported boards: https://circuitpython.org/downloads * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register """ __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_HTS221.git" import adafruit_bus_device.i2c_device as i2cdevice from adafruit_register.i2c_bit import ROBit, RWBit from adafruit_register.i2c_bits import ROBits, RWBits from adafruit_register.i2c_struct import ROUnaryStruct from micropython import const try: from typing import Sequence, Tuple, Union from busio import I2C except ImportError: pass _WHO_AM_I = const(0x0F) _CTRL_REG1 = const(0x20) _CTRL_REG2 = const(0x21) _CTRL_REG3 = const(0x22) _STATUS_REG = const(0x27) # some addresses are anded to set the top bit so that multi-byte reads will work _HUMIDITY_OUT_L = const(0x28 | 0x80) # Humidity output register (LSByte) _TEMP_OUT_L = const(0x2A | 0x80) # Temperature output register (LSByte) _H0_RH_X2 = const(0x30) # Humididy calibration LSB values _H1_RH_X2 = const(0x31) # Humididy calibration LSB values _T0_DEGC_X8 = const(0x32) # First byte of T0, T1 calibration values _T1_DEGC_X8 = const(0x33) # First byte of T0, T1 calibration values _T1_T0_MSB = const(0x35) # Top 2 bits of T0 and T1 (each are 10 bits) _H0_T0_OUT = const(0x36 | 0x80) # Humididy calibration Time 0 value _H1_T1_OUT = const(0x3A | 0x80) # Humididy calibration Time 1 value _T0_OUT = const(0x3C | 0x80) # T0_OUT LSByte _T1_OUT = const(0x3E | 0x80) # T1_OUT LSByte _HTS221_CHIP_ID = 0xBC _HTS221_DEFAULT_ADDRESS = 0x5F class CV: """struct helper""" @classmethod def add_values(cls, value_tuples: Sequence[Tuple[str, int, Union[int, float]]]) -> None: """creates CV entries""" cls.label = {} for value_tuple in value_tuples: name, value, label = value_tuple setattr(cls, name, value) cls.label[value] = label @classmethod def is_valid(cls, value: int) -> bool: """Returns true if the given value is a member of the CV""" return value in cls.label class Rate(CV): """Options for :attr:`data_rate` +-----------------------+------------------------------------------------------------------+ | Rate | Description | +-----------------------+------------------------------------------------------------------+ | ``Rate.ONE_SHOT`` | Setting `data_rate` to ``Rate.ONE_SHOT`` takes a single humidity | | | and temperature measurement | +-----------------------+------------------------------------------------------------------+ | ``Rate.RATE_1_HZ`` | 1 Hz | +-----------------------+------------------------------------------------------------------+ | ``Rate.RATE_7_HZ`` | 7 Hz | +-----------------------+------------------------------------------------------------------+ | ``Rate.RATE_12_5_HZ`` | 12.5 Hz | +-----------------------+------------------------------------------------------------------+ """ pass Rate.add_values( ( ("ONE_SHOT", 0, 0), ("RATE_1_HZ", 1, 1), ("RATE_7_HZ", 2, 7), ("RATE_12_5_HZ", 3, 12.5), ) ) class HTS221: """Library for the ST HTS221 Humidity and Temperature Sensor :param ~busio.I2C i2c_bus: The I2C bus the HTS221 is connected to **Quickstart: Importing and using the HTS221** Here is an example of using the :class:`HTS221` class. First you will need to import the libraries to use the sensor .. code-block:: python import board import adafruit_hts221 Once this is done you can define your `board.I2C` object and define your sensor object .. code-block:: python i2c = board.I2C() # uses board.SCL and board.SDA hts = adafruit_hts221.HTS221(i2c) Now you have access to the :attr:`temperature` and :attr:`relative_humidity` attributes .. code-block:: python temperature = hts.temperature relative_humidity = hts.relative_humidity """ _chip_id = ROUnaryStruct(_WHO_AM_I, " None: self.i2c_device = i2cdevice.I2CDevice(i2c_bus, _HTS221_DEFAULT_ADDRESS) if self._chip_id not in {_HTS221_CHIP_ID}: raise RuntimeError("Failed to find HTS221HB! Found chip ID 0x%x" % self._chip_id) self._boot() self.enabled = True self.data_rate = Rate.RATE_12_5_HZ t1_t0_msbs = self._t1_t0_deg_c_x8_msbits self.calib_temp_value_0 = self._t0_deg_c_x8_lsbyte self.calib_temp_value_0 |= (t1_t0_msbs & 0b0011) << 8 self.calibrated_value_1 = self._t1_deg_c_x8_lsbyte self.calibrated_value_1 |= (t1_t0_msbs & 0b1100) << 6 self.calib_temp_value_0 >>= 3 # divide by 8 to remove x8 self.calibrated_value_1 >>= 3 # divide by 8 to remove x8 self.calib_temp_meas_0 = self._t0_out self.calib_temp_meas_1 = self._t1_out self.calib_hum_value_0 = self._h0_rh_x2 self.calib_hum_value_0 >>= 1 # divide by 2 to remove x2 self.calib_hum_value_1 = self._h1_rh_x2 self.calib_hum_value_1 >>= 1 # divide by 2 to remove x2 self.calib_hum_meas_0 = self._h0_t0_out self.calib_hum_meas_1 = self._h1_t0_out # This is the closest thing to a software reset. It re-loads the calibration values from flash def _boot(self) -> None: self._boot_bit = True # wait for the reset to finish while self._boot_bit: pass @property def relative_humidity(self) -> float: """The current relative humidity measurement in %rH""" calibrated_value_delta = self.calib_hum_value_1 - self.calib_hum_value_0 calibrated_measurement_delta = self.calib_hum_meas_1 - self.calib_hum_meas_0 calibration_value_offset = self.calib_hum_value_0 calibrated_measurement_offset = self.calib_hum_meas_0 zeroed_measured_humidity = self._raw_humidity - calibrated_measurement_offset correction_factor = calibrated_value_delta / calibrated_measurement_delta adjusted_humidity = zeroed_measured_humidity * correction_factor + calibration_value_offset return adjusted_humidity @property def temperature(self) -> float: """The current temperature measurement in degrees Celsius""" calibrated_value_delta = self.calibrated_value_1 - self.calib_temp_value_0 calibrated_measurement_delta = self.calib_temp_meas_1 - self.calib_temp_meas_0 calibration_value_offset = self.calib_temp_value_0 calibrated_measurement_offset = self.calib_temp_meas_0 zeroed_measured_temp = self._raw_temperature - calibrated_measurement_offset correction_factor = calibrated_value_delta / calibrated_measurement_delta adjusted_temp = (zeroed_measured_temp * correction_factor) + calibration_value_offset return adjusted_temp @property def data_rate(self) -> int: """The rate at which the sensor measures :attr:`relative_humidity` and :attr:`temperature`. :attr:`data_rate` should be set to one of the values of :class:`adafruit_hts221.Rate`. Note that setting :attr:`data_rate` to ``Rate.ONE_SHOT`` will cause :attr:`relative_humidity` and :attr:`temperature` measurements to only update when :meth:`take_measurements` is called.""" return self._data_rate @data_rate.setter def data_rate(self, value: int) -> None: if not Rate.is_valid(value): raise AttributeError("data_rate must be a `Rate`") self._data_rate = value @property def humidity_data_ready(self) -> bool: """Returns true if a new relative humidity measurement is available to be read""" return self._humidity_status_bit @property def temperature_data_ready(self) -> bool: """Returns true if a new temperature measurement is available to be read""" return self._temperature_status_bit def take_measurements(self) -> None: """Update the value of :attr:`relative_humidity` and :attr:`temperature` by taking a single measurement. Only meaningful if :attr:`data_rate` is set to ``ONE_SHOT``""" self._one_shot_bit = True while self._one_shot_bit: pass